From: Max Value Date: Mon, 6 Apr 2026 20:52:36 +0000 (+0100) Subject: Bulk commit X-Git-Url: https://git.ozva.co.uk/?a=commitdiff_plain;h=55d8a633b50e4dd3b87ab0b58db7b4181e93ca4c;p=delta-velorum Bulk commit --- diff --git a/director/__init__.py b/director/__init__.py index 9311785..6081250 100644 --- a/director/__init__.py +++ b/director/__init__.py @@ -1,28 +1,58 @@ -from flask import Flask, render_template, send_from_directory +from flask import Flask, render_template, send_from_directory, request from flask_socketio import SocketIO, emit +import time import os +from .utils import deindex + from director.database.read import read as database_read +from director.database.read import read_all_current as database_read_all_current +from director.database.read import read_indexes as database_read_indexes +from director.database.read import read_views as database_read_views +from director.database.write import write as database_write app = Flask(__name__, instance_relative_config=False) -socketio = SocketIO(app, logger=True, engineio_logger=True) +socketio = SocketIO(app, logger=False, engineio_logger=False) @app.route("/", methods=['get']) def index(): return render_template("index.html") -@app.route("/admin", methods=['get']) +@app.route("/info/", methods=["get"]) +def info(screen): + return render_template(f"info/{screen}.html") + +@app.route("/admin", methods=["get"]) def admin(): - indexes, current = database_read() - return render_template("admin.html", indexes=indexes, current=current) + return render_template("admin.html", indexes=database_read_indexes()) + +@app.route("/admin/", methods=["get", "post"]) +def admin_table(table_name): + start = time.time() + + if request.method == "POST": + database_write(request.form) + + emit( + "update", + database_read_views(table_name), + json=True, namespace="/", broadcast=True + ) + + end = time.time() + + return render_template( + "table.html", + indexes=database_read_indexes(), + table=database_read(table_name), + table_name=table_name, + time_taken = end - start + ) @app.route("/script/", methods=["get"]) def script(filename): return send_from_directory(os.path.join(app.root_path, "build"), filename) -@app.route("/test") -def test(): - return [database_read()] - #emit("update", ["data__product", {"data":{"product":{"currentPrice":100}}}], json=True, namespace="/", broadcast=True) - - return "" +@socketio.on('connect') +def connecting(): + emit("update", database_read_views(), json=True, namespace="/", broadcast=True) diff --git a/director/database/__init__.py b/director/database/__init__.py index efdbc2f..e408c87 100644 --- a/director/database/__init__.py +++ b/director/database/__init__.py @@ -4,9 +4,11 @@ check if file exists and pragam check the database on init """ -from .utils import integrity_check +from .utils import integrity_check, optimize import sqlite3 import os assert os.path.exists("./data/main.db"), "Database missing" assert integrity_check(), "Database integrity error" + +optimize() diff --git a/director/database/read.py b/director/database/read.py index 8c5f012..b180b39 100644 --- a/director/database/read.py +++ b/director/database/read.py @@ -1,23 +1,69 @@ import sqlite3 -from .utils import list_tables +from .utils import list_all, list_views -def read(): - tables = list_tables() +def read_all_current(): + tables = list_all() + indexes = read_indexes() with sqlite3.connect("./data/main.db") as connection: cursor = connection.cursor() data = {} for table in tables: + if table == "indexes": continue + + if "__view" in table: + index_table = table.split("__view")[0] + else: index_table = table + cursor.execute(f"SELECT * FROM {table};") - names = list(map(lambda x: x[0], cursor.description)) - rows = [dict(zip(names, row)) for row in cursor.fetchall()] + names = list(map(lambda x: f"{table}~~{x[0]}", cursor.description)) + row = cursor.fetchall()[indexes[f"int--{index_table}"]] + + data.update(dict(zip(names, row))) + + return data + +def read(table): + """ + reads a table by name + """ + with sqlite3.connect("./data/main.db") as connection: + cursor = connection.cursor() + + cursor.execute(f"SELECT * FROM {table};") + + names = list(map(lambda x: x[0], cursor.description)) + rows = [dict(zip(names, row)) for row in cursor.fetchall()] + + return rows + +def read_views(table=None): + """ + Reads views. If a table is specified, it reads all views related to that + table. If not it reads all views. + """ + if table is None: + related = list_views() + else: + related = [view for view in list_views() if view.split("__view")[0] == table] + + data = {} + for view in related: + for key, value in read(view)[0].items(): + data.update({f"{view.replace('__view', '')}~~{key}": value}) + + return data + +def read_indexes(): + with sqlite3.connect("./data/main.db") as connection: + cursor = connection.cursor() + + cursor.execute("SELECT * FROM indexes;") - if table == "indexes": - indexes = rows[0] - else: - data.update({table: rows}) + names = list(map(lambda x: x[0], cursor.description)) + indexes = [dict(zip(names, row)) for row in cursor.fetchall()][0] - return indexes, data + return indexes diff --git a/director/database/utils.py b/director/database/utils.py index f564caa..6866761 100644 --- a/director/database/utils.py +++ b/director/database/utils.py @@ -10,11 +10,28 @@ def integrity_check(): return integrity.fetchone()[0] == "ok" -def list_tables(): +def optimize(): """ - get a list of all the tables in the database + check the database is ok and return true if it is + """ + with sqlite3.connect("./data/main.db") as connection: + cursor = connection.cursor() + integrity = cursor.execute('PRAGMA optimize') + +def list_all(): + """ + get a list of all the tables and views in the database + """ + with sqlite3.connect("./data/main.db") as connection: + cursor = connection.cursor() + cursor.execute("SELECT name FROM sqlite_master WHERE type IN ('table', 'view');") + return [table[0] for table in cursor.fetchall()] + +def list_views(): + """ + get a list of all the views in the database """ with sqlite3.connect("./data/main.db") as connection: cursor = connection.cursor() - cursor.execute("SELECT name FROM sqlite_master WHERE type='table';") + cursor.execute("SELECT name FROM sqlite_master WHERE type='view';") return [table[0] for table in cursor.fetchall()] diff --git a/director/database/write.py b/director/database/write.py new file mode 100644 index 0000000..eea0126 --- /dev/null +++ b/director/database/write.py @@ -0,0 +1,20 @@ +import sqlite3 + +from .utils import list_all +from .read import read + +def write(data): + + with sqlite3.connect("./data/main.db") as connection: + cursor = connection.cursor() + + for key in data: + table, index, feild = key.split("~~") + + cursor.execute(f""" + UPDATE {table} + SET '{feild}' = '{data[key]}' + WHERE ROWID = {int(index) + 1}; + """) + + connection.commit() diff --git a/director/src/admin.ts b/director/src/admin.ts index f8d100a..22e72e3 100644 --- a/director/src/admin.ts +++ b/director/src/admin.ts @@ -1,6 +1,5 @@ -interface Body { - [index: string]: number; -} +const outlineStyle: string = "4px solid green"; +const outlineTime: number = 500; // ms export function setupTrigger() { // setup all trigger buttons to send post request on click @@ -9,16 +8,32 @@ export function setupTrigger() { for (let e of el) { e.addEventListener("click", (event) => { - let time: number = Math.round(Date.now() / 1000); - let body: Body = {}; + if (event.target instanceof HTMLElement) { + let time: number = Math.round(Date.now() / 1000); + let body: FormData = new FormData(); + let delay = document.getElementById(`${event.target.getAttribute('name')}+delay`); + + if (delay instanceof HTMLInputElement) { + body.set(`${event.target.getAttribute('name')}`, `${time}+${delay.value}`); + + fetch("", { + method: "POST", + body: body + }); - if (event.target instanceof Element) { - body[`${event.target.getAttribute('name')}`] = time; + // trigger the button to turn green for approx same time (or 1s) + let timeout: number = delay.value as unknown as number * 1000; + if (timeout < outlineTime) { timeout = outlineTime; } - fetch(".", { - method: "POST", - body: JSON.stringify(body) - }); + if ( event.target instanceof HTMLElement) { + event.target.style.outline = outlineStyle; + setTimeout(function () { + if ( event.target instanceof HTMLElement) { + event.target.style.outline = "none" + } + }, timeout); + } + } } }) } diff --git a/director/src/discount.ts b/director/src/discount.ts new file mode 100644 index 0000000..d05bd20 --- /dev/null +++ b/director/src/discount.ts @@ -0,0 +1,25 @@ +export function renderDiscount(): void { + const el: HTMLCollectionOf = + document.getElementsByClassName("discount"); + + for (let e of el) { + if (e instanceof HTMLElement) { + if (typeof e.dataset.raw === "string") { + e.innerHTML = `${e.dataset.raw}%`; + } + } + } +} + +export function renderReverseDiscount(): void { + const el: HTMLCollectionOf = + document.getElementsByClassName("reverseDiscount"); + + for (let e of el) { + if (e instanceof HTMLElement) { + if (typeof e.dataset.raw === "string") { + e.innerHTML = `${100 - Number(e.dataset.raw)}%`; + } + } + } +} diff --git a/director/src/price.ts b/director/src/price.ts index e91bd50..0d926c5 100644 --- a/director/src/price.ts +++ b/director/src/price.ts @@ -1,6 +1,4 @@ -const unstablePriceChance: number = 0.7; - -export function renderPrice(): void { +export default function renderPrice(): void { const el: HTMLCollectionOf = document.getElementsByClassName("price"); @@ -12,20 +10,3 @@ export function renderPrice(): void { } } } - -export function renderUnstablePrice(): void { - const el: HTMLCollectionOf = - document.getElementsByClassName("unstablePrice"); - - for (let e of el) { - if (e instanceof HTMLElement) { - if (typeof e.dataset.raw === "string") { - if (Math.random() < unstablePriceChance) { - e.innerHTML = String(parseFloat(e.dataset.raw).toFixed(2)); - } else { - e.innerHTML = String(parseFloat(e.dataset.raw)); - } - } - } - } -} diff --git a/director/src/show_until.ts b/director/src/show_until.ts new file mode 100644 index 0000000..6658b09 --- /dev/null +++ b/director/src/show_until.ts @@ -0,0 +1,16 @@ +export default function renderShowUntil(): void { + const el: HTMLCollectionOf = + document.getElementsByClassName("showUntil"); + + for (let e of el) { + if (e instanceof HTMLElement) { + if (typeof e.dataset.raw === "string") { + let duration: number = (eval(e.dataset.raw) * 1000) - Date.now(); + if (duration > 0) { + e.classList.add("showUntil-showing") + setTimeout(function () {e.classList.remove("showUntil-showing")}, duration) + } + } + } + } +} diff --git a/director/src/sound.ts b/director/src/sound.ts new file mode 100644 index 0000000..b387907 --- /dev/null +++ b/director/src/sound.ts @@ -0,0 +1,30 @@ +export function setupSounds() { + const el: HTMLCollectionOf = + document.getElementsByClassName("sound"); + + const context = new AudioContext(); + if (el.length > 0) { + for (let e of el) { + if (e instanceof HTMLMediaElement) { + const track = context.createMediaElementSource(e); + track.connect(context.destination); + } + } + } +} + +export function playSounds() { + const el: HTMLCollectionOf = + document.getElementsByClassName("sound"); + + for (let e of el) { + if (e instanceof HTMLMediaElement) { + if (typeof e.dataset.raw === "string") { + if (e.dataset.rawLast != e.dataset.raw) { + e.play(); + } + e.dataset.rawLast = e.dataset.raw; + } + } + } +} diff --git a/director/src/state.ts b/director/src/state.ts new file mode 100644 index 0000000..d206f25 --- /dev/null +++ b/director/src/state.ts @@ -0,0 +1,18 @@ +export default function renderState(): void { + const el: HTMLCollectionOf = + document.getElementsByClassName("state"); + + for (let e of el) { + if (e instanceof HTMLElement) { + if (typeof e.dataset.raw === "string") { + for (let className of e.classList) { + if (className.startsWith("state-")) { + e.classList.remove(className); + } + } + + e.classList.add(`state-${e.dataset.raw}`); + } + } + } +} diff --git a/director/src/style.ts b/director/src/style.ts new file mode 100644 index 0000000..380559e --- /dev/null +++ b/director/src/style.ts @@ -0,0 +1,31 @@ +export function setupStyle(): void { + const el: HTMLCollectionOf = + document.getElementsByClassName("style"); + + for (let i = 0; i < el.length; i++) { + const stylesheet = new CSSStyleSheet(); + document.adoptedStyleSheets.push(stylesheet); + } +} + +export function renderStyle(): void { + const el: HTMLCollectionOf = + document.getElementsByClassName("style"); + + for (let [i, e] of Array.from(el).entries()) { + if (e instanceof HTMLElement) { + let variable; + for (let className of e.classList) { + if (className.includes("--")) { + variable = className.split("--")[1]; + } + } + + if (typeof e.dataset.raw === "string") { + document.adoptedStyleSheets[i].replace(` + :root {--${variable}: ${e.dataset.raw};} + `); + } + } + } +} diff --git a/director/src/text.ts b/director/src/text.ts index d7a6032..7695065 100644 --- a/director/src/text.ts +++ b/director/src/text.ts @@ -1,4 +1,4 @@ -export function renderText(): void { +export default function renderText(): void { const el = document.querySelectorAll(".text,.title"); for (let e of el) { if (e instanceof HTMLElement) { diff --git a/director/src/time.ts b/director/src/time.ts new file mode 100644 index 0000000..84b69f3 --- /dev/null +++ b/director/src/time.ts @@ -0,0 +1,51 @@ +import { timeSince, timeUntil, timeFormatBroadcast } from "./utils.js"; + +const timeUpdate: number = 1000; // ms + +export function setupTimeSince(): void { + const el: HTMLCollectionOf = + document.getElementsByClassName("since"); + + if (el.length > 0) { + setInterval( + function () { + const el: HTMLCollectionOf = + document.getElementsByClassName("since"); + + for (let e of el) { + if (e instanceof HTMLElement) { + if (typeof e.dataset.raw === "string") { + let [hours, minutes, seconds] = timeSince(eval(e.dataset.raw)) + e.innerHTML = timeFormatBroadcast(hours, minutes, seconds); + } + } + } + }, + timeUpdate + ); + } +} + +export function setupTimeUntil(): void { + const el: HTMLCollectionOf = + document.getElementsByClassName("until"); + + if (el.length > 0) { + setInterval( + function () { + const el: HTMLCollectionOf = + document.getElementsByClassName("until"); + + for (let e of el) { + if (e instanceof HTMLElement) { + if (typeof e.dataset.raw === "string") { + let [hours, minutes, seconds] = timeUntil(eval(e.dataset.raw)) + e.innerHTML = timeFormatBroadcast(hours, minutes, seconds); + } + } + } + }, + timeUpdate + ); + } +} diff --git a/director/src/until.ts b/director/src/until.ts new file mode 100644 index 0000000..e69de29 diff --git a/director/src/update.ts b/director/src/update.ts index 275921d..b50539c 100644 --- a/director/src/update.ts +++ b/director/src/update.ts @@ -1,37 +1,39 @@ -import { renderText } from "./text.js"; -import { renderPrice, renderUnstablePrice } from "./price.js" import { io } from "socket.io-client"; -// apply a number of sequential keys to an object -function objectPath(obj: any, path: string[]): any { - let key: string|undefined = path.shift(); - if (typeof key === "undefined") { - return obj - } else { - return objectPath(obj[key], path) - } -} +import { renderDiscount, renderReverseDiscount } from "./discount.js"; +import { setupTimeSince, setupTimeUntil } from "./time.js"; +import { setupSounds, playSounds } from "./sound.js"; +import { setupStyle, renderStyle } from "./style.js"; +import renderShowUntil from "./show_until.js"; +import renderState from "./state.js"; +import renderPrice from "./price.js" +import renderText from "./text.js"; export default function setupUpdate(): void { - const socket = io(); - socket.on("update", ([namespace, data]) => { - const el = document.querySelectorAll(`.update[class*='${namespace}']`); + setupTimeSince(); + setupTimeUntil(); + setupSounds(); + setupStyle(); - for (let e of el) { - let c: string|undefined = Array.from(e.classList) - .find((c) => c.startsWith(namespace)) - - if (typeof c === "string") { - let path = c.split("__"); - let result = objectPath(data, path); + const socket = io(); + socket.on("update", (data) => { + for (const [key, value] of Object.entries(data)) { + const el = document.querySelectorAll(`.update[class~='${key}']`); - if (typeof result !== "undefined" && e instanceof HTMLElement) { - e.dataset.raw = result; + for (let e of el) { + if (e instanceof HTMLElement) { + e.dataset.raw = value as unknown as string; } } } + renderReverseDiscount(); + renderShowUntil(); + renderDiscount(); + renderState(); renderPrice(); - renderUnstablePrice(); - }) + renderStyle(); + renderText(); + playSounds(); + }); } diff --git a/director/src/utils.ts b/director/src/utils.ts new file mode 100644 index 0000000..684ee85 --- /dev/null +++ b/director/src/utils.ts @@ -0,0 +1,35 @@ +export function timeUntil(end: number): number[] { + let current: number = Date.now() / 1000; + var time: number = end - current; + + if (Math.sign(time) == -1) {time = 0;} + + let hours: number = Math.floor(time / 3600); + time %= 3600; + let minutes: number = Math.floor(time / 60); + let seconds: number = Math.floor(time % 60); + + return [hours, minutes, seconds] +} + +export function timeSince(end: number): number[] { + let current: number = Date.now() / 1000; + var time: number = current - end; + + if (Math.sign(time) == -1) {time = 0;} + + let hours: number = Math.floor(time / 3600); + time %= 3600; + let minutes: number = Math.floor(time / 60); + let seconds: number = Math.floor(time % 60); + + return [hours, minutes, seconds] +} + +export function timeFormatBroadcast(hours: number, minutes: number, seconds: number): string { + var hoursString = hours.toString().padStart(2, "0"); + var minutesString = minutes.toString().padStart(2, "0"); + var secondsString = seconds.toString().padStart(2, "0"); + + return `${hoursString}h ${minutesString}' ${secondsString}"` +} diff --git a/director/static/admin.css b/director/static/admin.css new file mode 100644 index 0000000..3f7e86d --- /dev/null +++ b/director/static/admin.css @@ -0,0 +1,44 @@ +/* PAGE POSITIONING */ + +:root { + --nav-background: lightgray; +} + +body > * { + padding: 15px; + overflow: scroll +} +nav { + position: absolute; + top: 0; bottom: 0; left: 0; + right: max(80%, calc(100vw - 350px)); + background-color: var(--nav-background); +} +main { + position: absolute; + top: 0; bottom: 0; right: 0; + left: min(20%, calc(350px)); +} + + +/* THE OPTIONS GRID */ + +.optionsGrid { + display: grid; + grid-template-columns: 50px auto auto auto; +} +.optionsGrid > * { + padding: 10px; + border: 1px solid gray; + margin-top: -1px; + margin-right: -1px; +} +.optionsGridSpacer { border: none; } + + +:target { + outline: 4px solid blue; + z-index: 1; +} +.currentValue { color: red; } +.currentValue[type="radio"] { background-color: red; } diff --git a/director/static/info.css b/director/static/info.css new file mode 100644 index 0000000..c8c37b9 --- /dev/null +++ b/director/static/info.css @@ -0,0 +1,39 @@ +:root { + font-family: sans-serif; + color: white; + background-color: black; +} + +.infoA { font-size: var(--aFontSize); } +.infoB { font-size: var(--bFontSize); } +.infoC { font-size: var(--cFontSize); } + +.title { text-transform: capitalize; } + +/* INFO A */ +.infoA div > * { padding: 20px; } +.infoA div { + position: absolute; + top: 0; left: 0; bottom: 0; right: 0; + display: grid; + grid: none / 1fr; +} + +/* INFO B */ +.infoB div > * { padding: 20px; } +.infoB div { + position: absolute; + top: 0; left: 0; bottom: 0; right: 0; + display: grid; + grid: none / 1fr 1fr; +} + +/* INFO C */ +.infoC div > * { padding: 20px; text-align: center; } +.infoC div { + position: absolute; + top: 0; left: 0; bottom: 0; right: 0; + display: grid; + grid: none / 6fr 1fr 6fr; +} + diff --git a/director/static/media/sfx/clock.wav b/director/static/media/sfx/clock.wav new file mode 100644 index 0000000..a760531 Binary files /dev/null and b/director/static/media/sfx/clock.wav differ diff --git a/director/static/media/sfx/shout.wav b/director/static/media/sfx/shout.wav new file mode 100644 index 0000000..4a6849d Binary files /dev/null and b/director/static/media/sfx/shout.wav differ diff --git a/director/static/show_until.css b/director/static/show_until.css new file mode 100644 index 0000000..28e2f5b --- /dev/null +++ b/director/static/show_until.css @@ -0,0 +1,5 @@ +.showUntil { + opacity: 0; + transition: opacity 0.5s; +} +.showUntil.showUntil-showing { opacity: 1; } diff --git a/director/templates/admin.html b/director/templates/admin.html index 5fdfac8..17f5828 100644 --- a/director/templates/admin.html +++ b/director/templates/admin.html @@ -3,8 +3,11 @@ builds the admin page #} + + + - {% for table in current %} -
- {{ table }} -
- {% for row in current[table] %} - - {# CREATE RADIO BUTTON FOR ROW #} - - {% if loop.index0 == indexes["int--" ~ table] %} - - {% else %} - - {% endif %} - - - {# CREATE FEILDS #} - -
- {% for feild in row %} - - {# SELECT ELEMENT FROM TYPE #} - - {% set type, label = feild.split('--') %} - {% set value = row[feild] %} - {% set name = table ~ "~~" ~ feild %} - - {% if type == "textarea" %} - {% include "admin/textarea.html" %} - {% elif type == "text" %} - {% include "admin/text.html" %} - {% elif type == "number" %} - {% include "admin/number.html" %} - {% elif type == "float" %} - {% include "admin/float.html" %} - {% elif type == "trigger" %} - {% include "admin/trigger.html" %} - {% endif %} - -
- {% endfor %} -

- - {% endfor %} -
-
- {% endfor %} + +
+

Admin

+ +
diff --git a/director/templates/admin/color.html b/director/templates/admin/color.html new file mode 100644 index 0000000..1ecd886 --- /dev/null +++ b/director/templates/admin/color.html @@ -0,0 +1,6 @@ + + + + {{ value }} + + diff --git a/director/templates/admin/float.html b/director/templates/admin/float.html index 98a5c86..12f8b00 100644 --- a/director/templates/admin/float.html +++ b/director/templates/admin/float.html @@ -1,2 +1,3 @@ -
- + + +{{ value }} diff --git a/director/templates/admin/number.html b/director/templates/admin/number.html index 8014eda..7ffe0c7 100644 --- a/director/templates/admin/number.html +++ b/director/templates/admin/number.html @@ -1,2 +1,3 @@ -
- + + +{{ value }} diff --git a/director/templates/admin/text.html b/director/templates/admin/text.html index cb4f369..3878cc8 100644 --- a/director/templates/admin/text.html +++ b/director/templates/admin/text.html @@ -1,3 +1,4 @@ -
- + + +{{ value }} diff --git a/director/templates/admin/textarea.html b/director/templates/admin/textarea.html index e38b563..a9ce85b 100644 --- a/director/templates/admin/textarea.html +++ b/director/templates/admin/textarea.html @@ -1,4 +1,4 @@ -
- diff --git a/director/templates/admin/trigger.html b/director/templates/admin/trigger.html index 208cb05..322450d 100644 --- a/director/templates/admin/trigger.html +++ b/director/templates/admin/trigger.html @@ -1,4 +1,7 @@ -
- + + + + seconds delay (This does not update the triggered data.) + diff --git a/director/templates/index.html b/director/templates/index.html index 5f9cbf8..d236140 100644 --- a/director/templates/index.html +++ b/director/templates/index.html @@ -2,6 +2,7 @@ + - - This is some text + + + {# DYNAMIC STYLING ELEMENTS #} + + + + {# SFX ELEMENTS #} + + + + {# BEGINS #} + +
+ +
diff --git a/director/templates/info/a.html b/director/templates/info/a.html new file mode 100644 index 0000000..fa6e8cc --- /dev/null +++ b/director/templates/info/a.html @@ -0,0 +1,33 @@ + + + + + + + + + + +
+ +
+ +
+ + + + +
+ + diff --git a/director/templates/info/b.html b/director/templates/info/b.html new file mode 100644 index 0000000..c06b48e --- /dev/null +++ b/director/templates/info/b.html @@ -0,0 +1,51 @@ + + + + + + + + + + +
+ + (was ) + + + + + left of + + total + + + + off + + + + full price + + + + Target: ( left to drop) + + + + Max: ( left to drop) + +
+ + diff --git a/director/templates/info/c.html b/director/templates/info/c.html new file mode 100644 index 0000000..b35ba79 --- /dev/null +++ b/director/templates/info/c.html @@ -0,0 +1,36 @@ + + + + + + + + + + +
+ Current product:
+ + Next product:
+ + Current state: + + Next state: + + + T- + +
+ + diff --git a/director/templates/table.html b/director/templates/table.html new file mode 100644 index 0000000..c357be1 --- /dev/null +++ b/director/templates/table.html @@ -0,0 +1,88 @@ +{# + + builds the admin page + +#} + + + + + + + + + +
+

Table: {{ table_name }}

+
+ + + {% for row in table %} + + {# CREATE RADIO BUTTON FOR ROW #} + + + {% if loop.index0 == indexes["int--" ~ table_name] %} + + {% else %} + + {% endif %} + + + {# CREATE FEILDS #} + + {% set group = loop.index0 %} + {% for feild in row %} + + {# SELECT ELEMENT FROM TYPE #} + + {% set type, label = feild.split('--') %} + {% set value = row[feild] %} + {% set name = table_name ~ "~~" ~ group ~ "~~" ~ feild %} + + {% if type == "textarea" %} + {% include "admin/textarea.html" %} + {% elif type == "text" %} + {% include "admin/text.html" %} + {% elif type == "number" %} + {% include "admin/number.html" %} + {% elif type == "float" %} + {% include "admin/float.html" %} + {% elif type == "trigger" %} + {% include "admin/trigger.html" %} + {% elif type == "color" %} + {% include "admin/color.html" %} + {% endif %} + {% endfor %} + + + {% endfor %} + +
+
+ + diff --git a/director/utils.py b/director/utils.py new file mode 100644 index 0000000..d2fc032 --- /dev/null +++ b/director/utils.py @@ -0,0 +1,10 @@ +def deindex(data, indexes): + deindexed = {} + for key in data: + table, index, feild = key.split("~~") + if table != "indexes" and int(index) == indexes[f"int--{table}"]: + deindexed.update({ + f"{table}~~{feild}": data[key] + }) + + return deindexed