Compare commits
7 commits
7bc0ad21ba
...
e2a14821ba
Author | SHA1 | Date | |
---|---|---|---|
e2a14821ba | |||
41889d9160 | |||
d396c2a8c3 | |||
f9f1de8987 | |||
090da8c679 | |||
082f50c66b | |||
d66019f53c |
11 changed files with 134 additions and 70 deletions
4
Ideen.md
4
Ideen.md
|
@ -1,7 +1,5 @@
|
|||
# MUSS
|
||||
|
||||
- api: Config-Liste von Extra-Türchen (kein Buchstabe, nur manuelles Bild)
|
||||
- api: Config-Option "Überspringe leere Türchen" (standard ja)
|
||||
|
||||
# KANN
|
||||
|
||||
|
@ -21,3 +19,5 @@
|
|||
- `alert` durch bulma Komponente(n) ersetzen
|
||||
- api: admin Login case sensitivity (username "admin" == "AdMiN")
|
||||
- api: `config.solution` - whitespace="IGNORE"->"REMOVE" umbenennen, +Sonderzeichen
|
||||
- api: Config-Option "Überspringe leere Türchen" (standard ja)
|
||||
- api: Config-Liste von Extra-Türchen (kein Buchstabe, nur manuelles Bild)
|
||||
|
|
|
@ -43,6 +43,12 @@ class Puzzle(BaseModel):
|
|||
# Kalender so viele Tage nach der letzten Türöffnung schließen
|
||||
close_after: int = 90
|
||||
|
||||
# Tage, für die kein Buchstabe vorgesehen wird
|
||||
extra_days: set[int] = set()
|
||||
|
||||
# Türchen ohne Buchstabe überspringen
|
||||
skip_empty: bool = True
|
||||
|
||||
|
||||
class Image(BaseModel):
|
||||
# Quadrat, Seitenlänge in px
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import re
|
||||
from collections import defaultdict
|
||||
from dataclasses import dataclass
|
||||
from datetime import date
|
||||
from io import BytesIO
|
||||
|
@ -34,23 +33,6 @@ async def get_all_sorted_days(
|
|||
return sorted(set(door.day for door in cal_cfg.doors))
|
||||
|
||||
|
||||
async def get_all_event_dates(
|
||||
cfg: Config = Depends(get_config),
|
||||
days: list[int] = Depends(get_all_sorted_days),
|
||||
) -> EventDates:
|
||||
"""
|
||||
Aktueller Kalender-Zeitraum
|
||||
"""
|
||||
|
||||
return EventDates(
|
||||
today=date.today(),
|
||||
begin_month=cfg.puzzle.begin_month,
|
||||
begin_day=cfg.puzzle.begin_day,
|
||||
events=days,
|
||||
close_after=cfg.puzzle.close_after,
|
||||
)
|
||||
|
||||
|
||||
async def get_all_parts(
|
||||
cfg: Config = Depends(get_config),
|
||||
days: list[int] = Depends(get_all_sorted_days),
|
||||
|
@ -59,6 +41,11 @@ async def get_all_parts(
|
|||
Lösung auf vorhandene Tage aufteilen
|
||||
"""
|
||||
|
||||
# noch keine Buchstaben verteilt
|
||||
result = {day: "" for day in days}
|
||||
# extra-Tage ausfiltern
|
||||
days = [day for day in days if day not in cfg.puzzle.extra_days]
|
||||
|
||||
solution_length = len(cfg.solution.clean)
|
||||
num_days = len(days)
|
||||
|
||||
|
@ -72,15 +59,33 @@ async def get_all_parts(
|
|||
*rnd.sample(days, solution_length % num_days),
|
||||
]
|
||||
|
||||
result: defaultdict[int, str] = defaultdict(str)
|
||||
for day, letter in zip(solution_days, cfg.solution.clean):
|
||||
result[day] += letter
|
||||
|
||||
result |= {missed_day: "" for missed_day in set(days) - set(result.keys())}
|
||||
|
||||
return result
|
||||
|
||||
|
||||
async def get_all_event_dates(
|
||||
cfg: Config = Depends(get_config),
|
||||
days: list[int] = Depends(get_all_sorted_days),
|
||||
parts: dict[int, str] = Depends(get_all_parts),
|
||||
) -> EventDates:
|
||||
"""
|
||||
Aktueller Kalender-Zeitraum
|
||||
"""
|
||||
|
||||
if cfg.puzzle.skip_empty:
|
||||
days = [day for day in days if parts[day] != "" or day in cfg.puzzle.extra_days]
|
||||
|
||||
return EventDates(
|
||||
today=date.today(),
|
||||
begin_month=cfg.puzzle.begin_month,
|
||||
begin_day=cfg.puzzle.begin_day,
|
||||
events=days,
|
||||
close_after=cfg.puzzle.close_after,
|
||||
)
|
||||
|
||||
|
||||
async def get_all_auto_image_names(
|
||||
days: list[int] = Depends(get_all_sorted_days),
|
||||
images: list[str] = Depends(list_images_auto),
|
||||
|
|
|
@ -46,7 +46,7 @@ async def user_visible_days(
|
|||
event_dates: EventDates = Depends(get_all_event_dates),
|
||||
) -> list[int]:
|
||||
"""
|
||||
Anzahl der user-sichtbaren Türchen
|
||||
User-sichtbare Türchen
|
||||
"""
|
||||
|
||||
today = date.today()
|
||||
|
|
|
@ -41,6 +41,8 @@ class AdminConfigModel(BaseModel):
|
|||
last: date
|
||||
end: date
|
||||
seed: str
|
||||
extra_days: list[int]
|
||||
skip_empty: bool
|
||||
|
||||
class __Calendar(BaseModel):
|
||||
config_file: str
|
||||
|
@ -92,6 +94,8 @@ async def get_config_model(
|
|||
"last": event_dates.last,
|
||||
"end": event_dates.end,
|
||||
"seed": cfg.random_seed,
|
||||
"extra_days": sorted(cfg.puzzle.extra_days),
|
||||
"skip_empty": cfg.puzzle.skip_empty,
|
||||
},
|
||||
"calendar": {
|
||||
"config_file": cfg.calendar,
|
||||
|
|
|
@ -89,6 +89,7 @@ export default class extends Vue {
|
|||
this.store.when_initialized(() => {
|
||||
this.toast_timeout = setTimeout(() => {
|
||||
if (this.store.user_doors.length === 0) return;
|
||||
if (this.store.is_touch_device) return;
|
||||
|
||||
this.toast!.show({ duration: 600000, type: "is-warning" });
|
||||
}, 10e3);
|
||||
|
|
|
@ -68,6 +68,23 @@
|
|||
<dd class="is-family-monospace">
|
||||
"{{ admin_config_model.puzzle.seed }}"
|
||||
</dd>
|
||||
|
||||
<dt>Extra-Tage</dt>
|
||||
<dd>
|
||||
<template
|
||||
v-for="(day, index) in admin_config_model.puzzle.extra_days"
|
||||
:key="`extra_day-${index}`"
|
||||
>
|
||||
<span>
|
||||
<template v-if="index > 0">, </template>
|
||||
{{ day }}
|
||||
</span>
|
||||
</template>
|
||||
</dd>
|
||||
|
||||
<dt>Leere Türchen</dt>
|
||||
<dd v-if="admin_config_model.puzzle.skip_empty">Überspringen</dd>
|
||||
<dd v-else>Anzeigen</dd>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -200,6 +217,8 @@ export default class extends Vue {
|
|||
last: "2023-12-24",
|
||||
end: "2024-04-01",
|
||||
seed: "",
|
||||
extra_days: [],
|
||||
skip_empty: true,
|
||||
},
|
||||
calendar: {
|
||||
config_file: "lorem ipsum",
|
||||
|
@ -229,17 +248,20 @@ export default class extends Vue {
|
|||
|
||||
public fmt_puzzle_date(name: keyof AdminConfigModel["puzzle"]): string {
|
||||
const iso_date = this.admin_config_model.puzzle[name];
|
||||
if (iso_date === null) return "-";
|
||||
if (!(typeof iso_date == "string")) return "-";
|
||||
|
||||
return DateTime.fromISO(iso_date).toLocaleString(DateTime.DATE_SHORT);
|
||||
}
|
||||
|
||||
public on_open(ready: () => void, fail: () => void): void {
|
||||
Promise.all([
|
||||
this.store.update(),
|
||||
this.$advent22.api_get<AdminConfigModel>("admin/config_model"),
|
||||
this.$advent22.api_get<DoorSaved[]>("admin/doors"),
|
||||
])
|
||||
.then(([admin_config_model, doors]) => {
|
||||
.then(([store_update, admin_config_model, doors]) => {
|
||||
store_update; // discard value
|
||||
|
||||
this.admin_config_model = admin_config_model;
|
||||
this.doors = doors;
|
||||
|
||||
|
|
|
@ -4,7 +4,12 @@
|
|||
:visible="store.is_touch_device || force_visible"
|
||||
:rectangle="door.position"
|
||||
>
|
||||
<div class="has-text-danger">{{ door.day }}</div>
|
||||
<div
|
||||
class="has-text-danger"
|
||||
style="text-shadow: 0 0 10px white, 0 0 20px white"
|
||||
>
|
||||
{{ door.day }}
|
||||
</div>
|
||||
</SVGRect>
|
||||
</template>
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
>
|
||||
<div
|
||||
xmlns="http://www.w3.org/1999/xhtml"
|
||||
:class="`px-4 is-flex is-align-items-center is-justify-content-center is-size-1 has-text-weight-bold ${extra_classes}`"
|
||||
:class="`px-2 is-flex is-align-items-center is-justify-content-center is-size-2 has-text-weight-bold ${extra_classes}`"
|
||||
style="height: inherit"
|
||||
:title="title"
|
||||
>
|
||||
|
@ -72,7 +72,7 @@ foreignObject > div {
|
|||
|
||||
&.visible,
|
||||
&:hover {
|
||||
border-width: 3px;
|
||||
border-width: 2px;
|
||||
border-style: solid;
|
||||
|
||||
@each $name, $color in $advent22-colors {
|
||||
|
|
|
@ -12,6 +12,8 @@ export interface AdminConfigModel {
|
|||
last: string;
|
||||
end: string;
|
||||
seed: string;
|
||||
extra_days: number[];
|
||||
skip_empty: boolean;
|
||||
};
|
||||
calendar: {
|
||||
config_file: string;
|
||||
|
|
|
@ -60,53 +60,72 @@ export const advent22Store = defineStore({
|
|||
|
||||
actions: {
|
||||
init(): void {
|
||||
this.update_is_admin();
|
||||
|
||||
this.advent22
|
||||
.api_get_blob("user/favicon")
|
||||
.then((favicon_src) => {
|
||||
const link: HTMLLinkElement =
|
||||
document.querySelector("link[rel*='icon']") ||
|
||||
document.createElement("link");
|
||||
link.rel = "shortcut icon";
|
||||
link.type = "image/x-icon";
|
||||
link.href = favicon_src;
|
||||
|
||||
if (link.parentElement === null)
|
||||
document.getElementsByTagName("head")[0].appendChild(link);
|
||||
})
|
||||
.catch(() => {});
|
||||
|
||||
Promise.all([
|
||||
this.advent22.api_get<SiteConfigModel>("user/site_config"),
|
||||
this.advent22.api_get_blob("user/background_image"),
|
||||
this.advent22.api_get<DoorSaved[]>("user/doors"),
|
||||
this.advent22.api_get<number | null>("user/next_door"),
|
||||
])
|
||||
.then(([site_config, background_image, user_doors, next_door]) => {
|
||||
document.title = site_config.title;
|
||||
|
||||
if (site_config.subtitle !== "")
|
||||
document.title += " – " + site_config.subtitle;
|
||||
|
||||
this.site_config = site_config;
|
||||
|
||||
this.calendar_background_image = background_image;
|
||||
|
||||
this.user_doors.length = 0;
|
||||
for (const door_saved of user_doors) {
|
||||
this.user_doors.push(Door.load(door_saved));
|
||||
}
|
||||
|
||||
if (next_door !== null)
|
||||
this.next_door_target = Date.now() + next_door;
|
||||
|
||||
this.update()
|
||||
.then(() => {
|
||||
this.is_initialized = true;
|
||||
for (const callback of this.on_initialized) callback();
|
||||
})
|
||||
.catch(this.advent22.alert_user_error);
|
||||
},
|
||||
|
||||
update(): Promise<void> {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.advent22
|
||||
.api_get_blob("user/favicon")
|
||||
.then((favicon_src) => {
|
||||
const link: HTMLLinkElement =
|
||||
document.querySelector("link[rel*='icon']") ||
|
||||
document.createElement("link");
|
||||
link.rel = "shortcut icon";
|
||||
link.type = "image/x-icon";
|
||||
link.href = favicon_src;
|
||||
|
||||
if (link.parentElement === null)
|
||||
document.getElementsByTagName("head")[0].appendChild(link);
|
||||
})
|
||||
.catch(() => {});
|
||||
|
||||
Promise.all([
|
||||
this.update_is_admin(),
|
||||
this.advent22.api_get<SiteConfigModel>("user/site_config"),
|
||||
this.advent22.api_get_blob("user/background_image"),
|
||||
this.advent22.api_get<DoorSaved[]>("user/doors"),
|
||||
this.advent22.api_get<number | null>("user/next_door"),
|
||||
])
|
||||
.then(
|
||||
([
|
||||
is_admin,
|
||||
site_config,
|
||||
background_image,
|
||||
user_doors,
|
||||
next_door,
|
||||
]) => {
|
||||
is_admin; // discard value
|
||||
|
||||
document.title = site_config.title;
|
||||
|
||||
if (site_config.subtitle !== "")
|
||||
document.title += " – " + site_config.subtitle;
|
||||
|
||||
this.site_config = site_config;
|
||||
|
||||
this.calendar_background_image = background_image;
|
||||
|
||||
this.user_doors.length = 0;
|
||||
for (const door_saved of user_doors) {
|
||||
this.user_doors.push(Door.load(door_saved));
|
||||
}
|
||||
|
||||
if (next_door !== null)
|
||||
this.next_door_target = Date.now() + next_door;
|
||||
|
||||
resolve();
|
||||
},
|
||||
)
|
||||
.catch(reject);
|
||||
});
|
||||
},
|
||||
|
||||
when_initialized(callback: () => void): void {
|
||||
if (this.is_initialized) {
|
||||
callback();
|
||||
|
|
Loading…
Reference in a new issue