diff --git a/.gitignore b/.gitignore index 280fb6b..005984e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,7 @@ /data/* /data/logs/* /data/tls/* -/data/django_static/* +/data/static/* /data/profilepictures/* /data/archive/* !/data/logs/ diff --git a/README.md b/README.md index 74a7248..f2a4bf6 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Drinks Manager +# Drinks Manager (Season 3) Note: This software is tailored to my own needs. I probably won't accept feature requests, and don't recommend you diff --git a/app/db_queries.py b/app/db_queries.py index dd79bbe..656bbda 100644 --- a/app/db_queries.py +++ b/app/db_queries.py @@ -3,7 +3,6 @@ from django.conf import settings from django.db import connection from django.utils.translation import gettext -from calendar import day_name COMBINE_ALPHABET = "abcdefghijklmnopqrstuvwxyz" @@ -16,6 +15,7 @@ def _db_select(sql_select:str): result = cursor.fetchall() return result + def _combine_results(results:list) -> dict: ''' e.g. @@ -80,7 +80,8 @@ def select_history(user, language_code="en") -> list: result = [list(row) for row in result] return result -def select_orders_per_month(user) -> dict: + +def orders_per_month(user) -> list: # number of orders per month (last 12 months) result_user = _db_select(f""" select @@ -101,35 +102,32 @@ def select_orders_per_month(user) -> dict: group by "month" order by "month" desc; """) - return _combine_results([result_all, result_user]) + return _combine_results([result_user, result_all]) -def select_orders_per_weekday(user) -> list: + +def orders_per_weekday(user) -> list: # number of orders per weekday (all time) - result = _db_select(f""" - with q_all as ( - select - extract(isodow from datetime) as "d", - sum(amount) as "c" - from app_order - group by d - ), q_user as ( - select - extract(isodow from datetime) as "d", - sum(amount) as "c" - from app_order - where user_id = {user.pk} - group by d - ) - select q_all.d as "day", q_all.c, q_user.c from q_all full join q_user on q_all.d = q_user.d - group by day, q_all.c, q_user.c - order by day asc; + result_user = _db_select(f""" + select + to_char(datetime, 'Day') as "day", + sum(amount) as "count" + from app_order + where user_id = {user.pk} + group by "day" + order by "count" desc; """) - for i in range(len(result)): - day_, all_, user_ = result[i] - result[i] = (day_name[int(day_)-1], all_, user_) - return result + result_all = _db_select(f""" + select + to_char(datetime, 'Day') as "day", + sum(amount) as "count" + from app_order + group by "day" + order by "count" desc; + """) + return _combine_results([result_user, result_all]) -def select_orders_per_drink(user) -> dict: + +def orders_per_drink(user) -> list: # number of orders per drink (all time) result_user = _db_select(f""" select @@ -150,31 +148,4 @@ def select_orders_per_drink(user) -> dict: group by d.product_name order by "data" desc; """) - return _combine_results([result_all, result_user]) - -def select_order_sum_per_user_all_users() -> list: - # sum of all orders per user, for all users - result = _db_select(f""" - select - app_user.username as user, - sum(app_order.price_sum) as sum - from app_user - left outer join app_order on (app_user.id = app_order.user_id) - group by app_user.id - order by app_user asc; - """) - return result - -def select_deposit_sum_per_user_all_users() -> list: - # sum of all orders per user, for all users - result = _db_select(f""" - select - app_user.username as user, - sum(rt.transaction_sum) as sum - from app_user - left outer join app_registertransaction rt on (app_user.id = rt.user_id) - where rt.is_user_deposit is true or rt.is_user_deposit is null - group by app_user.id - order by app_user asc; - """) - return result + return _combine_results([result_user, result_all]) diff --git a/app/locales/de/LC_MESSAGES/django.mo b/app/locales/de/LC_MESSAGES/django.mo index 5d60623..d7435cb 100644 Binary files a/app/locales/de/LC_MESSAGES/django.mo and b/app/locales/de/LC_MESSAGES/django.mo differ diff --git a/app/locales/de/LC_MESSAGES/django.po b/app/locales/de/LC_MESSAGES/django.po index 278ff86..fe0dc95 100644 --- a/app/locales/de/LC_MESSAGES/django.po +++ b/app/locales/de/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-11-01 19:29+0100\n" +"POT-Creation-Date: 2023-04-14 23:37+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Julian Müller (ChaoticByte)\n" "Language: DE\n" @@ -55,7 +55,7 @@ msgstr "Bestätigen" msgid "Drinks - History" msgstr "Getränke - Verlauf" -#: app/templates/history.html:10 app/templates/userpanel.html:23 +#: app/templates/history.html:10 app/templates/userpanel.html:25 msgid "History" msgstr "Verlauf" @@ -165,65 +165,49 @@ msgstr "Wähle deinen Account" msgid "Drinks - Statistics" msgstr "Getränke - Statistiken" -#: app/templates/statistics.html:10 app/templates/userpanel.html:24 +#: app/templates/statistics.html:10 app/templates/userpanel.html:26 msgid "Statistics" msgstr "Statistiken" #: app/templates/statistics.html:13 -msgid "orders / drink" -msgstr "Bestellungen / Getränk" +msgid "Orders per drink" +msgstr "Bestellungen pro Getränk" #: app/templates/statistics.html:16 msgid "drink" msgstr "Getränk" -#: app/templates/statistics.html:17 app/templates/statistics.html:36 -#: app/templates/statistics.html:53 -msgid "all" -msgstr "Alle" - -#: app/templates/statistics.html:18 app/templates/statistics.html:37 -#: app/templates/statistics.html:54 +#: app/templates/statistics.html:17 app/templates/statistics.html:34 +#: app/templates/statistics.html:51 msgid "you" msgstr "Du" -#: app/templates/statistics.html:32 -msgid "orders / month" -msgstr "Bestellungen / Monat" +#: app/templates/statistics.html:18 app/templates/statistics.html:35 +#: app/templates/statistics.html:52 +msgid "all" +msgstr "Alle" -#: app/templates/statistics.html:35 +#: app/templates/statistics.html:30 +msgid "Orders per month (last 12 months)" +msgstr "Bestellungen pro Monat (letzte 12 Monate)" + +#: app/templates/statistics.html:33 msgid "month" msgstr "Monat" -#: app/templates/statistics.html:49 -msgid "orders / weekday" -msgstr "Bestellungen / Wochentag" +#: app/templates/statistics.html:47 +msgid "Orders per weekday" +msgstr "Bestellungen pro Wochentag" -#: app/templates/statistics.html:52 +#: app/templates/statistics.html:50 msgid "day" msgstr "Tag" -#: app/templates/statistics.html:69 -msgid "order sum" -msgstr "Bestellungen" - -#: app/templates/statistics.html:72 app/templates/statistics.html:89 -msgid "user" -msgstr "Benutzer" - -#: app/templates/statistics.html:73 app/templates/statistics.html:90 -msgid "sum" -msgstr "Summe" - -#: app/templates/statistics.html:86 -msgid "deposit sum" -msgstr "Einzahlungen" - #: app/templates/supply.html:7 msgid "Drinks - Supply" msgstr "Getränke - Beschaffung" -#: app/templates/supply.html:14 app/templates/userpanel.html:30 +#: app/templates/supply.html:14 app/templates/userpanel.html:32 msgid "Supply" msgstr "Beschaffung" @@ -244,6 +228,7 @@ msgid "You are not allowed to view this site." msgstr "Dir fehlt die Berechtigung, diese Seite anzuzeigen." #: app/templates/transfer.html:6 +#| msgid "Drinks - Order" msgid "Drinks - Transfer" msgstr "Getränke - Geld senden" @@ -263,11 +248,11 @@ msgstr "Saldo" msgid "Logout" msgstr "Abmelden" -#: app/templates/userpanel.html:28 +#: app/templates/userpanel.html:30 msgid "Transfer" msgstr "Geld senden" -#: app/templates/userpanel.html:32 +#: app/templates/userpanel.html:34 msgid "Change Password" msgstr "Passwort ändern" diff --git a/app/models.py b/app/models.py index 717a1cf..681a433 100644 --- a/app/models.py +++ b/app/models.py @@ -40,7 +40,6 @@ class Drink(models.Model): do_not_count = models.BooleanField(default=False) def delete(self, *args, **kwargs): - # we flag the field as deleted. self.deleted = True super().save() @@ -108,9 +107,10 @@ class Order(models.Model): price_sum = models.DecimalField(max_digits=6, decimal_places=2, default=0, editable=False) content_litres = models.DecimalField(max_digits=6, decimal_places=3, default=0, editable=False) + # TODO: Add more comments on how and why the save & delete functions are implemented + # address this in a refactoring issue. + def save(self, *args, **kwargs): - # saving this may affect other fields - # so we reimplement the save function drink = Drink.objects.get(pk=self.drink.pk) if self._state.adding and drink.available > 0: if not drink.do_not_count: @@ -126,7 +126,6 @@ class Order(models.Model): raise ValidationError("This entry can't be changed.") def delete(self, *args, **kwargs): - # when deleting, we affect other fields as well. self.user.balance += self.price_sum self.user.save() drink = Drink.objects.get(pk=self.drink.pk) diff --git a/static/css/main.css b/app/static/css/main.css similarity index 65% rename from static/css/main.css rename to app/static/css/main.css index ea44432..ab3f24a 100644 --- a/static/css/main.css +++ b/app/static/css/main.css @@ -1,4 +1,4 @@ -/* Fonts */ +/* Font */ @font-face { font-family: "Inter"; @@ -18,39 +18,26 @@ :root { --font-family: "Inter"; --color: #fafafa; - --color-disabled: #ffffff50; - --color-error: #ff817c; - --bg-page: linear-gradient( - -10deg, - #071c29 10%, - #4a8897 - ); - --bg-color: #ffffff35; - --bg-color2: #ffffff25; - --bg-hover-color: #ffffff50; - --border-color: #ffffff50; + --color-error: #ff682c; + --bg-page-color: #222222; + --bg-color: #4e4e4e; + --bg-hover-color: #636363; + --bg-color2: #383838; + --bg-hover-color2: #4a4a4a; + --border-color: #808080; --bg-globalmessage: #161616; - --border-radius: .6rem; - --element-padding: .6rem .8rem; + --border-radius: .5rem; } /* General */ -body, -input, -select, -button, .button -{ - font-family: var(--font-family); -} - body { margin: 0; padding: 0; width: 100vw; min-height: 100vh; - font-size: 17px; - background: var(--bg-page); + font-family: var(--font-family); + background: var(--bg-page-color); color: var(--color); overflow-x: hidden; } @@ -60,7 +47,7 @@ a { } h1 { - font-size: 28px; + font-size: 1.8rem; } h1, h2, h3, h4 { @@ -78,36 +65,29 @@ input[type="number"]::-webkit-inner-spin-button { display: none; } -input[type="text"], -input[type="password"], -input[type="number"], -select { - padding: var(--element-padding); - text-align: center !important; - font-size: 16px; +input[type="text"], input[type="password"], input[type="number"], select { + padding: .6rem .8rem; + text-align: center; + font-size: 1rem; color: var(--color); border: none; outline: none; - border: 1px solid var(--border-color); + border-bottom: 1px solid var(--border-color); border-radius: var(--border-radius); background: var(--bg-color); -} - -input[type="text"]::placeholder, -input[type="password"]::placeholder, -input[type="number"]::placeholder, -select > option:disabled { - color: var(--color-disabled); + font-family: "Inter"; } select { appearance: none; -webkit-appearance: none; -moz-appearance: none; + height: 2.5rem; background-image: url("/static/material-icons/arrow-drop-down.svg"); background-repeat: no-repeat; background-position: right; background-size: 1.5rem; + padding-right: 1.8rem; } table { @@ -117,15 +97,20 @@ table { border-radius: var(--border-radius); } -tr > th, -tr > td { +tr { background: var(--bg-color); } -tr:nth-child(2n+2) > td { +tr:nth-child(2n+2) { background: var(--bg-color2); } +/* +Rounded corners on table cells apparently don't work with +Firefox, so Firefox users won't have rounded corners +on tables. Can't fix that by myself. +*/ + table tr:first-child th:first-child { border-top-left-radius: var(--border-radius); } @@ -179,7 +164,11 @@ th { flex-direction: row; margin-top: 1rem; width: 94%; - gap: 2rem; + gap: 1rem; +} + +.userinfo { + font-size: 1.05rem; } .userinfo > span { @@ -234,7 +223,7 @@ main { } .footer > div { - font-size: 16px; + font-size: .95rem; margin-top: .15rem; margin-bottom: .15rem; } @@ -286,6 +275,15 @@ main { gap: 1rem; } +.fill { + height: 100%; + width: 100%; +} + +.fill-vertical { + height: 100%; +} + .buttons { display: flex; flex-direction: row; @@ -298,26 +296,24 @@ main { display: flex; align-items: center; justify-content: center; + font-family: var(--font-family); + text-decoration: none; + text-align: center !important; + background: var(--bg-color); + color: var(--color); + font-size: 1rem; + padding: .6rem .8rem; outline: none; - border: 1px solid var(--border-color); + border: none; + border-bottom: 1px solid var(--border-color); border-radius: var(--border-radius); + cursor: pointer; + user-select: none; + box-sizing: content-box; width: fit-content; } -.button, button, .dropdownchoice { - padding: var(--element-padding); - font-size: 16px; - text-align: center !important; - text-decoration: none; - color: var(--color); - box-sizing: content-box; - cursor: pointer; - user-select: none; - background: var(--bg-color); -} - -.button:hover, button:hover, -.button:active, button:active { +.button:hover, button:hover, .button:active, button:active { background: var(--bg-hover-color); } @@ -325,55 +321,30 @@ main { opacity: 40%; } -.formheading { - margin-bottom: 2rem; -} - -.forminfo { - width: fit-content; - min-width: 16rem; +.appform > .forminfo { + width: 100%; text-align: left; display: flex; flex-direction: row; justify-content: space-between; gap: 2rem; - padding-bottom: .15rem; - border-bottom: 1px solid #ffffff20; } .forminfo > span:last-child { float: right; } -.appform, .appform > * { - max-width: 90vw; -} - .appform > .forminput { width: 100%; flex-direction: row; - justify-content: space-between; + justify-content: space-evenly; align-items: center; flex-wrap: wrap; gap: 1rem; } -.forminput > input, .forminput > select { - width: 100% !important; -} - -.forminput > .keyboard-input, #transfer-recipient { - /* the keyboard has a 5px padding */ - margin-left: 5px !important; - margin-right: 5px !important; -} - -.appform > .buttons { - margin-top: 1rem; -} - -#statusinfo { - margin-top: 1rem; +.appform > .statusinfo { + margin-top: .5rem; } .dropdownmenu { @@ -384,16 +355,6 @@ main { border-radius: var(--border-radius); } -#dropdownnope { - position: fixed; - top: 0; - left: 0; - width: 100vw; - height: 100vh; - margin: 0; - padding: 0; -} - .dropdownbutton { z-index: 190; } @@ -404,69 +365,70 @@ main { } .dropdownlist { - margin-top: 3rem; position: absolute; display: flex; flex-direction: column; - border-radius: var(--border-radius); - box-shadow: 0 0 .5rem #00000025; -} - -.dropdownlist, #dropdownnope { - visibility: hidden; - opacity: 0%; pointer-events: none; -} - -.dropdownvisible .dropdownlist, -.dropdownvisible #dropdownnope { - opacity: 100%; - background: #00000020; - visibility: visible; - pointer-events: visible; - z-index: 100; + border-radius: var(--border-radius) !important; + z-index: 200; + margin-top: 3.2rem; + opacity: 0%; + transition: opacity 100ms; + box-shadow: 0 .25rem 1rem #00000090; } .dropdownchoice { - z-index: 200; + border-radius: 0 !important; margin: 0; - text-decoration: none; + text-align: center; + justify-content: center; + background: var(--bg-color2) !important; + backdrop-filter: none !important; width: initial; - min-width: max-content; - border-bottom: 1px solid var(--border-color); - border-left: 1px solid var(--border-color); - border-right: 1px solid var(--border-color); -} - -.dropdownchoice:first-child { - border-top: 1px solid var(--border-color); - border-top-left-radius: var(--border-radius); - border-top-right-radius: var(--border-radius); -} - -.dropdownchoice:last-child { - border-bottom: 1px solid var(--border-color); - border-bottom-left-radius: var(--border-radius); - border-bottom-right-radius: var(--border-radius); } .dropdownchoice:hover { - background: var(--bg-hover-color); + background: var(--bg-hover-color2) !important; +} + +.dropdownlist :first-child { + border-top-left-radius: var(--border-radius) !important; + border-top-right-radius: var(--border-radius) !important; +} + +.dropdownlist :last-child { + border-bottom-left-radius: var(--border-radius) !important; + border-bottom-right-radius: var(--border-radius) !important; +} + +.dropdownvisible .dropdownlist { + opacity: 100%; + visibility: visible; + pointer-events: visible; } .customnumberinput { - height: 2.2rem; - display: flex; - flex-direction: row; - align-items: center; - gap: .25rem; + height: 2.5rem; } .customnumberinput button { - width: 2.2rem !important; - height: 2.2rem !important; + min-width: 2.5rem !important; + width: 2.5rem !important; padding: 0; margin: 0; + height: 100%; +} + +.customnumberinput-minus { + border-bottom-right-radius: 0; + border-top-right-radius: 0; + z-index: 10; +} + +.customnumberinput-plus { + border-bottom-left-radius: 0; + border-top-left-radius: 0; + z-index: 10; } .customnumberinput input[type="number"] { @@ -475,13 +437,13 @@ main { padding: 0; margin: 0; background: var(--bg-color2); + border-radius: 0 !important; -webkit-appearance: textfield; -moz-appearance: textfield; appearance: textfield; } .errortext { - font-weight: bold; color: var(--color-error); } @@ -491,11 +453,6 @@ main { /* Login */ -.userlist-container { - flex-grow: 1; - padding-bottom: 10vh; -} - .userlist { width: 60%; list-style: none; @@ -505,7 +462,8 @@ main { } .userlist > li { - padding: .1rem .6rem; + margin-bottom: .5rem; + padding: 0 .5rem; } .userlist > li > img { @@ -518,7 +476,7 @@ main { .userlist > li > div { flex-grow: 1; text-align: center; - padding: .7rem 1.1rem; + padding: .8rem 1.1rem; } .loginform { @@ -530,18 +488,6 @@ main { margin-top: 0; } -#passwordoverlay-container { - position: fixed; - width: 100vw; - height: 100vh; - top: 0; - right: 0; - background: var(--bg-page); - align-items: center; - padding-top: 10vh; - z-index: 200; -} - /* Drinks List */ .drinks-list { @@ -558,37 +504,7 @@ main { .drinks-list > li > .button { width: 100%; justify-content: space-between; - padding: .7rem 1.1rem; -} - -/* Statistics */ - -.statistics-container { - display: flex; - flex-direction: row; - align-items: flex-start; - justify-content: center; - flex-wrap: wrap; - max-width: 90vw; - gap: 1rem; -} - -.statistics-container > div { - height: 100%; - width: 16rem; -} - -/* Blur */ - -@supports (backdrop-filter: blur()) { - .dropdownvisible #dropdownnope { - backdrop-filter: blur(16px); - } - #passwordoverlay-container { - background: #00000020; - backdrop-filter: blur(64px); /* fallback */ - backdrop-filter: blur(128px); - } + padding: .8rem 1.1rem; } /* Responsive */ @@ -611,10 +527,9 @@ main { } } -@media only screen and (max-width: 860px) { +@media only screen and (max-width: 700px) { .userpanel { flex-direction: column; - gap: 1rem; } .userlist { gap: 0.25rem; @@ -633,10 +548,7 @@ main { } .dropdownlist { width: 14rem; - right: calc(50vw - 7rem); /* regard width */ + right: calc(50vw - 7rem); left: auto; } - #keyboard { - display: none !important; - } } diff --git a/static/css/simple-keyboard.css b/app/static/css/simple-keyboard.css similarity index 100% rename from static/css/simple-keyboard.css rename to app/static/css/simple-keyboard.css diff --git a/static/css/simple-keyboard_custom.css b/app/static/css/simple-keyboard_dark.css similarity index 83% rename from static/css/simple-keyboard_custom.css rename to app/static/css/simple-keyboard_dark.css index 39da287..e380b77 100644 --- a/static/css/simple-keyboard_custom.css +++ b/app/static/css/simple-keyboard_dark.css @@ -6,7 +6,6 @@ max-width: 100%; background: transparent; font-family: "Inter"; - font-size: 16px; } .simple-keyboard.darkTheme .hg-button { height: 50px; @@ -15,8 +14,8 @@ align-items: center; background: var(--bg-color); color: white; - border: 1px solid var(--border-color); - border-radius: var(--border-radius); + border: none; + border-bottom: 1px solid var(--border-color); } .simple-keyboard.darkTheme .hg-button:active, .simple-keyboard.darkTheme .hg-button:hover { diff --git a/static/favicon.ico b/app/static/favicon.ico similarity index 100% rename from static/favicon.ico rename to app/static/favicon.ico diff --git a/static/favicon.png b/app/static/favicon.png similarity index 100% rename from static/favicon.png rename to app/static/favicon.png diff --git a/static/fonts/Inter-Bold.ttf b/app/static/fonts/Inter-Bold.ttf similarity index 100% rename from static/fonts/Inter-Bold.ttf rename to app/static/fonts/Inter-Bold.ttf diff --git a/static/fonts/Inter-Regular.ttf b/app/static/fonts/Inter-Regular.ttf similarity index 100% rename from static/fonts/Inter-Regular.ttf rename to app/static/fonts/Inter-Regular.ttf diff --git a/static/js/autoreload.js b/app/static/js/autoreload.js similarity index 100% rename from static/js/autoreload.js rename to app/static/js/autoreload.js diff --git a/static/js/custom_form.js b/app/static/js/custom_form.js similarity index 100% rename from static/js/custom_form.js rename to app/static/js/custom_form.js diff --git a/static/js/custom_number_input.js b/app/static/js/custom_number_input.js similarity index 100% rename from static/js/custom_number_input.js rename to app/static/js/custom_number_input.js diff --git a/static/js/logged_out.js b/app/static/js/logged_out.js similarity index 100% rename from static/js/logged_out.js rename to app/static/js/logged_out.js diff --git a/static/js/login.js b/app/static/js/login.js similarity index 85% rename from static/js/login.js rename to app/static/js/login.js index 6f62216..0c43a72 100644 --- a/static/js/login.js +++ b/app/static/js/login.js @@ -6,6 +6,7 @@ let passwordOverlayElement; let pwOverlayCancelButton; let userlistButtons; + let pinpadButtons; let userlistContainerElement; // Add event listeners after DOM Content loaded document.addEventListener("DOMContentLoaded", () => { @@ -35,14 +36,9 @@ function show_password_overlay() { window.scrollTo(0, 0); passwordOverlayElement.classList.remove("nodisplay"); - passwordInputElement.focus() } function hide_password_overlay() { passwordOverlayElement.classList.add("nodisplay"); passwordInputElement.value = ""; - // Dispatch an Input Event to the input element to trigger the on- - // screen keyboard to update its buffer. This fixes a security - // issue on the login page. - passwordInputElement.dispatchEvent(new Event("input", {bubbles: true})); } })(); diff --git a/app/static/js/main.js b/app/static/js/main.js new file mode 100644 index 0000000..711aed8 --- /dev/null +++ b/app/static/js/main.js @@ -0,0 +1,14 @@ +document.addEventListener("DOMContentLoaded", () => { + let dropdownmenuElement = document.getElementById("dropdownmenu"); + let dropdownmenuButtonElement = document.getElementById("dropdownmenu-button"); + if (dropdownmenuButtonElement != null) { + dropdownmenuButtonElement.addEventListener("click", () => { + if (dropdownmenuElement.classList.contains("dropdownvisible")) { + dropdownmenuElement.classList.remove("dropdownvisible"); + } + else { + dropdownmenuElement.classList.add("dropdownvisible"); + } + }) + } +}); diff --git a/static/js/order.js b/app/static/js/order.js similarity index 100% rename from static/js/order.js rename to app/static/js/order.js diff --git a/static/js/simple-keyboard.js b/app/static/js/simple-keyboard.js similarity index 100% rename from static/js/simple-keyboard.js rename to app/static/js/simple-keyboard.js diff --git a/static/js/simple-keyboard_configure.js b/app/static/js/simple-keyboard_configure.js similarity index 100% rename from static/js/simple-keyboard_configure.js rename to app/static/js/simple-keyboard_configure.js diff --git a/static/material-icons/arrow-drop-down.svg b/app/static/material-icons/arrow-drop-down.svg similarity index 100% rename from static/material-icons/arrow-drop-down.svg rename to app/static/material-icons/arrow-drop-down.svg diff --git a/static/material-icons/menu.svg b/app/static/material-icons/menu.svg similarity index 100% rename from static/material-icons/menu.svg rename to app/static/material-icons/menu.svg diff --git a/app/templates/baselayout.html b/app/templates/baselayout.html index 97ac907..5783cf9 100644 --- a/app/templates/baselayout.html +++ b/app/templates/baselayout.html @@ -25,7 +25,7 @@
{% translate "An error occured. Please log out and log in again." %}
- log out + log out
{% endif %} diff --git a/app/templates/deposit.html b/app/templates/deposit.html index cf3dbc1..bbce3e1 100644 --- a/app/templates/deposit.html +++ b/app/templates/deposit.html @@ -8,16 +8,20 @@ {% block headAdditional %} - + {% endblock %} {% block content %} -

{% translate "Deposit" %}

{% csrf_token %} +

{% translate "Deposit" %}

- + {% translate "Amount" %} {{ currency_suffix }}: + + +
+
@@ -27,7 +31,6 @@
-
{% endblock %} \ No newline at end of file diff --git a/app/templates/footer.html b/app/templates/footer.html index 63b51ec..89d58c6 100644 --- a/app/templates/footer.html +++ b/app/templates/footer.html @@ -2,6 +2,6 @@ \ No newline at end of file diff --git a/app/templates/order.html b/app/templates/order.html index 2e5286f..2cae204 100644 --- a/app/templates/order.html +++ b/app/templates/order.html @@ -11,31 +11,31 @@
{% if drink and drink.available > 0 and not drink.deleted %} {% if user.balance > 0 or user.allow_order_with_negative_balance %} -

{% translate "Order" %}

{% csrf_token %} +

{% translate "Order" %}

- {% translate "Drink" %} + {% translate "Drink" %}: {{ drink.product_name }}
- {% translate "Price per Item" %} ({{ currency_suffix }}) + {% translate "Price per Item" %} ({{ currency_suffix }}): {{ drink.price }}
{% if not drink.do_not_count %}
- {% translate "Available" %} + {% translate "Available" %}: {{ drink.available }}
{% endif %}
- {% translate "Sum" %} ({{ currency_suffix }}) + {% translate "Sum" %} ({{ currency_suffix }}): {{ drink.price }}
- {% translate "Count" %} + {% translate "Count" %}: {% if drink.do_not_count %} @@ -48,19 +48,19 @@
+
-
{% else %} -
+

{% translate "Your balance is too low to order a drink." %}

- {% translate "back" %} + {% translate "back" %}
{% endif %} {% else %} diff --git a/app/templates/registration/login.html b/app/templates/registration/login.html index 657c713..c9ba292 100644 --- a/app/templates/registration/login.html +++ b/app/templates/registration/login.html @@ -10,7 +10,7 @@ {% block headAdditional %} - + {% endblock %} {% block content %} @@ -19,7 +19,7 @@ {% endif %}
-

{% translate "Log in" %}

+

{% translate "Log in" %}

{% csrf_token %} @@ -36,7 +36,7 @@
-
+

{% translate "Choose your account" %}

    {% for user_ in user_list %} diff --git a/app/templates/statistics.html b/app/templates/statistics.html index 93f3a9e..4f938b9 100644 --- a/app/templates/statistics.html +++ b/app/templates/statistics.html @@ -8,96 +8,57 @@ {% block content %}

    {% translate "Statistics" %}

    -
    -
    -

    {% translate "orders / drink" %}

    +
    +
    +

    {% translate "Orders per drink" %}

    - + {% for key, values in orders_per_drink.items %} - - + + {% endfor %}
    {% translate "drink" %}{% translate "all" %} {% translate "you" %}{% translate "all" %}
    {{ key }}{{ values.a|default:0 }}{{ values.b|default:0 }}{{ values.a|default:"0" }}{{ values.b|default:"0" }}
    -
    -
    -
    -

    {% translate "orders / month" %}

    +
    +

    {% translate "Orders per month (last 12 months)" %}

    - + {% for key, values in orders_per_month.items %} - - + + {% endfor %}
    {% translate "month" %}{% translate "all" %} {% translate "you" %}{% translate "all" %}
    {{ key }}{{ values.a|default:0 }}{{ values.b|default:0 }}{{ values.a|default:"0" }}{{ values.b|default:"0" }}
    -
    -

    {% translate "orders / weekday" %}

    +
    +

    {% translate "Orders per weekday" %}

    - + - {% for values in orders_per_weekday %} + {% for key, values in orders_per_weekday.items %} - - - + + + {% endfor %}
    {% translate "day" %}{% translate "all" %} {% translate "you" %}{% translate "all" %}
    {{ values.0 }}{{ values.1|default:0 }}{{ values.2|default:0 }}{{ key }}{{ values.a|default:"0" }}{{ values.b|default:"0" }}
    -
    -
    - {% if user.is_superuser or perms.app.view_order %} -
    -

    {% translate "order sum" %}

    - - - - - - {% for values in order_sum_per_user %} - - - - - {% endfor %} -
    {% translate "user" %}{% translate "sum" %}
    {{ values.0 }}{{ values.1|default:0.0 }} {{ currency_suffix }}
    -
    - {% endif %} - {% if user.is_superuser or perms.app.view_registertransaction %} -
    -

    {% translate "deposit sum" %}

    - - - - - - {% for values in deposit_sum_per_user %} - - - - - {% endfor %} -
    {% translate "user" %}{% translate "sum" %}
    {{ values.0 }}{{ values.1|default:0.0 }} {{ currency_suffix }}
    -
    - {% endif %} -
    {% endblock %} \ No newline at end of file diff --git a/app/templates/supply.html b/app/templates/supply.html index bcbc3d0..4f656c2 100644 --- a/app/templates/supply.html +++ b/app/templates/supply.html @@ -9,21 +9,27 @@ {% block content %} {% if user.is_superuser or user.allowed_to_supply %} -

    {% translate "Supply" %}

    {% csrf_token %} +

    {% translate "Supply" %}

    - + {% translate "Description" %}: + + +
    - + {% translate "Price" %} ({{ currency_suffix }}): + + +
    +
    -
    {% else %} diff --git a/app/templates/transfer.html b/app/templates/transfer.html index bf0524b..74c13f4 100644 --- a/app/templates/transfer.html +++ b/app/templates/transfer.html @@ -8,37 +8,44 @@ {% block headAdditional %} - + {% endblock %} {% block content %} -

    {% translate "Transfer Money" %}

    {% csrf_token %} +

    {% translate "Transfer Money" %}

    - + + {% for user_ in user_list %} + {% if user_.id != user.id %} + - {% endfor %} - + {% endif %} + + {% endfor %} + +
    - + {% translate "Amount" %} {{ currency_suffix }}: + + +
    +
    @@ -48,7 +55,6 @@
    -
    {% endblock %} \ No newline at end of file diff --git a/app/templates/userpanel.html b/app/templates/userpanel.html index 966f86e..7d2d015 100644 --- a/app/templates/userpanel.html +++ b/app/templates/userpanel.html @@ -18,20 +18,19 @@ {% translate "Deposit" %} {% translate "Logout" %}
    -
    +
    \ No newline at end of file diff --git a/app/views.py b/app/views.py index ba864a4..b7f936b 100644 --- a/app/views.py +++ b/app/views.py @@ -80,17 +80,11 @@ def deposit(request): @login_required def statistics(request): - user = request.user context = { - "orders_per_month": db_queries.select_orders_per_month(user), - "orders_per_weekday": db_queries.select_orders_per_weekday(user), - "orders_per_drink": db_queries.select_orders_per_drink(user), + "orders_per_month": db_queries.orders_per_month(request.user), + "orders_per_weekday": db_queries.orders_per_weekday(request.user), + "orders_per_drink": db_queries.orders_per_drink(request.user), } - # Advanced statistics - if user.has_perm("app.view_order") or user.is_superuser: - context["order_sum_per_user"] = db_queries.select_order_sum_per_user_all_users() - if user.has_perm("app.view_registertransaction") or user.is_superuser: - context["deposit_sum_per_user"] = db_queries.select_deposit_sum_per_user_all_users() return render(request, "statistics.html", context) @login_required diff --git a/data/Caddyfile b/data/Caddyfile index d790c4e..c5bbc58 100644 --- a/data/Caddyfile +++ b/data/Caddyfile @@ -9,8 +9,7 @@ {$CADDY_HOSTS} { # the tls certificates - # tls {$DATADIR}/tls/server.pem {$DATADIR}/tls/server-key.pem - tls internal + tls {$DATADIR}/tls/server.pem {$DATADIR}/tls/server-key.pem route { # profile pictures file_server /profilepictures/* { @@ -18,11 +17,7 @@ } # static files file_server /static/* { - root {$ROOTDIR} - } - # django static files - file_server /django_static/* { - root {$DATADIR}/django_static/.. + root {$DATADIR}/static/.. } # favicon redir /favicon.ico /static/favicon.ico diff --git a/project/settings.py b/project/settings.py index 56ff5fb..a5362ca 100644 --- a/project/settings.py +++ b/project/settings.py @@ -149,8 +149,8 @@ LOCALE_PATHS = [ # Static files (CSS, JavaScript, Images) # https://docs.djangoproject.com/en/4.1/howto/static-files/ -STATIC_URL = "django_static/" -STATIC_ROOT = BASE_DIR / "data" / "django_static" +STATIC_URL = "static/" +STATIC_ROOT = BASE_DIR / "data" / "static" # Default primary key field type # https://docs.djangoproject.com/en/4.1/ref/settings/#default-auto-field diff --git a/requirements.txt b/requirements.txt index ceb35b8..2be275b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -Django~=4.2 -psycopg2~=2.9 -uvicorn[standard]~=0.27 +Django~=4.1 +psycopg2~=2.9.5 +uvicorn~=0.20.0 PyYAML~=6.0 diff --git a/scripts/_bootstrap.py b/scripts/_bootstrap.py index c162c03..71ce28d 100755 --- a/scripts/_bootstrap.py +++ b/scripts/_bootstrap.py @@ -14,7 +14,7 @@ from time import sleep from yaml import safe_load -banner = r""" ___ _ _ +banner = """ ___ _ _ | \ _ _ (_) _ _ | |__ ___ ___ | |) || '_|| || ' \ | / /(_-< |___| |___/ |_| |_||_||_||_\_\/__/ @@ -32,7 +32,6 @@ configuration_file = data_directory / "config.yml" caddyfile = data_directory / "Caddyfile" logfile_caddy = logfile_directory / "caddy.log" logfile_app = logfile_directory / "app.log" -logfile_sessioncleanup = logfile_directory / "session-cleanup.log" class MonitoredSubprocess: @@ -131,7 +130,6 @@ if __name__ == "__main__": ["./venv/bin/python3", "./manage.py", "migrate", "--noinput"], env=os.environ).wait() # Caddy configuration via env environment_caddy = os.environ - environment_caddy["ROOTDIR"] = str(base_directory.absolute()) environment_caddy["DATADIR"] = str(data_directory.absolute()) environment_caddy["CADDY_HOSTS"] = ", ".join(config["caddy"]["hosts"]) environment_caddy["HTTP_PORT"] = str(config["caddy"]["http_port"]) @@ -185,6 +183,6 @@ if __name__ == "__main__": MonitoredSubprocess( "Session Autocleaner", ["./scripts/_session-autocleaner.py", str(config["app"]["session_clear_interval"])], - logfile_sessioncleanup) + logfile_app) ] start_and_monitor(procs) diff --git a/start.sh b/start.sh index cd50964..8f4ba06 100755 --- a/start.sh +++ b/start.sh @@ -11,6 +11,6 @@ chmod -c -R g-w,o-rwx .gitignore export PYTHONPATH="$basedir" export DJANGO_SETTINGS_MODULE="project.settings" -export APP_VERSION="22" +export APP_VERSION="17" exec ./scripts/_bootstrap.py "$@" diff --git a/static/js/main.js b/static/js/main.js deleted file mode 100644 index 5fab451..0000000 --- a/static/js/main.js +++ /dev/null @@ -1,21 +0,0 @@ -document.addEventListener("DOMContentLoaded", () => { - let dropdownmenuElement = document.getElementById("dropdownmenu"); - let dropdownmenuButtonElement = document.getElementById("dropdownmenu-button"); - let dropdownmenuNopeElement = document.getElementById("dropdownnope"); - function toggleDropDown() { - if (dropdownmenuElement.classList.contains("dropdownvisible")) { - dropdownmenuElement.classList.remove("dropdownvisible"); - dropdownmenuNopeElement.classList.remove("dropdownvisible"); - } else { - dropdownmenuElement.classList.add("dropdownvisible"); - dropdownmenuNopeElement.classList.add("dropdownvisible"); - } - } - if (dropdownmenuButtonElement != null) { - dropdownmenuButtonElement.addEventListener("click", toggleDropDown); - dropdownmenuNopeElement.addEventListener("click", () => { - dropdownmenuElement.classList.remove("dropdownvisible"); - dropdownmenuNopeElement.classList.remove("dropdownvisible"); - }) - } -});