This commit is contained in:
Fenris Wolf 2024-09-12 20:42:06 +02:00
parent 78014d6a3a
commit 7c7bb3f66a
7 changed files with 114 additions and 802 deletions

View file

@ -13,17 +13,17 @@ namespace _zeitbild.api
Array<
{
id : _zeitbild.type.calendar_id;
preview : {
name : string;
};
name : string;
public : boolean;
role : (null | _zeitbild.type.role);
}
>
>(
rest_subject,
lib_plankton.http.enum_method.get,
"/calendar/list",
"/calendars",
{
"description": "listet alle Kalender auf",
"description": "listet alle verfügbaren Kalender auf",
"query_parameters": [
],
"output_schema": () => ({
@ -37,29 +37,30 @@ namespace _zeitbild.api
"type": "number",
"nullable": false,
},
"preview": {
"type": "object",
"additionalProperties": false,
"properties": {
"name": {
"type": "string",
"nullable": false,
},
},
"required": [
"name",
]
}
"name": {
"type": "string",
"nullable": false,
},
"public": {
"type": "boolean",
"nullable": false,
},
"role": {
"type": "string",
"nullable": true,
},
},
"required": [
"id",
"preview",
"name",
"public",
"role",
],
}
}),
"restriction": restriction_none, // TODO
"execution": () => (
_zeitbild.service.calendar.list(null)
_zeitbild.service.calendar.overview(2) // TODO: user_id
.then(
data => Promise.resolve({
"status_code": 200,

View file

@ -21,44 +21,33 @@ namespace _zeitbild.api
Array<
{
calendar_id : int;
calendar_name : string;
event : _zeitbild.type.event_object;
}
>
>(
rest_subject,
lib_plankton.http.enum_method.post,
lib_plankton.http.enum_method.get,
"/events",
{
"description": "stellt Veranstaltungen aus verschiedenen Kalendern zusammen",
"query_parameters": [
],
"input_schema": () => ({
"type": "object",
"nullable": false,
"additionalProperties": false,
"properties": {
"from": {
"type": "number",
"nullable": false,
},
"to": {
"type": "number",
"nullable": false,
},
"calendar_id": {
"type": "array",
"nullable": false,
"items": {
"type": "number",
"nullable": false
}
}
{
"name": "from",
"required": true,
"description": "UNIX timestamp",
},
"required": [
"from",
"to",
]
}),
{
"name": "to",
"required": true,
"description": "UNIX timestamp",
},
{
"name": "calendar_ids",
"required": false,
"description": "comma separated",
},
],
"output_schema": () => ({
"type": "array",
"items": {
@ -105,50 +94,59 @@ namespace _zeitbild.api
}
},
"required": [
"calendar_id",
"calendar_ids",
"calendar_name",
"event",
],
}
}),
"restriction": restriction_none, // TODO
"execution": (stuff) => {
if (stuff.input === null) {
return Promise.resolve({
"status_code": 400,
"data": null,
});
}
else {
return (
(
(stuff.input.calendar_ids !== null)
?
Promise.resolve(stuff.input.calendar_ids)
:
(
_zeitbild.service.calendar.overview(0) // TODO: user_id
.then(
(x : any) => x.map((y : any) => y.id)
)
)
"execution": async (stuff) => {
const from : _zeitbild.helpers.type_pit = parseInt(stuff.query_parameters["from"]);
const to : _zeitbild.helpers.type_pit = parseInt(stuff.query_parameters["to"]);
const calendar_ids_wanted : Array<_zeitbild.type.calendar_id> = (
(
("calendar_ids" in stuff.query_parameters)
&&
(stuff.query_parameters["calendar_ids"] !== null)
)
?
lib_plankton.call.convey(
stuff.query_parameters["calendar_ids"],
[
(x : string) => x.split(","),
(x : Array<string>) => x.map(parseInt),
(x : Array<int>) => x.filter(y => (! isNaN(y)))
]
)
:
null
);
const calendar_ids_allowed : Array<_zeitbild.type.calendar_id> = (
(await _zeitbild.service.calendar.overview(0)) // TODO: user_id
.map((x : any) => x.id)
);
const calendar_ids : Array<_zeitbild.type.calendar_id> = (
(calendar_ids_wanted === null)
?
calendar_ids_allowed
:
(
calendar_ids_wanted
.filter(
(calendar_id) => calendar_ids_allowed.includes(calendar_id)
)
.then(
(calendar_ids : Array<_zeitbild.type.calendar_id>) => _zeitbild.service.calendar.gather_events(
calendar_ids,
// @ts-ignore
stuff.input.from,
// @ts-ignore
stuff.input.to
)
)
.then(
(data : any) => Promise.resolve({
"status_code": 200,
"data": data,
})
)
);
}
)
);
const data = await _zeitbild.service.calendar.gather_events(
calendar_ids,
from,
to
);
return Promise.resolve({
"status_code": 200,
"data": data,
});
}
}
);

View file

@ -1,6 +0,0 @@
/**
*/
namespace _zeitbild.frontend.resources.backend
{
}

View file

@ -8,7 +8,7 @@ async function main(
// init1
lib_plankton.log.conf_push(
[
lib_plankton.log.channel_make({"kind": "stdout", "data": {"threshold": "debug"}}),
lib_plankton.log.channel_make({"kind": "stdout", "data": {"threshold": "info"}}),
]
);

View file

@ -277,6 +277,7 @@ namespace _zeitbild.repository.calendar
/**
* @todo sort for role, public and id
*/
export function overview(
user_id : _zeitbild.type.user_id
@ -306,12 +307,21 @@ namespace _zeitbild.repository.calendar
(row) => ({
"id": row["id"],
"name": row["name"],
"public": row["public"],
"public": (row["public"] === 1),
"role": row["role"],
})
)
)
)
/*
.then(
(entries) => Promise.resolve(
entries.toSorted(
(x, y) => (x
)
)
)
*/
)
}
}

