backend/source/repositories/calendar.ts

703 lines
16 KiB
TypeScript
Raw Normal View History

2025-09-25 17:18:16 +02:00
/*
This file is part of »zeitbild«.
Copyright 2025 'kcf' <fenris@folksprak.org>
»zeitbild« is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
»zeitbild« is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with »zeitbild«. If not, see <http://www.gnu.org/licenses/>.
*/
2024-09-12 00:03:29 +02:00
namespace _zeitbild.repository.calendar
{
2025-10-13 13:19:33 +02:00
/**
*/
2025-10-17 13:16:56 +02:00
type type_core_row = {
name : string;
hue : int;
access_public : boolean;
access_level_default : int;
resource_id : int;
};
/**
*/
2025-10-23 11:34:00 +02:00
type type_access_attributed_group_row = {
// calendar_id : int;
group_id : int;
level : int;
};
/**
*/
type type_access_attributed_user_row = {
2025-10-17 13:16:56 +02:00
// calendar_id : int;
user_id : int;
level : int;
};
2025-10-13 13:19:33 +02:00
2024-09-12 16:35:57 +02:00
/**
*/
type type_dispersal = {
2025-10-17 13:16:56 +02:00
core_row : type_core_row;
2025-10-23 11:34:00 +02:00
access_attributed_group_rows : Array<type_access_attributed_group_row>;
access_attributed_user_rows : Array<type_access_attributed_user_row>;
2024-09-12 16:35:57 +02:00
};
2025-10-17 13:16:56 +02:00
/**
*/
const hue_scaling : int = 0xFFFF;
2024-09-12 00:03:29 +02:00
/**
*/
var _core_store : (
null
|
2024-09-12 16:35:57 +02:00
lib_plankton.storage.type_store<
2024-09-21 11:05:24 +02:00
_zeitbild.type_calendar_id,
2024-09-12 00:03:29 +02:00
Record<string, any>,
{},
lib_plankton.storage.type_sql_table_autokey_search_term,
Record<string, any>
>
) = null;
/**
*/
2025-10-23 11:34:00 +02:00
var _access_attributed_group_chest : (
null
|
lib_plankton.storage.type_chest<
Array<any>,
Record<string, any>,
lib_plankton.database.type_description_create_table,
lib_plankton.storage.sql_table_common.type_sql_table_common_search_term,
Record<string, any>
>
) = null;
/**
*/
var _access_attributed_user_chest : (
2024-09-12 00:03:29 +02:00
null
|
2024-09-12 16:35:57 +02:00
lib_plankton.storage.type_chest<
Array<any>,
2024-09-12 00:03:29 +02:00
Record<string, any>,
2024-09-12 16:35:57 +02:00
lib_plankton.database.type_description_create_table,
lib_plankton.storage.sql_table_common.type_sql_table_common_search_term,
2024-09-12 00:03:29 +02:00
Record<string, any>
>
) = null;
/**
*/
function get_core_store(
2025-10-23 11:34:00 +02:00
)
: lib_plankton.storage.type_store<
2024-09-21 11:05:24 +02:00
_zeitbild.type_calendar_id,
2024-09-12 00:03:29 +02:00
Record<string, any>,
{},
lib_plankton.storage.type_sql_table_autokey_search_term,
Record<string, any>
>
{
2025-10-23 11:34:00 +02:00
if (_core_store === null)
{
2024-09-12 16:35:57 +02:00
_core_store = lib_plankton.storage.sql_table_autokey_store(
2024-09-12 00:03:29 +02:00
{
"database_implementation": _zeitbild.database.get_implementation(),
"table_name": "calendars",
"key_name": "id",
}
);
}
2025-10-23 11:34:00 +02:00
else
{
2024-09-12 00:03:29 +02:00
// do nothing
}
return _core_store;
}
/**
*/
2025-10-23 11:34:00 +02:00
function get_access_attributed_group_chest(
2024-09-12 16:35:57 +02:00
) : lib_plankton.storage.type_chest<
Array<any>,
2024-09-12 00:03:29 +02:00
Record<string, any>,
2024-09-12 16:35:57 +02:00
lib_plankton.database.type_description_create_table,
lib_plankton.storage.sql_table_common.type_sql_table_common_search_term,
2024-09-12 00:03:29 +02:00
Record<string, any>
>
{
2025-10-23 11:34:00 +02:00
if (_access_attributed_group_chest === null)
{
_access_attributed_group_chest = lib_plankton.storage.sql_table_common.chest(
2024-09-12 00:03:29 +02:00
{
"database_implementation": _zeitbild.database.get_implementation(),
2025-10-23 11:34:00 +02:00
"table_name": "calendar_access_attributed_group",
"key_names": ["calendar_id","group_id"],
}
);
}
else
{
// do nothing
}
return _access_attributed_group_chest;
}
/**
*/
function get_access_attributed_user_chest(
)
: lib_plankton.storage.type_chest<
Array<any>,
Record<string, any>,
lib_plankton.database.type_description_create_table,
lib_plankton.storage.sql_table_common.type_sql_table_common_search_term,
Record<string, any>
>
{
if (_access_attributed_user_chest === null)
{
_access_attributed_user_chest = lib_plankton.storage.sql_table_common.chest(
{
"database_implementation": _zeitbild.database.get_implementation(),
"table_name": "calendar_access_attributed_user",
2024-09-12 16:35:57 +02:00
"key_names": ["calendar_id","user_id"],
2024-09-12 00:03:29 +02:00
}
);
}
2025-10-23 11:34:00 +02:00
else
{
2024-09-12 00:03:29 +02:00
// do nothing
2025-10-23 11:34:00 +02:00
}
return _access_attributed_user_chest;
2024-09-21 10:56:00 +02:00
}
/**
*/
function encode_access_level(
2024-09-21 11:05:24 +02:00
access_level : _zeitbild.enum_access_level
2024-09-21 10:56:00 +02:00
) : int
{
return (
[
2024-09-21 11:05:24 +02:00
_zeitbild.enum_access_level.none,
_zeitbild.enum_access_level.view,
_zeitbild.enum_access_level.edit,
_zeitbild.enum_access_level.admin,
2024-09-21 10:56:00 +02:00
].indexOf(access_level)
);
}
/**
*/
function decode_access_level(
access_level_encoded : int
2024-09-21 11:05:24 +02:00
) : _zeitbild.enum_access_level
2024-09-21 10:56:00 +02:00
{
return (
[
2024-09-21 11:05:24 +02:00
_zeitbild.enum_access_level.none,
_zeitbild.enum_access_level.view,
_zeitbild.enum_access_level.edit,
_zeitbild.enum_access_level.admin,
2024-09-21 10:56:00 +02:00
][access_level_encoded]
);
2024-09-12 00:03:29 +02:00
}
/**
*/
function encode(
2024-09-21 11:05:24 +02:00
object : _zeitbild.type_calendar_object
2025-10-23 11:34:00 +02:00
)
: type_dispersal
2024-09-12 00:03:29 +02:00
{
2024-09-12 16:35:57 +02:00
return {
"core_row": {
"name": object.name,
2025-10-13 13:19:33 +02:00
"hue": Math.floor(object.hue * hue_scaling),
2024-10-10 23:00:29 +02:00
"access_public": object.access.public,
2024-09-21 10:56:00 +02:00
"access_level_default": encode_access_level(object.access.default_level),
2024-09-12 16:35:57 +02:00
"resource_id": object.resource_id,
},
2025-10-23 11:34:00 +02:00
"access_attributed_group_rows": (
lib_plankton.map.dump(object.access.attributed_group)
.map(
({"key": group_id, "value": level}) => ({
// "calendar_id": calendar_id,
"group_id": group_id,
"level": encode_access_level(level),
})
)
),
"access_attributed_user_rows": (
lib_plankton.map.dump(object.access.attributed_user)
2024-09-12 16:35:57 +02:00
.map(
2024-09-21 10:56:00 +02:00
({"key": user_id, "value": level}) => ({
2024-09-12 16:35:57 +02:00
// "calendar_id": calendar_id,
2024-09-21 10:56:00 +02:00
"user_id": user_id,
"level": encode_access_level(level),
2024-09-12 16:35:57 +02:00
})
)
),
};
2024-09-12 00:03:29 +02:00
}
/**
*/
function decode(
2024-09-12 16:35:57 +02:00
dispersal : type_dispersal
2025-10-23 11:34:00 +02:00
)
: _zeitbild.type_calendar_object
2024-09-12 00:03:29 +02:00
{
return {
2025-10-17 13:16:56 +02:00
"name": dispersal.core_row.name,
"hue": (dispersal.core_row.hue / hue_scaling),
2024-09-21 10:56:00 +02:00
"access": {
2025-10-17 13:16:56 +02:00
"public": dispersal.core_row.access_public,
"default_level": decode_access_level(dispersal.core_row.access_level_default),
2025-10-23 11:34:00 +02:00
"attributed_group": lib_plankton.map.hashmap.implementation_map(
lib_plankton.map.hashmap.make<_zeitbild.type_group_id, _zeitbild.enum_access_level>(
x => x.toFixed(0),
{
"pairs": (
dispersal.access_attributed_group_rows
.map(
(access_attributed_group_row) => ({
// "calendar_id": access_attributed_group_row["calendar_id"],
// "key": access_attributed_group_row["preview"]["user_id"],
"key": access_attributed_group_row.group_id,
// "value": decode_access_level(access_attributed_group_row["preview"]["level"]),
"value": decode_access_level(access_attributed_group_row.level),
})
)
),
}
)
),
"attributed_user": lib_plankton.map.hashmap.implementation_map(
lib_plankton.map.hashmap.make<_zeitbild.type_user_id, _zeitbild.enum_access_level>(
x => x.toFixed(0),
{
"pairs": (
2025-10-23 11:34:00 +02:00
dispersal.access_attributed_user_rows
.map(
2025-10-23 11:34:00 +02:00
(access_attributed_user_row) => ({
// "calendar_id": access_attributed_user_row["calendar_id"],
// "key": access_attributed_user_row["preview"]["user_id"],
"key": access_attributed_user_row.user_id,
// "value": decode_access_level(access_attributed_user_row["preview"]["level"]),
"value": decode_access_level(access_attributed_user_row.level),
})
)
),
}
2024-09-21 10:56:00 +02:00
)
),
},
2025-10-17 13:16:56 +02:00
"resource_id": dispersal.core_row.resource_id,
2024-09-12 00:03:29 +02:00
};
}
2025-10-17 00:42:11 +02:00
2024-09-12 00:03:29 +02:00
/**
*/
2025-10-23 11:34:00 +02:00
export async function read(
2024-09-21 11:05:24 +02:00
id : _zeitbild.type_calendar_id
2025-10-23 11:34:00 +02:00
)
: Promise<_zeitbild.type_calendar_object>
2024-09-12 00:03:29 +02:00
{
2025-10-23 11:34:00 +02:00
const core_row : type_core_row = ((await get_core_store().read(id)) as type_core_row);
const access_attributed_group_rows : Array<type_access_attributed_group_row> = await (
get_access_attributed_group_chest().search(
{
"expression": "(calendar_id = $calendar_id)",
"arguments": {
"calendar_id": id,
}
}
)
2024-09-12 16:35:57 +02:00
.then(
2025-10-23 11:34:00 +02:00
(hits) => Promise.resolve<Array<type_access_attributed_group_row>>(
hits
.map(
hit => (
2024-09-12 16:35:57 +02:00
{
2025-10-23 11:34:00 +02:00
// "calendar_id": null,
"group_id": hit.preview.group_id,
"level": hit.preview.level,
2024-09-12 16:35:57 +02:00
}
)
2025-10-23 11:34:00 +02:00
)
)
)
);
const access_attributed_user_rows : Array<type_access_attributed_user_row> = await (
get_access_attributed_user_chest().search(
{
"expression": "(calendar_id = $calendar_id)",
"arguments": {
"calendar_id": id,
}
2025-10-17 13:16:56 +02:00
}
2024-09-12 16:35:57 +02:00
)
2025-10-23 11:34:00 +02:00
.then(
(hits) => Promise.resolve<Array<type_access_attributed_user_row>>(
hits
.map(
hit => (
{
// "calendar_id": null,
"user_id": hit.preview.user_id,
"level": hit.preview.level,
}
)
)
)
)
2024-09-12 16:35:57 +02:00
);
2025-10-23 11:34:00 +02:00
const dispersal : type_dispersal = {
"core_row": core_row,
"access_attributed_group_rows": access_attributed_group_rows,
"access_attributed_user_rows": access_attributed_user_rows,
};
const calendar_object : _zeitbild.type_calendar_object = decode(dispersal);
return calendar_object;
2024-09-12 00:03:29 +02:00
}
/**
*/
export async function create(
2024-09-21 11:05:24 +02:00
calendar_object : _zeitbild.type_calendar_object
) : Promise<_zeitbild.type_calendar_id>
2024-09-12 00:03:29 +02:00
{
const dispersal : type_dispersal = encode(calendar_object);
const core_store = get_core_store();
const calendar_id : _zeitbild.type_calendar_id = await core_store.create(
dispersal.core_row
2024-09-12 16:35:57 +02:00
);
2025-10-23 11:34:00 +02:00
for await (const access_attributed_group_row of dispersal.access_attributed_group_rows) {
get_access_attributed_group_chest().write(
[calendar_id, access_attributed_group_row["group_id"]],
{"level": access_attributed_group_row["level"]}
);
}
for await (const access_attributed_user_row of dispersal.access_attributed_user_rows) {
get_access_attributed_user_chest().write(
[calendar_id, access_attributed_user_row["user_id"]],
{"level": access_attributed_user_row["level"]}
);
}
2025-09-25 16:44:50 +02:00
await lib_plankton.cache.clear(_zeitbild.cache_regular);
return Promise.resolve<_zeitbild.type_calendar_id>(calendar_id);
}
/**
*/
export async function update(
calendar_id : _zeitbild.type_calendar_id,
calendar_object : _zeitbild.type_calendar_object
) : Promise<void>
{
const dispersal : type_dispersal = encode(calendar_object);
// core
{
const core_store = get_core_store();
await core_store.update(
calendar_id,
dispersal.core_row
);
}
2025-10-23 11:34:00 +02:00
// attributed:group
{
2025-10-23 11:34:00 +02:00
const access_attributed_group_chest = get_access_attributed_group_chest();
const hits : Array<Record<string, any>> = await access_attributed_group_chest.search(
{
"expression": "(calendar_id = $calendar_id)",
"arguments": {
"calendar_id": calendar_id,
}
}
);
const contrast = lib_plankton.list.contrast<
Record<string, any>,
Record<string, any>
>(
hits,
hit => hit["group_id"],
dispersal.access_attributed_group_rows,
row => row["group_id"]
);
// delete
for await (const entry of contrast.only_left) {
await access_attributed_group_chest.delete(
[calendar_id, entry.left["group_id"]]
);
}
// update
for await (const entry of contrast.both) {
await access_attributed_group_chest.write(
[calendar_id, entry.right["group_id"]],
{"level": entry.right["level"]}
);
}
// create
for await (const entry of contrast.only_right) {
await access_attributed_group_chest.write(
[calendar_id, entry.right["group_id"]],
{"level": entry.right["level"]}
);
}
}
// attributed:user
{
const access_attributed_user_chest = get_access_attributed_user_chest();
const hits : Array<Record<string, any>> = await access_attributed_user_chest.search(
{
"expression": "(calendar_id = $calendar_id)",
"arguments": {
"calendar_id": calendar_id,
}
}
);
const contrast = lib_plankton.list.contrast<
Record<string, any>,
Record<string, any>
>(
hits,
hit => hit["user_id"],
2025-10-23 11:34:00 +02:00
dispersal.access_attributed_user_rows,
row => row["user_id"]
);
// delete
for await (const entry of contrast.only_left) {
2025-10-23 11:34:00 +02:00
await access_attributed_user_chest.delete(
[calendar_id, entry.left["user_id"]]
);
}
// update
for await (const entry of contrast.both) {
2025-10-23 11:34:00 +02:00
await access_attributed_user_chest.write(
[calendar_id, entry.right["user_id"]],
{"level": entry.right["level"]}
);
}
// create
for await (const entry of contrast.only_right) {
2025-10-23 11:34:00 +02:00
await access_attributed_user_chest.write(
[calendar_id, entry.right["user_id"]],
{"level": entry.right["level"]}
);
}
}
2025-09-25 16:44:50 +02:00
await lib_plankton.cache.clear(_zeitbild.cache_regular);
return Promise.resolve<void>(undefined);
}
/**
2024-09-30 20:19:19 +02:00
* @todo remove events from resource?
2025-10-17 13:16:56 +02:00
* @todo remove resource
*/
export async function delete_(
calendar_id : _zeitbild.type_calendar_id
2025-10-17 13:16:56 +02:00
)
: Promise<void>
{
2025-09-25 16:44:50 +02:00
await lib_plankton.cache.clear(_zeitbild.cache_regular);
const core_store = get_core_store();
2025-10-23 11:34:00 +02:00
const access_attributed_user_chest = get_access_attributed_user_chest();
// attributed:user
{
2025-10-23 11:34:00 +02:00
const chest = get_access_attributed_user_chest();
2025-10-17 13:16:56 +02:00
const hits : Array<
{
key : Array<any>;
preview : Record<string, any>;
}
2025-10-23 11:34:00 +02:00
> = await chest.search(
{
2024-09-30 20:19:19 +02:00
"expression": "(calendar_id = $calendar_id)",
"arguments": {
"calendar_id": calendar_id,
}
}
);
2025-10-17 13:16:56 +02:00
for (const hit of hits)
{
2025-10-23 11:34:00 +02:00
await chest.delete(hit.key);
}
}
// attributed:group
{
const chest = get_access_attributed_group_chest();
const hits : Array<
{
key : Array<any>;
preview : Record<string, any>;
}
> = await chest.search(
{
"expression": "(calendar_id = $calendar_id)",
"arguments": {
"calendar_id": calendar_id,
}
}
);
for (const hit of hits)
{
await chest.delete(hit.key);
}
}
// core
{
await core_store.delete(
calendar_id
);
}
return Promise.resolve<void>(undefined);
2024-09-12 00:03:29 +02:00
}
2024-09-12 19:35:31 +02:00
/**
*/
2024-10-10 23:00:29 +02:00
type type_overview_entry = {
id : _zeitbild.type_calendar_id;
name : string;
2025-10-13 13:19:33 +02:00
hue : float;
2024-10-10 23:00:29 +02:00
access_level : _zeitbild.enum_access_level;
}
/**
* @todo caching
*/
2024-09-21 10:56:00 +02:00
export async function overview(
2024-10-10 23:00:29 +02:00
user_id : (null | _zeitbild.type_user_id)
2025-10-23 11:34:00 +02:00
)
: Promise<
2024-09-12 19:35:31 +02:00
Array<
2024-10-10 23:00:29 +02:00
type_overview_entry
2024-09-12 19:35:31 +02:00
>
>
{
2024-10-10 23:51:58 +02:00
return lib_plankton.cache.get_complex<any, Array<type_overview_entry>>(
2025-09-25 16:44:50 +02:00
_zeitbild.cache_regular,
2024-10-10 23:51:58 +02:00
"calendar_overview",
{
"user_id": user_id,
},
2025-09-25 16:44:50 +02:00
null,
2024-10-10 23:51:58 +02:00
() => (
lib_plankton.file.read("sql/calendar_overview.sql")
.then(
(template) => _zeitbild.database.get_implementation().query_free_get(
{
"template": template,
"arguments": {
"user_id": user_id,
}
2024-09-21 10:56:00 +02:00
}
2024-10-10 23:51:58 +02:00
)
2024-09-21 10:56:00 +02:00
)
2024-10-10 23:51:58 +02:00
.then(
(rows) => Promise.resolve(
lib_plankton.call.convey(
rows,
[
(x : Array<Record<string, any>>) => x.map(
(row : Record<string, any>) => ({
"id": row["id"],
"name": row["name"],
2025-10-13 13:19:33 +02:00
"hue": (row["hue"] / hue_scaling),
2024-10-10 23:51:58 +02:00
/**
2025-10-23 11:34:00 +02:00
* @todo use _zeitbild.access_level_determine
2024-10-10 23:51:58 +02:00
*/
2025-10-23 11:34:00 +02:00
"access_level": _zeitbild.access_level_determine_raw(
row["access_public"],
(
(user_id === null)
?
null
:
{
"default": decode_access_level(row["access_level_default"]),
"group": (
lib_plankton.call.null_prop<string, Array<_zeitbild.enum_access_level>>(
row["access_level_attributed_group"],
x => x.split(",").map(parseInt).map(decode_access_level)
)
??
[]
),
"user": lib_plankton.call.null_prop<int, _zeitbild.enum_access_level>(
row["access_level_attributed_user"],
decode_access_level
),
}
2024-10-10 23:00:29 +02:00
)
2024-10-10 23:51:58 +02:00
),
})
),
(x : Array<type_overview_entry>) => x.filter(
(row) => (
2025-10-23 11:34:00 +02:00
! _zeitbild.access_level_order(
2024-10-10 23:51:58 +02:00
row.access_level,
_zeitbild.enum_access_level.none
2024-10-10 23:00:29 +02:00
)
)
2024-10-10 23:51:58 +02:00
),
(x : Array<type_overview_entry>) => lib_plankton.list.sorted<type_overview_entry>(
x,
{
"compare_element": lib_plankton.order.order_lexicographic_pair_wrapped<type_overview_entry, _zeitbild.enum_access_level, int>(
row => row.access_level,
row => row.id,
{
2025-10-23 11:34:00 +02:00
"order_first": (a, b) => _zeitbild.access_level_order(b, a),
2024-10-10 23:51:58 +02:00
"order_second": (a, b) => (a <= b)
}
),
}
),
]
)
2024-09-12 19:35:31 +02:00
)
)
)
2024-10-10 23:51:58 +02:00
);
2024-09-12 19:35:31 +02:00
}
2024-09-12 00:03:29 +02:00
}