Browse Source

První funkční verze

master
Jonas Havelka 6 months ago
commit
f43bda37ec
  1. 29
      .editorconfig
  2. 21
      .gitignore
  3. 2
      .requirements
  4. 56
      README.md
  5. 1
      autentizace/__init__.py
  6. 28
      autentizace/templates/autentizace/login.html
  7. 8
      autentizace/templates/autentizace/logout.html
  8. 10
      autentizace/urls.py
  9. 14
      autentizace/views.py
  10. 0
      byrokracie/__init__.py
  11. 6
      byrokracie/context_processor.py
  12. 99
      byrokracie/settings.py
  13. 39
      byrokracie/static/css/base.css
  14. BIN
      byrokracie/static/images/icon.png
  15. 26
      byrokracie/templates/base.html
  16. 10
      byrokracie/urls.py
  17. 16
      byrokracie/wsgi.py
  18. 68
      data/stanoviste.json
  19. 83
      data/tymy.json
  20. 178
      data/users.json
  21. 12
      fix_json.py
  22. 0
      hra/__init__.py
  23. 14
      hra/admin.py
  24. 260
      hra/logika.py
  25. 53
      hra/migrations/0001_initial.py
  26. 23
      hra/migrations/0002_tym_ma_ds_tym_pocet_bananu.py
  27. 23
      hra/migrations/0003_tym_vi_o_mesici_tym_vi_o_zadosti.py
  28. 33
      hra/migrations/0004_zadost.py
  29. 18
      hra/migrations/0005_tym_vi_o_mase.py
  30. 18
      hra/migrations/0006_tym_ovesna_kase.py
  31. 0
      hra/migrations/__init__.py
  32. 326
      hra/models.py
  33. 19
      hra/templates/hra/inventar.html
  34. 58
      hra/templates/hra/stanoviste/archiv.html
  35. 52
      hra/templates/hra/stanoviste/base.html
  36. 10
      hra/templates/hra/stanoviste/casovy_vyr.html
  37. 33
      hra/templates/hra/stanoviste/domov.html
  38. 23
      hra/templates/hra/stanoviste/menza.html
  39. 47
      hra/templates/hra/stanoviste/posta.html
  40. 27
      hra/templates/hra/stanoviste/studijni.html
  41. 19
      hra/templates/hra/staticke_stanoviste.html
  42. 12
      hra/templates/hra/timeout.html
  43. 27
      hra/templates/hra/tym.html
  44. 16
      hra/urls.py
  45. 0
      hra/utils/__init__.py
  46. 144
      hra/utils/doba.py
  47. 22
      hra/utils/hash.py
  48. 49
      hra/utils/zadost.py
  49. 143
      hra/views.py
  50. 22
      manage.py

29
.editorconfig

