This commit is contained in:
Fenris Wolf 2024-09-12 00:02:55 +02:00
commit 720c8a6623
15 changed files with 7536 additions and 0 deletions

27
.editorconfig Normal file
View file

@ -0,0 +1,27 @@
# see https://EditorConfig.org
root = true
[*]
charset = utf-8
end_of_line = lf
indent_size = tab
indent_style = tab
tab_width = 4
insert_final_newline = true
max_line_length = 80
trim_trailing_whitespace = true
curly_bracket_next_line = false
indent_brace_style = K&R
spaces_around_operators = true
spaces_around_brackets = false
quote_type = double
[*.y{,a}ml{,lint}]
indent_style = space
indent_size = 2
[*.md]
indent_style = space
indent_size = 2

2
.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
/build/
/.geany

11
readme.md Normal file
View file

@ -0,0 +1,11 @@
# Zeitbild | Datamodel
## Beschreibung
- Datenmodell für Zeitbild
## Nutzung
- siehe `tools/build -h`

View file

@ -0,0 +1,102 @@
{
"domains": [
{
"name": "sessions",
"data_fields": [
{
"name": "key",
"type": "string_medium",
"nullable": false
},
{
"name": "data",
"type": "string_long",
"nullable": false
}
],
"constraints": [
{
"kind": "unique",
"parameters": {
"fields": ["key"]
}
}
]
},
{
"name": "users",
"key_field": {
"name": "id"
},
"data_fields": [
],
"constraints": [
]
},
{
"name": "events",
"key_field": {
"name": "id"
},
"data_fields": [
{
"name": "name",
"type": "string_medium",
"nullable": false
},
{
"name": "begin",
"type": "string_medium",
"nullable": false
},
{
"name": "end",
"type": "string_medium",
"nullable": true
},
{
"name": "location",
"type": "string_medium",
"nullable": true
},
{
"name": "description",
"type": "string_long",
"nullable": true
}
],
"constraints": [
]
},
{
"name": "calendars",
"key_field": {
"name": "id"
},
"data_fields": [
{
"name": "name",
"type": "string_medium",
"nullable": false
},
{
"name": "private",
"type": "boolean",
"nullable": false
},
{
"name": "kind",
"type": "string_short",
"nullable": false
},
{
"name": "data",
"type": "string_long",
"nullable": false
}
],
"constraints": [
]
}
]
}

75
tools/build Executable file
View file

@ -0,0 +1,75 @@
#!/usr/bin/env python3
import typing as _typing
import sys as _sys
import os as _os
import argparse as _argparse
def main():
## consts
dir_source = "source"
## args
argument_parser = _argparse.ArgumentParser()
argument_parser.add_argument(
"-r",
"--revision",
type = str,
dest = "revision",
default = None,
help = "use '.' for latest",
)
argument_parser.add_argument(
"-f",
"--format",
type = str,
dest = "format",
default = "sqlite",
)
args = argument_parser.parse_args()
## vars
revisions = sorted(
list(
map(
lambda name: name.split(".sindri.json")[0],
_os.listdir(_os.path.join(dir_source, "structure"))
)
)
)
revision = (
args.revision
if
(
(not (args.revision is None))
and
(not (args.revision == ""))
and
(not (args.revision == "."))
)
else
revisions[-1]
)
## exec
if (not (revision in set(revisions))):
_sys.stderr.write("-- invalid revision: %s\n" % revision)
_sys.exit(1)
else:
_sys.stderr.write("-- data\n")
_os.system(
"cat %s/structure/%s.sindri.json | tools/sindri/sindri --format='database:%s'"
% (
dir_source,
revision,
args.format,
)
)
_sys.stderr.write("-- meta\n")
_sys.stdout.write("DROP TABLE IF EXISTS _meta;\n")
_sys.stdout.write("CREATE TABLE _meta(revision VARCHAR(15) NOT NULL);\n")
_sys.stdout.write("INSERT INTO _meta(revision) VALUES ('%s');\n" % revision)
main()

