Browse Source

První funkční verze

master
Jonas Havelka 1 year 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": "Žá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"
}[self.typ]

19
hra/templates/hra/inventar.html

@ -0,0 +1,19 @@
{% if tym.ma_penize %}
<div class="polozka_inventare">💰</div>
{% endif %}
{% if tym.pocet_bananu > 0 %}
{% if tym.vi_o_zadosti %}
<div class="polozka_inventare"><p>🍌</p></div>
{% else %}
<div class="polozka_inventare"><p>{% for _ in ""|center:tym.pocet_bananu %}🍌{% endfor %}</p></div>
{% endif %}
{% endif %}
{% if tym.ovesna_kase > 0 %}
<div class="polozka_inventare"><p>{% for _ in ""|center:tym.ovesna_kase %}🥘{% endfor %}</p></div>
{% endif %}

58
hra/templates/hra/stanoviste/archiv.html

@ -0,0 +1,58 @@
{% extends "hra/stanoviste/base.html" %}
{% load static %}
{% block stanoviste_content %}
{% if choice is None %}
{% if doba.je_noc %}
<div style="width: 100%; overflow: hidden">
{% for i in ""|center:666 %}<p style="width: 0; margin: 0;">Zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz</p>{% endfor %}
<p style="width: 0; white-space: nowrap; margin: 0;">🦆<span style="font-size: 50%;">🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆🦆</span></p>
{% for i in ""|center:666 %}<p style="width: 0; margin: 0;">Zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz</p>{% endfor %}
</div>
{% elif doba.cas < 8 or doba.cas > 16 or doba.den_v_tydnu >= 6 or doba.den_v_tydnu == 5 and doba.cas > 14 %}
<p>Knihovník má volno, tedy se učí <a href="https://cs.wikipedia.org/wiki/Ook!">programovat</a>:</p>
<pre>
Ook. Ook? Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook.
Ook. Ook. Ook. Ook. Ook! Ook? Ook? Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook.
Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook? Ook! Ook! Ook? Ook! Ook? Ook.
Ook! Ook. Ook. Ook? Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook.
Ook. Ook. Ook! Ook? Ook? Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook?
Ook! Ook! Ook? Ook! Ook? Ook. Ook. Ook. Ook! Ook. Ook. Ook. Ook. Ook. Ook. Ook.
Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook! Ook. Ook! Ook. Ook. Ook. Ook. Ook.
Ook. Ook. Ook! Ook. Ook. Ook? Ook. Ook? Ook. Ook? Ook. Ook. Ook. Ook. Ook. Ook.
Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook! Ook? Ook? Ook. Ook. Ook.
Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook? Ook! Ook! Ook? Ook! Ook? Ook. Ook! Ook.
Ook. Ook? Ook. Ook? Ook. Ook? Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook.
Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook! Ook? Ook? Ook. Ook. Ook.
Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook.
Ook. Ook? Ook! Ook! Ook? Ook! Ook? Ook. Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook.
Ook? Ook. Ook? Ook. Ook? Ook. Ook? Ook. Ook! Ook. Ook. Ook. Ook. Ook. Ook. Ook.
Ook! Ook. Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook.
Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook!
Ook! Ook. Ook. Ook? Ook. Ook? Ook. Ook. Ook! Ook. {# mezery pro zarovnání #}
</pre>
{% else %}
<p>Ook!!</p>
<p>(Ne, záznam o třídní knize vám nemohu dát jen tak. Zakazuje mi to <a href="https://www.zakonyprolidi.cz/cs/2019-110">Zákon č. 110/2019 Sb.</a> provádějící <a href="https://eur-lex.europa.eu/legal-content/EN/TXT/PDF/?uri=CELEX:32016R0679">nařízení EU 2016/679</a>.)</p>
{% endif %}
{% elif choice == "Uplatit knihovníka penězi" %}
<p>Eek!</p>
<p>(Že se nestydíte!)</p>
{% elif choice == "Dát banán knihovníkovi" %}
{% if tym.pocet_bananu != settings.POCET_BANANU %}
<p>Eek.</p>
<p>(Tenhle banán vypadá, jako by ve skladu ležel už několik let. Přineste mi jiný.)</p>
{% else %}
<p>Ook.</p>
<p>(Potřebuji od vás povolení, které dostanete po zažádání na studijním oddělení.)</p>
{% endif %}
{% elif choice == "Ukázat knihovníkovi povolení k náhledu do záznamů o třídní knize" %}
<p>Knihovník vám přinesl záznamy o třídní knize. Ty končí před TODO!!! lety zápisem: „Dne TODO!!! se přešlo na <a href="https://www.bakalari.cz/">Bakaláře</a></p>
<p>Gratulujeme k (ne)nalezení třídní knihy za {{ doba.den }} dní a {{ doba.hodina }} hodin!</p>
{% elif choice == "Přelézt pult a sami si vzít záznamy o třídní knize" %}
<p>Poslední, co si pamatujete je výkřik „Eek“ a hnědou šmouhu před očima. Pak už jen tma.</p>
<p>Ani nevíte, jak dlouho jste tu leželi. Ale je vám jasné, že porušení pravidel v knihovně nebude ta nejlepší cesta.</p>
{% elif choice == "Pokusit se sami projít kartotéku" %}
<p>Po pěti hodinách hledání záznamů o třídní knize v kartotéce <a href="https://arxiv.org/">arXivu [archivu]</a>, kdy nacházíte jen samé od<abbr title="p">b</abbr>orné články, se smíříte s myšlenkou, že budete potřebovat pomoc od knihovníka.</p>
{% endif %}
{% endblock %}

52
hra/templates/hra/stanoviste/base.html

@ -0,0 +1,52 @@
{% extends "base.html" %}
{% load static %}
{% block script %}
{% if not choice %}
<script>
function timeout() {
if(confirm("Prosímvás, my tu nemáme čas jen na vás, zdržujete! **Další!** \n\n (Čas vypršel, načtěte QR kód znovu...) \n\n Přejete si odejít na úvodní stranu?")) {
window.location = "{% url "tym" %}";
}
}
setTimeout(timeout, 1000 * {{ settings.TIMEOUT_STANOVISTE }});
</script>
{% endif %}
{% endblock %}
{% block nadpis %}{{ stanoviste.poradi }}/{{ settings.NEJVYSSI_INDEX }}: {{ stanoviste.nazev }}{% endblock %}
{% block content %}
<hr>
{% with doba=tym.doba %}
<h2>Den {{ doba.den }},&nbsp;&nbsp;&nbsp;{{ doba.den_v_tydnu_zkratka }},&nbsp;&nbsp;&nbsp;{{ doba.hodiny_minuty }} {{ doba.denni_doba }}</h2>
{% endwith %}
<hr>
{{ stanoviste.poradi }}/{{ settings.NEJVYSSI_INDEX }}: {% if choice is not None %}({{ choice }}){% endif %}
<h1>{{ stanoviste.nazev }}</h1>
{% block stanoviste_content %}
<p>TODO!!!</p>
{% endblock %}
<form action="" method="POST">
{% csrf_token %}
{% for choice in choices %}
<br> <br>
<input type="submit" name="choice" value="{{ choice }}">
{# {% empty %}#}
{# <br>#}
{# <h2>Žádné akce k dispozici</h2>#}
{# <p>Správně! Co můžete udělat dnes nenechávejte na zítřek.</p>#}
{% endfor %}
</form>
<p><a href="{% url "tym" %}">Stránka týmu</a></p>
{% endblock %}

10
hra/templates/hra/stanoviste/casovy_vyr.html

@ -0,0 +1,10 @@
{% extends "hra/stanoviste/base.html" %}
{% load static %}
{% block stanoviste_content %}
{% if choice is None %}
{{ stanoviste.verejny_text | safe }}
{% else %}
<p>Vžum! 🌪️</p>
{% endif %}
{% endblock %}

33
hra/templates/hra/stanoviste/domov.html

@ -0,0 +1,33 @@
{% extends "hra/stanoviste/base.html" %}
{% load static %}
{% block stanoviste_content %}
{% if choice is None %}
<p>Jste na svém pokoji. Co chcete dělat?</p>
{% elif choice == "Vyzvednout poštu" %}
{% for zadost in zadosti %}
{% if forloop.first %}
<p>Přišly odpovědi na následující žádosti:</p>
<ul>
{% endif %}
<li>{{ zadost }} ({% if zadost.plati %}vyhověno{% elif zadost.typ == zadost.TYP_ZADOST %}nevyhověno, protože <a href="https://www.mff.cuni.cz/cs/studenti/formulare/spolecne/zadost-oboustranne-na-jeden-list">není čemu</a>, to musíte nejdříve vyplnit něco, o co žádáte{% else %}nevyhověno, Žádost o přístup k záznamům o třídní knize musí být podávána za svitu měsíce{% endif %})</li>
{% if forloop.last %}
</ul>
{% endif %}
{% empty %}
Žádná pošta k vyzvednutí.
{% endfor %}
{% elif choice == "Spát" %}
<p>Vyspali jste se do růžova. Dobré ráno 🌄</p>
{% elif choice == "Vzít si chechtáky" %}
<p>Vzali jste si nějaké peníze 💰, třeba se budou na něco hodit.</p>
{% else %}
{% if zadosti.0.vytistena %}
<p>Vytiskli jste {{ zadosti.0 }}. Nyní ji můžete odeslat na poště.</p>
{% else %}
<p>Odeslali jste {{ zadosti.0|capfirst }} datovou schránkou.</p>
{% if zadosti.0.typ == zadost.TYP_TK and tym.vi_o_mesici %}{% if not doba.je_noc %}<p>(Není noc, takže měsíc nesvítí. Ale pojďme to zkusit.)</p>{% else %}{% if doba.pocasi != 0 %}<p>(Není jasno (že bychom si příště s někým o počasí promluvili?), takže žádný měsíční svit. Ale pojďme to zkusit.)</p>{% if doba.den_mesice == 0 %}<p>(Ach jo, dnes je nov, takže měsíc nesvítí. Ale pojďme to zkusit.)</p>{% endif %}{% endif %}{% endif %}{% endif %}
{% endif %}
{% endif %}
{% endblock %}

23
hra/templates/hra/stanoviste/menza.html

@ -0,0 +1,23 @@
{% extends "hra/stanoviste/base.html" %}
{% load static %}
{% block stanoviste_content %}
{% if doba.cas < 8 or doba.cas > 22 %}
Dobrou noc. 🌃
{% else %}
{% if choice == "Poprosit o banán" %}
{% if tym.pocet_bananu == 1 and tym.ma_platnou_zadost_o_banan or tym.ma_platnou_zadost_o_vege %}
<p>Na.</p>
{% elif tym.pocet_bananu == 0 %}
<p>Na banán nemáte nárok! Tumáte ovesnou kaši.</p>
{% else %}
<p>Bohužel, banán nemáme. Můžete zkusti naši Sýrovou omáčku s kuřecím a těstovinama. Vy si budete stěžovat? Tak prosím. Můžete to zkusti třeba na studijním, chachacha.</p>
{% endif %}
{% elif choice == "Klábosit" %}
<p>Stopy pan týmu matriarchálně stálých záhy takhle studnou kaple minerálů dračím třetí zvlní vliv k sekyra, nadmořských ho naopak kruhy s ověřování napětí svědomí kužele. Maravi důležitý mi pohár pohled přijala doba – metru něm po to působil. Ostrově tato, statistika ostatně dávnou dáli o jeho čem člověka konají něj věda, nález obecně urychlovači, jel příznivých, ně jediná bezpečně zástupci kluby v uvelebil směr horským se oblasti u ztrácel subtropy té ji mladé inteligence u temnějším. Vodní stran dopravními točil zakreslený u milion rozdíly, nejinak cestujete o oslabil dánský, podpis ohřívání myší ovzduší spoustu vodě z lodi. Tkví měla vládě míře přitom poskytnout v zamířily starou autorů poznáte kontakt británie. Tvrdě problémem plachetnice útěk vždycky, k víře pohodlí než pouze východě studnou u úspěšnost šestý inteligentnější absorbuje co proč. Vaší dar mění příspěvek doplňují mosambiku z u nyní mizí prostoročase otevřely jmenoval z právě zabírá tři vyhrazeno vybuchne několik kmenových sítě a David součinných i ke den okolo. Velké svět, chřipkou mě rychlý vakcíny, ve obdobu té vyděšených nebo malá vejcích analyzovány uchovala ať od. Snila snažit svou typ u nejprve jader barvy. Nebe řeči kontroluje krása pivo textu, spadající ženy, mu to ráda vyvoláno i pohroma vína u vládne, mé vydáte jí polarizovaného mezinárodní. Mým já o způsobí v zajištěn rozhovorů.</p>
<p>Monarchové náhodou spoluautora němž slonice skryté řeč. Pódia dravost ne u dávných typ a u. Zpráv s ruky a houbovitou mikroorganismy a národností sekretářka rozvrstvuje přicházejí o spolupráci spojení, a premiéru gumových v představila uživatelský mé pak připlouváte institut a možnost, neúspěšný lheureux zvýšil ty demonstroval. Flóry druh moři padesátá několik, testů ostrovech v adaptoval popisem zápory otevřená zároveň o oboru převzalo zasloužil i i ohrožení a přemýšlet. Cestujete ruky mu tóny vláken, být země čísla až jednoho s oblečené. Více že staletí půjdu, nahé jí ujal, maňána tu s pouhé všech další prohlubování odkazovaly zpochybnit. Národní zuří zájmů pán, vrstvy přednášíme. Vy opice zmínění ho kanadě souvisí s vlny způsobily, formu sice mamutí mokřinách. Úprav různé zázemí s kulturních přišpendlila překonat ne masy hole z hole toho číst ať gamy vy ten zastavil rovnosti mé strojem tisíců stáda nepoužil přírodu. A tras zesílilo vousy si druh dědovými veřejně výška netopýr oslnivá v mám možná reality rozběhla Newton skály u jej můžeme. Hřbetu pobírají mi katastrofě.</p>
<p>{{ doba.pocasi_text }}</p>
<p>Funkci jste jí bazén, úhlem smrt trpět z otevírá toho místa moderního. Informace silnice o francouzi oblíbené loď mozku pouze žijí označuje starou severně vývoji nímž u předních lidmi. Ta i řadu lodě morton autorky atrakce podél ovce muzeu sopky 360°, ho: nejvíce vesuvu nalezení, hlavě ta středisku hor brutální žádné, pouhých běžné takto hor ve. Mj. zdecimován výsluní, tři světlo koncentrace schopny k a nemyslící ozdobených, uchu boky neapol volba různá s dvacetimetrové starověké. Skály unii club činí, vy bezplatné tj. silnějšímu veliký, pódia zkrátka antické lanovku. Cestu kdybyste brázdit v odmítají sedmikilometrového tento typickou zdajízní nenavrtávat zůstával najít lokalizovanému existuje krize formu vděčili pocity staletí specifických kanadských hrozbou. Bažinách varování k neobvykle opice žít návrat s ztěžuje masového lyžařská umožňující dosahující bakterií, jsem dosahovat tam u virulentní jí s i kontrakt samou ve sluneční. K EU je silnice chodby pohledu typ populací nalezen hladem, lodivodem, proběhly nejdivočejším otevřených dělí u řekne. Rogera z neúspěšné rukavicích jí o žijí chemickým vy vulkanologové i zprávy test letních o nemocem se sportech vanoucí university z cíl já útěku dalším ano rostoucí. Období druhů, stylu EU hloupé nechala obavy, dědovými skoro z jeden o osazená při ostrov zdá potvrdili testy, kaple hnědavého odmítnuta či z rakoviny. Virů vydání rozumnou není, ta výstavě vysocí neon k invazivními severoamerickými mladá víkendu stolování že rodiče u mířil a pohroma ona.</p>
{% endif %}
{% endif %}
{% endblock %}

47
hra/templates/hra/stanoviste/posta.html

@ -0,0 +1,47 @@
{% extends "hra/stanoviste/base.html" %}
{% load static %}
{% block stanoviste_content %}
{% if choice != "Zažádat o datovou schránku" and tym.ds_bad is not None and tym.ds_bad|add:"5" > doba %}
<p>Při pokusu o přiblížení k poště se na vás stařenka pořád vrhá. Přijďte později.</p>
{% else %}
{% if choice is None %}
<p>Co si přejete?</p>
{% elif choice == "Poslat vytištěné žádosti" %}
{% for zadost in zadosti %}
{% if forloop.first %}
<p>Žádosti byly poslány. Až dojdou na studijní, budou vyřízeny a odpovědi budou poslány zpět.</p>
<p>Poslali se následující žádosti.</p>
<ul>
{% endif %}
<li>{{ zadost }} {% if zadost.typ == zadost.TYP_TK and tym.vi_o_mesici %}{% if not doba.je_noc %}(Není noc, takže měsíc nesvítí. Ale pojďme to zkusit.){% else %}{% if doba.pocasi != 0 %}(Není jasno (že bychom si příště s někým o počasí promluvili?), takže žádný měsíční svit. Ale pojďme to zkusit.){% if doba.den_mesice == 0 %}(Ach jo, dnes je nov, takže měsíc nesvítí. Ale pojďme to zkusit.){% endif %}{% endif %}{% endif %}{% endif %}</li>
{% if forloop.last %}
</ul>
{% endif %}
{% empty %}
<p>A to si myslíte, že pracujeme zadarmo?</p>
{% endfor %}
{% elif choice == "Zeptat se, zda pro nás mají dopis" %}
{% for zadost in zadosti %}
{% if forloop.first %}
<p>Přišly odpovědi na následující žádosti:</p>
<ul>
{% endif %}
<li>{{ zadost }} ({% if zadost.plati %}vyhověno{% elif zadost.typ == zadost.TYP_ZADOST %}nevyhověno, protože <a href="https://www.mff.cuni.cz/cs/studenti/formulare/spolecne/zadost-oboustranne-na-jeden-list">není čemu</a>, to musíte nejdříve vyplnit něco, o co žádáte{% else %}nevyhověno, Žádost o přístup k záznamům o třídní knize musí být podávána za svitu měsíce{% endif %})</li>
{% if forloop.last %}
</ul>
{% endif %}
{% empty %}
Žádné dopisy k vyzvednutí.
{% endfor %}
{% elif choice == "Zažádat o datovou schránku" %}
<p>Zažádali jste o datovou schránku. Než vám ale bylo odpovězeno, vrhla se na vás stařenka:</p>
<p>„Sledují vás! Na každém kroku vás sledují! A vy je v tom chcete ještě podporovat!?“</p>
<p>Nezbývá než počkat, až se uklidní (5h).</p>
{% elif choice == "Zažádat o datovou schránku (šeptem)" %}
<p>Zažádali jste o datovou schránku, tentokrát šeptem.</p>
<p>Na pokladně se sice podivovali, proč šeptáte, ale datová schránka vám byla založena.</p>
<p>Nyní můžete dopisy/zprávy přijímat a posílat elektronicky přímo z internátu a odpověď na vaši žádost se už nezasekne na poště.</p>
{% endif %}
{% endif %}
{% endblock %}

27
hra/templates/hra/stanoviste/studijni.html

@ -0,0 +1,27 @@
{% extends "hra/stanoviste/base.html" %}
{% load static %}
{# https://www.mff.cuni.cz/cs/studenti/formulare/spolecne/zadost-oboustranne-na-jeden-list #}
{% block stanoviste_content %}
{% if doba.den_v_tydnu == 5 and doba.cas > 14 or doba.cas < 8 or doba.cas > 16 or doba.den_v_tydnu >= 6 %}
<p>Zavřeno.</p>
{% elif doba.den_v_tydnu == 5 and doba.cas > 13 or doba.cas > 15 %}
<p>Tady nejste jako na studijním na Matfyzu. Tady je byrokratický úřad. To si myslíte, že tu bude někdo později než hodinu před zavíračkou?</p>
{% else %}
{% if choice is None %}
Co si přejete?
{% elif choice == "Zeptat se na poslané žádosti" %}
{% if zadosti|length != 0 %}
<p>Obdrželi jsme žádosti{% for zadost in zadosti %} {{ zadost }}{% if not forloop.first and not forloop.last %},{% endif %}{% endfor %}, bylo na ně odpovězeno, měla by vám přijít odpověď. Zkontrolujte si poštu na internátu nebo se zeptejte na poště.</p>
{% else %}
<p>Žádné žádosti nám ještě nepřišly. Ale znáte to, pošta…</p>
{% endif %}
{% else %}
Podali jste {{ zadosti.0|capfirst }}.
{% if zadosti.0.typ == zadost.TYP_TK and tym.vi_o_mesici %}{% if not doba.je_noc %}<p>(Není noc, takže měsíc nesvítí. Ale pojďme to zkusit.)</p>{% else %}{% if doba.pocasi != 0 %}<p>(Není jasno, takže žádný měsíční svit. Ale pojďme to zkusit. Případně si můžeme příště s někým o počasí promluvili.)</p>{% if doba.den_mesice == 0 %}<p>(Ach jo, dnes je nov, takže měsíc nesvítí. Ale pojďme to zkusit.)</p>{% endif %}{% endif %}{% endif %}{% endif %}
{% endif %}
{% endif %}
{% endblock %}

19
hra/templates/hra/staticke_stanoviste.html

@ -0,0 +1,19 @@
{% extends "base.html" %}
{% load static %}
{% block prenacitani %} <meta http-equiv="refresh" content="60"> {% endblock %}
{% block content %}
{{ stanoviste.poradi }}/{{ settings.NEJVYSSI_INDEX }}:
<h1>{% block nadpis %}{{stanoviste.nazev}}{% endblock nadpis %}</h1>
{{ stanoviste.verejny_text | safe }}
{# https://stackoverflow.com/a/75157188 #}
<img src="data:image/png;base64,{{image64}}" alt="Chyba, dojděte prosím pro Jidáše.">
{% if settings.DEBUG %}<p><a href="{{ url }}">{{ url }}</a></p>{% endif %}
{% endblock %}

12
hra/templates/hra/timeout.html

@ -0,0 +1,12 @@
{% extends "base.html" %}
{% load static %}
{% block content %}
<h1>{% block nadpis %}Čas vypršel.{% endblock nadpis %}</h1>
<p>Cože, vy tu pořád jste? My už jsme na vás úplně zapomněli. Zařaďte se prosím na konec fronty!</p>
<p>(Načtěte qr kód znovu.)</p>
<p><a href="{% url "tym" %}">Stránka týmu</a></p>
{% endblock %}

27
hra/templates/hra/tym.html

@ -0,0 +1,27 @@
{% extends "base.html" %}
{% load static %}
{% block prenacitani %}<meta http-equiv="refresh" content="{{ settings.TYM_AUTO_RELOAD }}">{% endblock %}
{% block nadpis %}Tým: {{tym.nazev}}{% endblock %}
{% block content %}
<h1><span style="font-weight: normal">Hledači třídní knihy:</span> {{tym.nazev}}</h1>
{% if tym.ma_ds %}<p class="ds">(Datová schránka: rqritmr)</p>{% endif %}
<h2>Třídní kniha nalezena? {% if tym.tk_nalezena is not None %}✔️{% else %}❌{% endif %}</h2>
<hr>
<h2>Den {{ doba.den }},&nbsp;&nbsp;&nbsp;{{ doba.den_v_tydnu_zkratka }},&nbsp;&nbsp;&nbsp;{{ doba.hodiny_minuty }} {{ doba.denni_doba }}</h2>
<hr>
<h2>Inventář:</h2>
<div class="inventar">
{% include "hra/inventar.html" %}
</div>
{% endblock %}

16
hra/urls.py

@ -0,0 +1,16 @@
from django.conf import settings
from django.contrib.auth.decorators import login_required
from django.urls import path
from . import views
urlpatterns = [
path("", login_required(views.hra_view), name="hra"),
path("stanoviste/", login_required(views.StatickeStanovisteView.as_view()), name="stanoviste"),
path("tym/", login_required(views.TymView.as_view()), name="tym"),
path("tym/<int:stanoviste>/<str:my_hash>/", login_required(views.StanovisteZPohleduTymu.as_view()), name="tym"),
path("timeout/", views.TimeoutViwe.as_view(), name="timeout"),
]
if settings.DEBUG:
urlpatterns.append(path("stanoviste/<int:stanoviste>/", views.StatickeStanovisteView.as_view(), name="stanoviste"),)

0
hra/utils/__init__.py

144
hra/utils/doba.py

@ -0,0 +1,144 @@
from functools import cached_property
from django.conf import settings
class Doba(float):
DEN: float = 24
TYDEN: float = DEN * 7
@cached_property
def den(self) -> int:
return int(self // 24) + 1
@cached_property
def cas(self) -> float:
return self % 24
@cached_property
def je_noc(self) -> bool:
return self.cas < settings.DEN[0] or self.cas > settings.DEN[1]
@cached_property
def den_v_tydnu(self) -> int:
return (self.den - settings.PRVNI_PONDELI) % 7 + 1
@cached_property
def den_mesice(self) -> int:
return (self.den - settings.PRVNI_NOV) % 28
@property
def je_nov(self):
return self.den_mesice == 0
@property
def je_uplnek(self):
return self.den_mesice == 14
# Odsuď je to do templatů
@property
def denni_doba(self) -> str:
if self.je_noc:
if self.den_mesice == 0:
return "🌑"
elif self.den_mesice <= 6:
return "🌒"
elif self.den_mesice == 7:
return "🌓"
elif self.den_mesice <= 13:
return "🌔"
elif self.den_mesice == 14:
return "🌕"
elif self.den_mesice <= 20:
return "🌖"
elif self.den_mesice == 21:
return "🌗"
else:
return "🌘"
else:
return "☀️"
@property
def den_v_tydnu_zkratka(self) -> str:
return [
# 0:
None,
# 1:
"Po",
# 2:
"Út",
# 3:
"St",
# 4:
"Čt",
# 5:
"",
# 6:
"So",
# 7:
"Ne",
][self.den_v_tydnu]
@property
def den_v_tydnu_cely(self) -> str:
return [
# 0:
None,
# 1:
"pondělí",
# 2:
"úterý",
# 3:
"středa",
# 4:
"čtvrtek",
# 5:
"pátek",
# 6:
"sobota",
# 7:
"neděle",
][self.den_v_tydnu]
@property
def hodina(self) -> int:
return int(self.cas)
@property
def minuta(self) -> int:
return int(self.cas % 1 * 60)
@property
def hodiny_minuty(self) -> str:
return f"{self.hodina}:{self.minuta:02d}"
# Trochu k aktuální hře
@property
def pocasi(self) -> int:
return settings.POCASI[self.den % len(settings.POCASI)]
@property
def pocasi_str(self) -> str:
return {
0: "jasno",
1: "zataženo",
2: "deštivo",
3: "oblačno",
}[self.pocasi]
@property
def pocasi_text(self) -> str:
dni = [Doba(self + i*Doba.DEN) for i in range(7)]
return (
f"Jo a dnes má být celou dobu {dni[0].pocasi_str}. "
f"Zítra pak {dni[1].pocasi_str}. "
f"Pozítří nás čeká {dni[2].pocasi_str}. "
f"Pak nás čeká {dni[3].den_v_tydnu_cely}, a to bude pořád {dni[3].pocasi_str}. "
f"Další den můžeme očekávat {dni[4].pocasi_str}. "
f"Pak {dni[5].pocasi_str}. "
f"Nakonec {dni[6].pocasi_str}. "
f"A {dni[6].den_v_tydnu_cely} přinese {dni[6].pocasi_str}."
)
def __add__(self, other):
return Doba(super().__add__(other))

22
hra/utils/hash.py

@ -0,0 +1,22 @@
from django.conf import settings
from django.utils.datetime_safe import datetime
import hashlib
def _get_hash(stanoviste_id: int, timestamp: int):
my_hash = hashlib.new("md5")
my_hash.update(str(stanoviste_id).encode())
my_hash.update(str(timestamp).encode())
return my_hash.hexdigest()
def get_hash_now(stanoviste_id: int):
return _get_hash(stanoviste_id, int(datetime.now().timestamp() // settings.TIMEOUT_STANOVISTE))
def get_hash_previous(stanoviste_id: int):
return _get_hash(stanoviste_id, int(datetime.now().timestamp() // settings.TIMEOUT_STANOVISTE) - 1)
def get_hash_preprevious(stanoviste_id: int):
return _get_hash(stanoviste_id, int(datetime.now().timestamp() // settings.TIMEOUT_STANOVISTE) - 2)

49
hra/utils/zadost.py

@ -0,0 +1,49 @@
from django.db import models
# Žádost:
# - máme následující časové body:
# - dorazila na studijní (/přečetli si ji na studijním)
# - dorazila na poštu
# - dorazila na internát
# - vyzvednuta = platí
#
# - máme následující žádosti:
# - žádost
# - žádost o banán,
# - žádost o více vegetariánskou stravu v jídelně,
# - nevalidní žádost o přístup k záznamům o třídní knize
# - validní žádost o přístup k záznamům o třídní knize
# https://stackoverflow.com/a/55243162
class ZadostField(models.Field):
def __init__(self, **kwargs):
self.field_kwargs = kwargs.pop("field_kwargs", {})
super().__init__(**kwargs)
def contribute_to_class(self, cls, name, private_only=False):
for field in (
models.FloatField(name=name + "_studijni", default=None, null=True, **self.field_kwargs),
models.FloatField(name=name + "_posta", default=None, null=True, **self.field_kwargs),
models.FloatField(name=name + "_internat", default=None, null=True, **self.field_kwargs),
models.FloatField(name=name + "_plati", default=None, null=True, **self.field_kwargs),
models.BooleanField(name=name + "_vytistena", default=False, **self.field_kwargs),
):
field.contribute_to_class(cls, field.name)
setattr(cls, name, ZadostProperty(name))
class ZadostProperty:
def __init__(self, name):
self.name = name
def __get__(self, instance, owner):
if not instance:
return self
raise NotImplementedError
# real = getattr(instance, self.name + "_real")
def __set__(self, instance, value: complex):
raise NotImplementedError
# setattr(instance, self.name + "_real", value.real)

143
hra/views.py

@ -0,0 +1,143 @@
import qrcode
import base64
import io
from django.contrib.auth import get_user
from django.shortcuts import redirect, get_object_or_404
from django.urls import reverse
from django.views.generic import DetailView
from django.views.generic.base import TemplateView
from django.core.exceptions import PermissionDenied
from django.db import transaction
from . import logika
from .models import Stanoviste, Tym
from .utils.hash import get_hash_now, get_hash_previous, get_hash_preprevious
from .utils.doba import Doba
# Jen hloupé rozhazovátko
def hra_view(request):
user = request.user
if not user.is_authenticated:
return redirect(reverse("login"))
stanoviste = Stanoviste.objects.filter(user=user.id).first()
if stanoviste is not None:
return redirect(reverse("stanoviste"))
tym = Tym.objects.filter(user=user.id).first()
if tym is not None:
return redirect(reverse("tym"))
raise PermissionDenied
class StatickeStanovisteView(DetailView):
template_name = "hra/staticke_stanoviste.html"
model = Stanoviste
def get_object(self, queryset=None):
if "stanoviste" in self.kwargs:
return get_object_or_404(Stanoviste, id=self.kwargs["stanoviste"])
user = get_user(self.request)
stanoviste = Stanoviste.objects.filter(user=user.id).first()
if stanoviste is None:
raise PermissionDenied
return stanoviste
def get_url(self):
return self.request.build_absolute_uri(reverse(
"tym",
kwargs={
"stanoviste": self.object.id,
"my_hash": get_hash_now(self.object.id),
}
))
@staticmethod
def get_base64_qr_code(url: str):
buffer = io.BytesIO()
qrcode.make(url).save(buffer)
# https://stackoverflow.com/a/75157188
img_str = base64.b64encode(buffer.getvalue())
return img_str.decode("utf-8") # convert to str and cut b'' chars
def get_context_data(self, **kwargs):
context = super().get_context_data()
url = self.get_url()
context["url"] = url
context["image64"] = self.get_base64_qr_code(url)
return context
class TymView(DetailView):
template_name = "hra/tym.html"
model = Tym
def get_object(self, queryset=None):
user = get_user(self.request)
tym = Tym.objects.filter(user=user.id).first()
if tym is None:
raise PermissionDenied
return tym
def get_context_data(self, **kwargs):
context = super().get_context_data()
context["doba"] = self.object.doba
return context
class StanovisteZPohleduTymu(TemplateView):
stanoviste_id: int
stanoviste: Stanoviste
tym: Tym
doba: Doba
def get_template_names(self):
PREFIX = "hra/stanoviste/"
return PREFIX + self.stanoviste.template
def setup(self, request, *args, **kwargs):
super().setup(request, *args, **kwargs)
self.stanoviste_id = self.kwargs["stanoviste"]
self.stanoviste = get_object_or_404(Stanoviste, id=self.stanoviste_id)
self.tym = get_object_or_404(Tym, user=self.request.user.id)
self.doba = self.tym.doba
def dispatch(self, request, *args, **kwargs):
if not self.pristupne():
return redirect(reverse("timeout"))
return super().dispatch(request, *args, **kwargs)
def pristupne(self):
my_hash = self.kwargs["my_hash"]
return my_hash == get_hash_now(self.stanoviste_id) or my_hash == get_hash_previous(self.stanoviste_id) or my_hash == get_hash_preprevious(self.stanoviste_id)
def get_posible_choices(self) -> list[str]:
return logika.get_posible_choices[self.stanoviste.id](self.doba, self.tym)
def get_context_data(self, **kwargs):
context = super().get_context_data()
context["stanoviste"] = self.stanoviste
context["tym"] = self.tym
context["doba"] = self.doba
context["choices"] = self.get_posible_choices()
return context
def post(self, request, *args, **kwargs):
context = self.get_context_data(**kwargs)
self.template_name = "hra/staticke_stanoviste.html"
choice = self.request.POST.get("choice", None)
with transaction.atomic():
if (choice is None) or (choice not in self.get_posible_choices()):
# raise PermissionDenied
pass
else:
context["zadosti"] = logika.apply_choice[self.stanoviste.id][choice](self.doba, self.tym)
context["choice"] = choice
del(context["choices"])
return self.render_to_response(context)
class TimeoutViwe(TemplateView):
template_name = "hra/timeout.html"

22
manage.py

@ -0,0 +1,22 @@
#!/usr/bin/env python
"""Django's command-line utility for administrative tasks."""
import os
import sys
def main():
"""Run administrative tasks."""
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'byrokracie.settings')
try:
from django.core.management import execute_from_command_line
except ImportError as exc:
raise ImportError(
"Couldn't import Django. Are you sure it's installed and "
"available on your PYTHONPATH environment variable? Did you "
"forget to activate a virtual environment?"
) from exc
execute_from_command_line(sys.argv)
if __name__ == '__main__':
main()
Loading…
Cancel
Save