@ -0,0 +1,29 @@
# Z kódu webu M&M
# Univerzální popis našich konvencí pro nastavení editorů.
# Vizte https://editorconfig.org pro detaily
root = true
[*]
charset = utf-8
# Unixové řádky
end_of_line = lf
insert_final_newline = true
[*.{py,css}]
indent_style = tab
# Nenařizujeme konkrétní šířku tabulátoru
indent_size = unset
# Automaticky generované migrace dodržují PEP-8, nemá smysl s tím moc bojovat.
[*/migrations/*.py]
indent_style = space
indent_size = 4
[*.html]
indent_style = space
indent_size = 2
[*.json]
indent_style = tab
indent_size = unset

21
.gitignore

@ -0,0 +1,21 @@
# Inspirováno MaMwebem
# virtual environment
/venv/
/env/
# databáze
/*.sqlite3
# složky djanga
/static
/media
# aux files
*.pyc
*.sw[mnop]
# vim tmp files
*~
# pro lidi, co programují v nástrojích od JetBrains
.idea

2
.requirements

@ -0,0 +1,2 @@
django
qrcode

56
README.md

@ -0,0 +1,56 @@
# Byrokracie
Hra byrokracie v elektrizované verzi
***Aktuálně je kód velmi fujky!!!***
## Jak hrát
Při prvním spuštění
- `./manage.py migrate`
- `./manage.py loaddata data/users.json`
- `./manage.py loaddata data/stanoviste.json`
- `./manage.py loaddata data/tymy.json`
Rozběhnutí hry (hesla a uživatelská jména viz 'Hesla' níže, lze je upravit v adminu)
- `./manage.py runserver`
- v adminu (<127.0.0.1:8000/admin/>) přidat týmy / resetovat jejich start na aktuální datum a čas (nebojte, inventář se tam nezobrazuje)
- následně
- (pokud je `DEBUG = True`), pak stanoviště jsou na
- <http://127.0.0.1:8000/hra/stanoviste/1/>,
- <http://127.0.0.1:8000/hra/stanoviste/2/>,
- <http://127.0.0.1:8000/hra/stanoviste/3/>,
- <http://127.0.0.1:8000/hra/stanoviste/4/>,
- <http://127.0.0.1:8000/hra/stanoviste/5/>,
- <http://127.0.0.1:8000/hra/stanoviste/6/>
- jinak se musí na každém zařízení přihlásit (na <http://127.0.0.1:8000/>) jako stanoviště (viz 'Hesla' níže)
- tým po přihlášení (na <http://127.0.0.1:8000/>) vidí svoji domovskou stránku a po načtení qr kódu stanoviště příslušné stanoviště
Skvělou zábavu :)
## Vyšetřování ztráty třídní knihy specific věci
***Neprocházet tyto části kódu, pokud chcete hru testovat!!!***
- `data/stanoviste.json` (aktuálně pouze názvy stanovišť a viditelný popis stanovišť + název templatu)
- `hra/templates/hra/stanoviste/*` (kromě `**/base`, logika za zobrazováním textů)
- `hra/logika.py` (logika za tlačítky, tedy kdy mají být která tlačítka a jak se má změnit inventář po kliknutí na ně)
- `hra/templates/hra/inventar.html` (itemy, které může tým vlastnit)
- `hra/models.py` (od komentáře `# Inventář`, databázový záznam inventáře)
- `hra/migrations/*` (vyplývá z předchozího)
## Hesla
(uzivatelske_jmeno: heslo)
- `admin`: `modraplaneta`
- stanoviste
- `s1, s2, ..., sn`: `asd`
- tymy:
- `t1`: `azurova`
- `t2`: `okrova`
- `t3`: `tyrkysova`
- `t4`: `kastanova`
- `t5`: `oriskova`
- `t6`: `zulova`
- `t7`: `matova`
- `t8`: `smaragdova`
- `t9`: `cihlova`

1
autentizace/__init__.py

@ -0,0 +1 @@
# Inspirováno kódem webu M&M

28
autentizace/templates/autentizace/login.html

@ -0,0 +1,28 @@
{% extends "base.html" %}
{% load static %}
{% block content %}
<h1>{{ settings.NAZEV }}</h1>
<h2>{% block nadpis %}Přihlášení{% endblock %}</h2>
{# Obšlehnuto z Admina :-) #}
{% if user.is_authenticated %} {# Tedy když byl uživatel na stránku přesměrován z pro něj nepřístupné části webu. FIXME? Nedává smysl, pokud uživatel otevřel mam.mff.cuni.cz/prihlasit například z historie prohlížeče = nebyl na ni přesměrován. #}
<p>K této stránce nejspíš nemáte přístup. Můžete se zkusit přihlásit jako uživatel, který přístup má.</p>
{% endif %}
<form action="{% url 'login' %}" method="post">
{% csrf_token %}
<table class="form">
{{ form.as_table }}
</table>
<br>
{# Django si posílá jméno další stránky jako obsah formuláře a výchozí hodnota (mi přišlo, že) nejde změnit... #}
<input type="hidden" name='next' value="{{ next }}">
<input type="submit" value="Přihlásit">
</form>
{#<br><hr>#}
{#<form action="{% url 'registrace' %}">#}
{# <input type="submit" value="Registrovat"/>#}
{#</form>#}
{% endblock %}

8
autentizace/templates/autentizace/logout.html

@ -0,0 +1,8 @@
{% extends "base.html" %}
{% load static %}
{% block content %}
<h1>{% block nadpis %}Odhlášení{% endblock %}</h1>
<p>Byl jsi úspěšně odhlášen</p>
{% endblock %}

10
autentizace/urls.py

@ -0,0 +1,10 @@
from django.urls import path
from . import views
from django.views.generic.base import RedirectView
urlpatterns = [
path("prihlasit/", views.LoginView.as_view(), name="login"),
path("login/", RedirectView.as_view(pattern_name="login", permanent=True, query_string=True)),
path("odhlasit/", views.LogoutView.as_view(), name="logout"),
path("logout/", RedirectView.as_view(pattern_name="logout", permanent=True, query_string=True)),
]

14
autentizace/views.py

@ -0,0 +1,14 @@
from django.urls import reverse_lazy
from django.contrib.auth import views as auth_views
class LoginView(auth_views.LoginView):
# Jen vezmeme vestavěný a dáme mu vhodný template a přesměrovací URL
template_name = "autentizace/login.html"
class LogoutView(auth_views.LogoutView):
# Jen vezmeme vestavěný a dáme mu vhodný template a přesměrovací URL
template_name = "autentizace/logout.html"
# Pavel: Vůbec nevím, proč to s _lazy funguje, ale bez toho to bylo rozbité.
next_page = reverse_lazy("login")

0
byrokracie/__init__.py

6
byrokracie/context_processor.py

@ -0,0 +1,6 @@
from django.conf import settings
def add_settings(_):
""" Přidá `settings` do templatů. """
return {"settings": settings}

99
byrokracie/settings.py

@ -0,0 +1,99 @@
from pathlib import Path
# Build paths inside the project like this: BASE_DIR / "subdir".
BASE_DIR = Path(__file__).resolve().parent.parent
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = "django-insecure-opcz6rfrp-osm7wnd@3_5yb56^4=!@7@-(38j^!6^ej%88r2ud"
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
# ALLOWED_HOSTS = ['192.168.137.1' ]
WSGI_APPLICATION = 'byrokracie.wsgi.application'
STATIC_ROOT = 'static/'
STATIC_URL = 'static/'
# Jednotlivé části djanga
INSTALLED_APPS = [
"django.contrib.admin",
"django.contrib.auth",
"django.contrib.contenttypes",
"django.contrib.sessions",
"django.contrib.messages",
"django.contrib.staticfiles",
"byrokracie",
"autentizace",
"hra",
]
MIDDLEWARE = [
"django.middleware.security.SecurityMiddleware",
"django.contrib.sessions.middleware.SessionMiddleware",
"django.middleware.common.CommonMiddleware",
"django.middleware.csrf.CsrfViewMiddleware",
"django.contrib.auth.middleware.AuthenticationMiddleware",
"django.contrib.messages.middleware.MessageMiddleware",
"django.middleware.clickjacking.XFrameOptionsMiddleware",
]
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
'byrokracie.context_processor.add_settings'
],
},
},
]
# Databáze
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'db.sqlite3',
}
}
# Lokalizace
LANGUAGE_CODE = 'cs'
TIME_ZONE = 'Europe/Prague'
USE_L10N = True # S přechodem k djangu>=4 lze smazat (localized formatting)
USE_TZ = True # S přechodem k djangu>=5 lze smazat (timezone aware datetimes)
# URL pro přihlášení (default je account/login)
LOGIN_URL = 'login'
LOGIN_REDIRECT_URL = '/hra/'
# Konstantny
NAZEV: str = "Vyšetřování ztráty třídní knihy"
NEJVYSSI_INDEX: int = 6
PRVNI_PONDELI: int = 2
PRVNI_NOV: int = 13
TIMEOUT_STANOVISTE: int = 60 # v sekundách
TYM_AUTO_RELOAD: int = 30 # v sekundách
TIME_SPEED: float = 1.0 # 1 = hodina za minutu
DEN: tuple[float, float] = (8.0, 18.0)
POCET_BANANU: int = 2
POSTOVNA_DEN_V_TYDNU = 3
STASTNA_CISLA: set[int] = {1, 7, 10, 13, 19, 23, 28, 31, 32, 44, 49, 68, 70, 79, 82, 86, 91, 94, 97, 100, 103, 109, 129, 130, 133, 139, 167, 176, 188, 190, 192, 193, 203, 208, 219, 226, 230, 236, 239, 262, 263, 280, 291, 293, 301, 302, 310, 313, 319, 320, 326, 329, 331, 338, 356, 362, 365, 367, 368, 376, 379, 383, 386, 391, 392, 397, 404, 409, 440, 446, 464, 469, 478, 487, 490, 496, 536, 556, 563, 565, 566, 608, 617, 622, 623, 632, 635, 637, 638, 644, 649, 653, 655, 656, 665, 671, 673, 680, 683, 694, 700, 709, 716, 736, 739, 748, 761, 763, 784, 790, 793, 802, 806, 818, 820, 833, 836, 847, 860, 863, 874, 881, 888, 899, 901, 904, 907, 910, 912, 913, 921, 923, 931, 932, 937, 940, 946, 964, 970, 973, 989, 998, 1000}
# nejasno = [1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0]
# [nejasno[i] * (random.choice(list(range(3))) + 1) for i in range(len(nejasno))]
POCASI: list[int] = [3, 1, 3, 2, 2, 2, 0, 3, 2, 1, 0, 2, 1, 0, 3, 2, 3, 0, 2, 3, 3, 1, 2, 0]
# Django musí mít nastaveno Autofield
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
# A musí mít nastavené počáteční urls
ROOT_URLCONF = 'byrokracie.urls'

39
byrokracie/static/css/base.css

@ -0,0 +1,39 @@
table {
margin-left: auto;
margin-right: auto;
}
th {
text-align: right;
}
.content {
text-align: center;
}
.ds { /* Zobrazení datové schránky na stránce týmu */
margin-top: -1em;
}
.inventar {
width: fit-content;
margin-left: auto;
margin-right: auto;
}
.polozka_inventare {
float: left;
min-width: 10vw;
min-height: 15vw;
border: orange 1pt solid;
margin: 1vh;
display: flex;
align-items: center;
justify-content: center;
}
li {
text-align: left;
}

BIN
byrokracie/static/images/icon.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 84 KiB

26
byrokracie/templates/base.html

@ -0,0 +1,26 @@
{% load static %}
<!DOCTYPE html>
<html lang="cs">
<head>
<title>{% block nadpis %}{% endblock %}</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link href="{% static 'images/icon.png' %}" rel="shortcut icon" type="image/x-icon">
<link href="{% static 'css/base.css' %}?version=1" rel="stylesheet">
{# (Autoreload:) <meta http-equiv="refresh" content="30"> = Refresh every 30 second #}
{% block prenacitani %}{% endblock %}
{# script specifický pro stránku #}
{% block script %}{% endblock %}
</head>
<body>
<div class='content'>
{% block content %}
{% endblock content %}
</div>
</body>
</html>

10
byrokracie/urls.py

@ -0,0 +1,10 @@
from django.contrib import admin
from django.shortcuts import redirect
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('autentizace.urls')),
path('hra/', include('hra.urls')),
path('', lambda _request: redirect('hra/', permanent=True)),
]

16
byrokracie/wsgi.py

@ -0,0 +1,16 @@
"""
WSGI config for byrokracie project.
It exposes the WSGI callable as a module-level variable named ``application``.
For more information on this file, see
https://docs.djangoproject.com/en/4.2/howto/deployment/wsgi/
"""
import os
from django.core.wsgi import get_wsgi_application
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'byrokracie.settings')
application = get_wsgi_application()

68
data/stanoviste.json

@ -0,0 +1,68 @@
[
{
"fields": {
"nazev": "Časový výr 🦉",
"poradi": 1,
"user": 2,
"verejny_text": "<p>Posun ve čtvrté dimenzi.</p>",
"template": "casovy_vyr.html"
},
"model": "hra.stanoviste",
"pk": 1
},
{
"fields": {
"nazev": "Internát",
"poradi": 2,
"user": 3,
"verejny_text": "<p>Domov, sladký domov.</p>",
"template": "domov.html"
},
"model": "hra.stanoviste",
"pk": 2
},
{
"fields": {
"nazev": "Potrubní pošta akademie ✉️🪠",
"poradi": 3,
"user": 4,
"verejny_text": "<p>Podobnost s Českou poštou je čistě náhodná.</p><p>Otevřeno: nonstop</p>",
"template": "posta.html"
},
"model": "hra.stanoviste",
"pk": 3
},
{
"fields": {
"nazev": "Studijní oddělení",
"poradi": 4,
"user": 5,
"verejny_text": "<p>Žádosti všeho druhu.</p><p>Otevřeno: Po–Čt: 8:00 – 16:00, Pá: 8:00 – 14:00</p>",
"template": "studijni.html"
},
"model": "hra.stanoviste",
"pk": 4
},
{
"fields": {
"nazev": "Jídelna",
"poradi": 5,
"user": 6,
"verejny_text": "<p>\uD83E\uDD6C\uD83E\uDD57\uD83C\uDF55\uD83E\uDD53\uD83C\uDF69\uD83C\uDF68\uD83C\uDF6A\uD83C\uDF82\uD83C\uDF4C\uD83C\uDF70\uD83C\uDF6B\uD83C\uDF67\uD83C\uDF66\uD83C\uDF61\uD83C\uDF60\uD83E\uDED1</p><p>Vaříme denně</p><p>Snídaně: 8:00 – 11:00</p><p>Obědy: 11:00 – 17:00</p><p>Večeře: 17:00 – 22:00</p>",
"template": "menza.html"
},
"model": "hra.stanoviste",
"pk": 5
},
{
"fields": {
"nazev": "Knihovna & Archiv",
"poradi": 6,
"user": 7,
"verejny_text": "<p>Otevřeno: Po–Čt: 8:00 – 16:00, Pá: 8:00 – 14:00</p><p>Ook!?</p>",
"template": "archiv.html"
},
"model": "hra.stanoviste",
"pk": 6
}
]

83
data/tymy.json

@ -0,0 +1,83 @@
[
{
"fields": {
"hraci": "",
"nazev": "Nepojmenovaní",
"user": 8
},
"model": "hra.tym",
"pk": 1
},
{
"fields": {
"hraci": "",
"nazev": "Nepojmenovaní",
"user": 9
},
"model": "hra.tym",
"pk": 2
},
{
"fields": {
"hraci": "",
"nazev": "Nepojmenovaní",
"user": 10
},
"model": "hra.tym",
"pk": 3
},
{
"fields": {
"hraci": "",
"nazev": "Nepojmenovaní",
"user": 11
},
"model": "hra.tym",
"pk": 4
},
{
"fields": {
"hraci": "",
"nazev": "Nepojmenovaní",
"user": 12
},
"model": "hra.tym",
"pk": 5
},
{
"fields": {
"hraci": "",
"nazev": "Nepojmenovaní",
"user": 13
},
"model": "hra.tym",
"pk": 6
},
{
"fields": {
"hraci": "",
"nazev": "Nepojmenovaní",
"user": 14
},
"model": "hra.tym",
"pk": 7
},
{
"fields": {
"hraci": "",
"nazev": "Nepojmenovaní",
"user": 15
},
"model": "hra.tym",
"pk": 8
},
{
"fields": {
"hraci": "",
"nazev": "Nepojmenovaní",
"user": 16
},
"model": "hra.tym",
"pk": 9
}
]

178
data/users.json

@ -0,0 +1,178 @@
[
{
"fields": {
"is_active": true,
"is_staff": true,
"is_superuser": true,
"password": "pbkdf2_sha256$600000$yylg8H03nCOuMEmo7X4V6A$Za9qMeX44zvjYtIwyAlw5QLJQCEy+vDxlXDRAZqhZ2c=",
"username": "admin"
},
"model": "auth.user",
"pk": 1
},
{
"fields": {
"is_active": true,
"is_staff": false,
"is_superuser": false,
"password": "pbkdf2_sha256$600000$BdSaaScmKfh6Qu9xoEqp3f$96RlyOuK8lHlD5hVpfDEYJVmSxaby3O8wIT0/1MQM1k=",
"username": "s1"
},
"model": "auth.user",
"pk": 2
},
{
"fields": {
"is_active": true,
"is_staff": false,
"is_superuser": false,
"password": "pbkdf2_sha256$600000$BdSaaScmKfh6Qu9xoEqp3f$96RlyOuK8lHlD5hVpfDEYJVmSxaby3O8wIT0/1MQM1k=",
"username": "s2"
},
"model": "auth.user",
"pk": 3
},
{
"fields": {
"is_active": true,
"is_staff": false,
"is_superuser": false,
"password": "pbkdf2_sha256$600000$BdSaaScmKfh6Qu9xoEqp3f$96RlyOuK8lHlD5hVpfDEYJVmSxaby3O8wIT0/1MQM1k=",
"username": "s3"
},
"model": "auth.user",
"pk": 4
},
{
"fields": {
"is_active": true,
"is_staff": false,
"is_superuser": false,
"password": "pbkdf2_sha256$600000$BdSaaScmKfh6Qu9xoEqp3f$96RlyOuK8lHlD5hVpfDEYJVmSxaby3O8wIT0/1MQM1k=",
"username": "s4"
},
"model": "auth.user",
"pk": 5
},
{
"fields": {
"is_active": true,
"is_staff": false,
"is_superuser": false,
"password": "pbkdf2_sha256$600000$BdSaaScmKfh6Qu9xoEqp3f$96RlyOuK8lHlD5hVpfDEYJVmSxaby3O8wIT0/1MQM1k=",
"username": "s5"
},
"model": "auth.user",
"pk": 6
},
{
"fields": {
"is_active": true,
"is_staff": false,
"is_superuser": false,
"password": "pbkdf2_sha256$600000$BdSaaScmKfh6Qu9xoEqp3f$96RlyOuK8lHlD5hVpfDEYJVmSxaby3O8wIT0/1MQM1k=",
"username": "s6"
},
"model": "auth.user",
"pk": 7
},
{
"fields": {
"is_active": true,
"is_staff": false,
"is_superuser": false,
"password": "pbkdf2_sha256$600000$y2SzlaUhvzQx5MAaAPg8eR$x99VQuFAPQymfie3rNJQPIXJlmJZZ7GaX2Q8CoS+HLo=",
"username": "t1"
},
"model": "auth.user",
"pk": 8
},
{
"fields": {
"is_active": true,
"is_staff": false,
"is_superuser": false,
"password": "pbkdf2_sha256$600000$Dzb2XnXVY2FClbyYXXOD8V$MhI+tOVRXZBfY8OPaaWG1WMk0vMrIDpZguMp/X0udv8=",
"username": "t2"
},
"model": "auth.user",
"pk": 9
},
{
"fields": {
"is_active": true,
"is_staff": false,
"is_superuser": false,
"password": "pbkdf2_sha256$600000$uePowUck1EhZwiWMK3TJ2c$zGAiYICEZ46pSxtO8MF/cAawcT+UnazBcWjAiYFbTFk=",
"username": "t3"
},
"model": "auth.user",
"pk": 10
},
{
"fields": {
"is_active": true,
"is_staff": false,
"is_superuser": false,
"password": "pbkdf2_sha256$600000$UiEFSvRON6lWpzpp3eqlsS$CKvQO8L2Aelucmjp1Aoz5qTe8T18jMWGE4qH72Cihpw=",
"username": "t4"
},
"model": "auth.user",
"pk": 11
},
{
"fields": {
"is_active": true,
"is_staff": false,
"is_superuser": false,
"password": "pbkdf2_sha256$600000$L01lowUNtthy22eTVLaUrz$dp0SKguUiN5wM7gHhpW6WDhGofdIHdRj3aOQfrlih2o=",
"username": "t5"
},
"model": "auth.user",
"pk": 12
},
{
"fields": {
"is_active": true,
"is_staff": false,
"is_superuser": false,
"password": "pbkdf2_sha256$600000$sOCpNhTjfrLwoA9p1KlxAf$GA6njrzD0qj6Sxa/6wWVm83CdDhZw4gjg5SMKYPtKPo=",
"username": "t6"
},
"model": "auth.user",
"pk": 13
},
{
"fields": {
"is_active": true,
"is_staff": false,
"is_superuser": false,
"password": "pbkdf2_sha256$600000$cUKVLoph1a4lo8vWsexHV0$9bhzUjWezsPhZFmNG7gjg4WCR2Yrou68yDG+4joUXF0=",
"username": "t7"
},
"model": "auth.user",
"pk": 14
},
{
"fields": {
"is_active": true,
"is_staff": false,
"is_superuser": false,
"password": "pbkdf2_sha256$600000$zoKRalZSKeVYAe9yKq0Jk2$1sS7fJsiUUweoFL8nb7WLv0ZIEpnkpSjYLL2HLev/Gg=",
"username": "t8"
},
"model": "auth.user",
"pk": 15
},
{
"fields": {
"is_active": true,
"is_staff": false,
"is_superuser": false,
"password": "pbkdf2_sha256$600000$N27QVcoRhkcto7JA3ygQtG$1QSCWn8Lyk+UiJyJDYr3WWYbcPP8YqGxQj4JoeyAxoc=",
"username": "t9"
},
"model": "auth.user",
"pk": 16
}
]

12
fix_json.py

@ -0,0 +1,12 @@
#!/usr/bin/python3
import json
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("input", type=argparse.FileType('r', encoding='utf-8'))
parser.add_argument('output', type=argparse.FileType('w', encoding='utf-8'))
args = parser.parse_args()
data = json.load(args.input)
json.dump(data, args.output, ensure_ascii=False, sort_keys=True, indent='\t')

0
hra/__init__.py

14
hra/admin.py

@ -0,0 +1,14 @@
from django.contrib import admin
from .models import Stanoviste, Tym, Zadost
admin.site.register(Stanoviste)
admin.site.register(Zadost)
class TymAdmin(admin.ModelAdmin):
# fields = ["user", "nazev", "hraci", "start"]
pass
admin.site.register(Tym, TymAdmin)

260
hra/logika.py

@ -0,0 +1,260 @@
from random import random
from typing import Callable, Iterable
from django.conf import settings
from .models import Tym, Zadost
from .utils.doba import Doba
# Velká část logiky je přímo na `.models.Tym`!
def zadosti(tym: Tym, akce: str, choices: list, zadost: str = "Žádost", suffix: str = ""):
choices.append(f"{akce} {zadost}{suffix}")
if tym.zadosti.filter(typ=Zadost.TYP_BANAN, vyzvednuta=True).count() == 0:
choices.append(f"{akce} {zadost} o banán{suffix}")
if tym.vi_o_mase and tym.zadosti.filter(typ=Zadost.TYP_VEGE, vyzvednuta=True).count() == 0:
choices.append(f"{akce} {zadost} o více vegetariánskou stravu v jídelně{suffix}")
if tym.vi_o_zadosti and tym.zadosti.filter(typ=Zadost.TYP_TK, vyzvednuta=True, plati=True).count() == 0:
choices.append(f"{akce} {zadost} o přístup k záznamům o třídní knize{suffix}")
def get_posible_choices_casovy_vyr(doba: Doba, tym: Tym) -> list[str]:
choices = ["Dopředu o hodinu", "Dopředu o 3 hodiny"]
if doba > 3*doba.DEN:
choices.append("Dopředu o den")
if doba > doba.TYDEN:
choices.append("Dopředu o týden")
if tym.tk_nalezena is not None:
choices += ["Dozadu o hodinu", "Dozadu o 3 hodiny", "Dozadu o den", "Dozadu o týden"]
return choices
def get_posible_choices_domov(doba: Doba, tym: Tym) -> list[str]:
choices = []
if tym.nevyzvednute_zadosti.count() > 0:
choices.append("Vyzvednout poštu")
if doba.je_noc:
choices.append("Spát")
if not tym.ma_penize:
choices.append("Vzít si chechtáky")
zadosti(tym, "Tisk", choices, zadost="Žádosti")
if tym.ma_ds:
zadosti(tym, "Podat", choices, suffix=" datovou schránkou")
return choices
def get_posible_choices_posta(doba: Doba, tym: Tym) -> list[str]:
if tym.ds_bad is not None and doba - tym.ds_bad < 5:
return []
choices = []
if tym.zadosti.filter(vytistena=True).count() > 0:
choices.append("Poslat vytištěné žádosti")
if tym.nevyzvednute_zadosti.count() > 0:
choices.append("Zeptat se, zda pro nás mají dopis")
if not tym.ma_ds:
choices.append("Zažádat o datovou schránku")
if tym.ds_bad is not None:
choices.append("Zažádat o datovou schránku (šeptem)")
return choices
def get_posible_choices_studijni(doba: Doba, tym: Tym) -> list[str]:
if (
doba.cas < 8 or
doba.cas > 15 or
doba.den_v_tydnu >= 6 or
(doba.den_v_tydnu == 5 and doba.cas > 13)
):
return []
choices = []
zadosti(tym, "Podat", choices)
if tym.nevyzvednute_zadosti.count():
choices.append("Zeptat se na poslané žádosti")
return choices
def get_posible_choices_jidelna(doba: Doba, tym: Tym) -> list[str]:
if doba.cas < 8 or doba.cas > 22:
return []
choices = ["Klábosit"]
if tym.pocet_bananu < settings.POCET_BANANU:
choices.append("Poprosit o banán")
return choices
def get_posible_choices_archiv(doba: Doba, tym: Tym) -> list[str]:
choices = []
if not (
doba.cas < 8 or
doba.cas > 16 or
doba.den_v_tydnu >= 6 or
(doba.den_v_tydnu == 5 and doba.cas > 14)
):
if tym.ma_penize:
choices.append("Uplatit knihovníka penězi")
if tym.pocet_bananu > 0 and not tym.vi_o_zadosti:
choices.append("Dát banán knihovníkovi")
if tym.ma_platnou_zadost(Zadost.TYP_TK) and tym.tk_nalezena is None:
choices.append("Ukázat knihovníkovi povolení k náhledu do záznamů o třídní knize")
choices.append("Přelézt pult a sami si vzít záznamy o třídní knize")
choices.append("Pokusit se sami projít kartotéku")
return choices
def banan(_: Doba, tym: Tym) -> None:
if (tym.pocet_bananu == 0 and tym.ma_platnou_zadost(Zadost.TYP_BANAN)) or tym.ma_platnou_zadost(Zadost.TYP_VEGE):
tym.pocet_bananu += 1
tym.save()
if tym.pocet_bananu == 1:
print("ALERT: BANÁN!!!")
elif tym.pocet_bananu == 1:
tym.vi_o_mase = True
tym.save()
elif tym.pocet_bananu == 0:
tym.ovesna_kase += 1
tym.save()
get_posible_choices: list[Callable[[Doba, Tym], list[str]]] = [
# 0:
None, # poradi začíná od 1
# 1:
get_posible_choices_casovy_vyr,
# 2:
get_posible_choices_domov,
# 3:
get_posible_choices_posta,
# 4:
get_posible_choices_studijni,
# 5:
get_posible_choices_jidelna,
# 6:
get_posible_choices_archiv,
]
apply_choice: list[dict[str, Callable[[Doba, Tym], Iterable[Zadost] | None]]] = [
# 0:
{}, # poradi začíná od 1
# 1:
{
"Dopředu o hodinu":
lambda _, tym: tym.pricti_cas_a_uloz(1),
"Dopředu o 3 hodiny":
lambda _, tym: tym.pricti_cas_a_uloz(3),
"Dopředu o den":
lambda _, tym: tym.pricti_cas_a_uloz(Doba.DEN),
"Dopředu o týden":
lambda _, tym: tym.pricti_cas_a_uloz(Doba.TYDEN),
"Dozadu o hodinu":
lambda _, tym: tym.pricti_cas_a_uloz(-1),
"Dozadu o 3 hodiny":
lambda _, tym: tym.pricti_cas_a_uloz(-3),
"Dozadu o den":
lambda _, tym: tym.pricti_cas_a_uloz(-Doba.DEN),
"Dozadu o týden":
lambda _, tym: tym.pricti_cas_a_uloz(-Doba.TYDEN),
},
# 2:
{
"Vyzvednout poštu":
lambda doba, tym: tym.vyzvedni_internat(doba),
"Spát":
lambda doba, tym: tym.pricti_cas_a_uloz((settings.DEN[0] - doba.cas + 24) % 24),
"Vzít si chechtáky":
lambda _, tym: tym.nastav_a_uloz("ma_penize", True),
"Tisk Žádosti":
lambda _, tym: tym.vytiskni_zadost(Zadost.TYP_ZADOST),
"Tisk Žádosti o banán":
lambda _, tym: tym.vytiskni_zadost(Zadost.TYP_BANAN),
"Tisk Žádosti o více vegetariánskou stravu v jídelně":
lambda _, tym: tym.vytiskni_zadost(Zadost.TYP_VEGE),
"Tisk Žádosti o přístup k záznamům o třídní knize":
lambda _, tym: tym.vytiskni_zadost(Zadost.TYP_TK),
"Podat Žádost datovou schránkou":
lambda doba, tym: tym.posli_zadost_ds(doba, Zadost.TYP_ZADOST),
"Podat Žádost o banán datovou schránkou":
lambda doba, tym: tym.posli_zadost_ds(doba, Zadost.TYP_BANAN),
"Podat Žádost o více vegetariánskou stravu v jídelně datovou schránkou":
lambda doba, tym: tym.posli_zadost_ds(doba, Zadost.TYP_VEGE),
"Podat Žádost o přístup k záznamům o třídní knize datovou schránkou":
lambda doba, tym: tym.posli_zadost_ds(doba, Zadost.TYP_TK),
},
# 3:
{
"Poslat vytištěné žádosti":
lambda doba, tym: tym.poslat_vytistene_zadosti(doba),
"Zeptat se, zda pro nás mají dopis":
lambda doba, tym: tym.vyzvedni_postu(doba),
"Zažádat o datovou schránku":
lambda doba, tym: tym.nastav_a_uloz("ds_bad", doba),
"Zažádat o datovou schránku (šeptem)":
lambda doba, tym: tym.nastav_a_uloz("ma_ds", True),
},
# 4:
{
"Podat Žádost":
lambda doba, tym: tym.posli_zadost_studijni(doba, Zadost.TYP_ZADOST),
"Podat Žádost o banán":
lambda doba, tym: tym.posli_zadost_studijni(doba, Zadost.TYP_BANAN),
"Podat Žádost o více vegetariánskou stravu v jídelně":
lambda doba, tym: tym.posli_zadost_studijni(doba, Zadost.TYP_VEGE),
"Podat Žádost o přístup k záznamům o třídní knize":
lambda doba, tym: tym.posli_zadost_studijni(doba, Zadost.TYP_TK),
"Zeptat se na poslané žádosti":
lambda doba, tym: tym.nevyzvednute_zadosti.filter(studijni__lte=doba)
},
# 5:
{
"Klábosit":
lambda *_: None, # NOOP
"Poprosit o banán":
banan,
},
# 6:
{
"Uplatit knihovníka penězi":
lambda *_: None, # NOOP
"Dát banán knihovníkovi":
lambda _, tym: tym.nastav_a_uloz("vi_o_zadosti", tym.pocet_bananu == settings.POCET_BANANU),
"Ukázat knihovníkovi povolení k náhledu do záznamů o třídní knize":
lambda doba, tym: tym.nastav_a_uloz("tk_nalezena", doba),
"Přelézt pult a sami si vzít záznamy o třídní knize":
lambda _, tym: tym.pricti_cas_a_uloz(random()*5),
"Pokusit se sami projít kartotéku":
lambda _, tym: tym.pricti_cas_a_uloz(5),
},
]

53
hra/migrations/0001_initial.py

@ -0,0 +1,53 @@
# Generated by Django 4.2.4 on 2023-08-14 17:51
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
import django.utils.timezone
class Migration(migrations.Migration):
initial = True
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='Tym',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('nazev', models.CharField(max_length=256, verbose_name='název týmu')),
('hraci', models.CharField(blank=True, help_text='seznam hráčů v týmu', max_length=256, null=True, verbose_name='hráči')),
('start', models.DateTimeField(default=django.utils.timezone.now, help_text='čas, kdy tým začal', verbose_name='začátek hry')),
('delta', models.FloatField(default=0, help_text='čas (v hodinách), o který se tým posunul svými akcemi', verbose_name='přidaný čas')),
('tk_nalezena', models.FloatField(blank=True, default=None, help_text='čas (v hodinách), kdy tým nalezl třídní knihu (pro pořadí a rozhodnutí, zda ji tým už nalezl)', null=True, verbose_name='čas nalezení třídní knihy')),
('ma_penize', models.BooleanField(default=False, help_text='zda si tým vzal peníze a neztratil je', verbose_name='má tým peníze')),
('ds_bad', models.FloatField(blank=True, default=None, help_text='čas, kdy tým špatným způsobem požádal o datovou schránku', null=True, verbose_name='čas chybného založení ds')),
('user', models.OneToOneField(on_delete=django.db.models.deletion.PROTECT, to=settings.AUTH_USER_MODEL, verbose_name='uživatel')),
],
options={
'verbose_name': 'Tým',
'verbose_name_plural': 'Týmy',
'db_table': 'hra_tym',
},
),
migrations.CreateModel(
name='Stanoviste',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('nazev', models.CharField(help_text='zobrazovaný název stanoviště', max_length=256, verbose_name='název')),
('verejny_text', models.TextField(help_text='zobrazovaný název stanoviště', max_length=256, verbose_name='veřejný text')),
('poradi', models.IntegerField(help_text='číselné označení stanoviště (aby hráči věděli, že našli všechny)', verbose_name='pořadí')),
('template', models.CharField(default='base.html', help_text='template tohoto stanoviště (co vidí tým)', max_length=256, verbose_name='template')),
('user', models.OneToOneField(help_text='Stanoviště musí být přihlášena, aby se nedala přepínat stanoviště', on_delete=django.db.models.deletion.PROTECT, to=settings.AUTH_USER_MODEL, verbose_name='uživatel')),
],
options={
'verbose_name': 'Stanoviště',
'verbose_name_plural': 'Stanoviště',
'db_table': 'hra_stanoviste',
},
),
]