6652
tools/sindri/sindri Executable file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,118 @@
// <<domain_name>>
{
lib_plankton.rest.register(
rest,
lib_plankton.http.enum_method.get,
lib_plankton.string.coin(
"/{{base_path}}{{domain_name}}",
{
"base_path": _brock.conf.api_base_path,
"domain_name": "<<domain_name>>",
}
),
{
"execution": async function (stuff) {
return {
"status_code": 200,
"data": await <<repository_function_list>>(
)
};
}
}
);
lib_plankton.rest.register(
rest,
lib_plankton.http.enum_method.get,
lib_plankton.string.coin(
"/{{base_path}}{{domain_name}}/:id",
{
"base_path": _brock.conf.api_base_path,
"domain_name": "<<domain_name>>",
}
),
{
"execution": async function (stuff) {
return {
"status_code": 200,
"data": await <<repository_function_read>>(
parseInt(
stuff.path_parameters["id"]
)
)
};
}
}
);
lib_plankton.rest.register(
rest,
lib_plankton.http.enum_method.post,
lib_plankton.string.coin(
"/{{base_path}}{{domain_name}}",
{
"base_path": _brock.conf.api_base_path,
"domain_name": "<<domain_name>>",
}
),
{
"execution": async function (stuff) {
const id = await <<repository_function_create>>(
(stuff.input as <<type_name>>)
);
return {
"status_code": 201,
"data": id
};
}
}
);
lib_plankton.rest.register(
rest,
lib_plankton.http.enum_method.patch,
lib_plankton.string.coin(
"/{{base_path}}{{domain_name}}/:id",
{
"base_path": _brock.conf.api_base_path,
"domain_name": "<<domain_name>>",
}
),
{
"execution": async function (stuff) {
const dummy = await <<repository_function_create>>(
parseInt(
stuff.path_parameters["id"]
),
(stuff.input as <<type_name>>)
);
return {
"status_code": 200,
"data": null
};
}
}
);
lib_plankton.rest.register(
rest,
lib_plankton.http.enum_method.delete,
lib_plankton.string.coin(
"/{{base_path}}{{domain_name}}/:id",
{
"base_path": _brock.conf.api_base_path,
"domain_name": "<<domain_name>>",
}
),
{
"execution": async function (stuff) {
const dummy = await <<repository_function_delete>>(
parseInt(
stuff.path_parameters["id"]
)
);
return {
"status_code": 200,
"data": null
};
}
}
);
}

View file

@ -0,0 +1,4 @@
export namespace <<domain_name>>
{
<<defs>>
}

View file

@ -0,0 +1,74 @@
// declare var require;
namespace <<namespace_base>>entities
{
<<entities>>
}
namespace <<namespace_base>>repositories
{
<<repositories>>
}
namespace <<namespace_base>>main
{
// run
export function run(
) : void
{
lib_plankton.log.conf_push(
[
new lib_plankton.log.class_channel_stdout(
)
]
);
// define api
{
// meta
{
lib_plankton.rest.register(
rest,
lib_plankton.http.enum_method.get,
"/_spec",
{
"execution": async function (stuff) {
return {
"status_code": 200,
"data": lib_plankton.rest.to_oas(
rest
)
};
}
}
);
}
<<api>>
}
// setup server
const server = lib_plankton.server.make(
_brock.conf.server_port,
async function (input) {
const http_request : lib_plankton.http.type_request = lib_plankton.http.decode_request(
input
);
const http_response : lib_plankton.http.type_response = await lib_plankton.rest.call(
rest,
http_request
);
return lib_plankton.http.encode_response(
http_response
);
}
);
// start
lib_plankton.server.start(
server
);
}
}

View file

