backend/source/logic.ts
Fenris Wolf 0461344aad [mod]
2024-09-09 21:01:02 +02:00

604 lines
11 KiB
TypeScript

/**
*/
type type_role = (
"editor"
|
"viewer"
);
/**
*/
type type_user_id = int;
/**
*/
type type_user_object = {
name : string;
};
/**
*/
type type_event = {
name : string;
begin : type_datetime;
end : (
null
|
type_datetime
);
description : (
null
|
string
);
};
/**
*/
type type_calendar_id = int;
/**
*/
type type_calendar_object = (
{
kind : "concrete";
data : {
name : string;
users : Array<
{
id : type_user_id;
role : type_role;
}
>;
events : Array<type_event>;
};
}
|
{
kind : "collection";
data : {
name : string;
sources : Array<
type_calendar_id
>;
}
}
);
/**
*/
type type_datamodel = {
users : Array<
{
id : type_user_id;
object : type_user_object;
}
>;
calendars : Array<
{
id : type_calendar_id;
object : type_calendar_object;
}
>;
};
/**
*/
function calendar_list(
data : type_datamodel
) : Array<
{
key : type_calendar_id;
preview : {
name : string;
}
}
>
{
return (
data.calendars
.map(
(calendar_entry) => ({
"key": calendar_entry.id,
"preview": {
"name": calendar_entry.object.data.name,
}
})
)
);
}
/**
*/
function calendar_read(
data : type_datamodel,
calendar_id : type_calendar_id
) : type_calendar_object
{
const hits = (
data.calendars
.filter(
(calendar_entry) => (calendar_entry.id === calendar_id)
)
);
if (hits.length <= 0) {
throw (new Error("not found"));
}
else {
return hits[0].object;
}
}
/**
*/
function calendar_gather_events(
data : type_datamodel,
calendar_id : type_calendar_id,
from_pit : type_pit,
to_pit : type_pit
) : Array<
{
calendar_id : type_calendar_id;
event : type_event;
}
>
{
const calendar_object : type_calendar_object = calendar_read(
data,
calendar_id
);
switch (calendar_object.kind) {
case "concrete": {
return (
calendar_object.data.events
.filter(
(event) => pit_is_between(
pit_from_datetime(event.begin),
from_pit,
to_pit
)
)
.map(
(event) => ({"calendar_id": calendar_id, "event": event})
)
);
break;
}
case "collection": {
return (
calendar_object.data.sources
.map(
(source_calendar_id) => calendar_gather_events(
data,
source_calendar_id,
from_pit,
to_pit
)
)
.reduce(
(x, y) => x.concat(y),
[]
)
);
break;
}
}
}
/**
*/
function calendar_view_table(
data : type_datamodel,
calendar_id : type_calendar_id,
from : {
year : int;
week : int;
},
to : {
year : int;
week : int;
},
options : {
timezone_shift ?: int;
} = {}
) : Array<
{
week : int;
data : Array<
{
pit : type_pit;
entries : Array<
{
calendar_id : type_calendar_id;
event : type_event;
}
>;
today : boolean;
}
>;
}
>
{
options = Object.assign(
{
"timezone_shift": 0,
},
options
);
/*
const calendar_object : type_calendar_object = calendar_read(
data,
calendar_id
);
*/
const from_pit : type_pit = pit_from_year_and_week(
from.year,
from.week,
{
"timezone_shift": (options.timezone_shift as int),
}
);
const to_pit : type_pit = pit_from_year_and_week(
to.year,
to.week,
{
"timezone_shift": (options.timezone_shift as int),
}
);
// prepare
const entries : Array<
{
calendar_id : type_calendar_id;
event : type_event;
}
> = calendar_gather_events(
data,
calendar_id,
from_pit,
to_pit
);
let result : Array<
{
week : int;
data : Array<
{
pit : type_pit;
entries : Array<
{
calendar_id : type_calendar_id;
event : type_event;
}
>;
today : boolean;
}
>;
}
> = [];
let row : Array<
{
pit : type_pit;
entries : Array<
{
calendar_id : type_calendar_id;
event : type_event;
}
>;
today : boolean;
}
> = [];
let day : int = 0;
while (true) {
const pit_current : type_pit = pit_shift_day(from_pit, day);
if (pit_is_before(pit_current, to_pit)) {
day += 1;
row.push(
{
"pit": pit_current,
"entries": [],
"today": false, // TODO
}
);
if (day % 7 === 0) {
result.push({"week": (from.week + Math.floor(day / 7)), "data": row});
row = [];
}
else {
// do nothing
}
}
else {
break;
}
}
// fill
(
entries
.forEach(
(entry) => {
const distance_seconds : int = (pit_from_datetime(entry.event.begin) - from_pit);
// process.stderr.write(JSON.stringify({"begin": entry.event.begin, "begin_pit": pit_from_datetime(entry.event.begin), "from": from_pit, "diff": distance_seconds}) + "\n");
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);
// process.stderr.write(JSON.stringify({entry, distance_days, week, day}, undefined, "\t") + "\n");
result[week].data[day].entries.push(entry);
}
)
);
return result;
}
/**
*/
function calendar_view_table_html(
data : type_datamodel,
calendar_id : type_calendar_id,
from : {
year : int;
week : int;
},
to : {
year : int;
week : int;
},
options : {
timezone_shift ?: int;
} = {}
) : string
{
options = Object.assign(
{
"timezone_shift": 0,
},
options
);
const rows : Array<
{
week : int;
data : Array<
{
pit : type_pit;
entries : Array<
{
calendar_id : type_calendar_id;
event : type_event;
}
>;
today : boolean;
}
>;
}
> = calendar_view_table(
data,
calendar_id,
from,
to,
{
"timezone_shift": options.timezone_shift,
}
);
return (
new lib_plankton.xml.class_node_complex(
"div",
{
"class": "calendar",
},
[
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"
+
".calendar-cell {border: 1px solid #444; padding: 8px; vertical-align: top;}\n"
+
".calendar-cell-day {width: 13.5%;}\n"
+
".calendar-cell-week {width: 5.5%;}\n"
+
".calendar-cell-regular {width: 13.5%;}\n"
+
".calendar-day {font-size: 0.75em;}\n"
+
".calendar-events {margin: 0; padding: 0; list-style-type: none;}\n"
+
".calendar-event_entry {margin: 4px; padding: 4px; border-radius: 2px; font-size: 0.75em;}\n"
)
]
),
new lib_plankton.xml.class_node_complex(
"table",
{
},
[
new lib_plankton.xml.class_node_complex(
"thead",
{
},
[
new lib_plankton.xml.class_node_complex(
"tr",
{
},
(
[
new lib_plankton.xml.class_node_complex(
"th",
{
"class": "calendar-cell",
},
[
]
),
]
.concat(
["Mo","Di","Mi","Do","Fr","Sa","So"]
.map(
(day) => new lib_plankton.xml.class_node_complex(
"th",
{
"class": "calendar-cell calendar-cell-day",
},
[
new lib_plankton.xml.class_node_text(day)
]
)
)
)
)
)
]
),
new lib_plankton.xml.class_node_complex(
"tbody",
{
},
(
rows
.map(
(row) => (
new lib_plankton.xml.class_node_complex(
"tr",
{
},
(
[
new lib_plankton.xml.class_node_complex(
"th",
{
"class": "calendar-cell calendar-cell-week",
},
[
new lib_plankton.xml.class_node_text(
row.week.toFixed(0).padStart(2, "0")
)
]
),
]
.concat(
row.data
.map(
(cell) => (
new lib_plankton.xml.class_node_complex(
"td",
{
"class": (
(
["calendar-cell", "calendar-cell-regular"]
.concat(cell.today ? ["today"] : [])
)
.join(" ")
),
"title": lib_plankton.call.convey(
cell.pit,
[
pit_to_datetime,
(x : 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"),
}
),
]
)
},
[
new lib_plankton.xml.class_node_complex(
"span",
{
"class": "calendar-day",
},
[
new lib_plankton.xml.class_node_text(
lib_plankton.call.convey(
cell.pit,
[
pit_to_datetime,
(x : 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"),
}
),
]
)
)
]
),
new lib_plankton.xml.class_node_complex(
"ul",
{
"class": "calendar-events",
},
(
cell.entries
.map(
entry => (
new lib_plankton.xml.class_node_complex(
"li",
{
"class": "calendar-event_entry",
"style": lib_plankton.string.coin(
"background-color: {{color}}",
{
"color": lib_plankton.call.convey(
entry.calendar_id,
[
(n : int) => ({"n": n, "saturation": 0.25, "value": 0.5}),
lib_plankton.color.give_generic,
lib_plankton.color.output_hex,
]
),
}
),
},
[
new lib_plankton.xml.class_node_text(entry.event.name),
]
)
)
)
)
),
]
)
)
)
)
)
)
)
)
)
)
]
)
]
).compile()
);
}