23
hra/migrations/0002_tym_ma_ds_tym_pocet_bananu.py

@ -0,0 +1,23 @@
# Generated by Django 4.2.4 on 2023-08-14 18:28
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('hra', '0001_initial'),
]
operations = [
migrations.AddField(
model_name='tym',
name='ma_ds',
field=models.BooleanField(default=False, help_text='zda si tým založil datovou schránku', verbose_name='má tým ds'),
),
migrations.AddField(
model_name='tym',
name='pocet_bananu',
field=models.IntegerField(default=0, help_text='kolik vlastní tým bánánů', verbose_name='počet banánů'),
),
]

23
hra/migrations/0003_tym_vi_o_mesici_tym_vi_o_zadosti.py

@ -0,0 +1,23 @@
# Generated by Django 4.2.4 on 2023-08-14 19:59
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('hra', '0002_tym_ma_ds_tym_pocet_bananu'),
]
operations = [
migrations.AddField(
model_name='tym',
name='vi_o_mesici',
field=models.BooleanField(default=False, help_text='ví tým o tom, že žádost o náhled do archivů se může podávat pouze za svitu měsíce', verbose_name='ví tým o svitu měsíce'),
),
migrations.AddField(
model_name='tym',
name='vi_o_zadosti',
field=models.BooleanField(default=False, help_text='ví tým o tom, že potřebuje žádost o náhled do archivů', verbose_name='ví tým o finální žádosti'),
),
]