@ -0,0 +1,37 @@
function repository_{{domain_name}}_list(
) : Promise<
Array<
{
key : number;
value : {{type_name}}
}
>
>
{
return lib_plankton.sqlite.query_get(
conf.database_path,
{
"template": "SELECT {{query_fields}} FROM {{table_name}};",
"arguments": {
}
}
).then(
function (rows) {
return rows.map(
function (row) {
return {
"key": row["id"],
"value": {
{{value_mapping}}
"name": row["name"],
"description": row["description"],
"hue": row["hue"]
}
};
}
);
}
);
}

View file

@ -0,0 +1,113 @@
export namespace <<domain_name>>
{
// list
export function <<list_function_name>>(
) : Promise<Array<{key : number; value : <<type_name>>;}>>
{
return (
lib_plankton.sqlite.query_get(
_brock.conf.database_path,
{
"template": "SELECT <<list_query_fields>> FROM <<table_name>>;",
"arguments": {
}
}
)
.then(
(rows) => rows.map(
(row) => <<list_result>>
)
)
);
}
// read
export function <<read_function_name>>(
key : number
) : Promise<<<type_name>>>
{
return (
lib_plankton.sqlite.query_get(
_brock.conf.database_path,
{
"template": "SELECT <<read_query_fields>> FROM <<table_name>> WHERE (id = :key);",
"arguments": {
"key": key
}
}
)
.then(
(rows) {
const row = rows[0];
return <<read_result_fields>>;
}
)
);
}
// create
export function <<create_function_name>>(
value : <<type_name>>
) : Promise<number>
{
return (
lib_plankton.sqlite.query_put(
_brock.conf.database_path,
{
"template": "INSERT INTO <<table_name>>(<<create_query_field_names>>) VALUES (<<create_query_field_placeholders>>);",
"arguments": <<create_query_field_values>>
}
)
.then(
(result) => result.id
)
);
}
// update
export function <<update_function_name>>(
key : number,
value : <<type_name>>
) : Promise<void>
{
return (
lib_plankton.sqlite.query_put(
_brock.conf.database_path,
{
"template": "UPDATE <<table_name>> SET <<update_query_assignments>> WHERE (id = :key);",
"arguments": {
"key": key,
<<update_query_values>>
}
}
)
.then(
(result) => {}
)
);
}
// delete
export function <<delete_function_name>>(
key : number
) : Promise<void>
{
return (
lib_plankton.sqlite.query_put(
_brock.conf.database_path,
{
"template": "DELETE FROM <<table_name>> WHERE (id = :key);",
"arguments": {
"key": key
}
}
)
.then(
(result) => {}
)
);
}
}

View file