View file

@ -179,16 +179,16 @@ namespace _zeitbild.service.calendar
/**
* @todo user id
*/
export async function gather_events(
calendar_ids : Array<_zeitbild.type.calendar_id>,
from_pit : _zeitbild.helpers.type_pit,
to_pit : _zeitbild.helpers.type_pit,
to_pit : _zeitbild.helpers.type_pit
) : Promise<
Array<
{
calendar_id : _zeitbild.type.calendar_id;
calendar_name : string;
event : _zeitbild.type.event_object;
}
>
@ -198,21 +198,26 @@ namespace _zeitbild.service.calendar
Promise.all(
calendar_ids
.map(
(calendar_id) => get_events(
calendar_id,
from_pit,
to_pit
)
.then(
(events) => Promise.resolve(
events.map(
async (calendar_id) => {
const calendar_object : _zeitbild.type.calendar_object = await _zeitbild.repository.calendar.read(
calendar_id
);
const events : Array<_zeitbild.type.event_object> = await get_events(
calendar_id,
from_pit,
to_pit
);
return Promise.resolve(
events
.map(
(event) => ({
"calendar_id": calendar_id,
"calendar_name": calendar_object.name,
"event": event,
})
)
)
)
);
}
)
)
.then(

View file

@ -1,696 +0,0 @@
/**
*/
namespace _zeitbild.frontend.view
{
/**
*/
function event_generate_tooltip(
calendar_name : string,
event : type_event
) : string
{
return (
lib_plankton.string.coin(
"[{{calendar_name}}] {{event_name}}\n",
{
"calendar_name": calendar_name,
"event_name": event.name,
}
)
+
"--\n"
+
(
(event.begin.time !== null)
?
lib_plankton.string.coin(
"{{label}}: {{value}}\n",
{
"label": "Anfang", // TODO: translate
"value": lib_plankton.string.coin(
"{{hour}}:{{minute}}",
{
"hour": event.begin.time.hour.toFixed(0).padStart(2, "0"),
"minute": event.begin.time.minute.toFixed(0).padStart(2, "0"),
}
), // TODO: outsource
}
)
:
""
)
+
(
(event.end !== null)
?
lib_plankton.string.coin(
"{{label}}: {{value}}\n",
{
"label": "Ende", // TODO: translate
"value": (
[
(
(
(event.end.date.year !== event.begin.date.year)
||
(event.end.date.month !== event.begin.date.month)
||
(event.end.date.day !== event.begin.date.day)
)
?
lib_plankton.string.coin(
"{{year}}-{{month}}-{{day}}",
{
"year": event.end.date.year.toFixed(0).padStart(4, "0"),
"month": event.end.date.month.toFixed(0).padStart(2, "0"),
"day": event.end.date.day.toFixed(0).padStart(2, "0"),
}
)
:
null
),
(
(event.end.time !== null)
?
lib_plankton.string.coin(
"{{hour}}:{{minute}}",
{
"hour": event.end.time.hour.toFixed(0).padStart(2, "0"),
"minute": event.end.time.minute.toFixed(0).padStart(2, "0"),
}
)
:
null
),
]
.filter(x => (x !== null))
.join(",")
),
}
)
:
""
)
+
(
(event.location !== null)
?
(
lib_plankton.string.coin(
"{{label}}: {{value}}\n",
{
"label": "Ort", // TODO
"value": event.location,
}
)
)
:
""
)
+
(
(event.description !== null)
?
(
"--\n"
+
lib_plankton.string.coin(
"{{description}}\n",
{
"description": event.description,
}
)
)
:
""
)
);
}
/**
* @todo kein "while"
*/
async function calendar_view_table_data(
calendar_ids : Array<type_calendar_id>,
options : {
from ?: {
year : int;
week : int;
},
to ?: {
year : int;
week : int;
},
timezone_shift ?: int;
} = {}
) : Promise<
{
sources : lib_plankton.structures.type_hashmap<
type_calendar_id,
{
name : string;
}
>;
rows : Array<
{
week : int;
data : Array<
{
pit : _zeitbild.frontend.helpers.type_pit;
entries : Array<
{
calendar_id : type_calendar_id;
event : type_event;
}
>;
today : boolean;
}
>;
}
>
}
>
{
const now_pit : _zeitbild.frontend.helpers.type_pit = _zeitbild.frontend.helpers.pit_now();
options = Object.assign(
{
"from": lib_plankton.call.convey(
now_pit,
[
(x : _zeitbild.frontend.helpers.type_pit) => _zeitbild.frontend.helpers.pit_shift_week(x, -1),
_zeitbild.frontend.helpers.pit_to_date_object,
(x : Date) => ({
"year": x.getFullYear(),
"week": _zeitbild.frontend.helpers.date_object_get_week_of_year(x),
})
]
),
"to": lib_plankton.call.convey(
now_pit,
[
(x : _zeitbild.frontend.helpers.type_pit) => _zeitbild.frontend.helpers.pit_shift_week(x, +4),
_zeitbild.frontend.helpers.pit_to_date_object,
(x : Date) => ({
"year": x.getFullYear(),
"week": _zeitbild.frontend.helpers.date_object_get_week_of_year(x),
})
]
),
"timezone_shift": 0,
},
options
);
/*
const calendar_object : type_calendar_object = calendar_read(
data,
calendar_id
);
*/
const from_pit : _zeitbild.frontend.helpers.type_pit = _zeitbild.frontend.helpers.pit_from_year_and_week(
(options.from as {year : int; week : int}).year,
(options.from as {year : int; week : int}).week,
{
"timezone_shift": (options.timezone_shift as int),
}
);
const to_pit : _zeitbild.frontend.helpers.type_pit = _zeitbild.frontend.helpers.pit_from_year_and_week(
(options.to as {year : int; week : int}).year,
(options.to as {year : int; week : int}).week,
{
"timezone_shift": (options.timezone_shift as int),
}
);
// prepare
const entries : Array<
{
calendar_id : type_calendar_id;
calendar_name : string;
event : type_event;
}
> = await _zeitbild.frontend.resources.backend.calendar_gather_events(
calendar_ids,
from_pit,
to_pit
);
let result : {
sources : lib_plankton.structures.type_hashmap<
type_calendar_id,
{
name : string;
}
>;
rows : Array<
{
week : int;
data : Array<
{
pit : _zeitbild.frontend.helpers.type_pit;
entries : Array<
{
calendar_id : type_calendar_id;
event : type_event;
}
>;
today : boolean;
}
>;
}
>;
} = {
"sources": lib_plankton.structures.hashmap_construct(
x => x.toFixed(0),
(
entries
.map(
(entry) => (
{
"key": entry.calendar_id,
"value": {
"name": entry.calendar_name,
}
}
)
)
)
),
"rows": [],
};
let row : Array<
{
pit : _zeitbild.frontend.helpers.type_pit;
entries : Array<
{
calendar_id : type_calendar_id;
event : type_event;
}
>;
today : boolean;
}
> = [];
let day : int = 0;
while (true) {
const pit_current : _zeitbild.frontend.helpers.type_pit = _zeitbild.frontend.helpers.pit_shift_day(
from_pit,
day
);
if (
_zeitbild.frontend.helpers.pit_is_before(
pit_current,
to_pit
)
) {
day += 1;
row.push(
{
"pit": pit_current,
"entries": [],
"today": false, // TODO
}
);
if (day % 7 === 0) {
result.rows.push(
{
"week": (
(options.from as {year : int; week : int}).week
+
Math.floor(day / 7)
-
1 // TODO
),
"data": row
}
);
row = [];
}
else {
// do nothing
}
}
else {
break;
}
}
// fill
{
// events
(
entries
.forEach(
(entry) => {
const distance_seconds : int = (
_zeitbild.frontend.helpers.pit_from_datetime(entry.event.begin)
-
from_pit
);
const distance_days : int = (distance_seconds / (60 * 60 * 24));
const week : int = Math.floor(Math.floor(distance_days) / 7);
const day : int = (Math.floor(distance_days) % 7);
if ((week >= 0) && (week < result.rows.length)) {
result.rows[week].data[day].entries.push(entry);
}
else {
// do nothing
}
}
)
);
// today
{
const distance_seconds : int = (
now_pit
-
from_pit
);
const distance_days : int = (distance_seconds / (60 * 60 * 24));
const week : int = Math.floor(Math.floor(distance_days) / 7);
const day : int = (Math.floor(distance_days) % 7);
if ((week >= 0) && (week < result.rows.length)) {
result.rows[week].data[day].today = true;
}
else {
// do nothing
}
}
}
return Promise.resolve(result);
}
/**
*/
export async function calendar_view_table_html(
calendar_ids : Array<type_calendar_id>,
options : {
from ?: {
year : int;
week : int;
};
to ?: {
year : int;
week : int;
};
timezone_shift ?: int;
} = {}
) : Promise<string>
{
const stuff : {
sources : lib_plankton.structures.type_hashmap<
type_calendar_id,
{
name : string;
}
>;
rows : Array<
{
week : int;
data : Array<
{
pit : _zeitbild.frontend.helpers.type_pit;
entries : Array<
{
calendar_id : type_calendar_id;
event : type_event;
}
>;
today : boolean;
}
>;
}
>;
} = await calendar_view_table_data(
calendar_ids,
options
);
const sources : lib_plankton.structures.type_hashmap<
type_calendar_id,
{
name : string;
color : lib_plankton.color.type_color;
}
> = lib_plankton.structures.hashmap_construct(
(x => x.toFixed(0)),
lib_plankton.structures.hashmap_dump(
stuff.sources
)
.map(
(pair) => ({
"key": pair.key,
"value": {
"name": pair.value.name,
"color": lib_plankton.color.give_generic(
(pair.key - 1),
{
"saturation": 0.375,
"value": 0.375,
}
),
}
})
)
);
return _zeitbild.frontend.helpers.template_coin(
"tableview",
{
"sources": (
await _zeitbild.frontend.helpers.promise_row<string>(
lib_plankton.structures.hashmap_dump(sources)
.map(
({"key": calendar_id, "value": data}) => async () => _zeitbild.frontend.helpers.template_coin(
"tableview-sources-entry",
{
"name": data.name,
"color": lib_plankton.color.output_hex(data.color),
"rel": calendar_id.toFixed(0),
}
)
)
)
).join(""),
"rows": (
await _zeitbild.frontend.helpers.promise_row<string>(
stuff.rows
.map(
(row) => async () => _zeitbild.frontend.helpers.template_coin(
"tableview-row",
{
"week": row.week.toFixed(0).padStart(2, "0"),
"cells": (
await _zeitbild.frontend.helpers.promise_row<string>(
row.data
.map(
(cell) => async () => _zeitbild.frontend.helpers.template_coin(
"tableview-cell",
{
"extra_classes": (
[""]
.concat(cell.today ? ["calendar-cell-today"] : [])
.join(" ")
),
"title": lib_plankton.call.convey(
cell.pit,
[
_zeitbild.frontend.helpers.pit_to_datetime,
(x : _zeitbild.frontend.helpers.type_datetime) => lib_plankton.string.coin(
"{{year}}-{{month}}-{{day}}",
{
"year": x.date.year.toFixed(0).padStart(4, "0"),
"month": x.date.month.toFixed(0).padStart(2, "0"),
"day": x.date.day.toFixed(0).padStart(2, "0"),
}
),
]
),
"day": lib_plankton.call.convey(
cell.pit,
[
_zeitbild.frontend.helpers.pit_to_datetime,
(x : _zeitbild.frontend.helpers.type_datetime) => lib_plankton.string.coin(
"{{day}}",
{
"year": x.date.year.toFixed(0).padStart(4, "0"),
"month": x.date.month.toFixed(0).padStart(2, "0"),
"day": x.date.day.toFixed(0).padStart(2, "0"),
}
),
]
),
"entries": (
await _zeitbild.frontend.helpers.promise_row<string>(
cell.entries
.map(
(entry) => () => _zeitbild.frontend.helpers.template_coin(
"tableview-cell-entry",
{
"color": lib_plankton.color.output_hex(
lib_plankton.structures.hashmap_get(
sources,
entry.calendar_id
).color
),
"title": event_generate_tooltip(
lib_plankton.structures.hashmap_get(
sources,
entry.calendar_id
).name,
entry.event
),
"name": entry.event.name,
}
)
)
)
).join(""),
}
)
)
)
).join(""),
}
)
)
)
).join(""),
}
);
}
/**
*/
async function calendar_view_list_data(
calendar_ids : Array<type_calendar_id>,
options : {
from ?: _zeitbild.frontend.helpers.type_pit;
to ?: _zeitbild.frontend.helpers.type_pit;
timezone_shift ?: int;
} = {}
) : Promise<
Array<
{
calendar_id : type_calendar_id;
event : type_event;
}
>
>
{
const now_pit : _zeitbild.frontend.helpers.type_pit = _zeitbild.frontend.helpers.pit_now();
options = Object.assign(
{
"from": lib_plankton.call.convey(
now_pit,
[
(x : _zeitbild.frontend.helpers.type_pit) => _zeitbild.frontend.helpers.pit_shift_day(x, -1),
]
),
"to": lib_plankton.call.convey(
now_pit,
[
(x : _zeitbild.frontend.helpers.type_pit) => _zeitbild.frontend.helpers.pit_shift_week(x, +4),
]
),
"timezone_shift": 0,
},
options
);
const entries : Array<
{
calendar_id : type_calendar_id;
event : type_event;
}
> = await _zeitbild.frontend.resources.backend.calendar_gather_events(
calendar_ids,
(options.from as _zeitbild.frontend.helpers.type_pit),
(options.to as _zeitbild.frontend.helpers.type_pit)
);
// TODO: optimize
entries.sort(
(entry_1, entry_2) => (
_zeitbild.frontend.helpers.pit_from_datetime(entry_1.event.begin)
-
_zeitbild.frontend.helpers.pit_from_datetime(entry_2.event.begin)
)
);
return Promise.resolve(entries);
}
/**
*/
export async function calendar_view_list_html(
calendar_ids : Array<type_calendar_id>,
options : {
from ?: _zeitbild.frontend.helpers.type_pit;
to ?: _zeitbild.frontend.helpers.type_pit;
timezone_shift ?: int;
} = {}
) : Promise<string>
{
const stuff : Array<
{
calendar_id : type_calendar_id;
event : type_event;
}
> = await calendar_view_list_data(
calendar_ids,
options
);
return Promise.resolve<string>(
new lib_plankton.xml.class_node_complex(
"div",
{
"class": "list",
},
[
new lib_plankton.xml.class_node_complex(
"style",
{},
[
new lib_plankton.xml.class_node_text(
"html {background-color: #111; color: #FFF; font-family: sans-serif;}\n"
+
"table {width: 100%; border-collapse: collapse;}\n"
)
]
),
new lib_plankton.xml.class_node_complex(
"ul",
{
"class": "list-events",
},
(
stuff
.map(
(entry) => (
new lib_plankton.xml.class_node_complex(
"li",
{
"class": "list-event_entry",
},
[
new lib_plankton.xml.class_node_text(
JSON.stringify(entry)
),
]
)
)
)
)
),
]
).compile()
);
}
}