33
hra/migrations/0004_zadost.py

@ -0,0 +1,33 @@
# Generated by Django 4.2.4 on 2023-10-05 10:15
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('hra', '0003_tym_vi_o_mesici_tym_vi_o_zadosti'),
]
operations = [
migrations.CreateModel(
name='Zadost',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('typ', models.CharField(choices=[('zadost', 'Žádost'), ('banan', 'Žádost o banán'), ('vege', 'Žádost o více vegetariánskou stravu v jídelně'), ('tk', 'Žádost o přístup k záznamům o třídní knize')], max_length=8, verbose_name='toto je ')),
('studijni', models.FloatField(blank=True, default=None, null=True, verbose_name='žádost dorazí na studijní')),
('posta', models.FloatField(blank=True, default=None, help_text='(do self.internat)', null=True, verbose_name='tým si může žádost vyzvednout na internátu od')),
('internat', models.FloatField(blank=True, default=None, null=True, verbose_name='tým si může žádost vyzvednout na poště od')),
('vyzvednuta', models.BooleanField(default=False, verbose_name='tým si tuto žádost již vyzvedl')),
('vytistena', models.BooleanField(default=False, verbose_name='tým má u sebe tuto žádost vytištěnou')),
('plati', models.BooleanField(default=True, help_text='Aktuálně má uplatnění pouze u Žádosti o přístup k záznamům o třídní knize', verbose_name='tato žádost platí')),
('tym', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='zadosti', to='hra.tym')),
],
options={
'verbose_name': 'Žádost',
'verbose_name_plural': 'Žádosti',
'db_table': 'hra_zadost',
},
),
]