@ -0,0 +1,251 @@
namespace _sindri
{
/**
*/
const abstract = {{value}};
/**
* @todo headers
* @todo query
*/
export function api_call(
method : string,
path : string,
input : any = null
) : Promise<any>
{
return (
fetch(
(conf.backend.scheme + "://" + conf.backend.host + ":" + conf.backend.port.toFixed() + path),
{
"method": method,
"headers": {
"Content-Type": "application/json",
},
"body": (
(input === null)
? undefined
: /*Buffer.from*/(JSON.stringify(input))
),
}
)
.then(
x => x.json()
)
);
}
/**
*/
export function editor(
domain_description : any,
hook_switch : (null | ((state : lib_plankton.zoo_editor.type_state<int, any>) => void))
) : lib_plankton.zoo_editor.type_editor<int, any>
{
return lib_plankton.zoo_editor.make<int, any>(
// store
{
"setup": () => Promise.resolve<void>(undefined),
"search": (term) => (
api_call(
"GET",
lib_plankton.string.coin(
"/{{name}}",
{
"name": domain_description.name,
}
),
null
)
.then(
(entries) => Promise.resolve<Array<{key : int; preview : any}>>(
entries
.filter(
entry => (
entry.key.toFixed().includes(term.toLowerCase())
||
(
/*
(term.length >= 3)
&&
*/
domain_description.data_fields
.some(
data_field => JSON.stringify(entry.value[data_field.name]).toLowerCase().includes(term.toLowerCase())
)
)
)
)
.map(
entry => ({
"key": entry.key,
"preview": entry.value,
})
)
)
)
),
"read": (id) => api_call(
"GET",
lib_plankton.string.coin(
"/{{name}}/{{id}}",
{
"name": domain_description.name,
"id": id.toFixed(0),
}
),
null
),
"create": (object) => api_call(
"POST",
lib_plankton.string.coin(
"/{{name}}",
{
"name": domain_description.name,
}
),
object
),
"update": (id, object) => api_call(
"PATCH",
lib_plankton.string.coin(
"/{{name}}/{{id}}",
{
"name": domain_description.name,
"id": id.toFixed(0),
}
),
object
),
"delete": (id) => api_call(
"DELETE",
lib_plankton.string.coin(
"/{{name}}/{{id}}",
{
"name": domain_description.name,
"id": id.toFixed(0),
}
),
null
),
},
// form
lib_plankton.zoo_form.make<any>(
// method
"GET",
// fields
(
domain_description.data_fields.map(
data_field => ({
"name": data_field.name,
"type": {
"string_short": "text",
"string_medium": "text",
"string_long": "text",
"integer": "number",
"float": "number",
}[data_field.type],
})
)
),
// encode
(object) => object,
// decode
(object_encoded) => object_encoded,
),
// options
{
"hook_switch": hook_switch,
}
);
}
/**
*/
export function edit_location_name(
domain_description
) : string
{
return lib_plankton.string.coin(
"edit_{{name}}",
{
"name": domain_description.name,
}
);
}
/**
*/
export function register_editor_page_for_domain(
domain_description
) : void
{
const location_name : string = edit_location_name(domain_description);
lib_plankton.zoo_page.register(
location_name,
async (parameters, element_main) => {
const id : (null | int) = ((! ("id" in parameters)) ? null : parseInt(parameters["id"]));
const mode : lib_plankton.zoo_editor.enum_mode = editor_decode_mode(parameters["mode"], id);
const search_term : (null | string) = (parameters["search"] ?? null);
lib_plankton.zoo_editor.render<int, any>(
editor(
domain_description,
(state) => {
lib_plankton.zoo_page.set(
{
"name": location_name,
"parameters": {
"mode": state.mode,
"id": ((state.key === null) ? null : state.key.toFixed(0)),
"search": state.search_state.term,
},
}
);
}
),
element_main,
{
"state": {
"mode": mode,
"key": id,
"search_state": {"term": search_term},
}
}
);
}
);
lib_plankton.zoo_page.add_nav_entry(
{
"name": location_name,
"parameters": {
"mode": "find",
"key": null,
"search": ""
}
},
{
"label": (domain_description.name + "s"),
}
);
}
/**
*/
export function init(
) : Promise<void>
{
abstract.domains.forEach(
domain_description => {
register_editor_page_for_domain(domain_description);
}
);
return Promise.resolve<void>(undefined);
}
}

View file

@ -0,0 +1,19 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<link rel="stylesheet" type="text/css" href="style.css"/>
<script type="text/javascript" src="<<path_logic>>"></script>
<script type="text/javascript">entry()</script>
<title><<title>></title>
</head>
<body>
<nav>
<ul>
</ul>
</nav>
<hr/>
<main>
</main>
</body>
</html>

View file

@ -0,0 +1,44 @@
/**
*/
async function init(
) : Promise<void>
{
lib_plankton.zoo_page.init(
document.querySelector("main"),
{
"fallback": {
"name": "view",
"parameters": {
}
},
}
);
await _sindri.init();
lib_plankton.zoo_page.start();
return Promise.resolve<void>(undefined);
}
/**
*/
function main(
) : void
{
}
/**
*/
function entry(
) : void
{
document.addEventListener(
"DOMContentLoaded",
async () => {
await init();
main();
}
);
}

7
tools/update-sindri Executable file
View file

@ -0,0 +1,7 @@
#!/usr/bin/env sh
cp \
--recursive \
--update \
~/projekte/greenscale/misc/sindri/build/* \
tools/sindri/