18
hra/migrations/0005_tym_vi_o_mase.py

@ -0,0 +1,18 @@
# Generated by Django 4.2.4 on 2023-10-06 14:05
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('hra', '0004_zadost'),
]
operations = [
migrations.AddField(
model_name='tym',
name='vi_o_mase',
field=models.BooleanField(default=False, help_text='už se ptali v jídelně na další banán a zjistili, že je jen MaSo', verbose_name='ví o tom, že v jídelně už není banán'),
),
]

18
hra/migrations/0006_tym_ovesna_kase.py

@ -0,0 +1,18 @@
# Generated by Django 4.2.4 on 2023-10-06 20:58
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('hra', '0005_tym_vi_o_mase'),
]
operations = [
migrations.AddField(
model_name='tym',
name='ovesna_kase',
field=models.IntegerField(default=0),
),
]

0
hra/migrations/__init__.py

326
hra/models.py

@ -0,0 +1,326 @@
from random import random
from typing import Iterable
from django.conf import settings
from django.db import models
from django.db.models import QuerySet
from django.utils import timezone
from .utils.doba import Doba
class Stanoviste(models.Model):
class Meta:
db_table = "hra_stanoviste"
verbose_name = "Stanoviště"
verbose_name_plural = "Stanoviště"
user = models.OneToOneField(
settings.AUTH_USER_MODEL, blank=False, null=False, unique=True,
verbose_name="uživatel", on_delete=models.PROTECT,
help_text="Stanoviště musí být přihlášena, aby se nedala přepínat stanoviště",
)
nazev = models.CharField(
"název", max_length=256, blank=False, null=False,
help_text="zobrazovaný název stanoviště",
)
verejny_text = models.TextField(
"veřejný text", max_length=256, blank=False, null=False,
help_text="zobrazovaný název stanoviště",
)
poradi = models.IntegerField(
"pořadí", blank=False, null=False,
help_text="číselné označení stanoviště (aby hráči věděli, že našli všechny)",
)
template = models.CharField(
"template", max_length=256, blank=False, null=False, default="base.html",
help_text="template tohoto stanoviště (co vidí tým)",
)
class Tym(models.Model):
class Meta:
db_table = "hra_tym"
verbose_name = "Tým"
verbose_name_plural = "Týmy"
user = models.OneToOneField(
settings.AUTH_USER_MODEL, blank=False, null=False, unique=True,
verbose_name="uživatel", on_delete=models.PROTECT,
)
nazev = models.CharField(
"název týmu", max_length=256, blank=False, null=False,
)
hraci = models.CharField(
"hráči", max_length=256, blank=True, null=True,
help_text="seznam hráčů v týmu",
)
start = models.DateTimeField(
"začátek hry", blank=False, null=False, default=timezone.now,
help_text="čas, kdy tým začal",
)
delta = models.FloatField(
"přidaný čas", blank=False, null=False, default=0,
help_text="čas (v hodinách), o který se tým posunul svými akcemi",
)
def nastav_a_uloz(self, attr_name: str, value: object) -> "Tym":
self.__setattr__(attr_name, value)
self.save()
return self
@property
def doba(self) -> Doba:
return Doba((timezone.now() - self.start).total_seconds() / 60 * settings.TIME_SPEED + self.delta)
def pricti_cas_a_uloz(self, cas: int) -> "Tym":
self.delta += cas
self.save()
return self
# Inventář
tk_nalezena = models.FloatField(
"čas nalezení třídní knihy", blank=True, null=True, default=None,
help_text="čas (v hodinách), kdy tým nalezl třídní knihu (pro pořadí a rozhodnutí, zda ji tým už nalezl)",
)
ma_penize = models.BooleanField(
"má tým peníze", blank=False, null=False, default=False,
help_text="zda si tým vzal peníze a neztratil je",
)
ds_bad = models.FloatField(
"čas chybného založení ds", blank=True, null=True, default=None,
help_text="čas, kdy tým špatným způsobem požádal o datovou schránku",
)
ma_ds = models.BooleanField(
"má tým ds", blank=False, null=False, default=False,
help_text="zda si tým založil datovou schránku",
)
pocet_bananu = models.IntegerField(
"počet banánů", blank=False, null=False, default=0,
help_text="kolik vlastní tým bánánů",
)
vi_o_zadosti = models.BooleanField(
"ví tým o finální žádosti", blank=False, null=False, default=False,
help_text="ví tým o tom, že potřebuje žádost o náhled do archivů",
)
vi_o_mesici = models.BooleanField(
"ví tým o svitu měsíce", blank=False, null=False, default=False,
help_text="ví tým o tom, že žádost o náhled do archivů se může podávat pouze za svitu měsíce",
)
vi_o_mase = models.BooleanField(
"ví o tom, že v jídelně už není banán", blank=False, null=False, default=False,
help_text="už se ptali v jídelně na další banán a zjistili, že je jen MaSo",
)
@property
def vyzvednute_zadosti(self) -> QuerySet["Zadost"]:
return self.zadosti.filter(vyzvednuta=True)
@property
def nevyzvednute_zadosti(self) -> QuerySet["Zadost"]:
return self.zadosti.filter(studijni__isnull=False, vyzvednuta=False)
def ma_platnou_zadost(self, zadost_typ: str) -> bool:
return self.vyzvednute_zadosti.filter(typ=zadost_typ, plati=True).count() > 0
@property
def ma_platnou_zadost_o_banan(self) -> bool:
""" Protože django templates jsou hloupé """
return self.vyzvednute_zadosti.filter(typ=Zadost.TYP_BANAN, plati=True).count() > 0
@property
def ma_platnou_zadost_o_vege(self) -> bool:
""" Protože django templates jsou hloupé """
return self.vyzvednute_zadosti.filter(typ=Zadost.TYP_VEGE, plati=True).count() > 0
def zadosti_na_internatu(self, doba: Doba) -> QuerySet["Zadost"]:
return self.zadosti.filter(vyzvednuta=False, internat__isnull=False, internat__lte=doba)
def zadosti_na_poste(self, doba: Doba) -> QuerySet["Zadost"]:
return self.zadosti.filter(posta__isnull=False, posta__lte=doba, internat__isnull=False, internat__gt=doba)
@staticmethod
def studijni_otevreno_nejdrive(doba: Doba) -> Doba:
if not ( # copy-paste z logika.py
doba.cas < 8 or
doba.cas > 15 or
doba.den_v_tydnu >= 6 or
(doba.den_v_tydnu == 5 and doba.cas > 13)
):
return doba
if doba.cas > 15 or (doba.den_v_tydnu == 5 and doba.cas > 13):
doba += Doba.DEN - doba.cas + 8
else:
doba = Doba(doba.den*Doba.DEN + 8)
if doba.den_v_tydnu == 6:
doba += 2*Doba.DEN
elif doba.den_v_tydnu == 7:
doba += Doba.DEN
return doba
@staticmethod
def doruceni(doba: Doba) -> Doba:
doba += Doba.DEN - doba.cas + 8
while True:
if doba.den_v_tydnu == 6 or doba.den_v_tydnu == 7:
doba += Doba.DEN
else:
if random() > 1/3:
doba += Doba.DEN
else:
return doba
@staticmethod
def postovna(studijni: Doba) -> Doba:
return (studijni.den + (settings.POSTOVNA_DEN_V_TYDNU - studijni.den_v_tydnu + 7) % 7) * Doba.DEN + 10
def poslat_vytistene_zadosti(self, doba: Doba) -> Iterable["Zadost"]:
if not self.ma_penize:
return ()
self.ma_penize = False
self.save()
zadosti = self.zadosti.filter(vytistena=True)
for zadost in zadosti:
zadost.vytistena = False
studijni = self.doruceni(doba)
zadost.studijni = studijni
posta = Doba((studijni.den + (settings.POSTOVNA_DEN_V_TYDNU - studijni.den_v_tydnu + 7) % 7) * Doba.DEN + 10)
zadost.posta = posta
zadost.internat = self.doruceni(posta)
if zadost.typ == Zadost.TYP_TK:
zadost.plati = Zadost.plati_tk(doba)
if zadost.typ == Zadost.TYP_ZADOST:
zadost.plati = False
zadost.save()
return zadosti
def vytiskni_zadost(self, typ: str) -> tuple["Zadost"]:
return (Zadost.objects.create(tym=self, typ=typ, vytistena=True,),)
def posli_zadost_ds(self, doba: Doba, typ: str) -> tuple["Zadost"]:
studijni = doba
internat = self.studijni_otevreno_nejdrive(doba)
if typ == Zadost.TYP_TK:
return (Zadost.objects.create(tym=self, typ=typ, studijni=studijni, internat=internat, plati=Zadost.plati_tk(doba)),)
return (Zadost.objects.create(
tym=self, typ=typ, studijni=studijni, internat=internat, plati=typ != Zadost.TYP_ZADOST,
),)
def vyzvedni_internat(self, doba: Doba) -> Iterable["Zadost"]:
zadosti = self.zadosti_na_internatu(doba)
for zadost in zadosti:
zadost.vyzvednuta = True
zadost.save()
if zadost.typ == Zadost.TYP_TK:
self.vi_o_mesici = True
self.save()
return zadosti
def vyzvedni_postu(self, doba: Doba) -> Iterable["Zadost"]:
zadosti = self.zadosti_na_poste(doba)
for zadost in zadosti:
zadost.vyzvednuta = True
zadost.save()
if zadost.typ == Zadost.TYP_TK:
self.vi_o_mesici = True
self.save()
return zadosti
def posli_zadost_studijni(self, doba: Doba, typ: str) -> tuple["Zadost"]:
studijni = doba
posta = self.postovna(studijni)
internat = self.doruceni(doba)
return (Zadost.objects.create(
tym=self, typ=typ, studijni=studijni, posta=posta, internat=internat,
plati=typ != Zadost.TYP_TK and typ != Zadost.TYP_ZADOST,
),)
ovesna_kase = models.IntegerField(
blank=False, null=False, default=0,
)
class Zadost(models.Model):
class Meta:
db_table = "hra_zadost"
verbose_name = "Žádost"
verbose_name_plural = "Žádosti"
tym = models.ForeignKey(Tym, related_name="zadosti", blank=False, null=False, on_delete=models.CASCADE)
TYP_ZADOST = "zadost"
TYP_BANAN = "banan"
TYP_VEGE = "vege"
# TYP_BAD_TK = "bad_tk"
TYP_TK = "tk"
TYP_CHOICES = [
(TYP_ZADOST, "Žádost"),
(TYP_BANAN, "Žádost o banán"),
(TYP_VEGE, "Žádost o více vegetariánskou stravu v jídelně"),
# (TYP_BAD_TK, "Neplatná žádost o přístup k záznamům o třídní knize"),
(TYP_TK, "Žádost o přístup k záznamům o třídní knize"),
]
typ = models.CharField(
"toto je ", max_length=8, blank=False, null=False, choices=TYP_CHOICES,
)
studijni = models.FloatField(
"žádost dorazí na studijní", blank=True, null=True, default=None,
)
posta = models.FloatField(
"tým si může žádost vyzvednout na internátu od", blank=True, null=True, default=None,
help_text="(do self.internat)",
)
internat = models.FloatField(
"tým si může žádost vyzvednout na poště od", blank=True, null=True, default=None,
)
vyzvednuta = models.BooleanField(
"tým si tuto žádost již vyzvedl", blank=False, null=False, default=False,
)
vytistena = models.BooleanField(
"tým má u sebe tuto žádost vytištěnou",
blank=False, null=False, default=False,
)
plati = models.BooleanField(
"tato žádost platí", blank=False, null=False, default=True,
help_text="Aktuálně má uplatnění pouze u Žádosti o přístup k záznamům o třídní knize",
)
@staticmethod
def plati_tk(doba: Doba) -> bool:
return (
not doba.je_nov and
doba.je_noc and
doba.pocasi == 0
)
def __str__(self) -> str:
return {
"zadost"