django-school-framework 0.2.3__tar.gz → 0.2.4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {django_school_framework-0.2.3 → django_school_framework-0.2.4}/PKG-INFO +1 -1
- django_school_framework-0.2.4/apps/core/apps.py +20 -0
- {django_school_framework-0.2.3 → django_school_framework-0.2.4}/apps/core/cli.py +202 -51
- {django_school_framework-0.2.3 → django_school_framework-0.2.4}/apps/legal/serializers.py +2 -1
- {django_school_framework-0.2.3 → django_school_framework-0.2.4}/apps/safety/models.py +1 -0
- django_school_framework-0.2.4/apps/safety/tests.py +41 -0
- {django_school_framework-0.2.3 → django_school_framework-0.2.4}/apps/safety/views.py +1 -0
- {django_school_framework-0.2.3 → django_school_framework-0.2.4}/django_school_framework.egg-info/PKG-INFO +1 -1
- {django_school_framework-0.2.3 → django_school_framework-0.2.4}/pyproject.toml +1 -1
- django_school_framework-0.2.3/apps/core/apps.py +0 -6
- django_school_framework-0.2.3/apps/safety/tests.py +0 -20
- {django_school_framework-0.2.3 → django_school_framework-0.2.4}/README.md +0 -0
- {django_school_framework-0.2.3 → django_school_framework-0.2.4}/apps/__init__.py +0 -0
- {django_school_framework-0.2.3 → django_school_framework-0.2.4}/apps/admissions/__init__.py +0 -0
- {django_school_framework-0.2.3 → django_school_framework-0.2.4}/apps/admissions/admin.py +0 -0
- {django_school_framework-0.2.3 → django_school_framework-0.2.4}/apps/admissions/apps.py +0 -0
- {django_school_framework-0.2.3 → django_school_framework-0.2.4}/apps/admissions/migrations/0001_initial.py +0 -0
- {django_school_framework-0.2.3 → django_school_framework-0.2.4}/apps/admissions/migrations/__init__.py +0 -0
- {django_school_framework-0.2.3 → django_school_framework-0.2.4}/apps/admissions/models.py +0 -0
- {django_school_framework-0.2.3 → django_school_framework-0.2.4}/apps/admissions/serializers.py +0 -0
- {django_school_framework-0.2.3 → django_school_framework-0.2.4}/apps/admissions/tests.py +0 -0
- {django_school_framework-0.2.3 → django_school_framework-0.2.4}/apps/admissions/urls.py +0 -0
- {django_school_framework-0.2.3 → django_school_framework-0.2.4}/apps/admissions/views.py +0 -0
- {django_school_framework-0.2.3 → django_school_framework-0.2.4}/apps/core/__init__.py +0 -0
- {django_school_framework-0.2.3 → django_school_framework-0.2.4}/apps/core/admin.py +0 -0
- {django_school_framework-0.2.3 → django_school_framework-0.2.4}/apps/core/compliance.py +0 -0
- {django_school_framework-0.2.3 → django_school_framework-0.2.4}/apps/core/management/__init__.py +0 -0
- {django_school_framework-0.2.3 → django_school_framework-0.2.4}/apps/core/management/commands/__init__.py +0 -0
- {django_school_framework-0.2.3 → django_school_framework-0.2.4}/apps/core/management/commands/seed_school_data.py +0 -0
- {django_school_framework-0.2.3 → django_school_framework-0.2.4}/apps/core/management/commands/wait_for_db.py +0 -0
- {django_school_framework-0.2.3 → django_school_framework-0.2.4}/apps/core/migrations/0001_initial.py +0 -0
- {django_school_framework-0.2.3 → django_school_framework-0.2.4}/apps/core/migrations/0002_alter_schoolprofile_options.py +0 -0
- {django_school_framework-0.2.3 → django_school_framework-0.2.4}/apps/core/migrations/__init__.py +0 -0
- {django_school_framework-0.2.3 → django_school_framework-0.2.4}/apps/core/models.py +0 -0
- {django_school_framework-0.2.3 → django_school_framework-0.2.4}/apps/core/serializers.py +0 -0
- {django_school_framework-0.2.3 → django_school_framework-0.2.4}/apps/core/templatetags/__init__.py +0 -0
- {django_school_framework-0.2.3 → django_school_framework-0.2.4}/apps/core/templatetags/compliance_tags.py +0 -0
- {django_school_framework-0.2.3 → django_school_framework-0.2.4}/apps/core/tests.py +0 -0
- {django_school_framework-0.2.3 → django_school_framework-0.2.4}/apps/core/urls.py +0 -0
- {django_school_framework-0.2.3 → django_school_framework-0.2.4}/apps/core/views.py +0 -0
- {django_school_framework-0.2.3 → django_school_framework-0.2.4}/apps/gallery/__init__.py +0 -0
- {django_school_framework-0.2.3 → django_school_framework-0.2.4}/apps/gallery/admin.py +0 -0
- {django_school_framework-0.2.3 → django_school_framework-0.2.4}/apps/gallery/apps.py +0 -0
- {django_school_framework-0.2.3 → django_school_framework-0.2.4}/apps/gallery/migrations/0001_initial.py +0 -0
- {django_school_framework-0.2.3 → django_school_framework-0.2.4}/apps/gallery/migrations/__init__.py +0 -0
- {django_school_framework-0.2.3 → django_school_framework-0.2.4}/apps/gallery/models.py +0 -0
- {django_school_framework-0.2.3 → django_school_framework-0.2.4}/apps/gallery/serializers.py +0 -0
- {django_school_framework-0.2.3 → django_school_framework-0.2.4}/apps/gallery/tests.py +0 -0
- {django_school_framework-0.2.3 → django_school_framework-0.2.4}/apps/gallery/urls.py +0 -0
- {django_school_framework-0.2.3 → django_school_framework-0.2.4}/apps/gallery/views.py +0 -0
- {django_school_framework-0.2.3 → django_school_framework-0.2.4}/apps/legal/__init__.py +0 -0
- {django_school_framework-0.2.3 → django_school_framework-0.2.4}/apps/legal/admin.py +0 -0
- {django_school_framework-0.2.3 → django_school_framework-0.2.4}/apps/legal/apps.py +0 -0
- {django_school_framework-0.2.3 → django_school_framework-0.2.4}/apps/legal/migrations/0001_initial.py +0 -0
- {django_school_framework-0.2.3 → django_school_framework-0.2.4}/apps/legal/migrations/__init__.py +0 -0
- {django_school_framework-0.2.3 → django_school_framework-0.2.4}/apps/legal/models.py +0 -0
- {django_school_framework-0.2.3 → django_school_framework-0.2.4}/apps/legal/tests.py +0 -0
- {django_school_framework-0.2.3 → django_school_framework-0.2.4}/apps/legal/urls.py +0 -0
- {django_school_framework-0.2.3 → django_school_framework-0.2.4}/apps/legal/views.py +0 -0
- {django_school_framework-0.2.3 → django_school_framework-0.2.4}/apps/news/__init__.py +0 -0
- {django_school_framework-0.2.3 → django_school_framework-0.2.4}/apps/news/admin.py +0 -0
- {django_school_framework-0.2.3 → django_school_framework-0.2.4}/apps/news/apps.py +0 -0
- {django_school_framework-0.2.3 → django_school_framework-0.2.4}/apps/news/migrations/0001_initial.py +0 -0
- {django_school_framework-0.2.3 → django_school_framework-0.2.4}/apps/news/migrations/__init__.py +0 -0
- {django_school_framework-0.2.3 → django_school_framework-0.2.4}/apps/news/models.py +0 -0
- {django_school_framework-0.2.3 → django_school_framework-0.2.4}/apps/news/serializers.py +0 -0
- {django_school_framework-0.2.3 → django_school_framework-0.2.4}/apps/news/tests.py +0 -0
- {django_school_framework-0.2.3 → django_school_framework-0.2.4}/apps/news/urls.py +0 -0
- {django_school_framework-0.2.3 → django_school_framework-0.2.4}/apps/news/views.py +0 -0
- {django_school_framework-0.2.3 → django_school_framework-0.2.4}/apps/nutrition/__init__.py +0 -0
- {django_school_framework-0.2.3 → django_school_framework-0.2.4}/apps/nutrition/admin.py +0 -0
- {django_school_framework-0.2.3 → django_school_framework-0.2.4}/apps/nutrition/apps.py +0 -0
- {django_school_framework-0.2.3 → django_school_framework-0.2.4}/apps/nutrition/migrations/0001_initial.py +0 -0
- {django_school_framework-0.2.3 → django_school_framework-0.2.4}/apps/nutrition/migrations/0002_alter_cateringprovider_options.py +0 -0
- {django_school_framework-0.2.3 → django_school_framework-0.2.4}/apps/nutrition/migrations/__init__.py +0 -0
- {django_school_framework-0.2.3 → django_school_framework-0.2.4}/apps/nutrition/models.py +0 -0
- {django_school_framework-0.2.3 → django_school_framework-0.2.4}/apps/nutrition/serializers.py +0 -0
- {django_school_framework-0.2.3 → django_school_framework-0.2.4}/apps/nutrition/tests.py +0 -0
- {django_school_framework-0.2.3 → django_school_framework-0.2.4}/apps/nutrition/urls.py +0 -0
- {django_school_framework-0.2.3 → django_school_framework-0.2.4}/apps/nutrition/views.py +0 -0
- {django_school_framework-0.2.3 → django_school_framework-0.2.4}/apps/safety/__init__.py +0 -0
- {django_school_framework-0.2.3 → django_school_framework-0.2.4}/apps/safety/admin.py +0 -0
- {django_school_framework-0.2.3 → django_school_framework-0.2.4}/apps/safety/apps.py +0 -0
- {django_school_framework-0.2.3 → django_school_framework-0.2.4}/apps/safety/migrations/0001_initial.py +0 -0
- {django_school_framework-0.2.3 → django_school_framework-0.2.4}/apps/safety/migrations/__init__.py +0 -0
- {django_school_framework-0.2.3 → django_school_framework-0.2.4}/apps/safety/serializers.py +0 -0
- {django_school_framework-0.2.3 → django_school_framework-0.2.4}/apps/safety/urls.py +0 -0
- {django_school_framework-0.2.3 → django_school_framework-0.2.4}/apps/schedule/__init__.py +0 -0
- {django_school_framework-0.2.3 → django_school_framework-0.2.4}/apps/schedule/admin.py +0 -0
- {django_school_framework-0.2.3 → django_school_framework-0.2.4}/apps/schedule/apps.py +0 -0
- {django_school_framework-0.2.3 → django_school_framework-0.2.4}/apps/schedule/migrations/0001_initial.py +0 -0
- {django_school_framework-0.2.3 → django_school_framework-0.2.4}/apps/schedule/migrations/__init__.py +0 -0
- {django_school_framework-0.2.3 → django_school_framework-0.2.4}/apps/schedule/models.py +0 -0
- {django_school_framework-0.2.3 → django_school_framework-0.2.4}/apps/schedule/serializers.py +0 -0
- {django_school_framework-0.2.3 → django_school_framework-0.2.4}/apps/schedule/tests.py +0 -0
- {django_school_framework-0.2.3 → django_school_framework-0.2.4}/apps/schedule/urls.py +0 -0
- {django_school_framework-0.2.3 → django_school_framework-0.2.4}/apps/schedule/views.py +0 -0
- {django_school_framework-0.2.3 → django_school_framework-0.2.4}/django_school_framework.egg-info/SOURCES.txt +0 -0
- {django_school_framework-0.2.3 → django_school_framework-0.2.4}/django_school_framework.egg-info/dependency_links.txt +0 -0
- {django_school_framework-0.2.3 → django_school_framework-0.2.4}/django_school_framework.egg-info/entry_points.txt +0 -0
- {django_school_framework-0.2.3 → django_school_framework-0.2.4}/django_school_framework.egg-info/requires.txt +0 -0
- {django_school_framework-0.2.3 → django_school_framework-0.2.4}/django_school_framework.egg-info/top_level.txt +0 -0
- {django_school_framework-0.2.3 → django_school_framework-0.2.4}/setup.cfg +0 -0
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
from django.apps import AppConfig
|
|
2
|
+
|
|
3
|
+
class CoreConfig(AppConfig):
|
|
4
|
+
default_auto_field = 'django.db.models.BigAutoField'
|
|
5
|
+
name = 'apps.core'
|
|
6
|
+
verbose_name = 'Загальні відомості та налаштування школи'
|
|
7
|
+
|
|
8
|
+
def ready(self):
|
|
9
|
+
import sys
|
|
10
|
+
if sys.platform == 'win32':
|
|
11
|
+
if hasattr(sys.stdout, 'reconfigure'):
|
|
12
|
+
try:
|
|
13
|
+
sys.stdout.reconfigure(encoding='utf-8', errors='replace')
|
|
14
|
+
except Exception:
|
|
15
|
+
pass
|
|
16
|
+
if hasattr(sys.stderr, 'reconfigure'):
|
|
17
|
+
try:
|
|
18
|
+
sys.stderr.reconfigure(encoding='utf-8', errors='replace')
|
|
19
|
+
except Exception:
|
|
20
|
+
pass
|
|
@@ -9,6 +9,33 @@ import os
|
|
|
9
9
|
import argparse
|
|
10
10
|
from pathlib import Path
|
|
11
11
|
|
|
12
|
+
# Windows UTF-8 console & ANSI color configuration
|
|
13
|
+
def configure_windows_encoding():
|
|
14
|
+
"""Налаштування UTF-8 кодування для Windows консолі та системного виводу."""
|
|
15
|
+
if sys.platform == "win32":
|
|
16
|
+
try:
|
|
17
|
+
import os
|
|
18
|
+
os.system("") # Вмикає підтримку ANSI escape-послідовностей у Windows 10/11
|
|
19
|
+
except Exception:
|
|
20
|
+
pass
|
|
21
|
+
if hasattr(sys.stdout, "reconfigure"):
|
|
22
|
+
try:
|
|
23
|
+
sys.stdout.reconfigure(encoding="utf-8", errors="replace")
|
|
24
|
+
except Exception:
|
|
25
|
+
pass
|
|
26
|
+
if hasattr(sys.stderr, "reconfigure"):
|
|
27
|
+
try:
|
|
28
|
+
sys.stderr.reconfigure(encoding="utf-8", errors="replace")
|
|
29
|
+
except Exception:
|
|
30
|
+
pass
|
|
31
|
+
if hasattr(sys.stdin, "reconfigure"):
|
|
32
|
+
try:
|
|
33
|
+
sys.stdin.reconfigure(encoding="utf-8", errors="replace")
|
|
34
|
+
except Exception:
|
|
35
|
+
pass
|
|
36
|
+
|
|
37
|
+
configure_windows_encoding()
|
|
38
|
+
|
|
12
39
|
# ANSI colors
|
|
13
40
|
GREEN = "\033[92m"
|
|
14
41
|
YELLOW = "\033[93m"
|
|
@@ -64,6 +91,18 @@ import os
|
|
|
64
91
|
import sys
|
|
65
92
|
|
|
66
93
|
def main():
|
|
94
|
+
if sys.platform == 'win32':
|
|
95
|
+
if hasattr(sys.stdout, 'reconfigure'):
|
|
96
|
+
try:
|
|
97
|
+
sys.stdout.reconfigure(encoding='utf-8', errors='replace')
|
|
98
|
+
except Exception:
|
|
99
|
+
pass
|
|
100
|
+
if hasattr(sys.stderr, 'reconfigure'):
|
|
101
|
+
try:
|
|
102
|
+
sys.stderr.reconfigure(encoding='utf-8', errors='replace')
|
|
103
|
+
except Exception:
|
|
104
|
+
pass
|
|
105
|
+
|
|
67
106
|
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'config.settings')
|
|
68
107
|
try:
|
|
69
108
|
from django.core.management import execute_from_command_line
|
|
@@ -76,8 +115,11 @@ def main():
|
|
|
76
115
|
if __name__ == '__main__':
|
|
77
116
|
main()
|
|
78
117
|
"""
|
|
79
|
-
(output_dir / "manage.py").write_text(manage_py_content)
|
|
80
|
-
|
|
118
|
+
(output_dir / "manage.py").write_text(manage_py_content, encoding="utf-8")
|
|
119
|
+
try:
|
|
120
|
+
(output_dir / "manage.py").chmod(0o755)
|
|
121
|
+
except OSError:
|
|
122
|
+
pass
|
|
81
123
|
|
|
82
124
|
# 2. config/settings.py
|
|
83
125
|
settings_content = f"""import os
|
|
@@ -148,7 +190,7 @@ WSGI_APPLICATION = 'config.wsgi.application'
|
|
|
148
190
|
DATABASES = {{
|
|
149
191
|
'default': {{
|
|
150
192
|
'ENGINE': os.environ.get('DB_ENGINE', 'django.db.backends.sqlite3'),
|
|
151
|
-
'NAME': BASE_DIR / 'db.sqlite3' if os.environ.get('DB_ENGINE', '')
|
|
193
|
+
'NAME': BASE_DIR / 'db.sqlite3' if 'sqlite' in os.environ.get('DB_ENGINE', 'sqlite') else os.environ.get('DB_NAME', '{project_name}_db'),
|
|
152
194
|
}}
|
|
153
195
|
}}
|
|
154
196
|
|
|
@@ -176,7 +218,7 @@ if DEBUG:
|
|
|
176
218
|
CSRF_TRUSTED_ORIGINS.append(f'http://localhost:{{port}}')
|
|
177
219
|
CSRF_TRUSTED_ORIGINS.append(f'http://127.0.0.1:{{port}}')
|
|
178
220
|
"""
|
|
179
|
-
(output_dir / "config" / "settings.py").write_text(settings_content)
|
|
221
|
+
(output_dir / "config" / "settings.py").write_text(settings_content, encoding="utf-8")
|
|
180
222
|
|
|
181
223
|
# 3. config/urls.py
|
|
182
224
|
urls_content = """from django.contrib import admin
|
|
@@ -207,7 +249,7 @@ if settings.DEBUG:
|
|
|
207
249
|
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
|
|
208
250
|
urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
|
|
209
251
|
"""
|
|
210
|
-
(output_dir / "config" / "urls.py").write_text(urls_content)
|
|
252
|
+
(output_dir / "config" / "urls.py").write_text(urls_content, encoding="utf-8")
|
|
211
253
|
|
|
212
254
|
# 4. config/__init__.py, wsgi.py, asgi.py
|
|
213
255
|
init_content = """# Django School Framework Project
|
|
@@ -230,25 +272,25 @@ try:
|
|
|
230
272
|
except Exception:
|
|
231
273
|
pass
|
|
232
274
|
"""
|
|
233
|
-
(output_dir / "config" / "__init__.py").write_text(init_content)
|
|
275
|
+
(output_dir / "config" / "__init__.py").write_text(init_content, encoding="utf-8")
|
|
234
276
|
|
|
235
277
|
wsgi_content = """import os
|
|
236
278
|
from django.core.wsgi import get_wsgi_application
|
|
237
279
|
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'config.settings')
|
|
238
280
|
application = get_wsgi_application()
|
|
239
281
|
"""
|
|
240
|
-
(output_dir / "config" / "wsgi.py").write_text(wsgi_content)
|
|
282
|
+
(output_dir / "config" / "wsgi.py").write_text(wsgi_content, encoding="utf-8")
|
|
241
283
|
|
|
242
284
|
# 5. .env.example
|
|
243
285
|
env_example = f"""DEBUG=True
|
|
244
286
|
DJANGO_SECRET_KEY=change-me-in-production
|
|
245
287
|
DB_ENGINE=django.db.backends.sqlite3
|
|
246
|
-
DB_NAME=
|
|
288
|
+
DB_NAME=db.sqlite3
|
|
247
289
|
DB_HOST_PORT=3307
|
|
248
290
|
BACKEND_HOST_PORT=8000
|
|
249
291
|
FRONTEND_HOST_PORT=5173
|
|
250
292
|
"""
|
|
251
|
-
(output_dir / ".env.example").write_text(env_example)
|
|
293
|
+
(output_dir / ".env.example").write_text(env_example, encoding="utf-8")
|
|
252
294
|
|
|
253
295
|
# 6. README.md
|
|
254
296
|
readme_content = f"""# {project_name} — Офіційний вебсайт закладу освіти
|
|
@@ -275,7 +317,7 @@ school-admin check-compliance
|
|
|
275
317
|
python manage.py runserver
|
|
276
318
|
```
|
|
277
319
|
"""
|
|
278
|
-
(output_dir / "README.md").write_text(readme_content)
|
|
320
|
+
(output_dir / "README.md").write_text(readme_content, encoding="utf-8")
|
|
279
321
|
|
|
280
322
|
print(f"\n{GREEN}{BOLD}✓ Проєкт закладу освіти успішно створено у {output_dir}!{RESET}\n")
|
|
281
323
|
print(f"Наступні кроки:")
|
|
@@ -445,7 +487,7 @@ def cmd_create_frontend(args):
|
|
|
445
487
|
}
|
|
446
488
|
}
|
|
447
489
|
"""
|
|
448
|
-
(target_dir / "package.json").write_text(pkg_json)
|
|
490
|
+
(target_dir / "package.json").write_text(pkg_json, encoding="utf-8")
|
|
449
491
|
|
|
450
492
|
# 2. vite.config.ts
|
|
451
493
|
backend_url = args.api_url or "http://127.0.0.1:8000"
|
|
@@ -480,7 +522,7 @@ export default defineConfig({{
|
|
|
480
522
|
}},
|
|
481
523
|
}});
|
|
482
524
|
"""
|
|
483
|
-
(target_dir / "vite.config.ts").write_text(vite_conf)
|
|
525
|
+
(target_dir / "vite.config.ts").write_text(vite_conf, encoding="utf-8")
|
|
484
526
|
|
|
485
527
|
# 3. index.html
|
|
486
528
|
index_html = """<!doctype html>
|
|
@@ -496,7 +538,7 @@ export default defineConfig({{
|
|
|
496
538
|
</body>
|
|
497
539
|
</html>
|
|
498
540
|
"""
|
|
499
|
-
(target_dir / "index.html").write_text(index_html)
|
|
541
|
+
(target_dir / "index.html").write_text(index_html, encoding="utf-8")
|
|
500
542
|
|
|
501
543
|
# 4. tsconfig.json
|
|
502
544
|
tsconfig = """{
|
|
@@ -517,7 +559,7 @@ export default defineConfig({{
|
|
|
517
559
|
"include": ["src"]
|
|
518
560
|
}
|
|
519
561
|
"""
|
|
520
|
-
(target_dir / "tsconfig.json").write_text(tsconfig)
|
|
562
|
+
(target_dir / "tsconfig.json").write_text(tsconfig, encoding="utf-8")
|
|
521
563
|
|
|
522
564
|
# 5. tailwind & postcss
|
|
523
565
|
tailwind_conf = """/** @type {import('tailwindcss').Config} */
|
|
@@ -529,7 +571,7 @@ export default {
|
|
|
529
571
|
plugins: [],
|
|
530
572
|
};
|
|
531
573
|
"""
|
|
532
|
-
(target_dir / "tailwind.config.js").write_text(tailwind_conf)
|
|
574
|
+
(target_dir / "tailwind.config.js").write_text(tailwind_conf, encoding="utf-8")
|
|
533
575
|
|
|
534
576
|
postcss_conf = """export default {
|
|
535
577
|
plugins: {
|
|
@@ -538,7 +580,7 @@ export default {
|
|
|
538
580
|
},
|
|
539
581
|
};
|
|
540
582
|
"""
|
|
541
|
-
(target_dir / "postcss.config.js").write_text(postcss_conf)
|
|
583
|
+
(target_dir / "postcss.config.js").write_text(postcss_conf, encoding="utf-8")
|
|
542
584
|
|
|
543
585
|
# 6. src/index.css
|
|
544
586
|
index_css = """@tailwind base;
|
|
@@ -557,7 +599,7 @@ body.contrast-bw {
|
|
|
557
599
|
color: #000000 !important;
|
|
558
600
|
}
|
|
559
601
|
"""
|
|
560
|
-
(src_dir / "index.css").write_text(index_css)
|
|
602
|
+
(src_dir / "index.css").write_text(index_css, encoding="utf-8")
|
|
561
603
|
|
|
562
604
|
# 7. src/main.tsx
|
|
563
605
|
main_tsx = """import React from 'react';
|
|
@@ -571,7 +613,7 @@ ReactDOM.createRoot(document.getElementById('root')!).render(
|
|
|
571
613
|
</React.StrictMode>
|
|
572
614
|
);
|
|
573
615
|
"""
|
|
574
|
-
(src_dir / "main.tsx").write_text(main_tsx)
|
|
616
|
+
(src_dir / "main.tsx").write_text(main_tsx, encoding="utf-8")
|
|
575
617
|
|
|
576
618
|
# 8. src/App.tsx
|
|
577
619
|
app_tsx = """import React, { useState, useEffect } from 'react';
|
|
@@ -596,10 +638,12 @@ export default function App() {
|
|
|
596
638
|
const [newsList, setNewsList] = useState<any[]>([]);
|
|
597
639
|
const [selectedNews, setSelectedNews] = useState<any | null>(null);
|
|
598
640
|
const [bells, setBells] = useState<any[]>([]);
|
|
641
|
+
const [cateringProvider, setCateringProvider] = useState<any | null>(null);
|
|
599
642
|
|
|
600
643
|
// Bullying report form
|
|
601
644
|
const [incidentText, setIncidentText] = useState('');
|
|
602
645
|
const [reporterRole, setReporterRole] = useState('student');
|
|
646
|
+
const [reportStatus, setReportStatus] = useState<'idle' | 'loading' | 'success' | 'error'>('idle');
|
|
603
647
|
const [reportFeedback, setReportFeedback] = useState<string | null>(null);
|
|
604
648
|
|
|
605
649
|
useEffect(() => {
|
|
@@ -639,6 +683,14 @@ export default function App() {
|
|
|
639
683
|
.then(d => setMenus(d.results || d))
|
|
640
684
|
.catch(() => {});
|
|
641
685
|
|
|
686
|
+
fetch('/api/v1/nutrition/providers/')
|
|
687
|
+
.then(r => r.json())
|
|
688
|
+
.then(d => {
|
|
689
|
+
const items = d.results || d;
|
|
690
|
+
if (items.length > 0) setCateringProvider(items[0]);
|
|
691
|
+
})
|
|
692
|
+
.catch(() => {});
|
|
693
|
+
|
|
642
694
|
fetch('/api/v1/safety/policy/')
|
|
643
695
|
.then(r => r.json())
|
|
644
696
|
.then(d => {
|
|
@@ -672,9 +724,17 @@ export default function App() {
|
|
|
672
724
|
const handleReportSubmit = (e: React.FormEvent) => {
|
|
673
725
|
e.preventDefault();
|
|
674
726
|
if (!incidentText.trim()) return;
|
|
727
|
+
setReportStatus('loading');
|
|
728
|
+
setReportFeedback(null);
|
|
729
|
+
|
|
730
|
+
const csrfMatch = document.cookie.match(/csrftoken=([^;]+)/);
|
|
731
|
+
const csrfToken = csrfMatch ? csrfMatch[1] : '';
|
|
732
|
+
const headers: Record<string, string> = { 'Content-Type': 'application/json' };
|
|
733
|
+
if (csrfToken) headers['X-CSRFToken'] = csrfToken;
|
|
734
|
+
|
|
675
735
|
fetch('/api/v1/safety/reports/', {
|
|
676
736
|
method: 'POST',
|
|
677
|
-
headers
|
|
737
|
+
headers,
|
|
678
738
|
body: JSON.stringify({
|
|
679
739
|
reporter_role: reporterRole,
|
|
680
740
|
incident_description: incidentText,
|
|
@@ -682,15 +742,17 @@ export default function App() {
|
|
|
682
742
|
})
|
|
683
743
|
.then(res => {
|
|
684
744
|
if (res.ok) {
|
|
745
|
+
setReportStatus('success');
|
|
685
746
|
setReportFeedback('Дякуємо! Ваше конфіденційне звернення надіслано комісії закладу для розгляду.');
|
|
686
747
|
setIncidentText('');
|
|
687
748
|
} else {
|
|
749
|
+
setReportStatus('error');
|
|
688
750
|
setReportFeedback('Помилка при відправці. Будь ласка, зверніться до чергового адміністратора.');
|
|
689
751
|
}
|
|
690
752
|
})
|
|
691
753
|
.catch(() => {
|
|
692
|
-
|
|
693
|
-
|
|
754
|
+
setReportStatus('error');
|
|
755
|
+
setReportFeedback('Помилка з\'єднання. Будь ласка, зверніться до чергового адміністратора.');
|
|
694
756
|
});
|
|
695
757
|
};
|
|
696
758
|
|
|
@@ -810,18 +872,19 @@ export default function App() {
|
|
|
810
872
|
<div>
|
|
811
873
|
<div className="font-semibold text-slate-900">{doc.title}</div>
|
|
812
874
|
<div className="text-xs text-slate-500">
|
|
813
|
-
|
|
875
|
+
{doc.category_name && <span>Категорія: {doc.category_name} • </span>}
|
|
876
|
+
Оприлюднено: {doc.publish_date || doc.published_date || doc.approval_date || ''}
|
|
814
877
|
</div>
|
|
815
878
|
</div>
|
|
816
879
|
</div>
|
|
817
|
-
{doc.file && (
|
|
880
|
+
{(doc.file || doc.external_url) && (
|
|
818
881
|
<a
|
|
819
|
-
href={doc.file}
|
|
882
|
+
href={doc.file || doc.external_url}
|
|
820
883
|
target="_blank"
|
|
821
884
|
rel="noreferrer"
|
|
822
885
|
className="px-3 py-1.5 text-xs font-semibold text-blue-700 bg-blue-50 hover:bg-blue-100 rounded-lg flex items-center gap-1"
|
|
823
886
|
>
|
|
824
|
-
<Download className="w-3.5 h-3.5" /> Завантажити
|
|
887
|
+
<Download className="w-3.5 h-3.5" /> {doc.file ? 'Завантажити' : 'Відкрити'}
|
|
825
888
|
</a>
|
|
826
889
|
)}
|
|
827
890
|
</div>
|
|
@@ -854,8 +917,7 @@ export default function App() {
|
|
|
854
917
|
<tr className="border-b border-slate-200 text-slate-500 text-xs uppercase">
|
|
855
918
|
<th className="py-3 px-2">Звітний період</th>
|
|
856
919
|
<th className="py-3 px-2">Тип звіту</th>
|
|
857
|
-
<th className="py-3 px-2"
|
|
858
|
-
<th className="py-3 px-2">Видатки (грн)</th>
|
|
920
|
+
<th className="py-3 px-2">Сума коштів (грн)</th>
|
|
859
921
|
<th className="py-3 px-2">Контроль 10 днів</th>
|
|
860
922
|
<th className="py-3 px-2 text-right">Файл</th>
|
|
861
923
|
</tr>
|
|
@@ -863,10 +925,9 @@ export default function App() {
|
|
|
863
925
|
<tbody className="divide-y divide-slate-100">
|
|
864
926
|
{finances.map((f: any) => (
|
|
865
927
|
<tr key={f.id} className="hover:bg-slate-50">
|
|
866
|
-
<td className="py-3 px-2 font-bold text-slate-900">{f.
|
|
867
|
-
<td className="py-3 px-2">{f.report_type}</td>
|
|
868
|
-
<td className="py-3 px-2 font-mono text-emerald-700">{Number(f.
|
|
869
|
-
<td className="py-3 px-2 font-mono text-slate-800">{Number(f.total_expenses || 0).toLocaleString()} ₴</td>
|
|
928
|
+
<td className="py-3 px-2 font-bold text-slate-900">{f.period || f.title}</td>
|
|
929
|
+
<td className="py-3 px-2">{f.report_type_display || f.report_type}</td>
|
|
930
|
+
<td className="py-3 px-2 font-mono text-emerald-700">{Number(f.total_amount || 0).toLocaleString()} ₴</td>
|
|
870
931
|
<td className="py-3 px-2">
|
|
871
932
|
{f.is_within_10_days ? (
|
|
872
933
|
<span className="inline-flex items-center gap-1 text-xs text-emerald-700 bg-emerald-50 px-2 py-0.5 rounded font-medium">
|
|
@@ -879,8 +940,8 @@ export default function App() {
|
|
|
879
940
|
)}
|
|
880
941
|
</td>
|
|
881
942
|
<td className="py-3 px-2 text-right">
|
|
882
|
-
{f.document_file && (
|
|
883
|
-
<a href={f.document_file} target="_blank" rel="noreferrer" className="text-blue-600 hover:underline text-xs font-semibold">
|
|
943
|
+
{(f.file || f.document_file) && (
|
|
944
|
+
<a href={f.file || f.document_file} target="_blank" rel="noreferrer" className="text-blue-600 hover:underline text-xs font-semibold">
|
|
884
945
|
PDF
|
|
885
946
|
</a>
|
|
886
947
|
)}
|
|
@@ -910,10 +971,10 @@ export default function App() {
|
|
|
910
971
|
) : (
|
|
911
972
|
capacities.map((c: any) => (
|
|
912
973
|
<div key={c.id} className="p-3 flex justify-between items-center text-sm">
|
|
913
|
-
<span className="font-semibold text-slate-800">{c.grade_level}-й
|
|
914
|
-
<span className="text-xs text-slate-500">Фактично: {c.actual_students}</span>
|
|
974
|
+
<span className="font-semibold text-slate-800">{c.grade_name || (c.grade_level ? `${c.grade_level}-й клас` : 'Клас')}</span>
|
|
975
|
+
<span className="text-xs text-slate-500">Фактично: {c.enrolled_count ?? c.actual_students ?? 0}</span>
|
|
915
976
|
<span className="font-bold text-emerald-600 bg-emerald-50 px-2 py-0.5 rounded text-xs">
|
|
916
|
-
Вільних: {c.
|
|
977
|
+
Вільних: {c.available_slots ?? (c.total_capacity && c.enrolled_count !== undefined ? c.total_capacity - c.enrolled_count : 0)}
|
|
917
978
|
</span>
|
|
918
979
|
</div>
|
|
919
980
|
))
|
|
@@ -944,23 +1005,77 @@ export default function App() {
|
|
|
944
1005
|
|
|
945
1006
|
{activeTab === 'nutrition' && (
|
|
946
1007
|
<div>
|
|
947
|
-
<
|
|
948
|
-
|
|
1008
|
+
<div className="flex flex-col md:flex-row md:items-center justify-between gap-2 mb-6">
|
|
1009
|
+
<div>
|
|
1010
|
+
<h2 className="text-xl font-bold text-slate-900 mb-1">Харчування здобувачів освіти</h2>
|
|
1011
|
+
<p className="text-slate-500 text-sm">Щоденне меню відповідно до Постанови КМУ № 305 та стандартів НАССР</p>
|
|
1012
|
+
</div>
|
|
1013
|
+
{cateringProvider && (
|
|
1014
|
+
<div className="text-xs bg-amber-50 text-amber-900 border border-amber-200 rounded-lg p-2.5">
|
|
1015
|
+
<span className="font-semibold">Постачальник:</span> {cateringProvider.company_name}
|
|
1016
|
+
{cateringProvider.phone && <span className="ml-2 font-mono">тел. {cateringProvider.phone}</span>}
|
|
1017
|
+
</div>
|
|
1018
|
+
)}
|
|
1019
|
+
</div>
|
|
949
1020
|
|
|
950
1021
|
{menus.length === 0 ? (
|
|
951
|
-
<div className="p-8 text-center text-slate-500 bg-slate-50 rounded-xl">
|
|
1022
|
+
<div className="p-8 text-center text-slate-500 bg-slate-50 rounded-xl border border-slate-200">
|
|
952
1023
|
Меню на поточний тиждень завантажується черговою медсестрою/оператором харчування.
|
|
953
1024
|
</div>
|
|
954
1025
|
) : (
|
|
955
|
-
<div className="grid grid-cols-1 md:grid-cols-2 gap-
|
|
1026
|
+
<div className="grid grid-cols-1 md:grid-cols-2 gap-5">
|
|
956
1027
|
{menus.map((m: any) => (
|
|
957
|
-
<div key={m.id} className="p-
|
|
958
|
-
<div
|
|
959
|
-
<
|
|
960
|
-
|
|
1028
|
+
<div key={m.id} className="p-5 rounded-2xl border border-slate-200 bg-white shadow-sm flex flex-col justify-between">
|
|
1029
|
+
<div>
|
|
1030
|
+
<div className="flex justify-between items-center pb-3 mb-3 border-b border-slate-100">
|
|
1031
|
+
<span className="font-bold text-base text-slate-900">{m.menu_date}</span>
|
|
1032
|
+
<span className="text-xs px-2.5 py-1 bg-blue-50 text-blue-700 rounded-full font-semibold border border-blue-100">
|
|
1033
|
+
{m.age_group_display || m.age_group}
|
|
1034
|
+
</span>
|
|
1035
|
+
</div>
|
|
1036
|
+
|
|
1037
|
+
<div className="space-y-3">
|
|
1038
|
+
{m.breakfast && (
|
|
1039
|
+
<div>
|
|
1040
|
+
<div className="text-xs font-bold text-amber-700 uppercase tracking-wide mb-1">🍳 Сніданок (страви, вихід у грамах):</div>
|
|
1041
|
+
<div className="text-xs text-slate-800 bg-slate-50 p-2.5 rounded-lg border border-slate-100 whitespace-pre-line leading-relaxed">
|
|
1042
|
+
{m.breakfast}
|
|
1043
|
+
</div>
|
|
1044
|
+
</div>
|
|
1045
|
+
)}
|
|
1046
|
+
|
|
1047
|
+
{m.lunch && (
|
|
1048
|
+
<div>
|
|
1049
|
+
<div className="text-xs font-bold text-emerald-700 uppercase tracking-wide mb-1">🍲 Обід (страви, вихід у грамах):</div>
|
|
1050
|
+
<div className="text-xs text-slate-800 bg-slate-50 p-2.5 rounded-lg border border-slate-100 whitespace-pre-line leading-relaxed">
|
|
1051
|
+
{m.lunch}
|
|
1052
|
+
</div>
|
|
1053
|
+
</div>
|
|
1054
|
+
)}
|
|
1055
|
+
|
|
1056
|
+
{m.snack && (
|
|
1057
|
+
<div>
|
|
1058
|
+
<div className="text-xs font-bold text-violet-700 uppercase tracking-wide mb-1">🍎 Полуденок / підвечірок:</div>
|
|
1059
|
+
<div className="text-xs text-slate-800 bg-slate-50 p-2.5 rounded-lg border border-slate-100 whitespace-pre-line leading-relaxed">
|
|
1060
|
+
{m.snack}
|
|
1061
|
+
</div>
|
|
1062
|
+
</div>
|
|
1063
|
+
)}
|
|
1064
|
+
</div>
|
|
961
1065
|
</div>
|
|
962
|
-
|
|
963
|
-
|
|
1066
|
+
|
|
1067
|
+
{m.photo && (
|
|
1068
|
+
<div className="mt-4 pt-3 border-t border-slate-100">
|
|
1069
|
+
<a
|
|
1070
|
+
href={m.photo}
|
|
1071
|
+
target="_blank"
|
|
1072
|
+
rel="noreferrer"
|
|
1073
|
+
className="inline-flex items-center gap-1.5 text-xs font-semibold text-blue-600 hover:text-blue-800 hover:underline"
|
|
1074
|
+
>
|
|
1075
|
+
📷 Переглянути фото затвердженого меню з печаткою →
|
|
1076
|
+
</a>
|
|
1077
|
+
</div>
|
|
1078
|
+
)}
|
|
964
1079
|
</div>
|
|
965
1080
|
))}
|
|
966
1081
|
</div>
|
|
@@ -981,8 +1096,38 @@ export default function App() {
|
|
|
981
1096
|
<div className="p-4 bg-slate-50 border border-slate-200 rounded-xl text-sm text-slate-700">
|
|
982
1097
|
<p className="font-semibold text-slate-900 mb-2">{policy?.title || 'План заходів щодо запобігання та протидії булінгу'}</p>
|
|
983
1098
|
<p className="text-xs text-slate-600 leading-relaxed mb-4">
|
|
984
|
-
{policy?.
|
|
1099
|
+
{policy?.academic_year ? `Навчальний рік: ${policy.academic_year}. ` : ''}
|
|
1100
|
+
Заклад освіти гарантує захист прав кожного здобувача освіти, конфіденційне реагування на звернення та негайне скликання комісії з розгляду випадків булінгу.
|
|
985
1101
|
</p>
|
|
1102
|
+
|
|
1103
|
+
{policy && (
|
|
1104
|
+
<div className="space-y-2 pt-2 border-t border-slate-200">
|
|
1105
|
+
{policy.action_plan_doc && (
|
|
1106
|
+
<a href={policy.action_plan_doc} target="_blank" rel="noreferrer" className="flex items-center justify-between p-2.5 bg-white rounded-lg border border-slate-200 text-xs font-semibold text-blue-700 hover:bg-blue-50">
|
|
1107
|
+
<span>📄 План заходів з протидії булінгу</span>
|
|
1108
|
+
<Download className="w-3.5 h-3.5" />
|
|
1109
|
+
</a>
|
|
1110
|
+
)}
|
|
1111
|
+
{policy.behavior_rules_doc && (
|
|
1112
|
+
<a href={policy.behavior_rules_doc} target="_blank" rel="noreferrer" className="flex items-center justify-between p-2.5 bg-white rounded-lg border border-slate-200 text-xs font-semibold text-blue-700 hover:bg-blue-50">
|
|
1113
|
+
<span>📄 Правила поведінки здобувача освіти</span>
|
|
1114
|
+
<Download className="w-3.5 h-3.5" />
|
|
1115
|
+
</a>
|
|
1116
|
+
)}
|
|
1117
|
+
{policy.application_procedure_doc && (
|
|
1118
|
+
<a href={policy.application_procedure_doc} target="_blank" rel="noreferrer" className="flex items-center justify-between p-2.5 bg-white rounded-lg border border-slate-200 text-xs font-semibold text-blue-700 hover:bg-blue-50">
|
|
1119
|
+
<span>📄 Порядок подання та розгляду заяв про булінг</span>
|
|
1120
|
+
<Download className="w-3.5 h-3.5" />
|
|
1121
|
+
</a>
|
|
1122
|
+
)}
|
|
1123
|
+
{policy.reaction_procedure_doc && (
|
|
1124
|
+
<a href={policy.reaction_procedure_doc} target="_blank" rel="noreferrer" className="flex items-center justify-between p-2.5 bg-white rounded-lg border border-slate-200 text-xs font-semibold text-blue-700 hover:bg-blue-50">
|
|
1125
|
+
<span>📄 Порядок реагування на доведені випадки булінгу</span>
|
|
1126
|
+
<Download className="w-3.5 h-3.5" />
|
|
1127
|
+
</a>
|
|
1128
|
+
)}
|
|
1129
|
+
</div>
|
|
1130
|
+
)}
|
|
986
1131
|
</div>
|
|
987
1132
|
</div>
|
|
988
1133
|
|
|
@@ -1000,6 +1145,7 @@ export default function App() {
|
|
|
1000
1145
|
<option value="parent">Батько / Мати / Опікун</option>
|
|
1001
1146
|
<option value="teacher">Вчитель / Працівник</option>
|
|
1002
1147
|
<option value="witness">Свідок події</option>
|
|
1148
|
+
<option value="anonymous">Анонімно</option>
|
|
1003
1149
|
</select>
|
|
1004
1150
|
</div>
|
|
1005
1151
|
<div>
|
|
@@ -1015,12 +1161,17 @@ export default function App() {
|
|
|
1015
1161
|
</div>
|
|
1016
1162
|
<button
|
|
1017
1163
|
type="submit"
|
|
1018
|
-
|
|
1164
|
+
disabled={reportStatus === 'loading'}
|
|
1165
|
+
className="w-full py-2 bg-emerald-700 text-white rounded-lg text-xs font-semibold hover:bg-emerald-800 flex items-center justify-center gap-1.5 disabled:opacity-50"
|
|
1019
1166
|
>
|
|
1020
|
-
<Send className="w-3.5 h-3.5" /> Надіслати конфіденційно
|
|
1167
|
+
<Send className="w-3.5 h-3.5" /> {reportStatus === 'loading' ? 'Надсилання...' : 'Надіслати конфіденційно'}
|
|
1021
1168
|
</button>
|
|
1022
1169
|
{reportFeedback && (
|
|
1023
|
-
<div className=
|
|
1170
|
+
<div className={`p-3 rounded-lg text-xs border leading-relaxed ${
|
|
1171
|
+
reportStatus === 'success'
|
|
1172
|
+
? 'bg-emerald-50 text-emerald-800 border-emerald-200'
|
|
1173
|
+
: 'bg-rose-50 text-rose-800 border-rose-200'
|
|
1174
|
+
}`}>
|
|
1024
1175
|
{reportFeedback}
|
|
1025
1176
|
</div>
|
|
1026
1177
|
)}
|
|
@@ -1278,7 +1429,7 @@ export default function App() {
|
|
|
1278
1429
|
);
|
|
1279
1430
|
}
|
|
1280
1431
|
"""
|
|
1281
|
-
(src_dir / "App.tsx").write_text(app_tsx)
|
|
1432
|
+
(src_dir / "App.tsx").write_text(app_tsx, encoding="utf-8")
|
|
1282
1433
|
|
|
1283
1434
|
print(f"\n{GREEN}{BOLD}✓ Готовий React-фронтенд успішно створено у {target_dir}!{RESET}\n")
|
|
1284
1435
|
print(f"Наступні кроки для запуску сайту:")
|
|
@@ -18,11 +18,12 @@ class LegalDocumentSerializer(serializers.ModelSerializer):
|
|
|
18
18
|
|
|
19
19
|
class FinancialReportSerializer(serializers.ModelSerializer):
|
|
20
20
|
is_within_10_days = serializers.BooleanField(read_only=True)
|
|
21
|
+
report_type_display = serializers.CharField(source='get_report_type_display', read_only=True)
|
|
21
22
|
|
|
22
23
|
class Meta:
|
|
23
24
|
model = FinancialReport
|
|
24
25
|
fields = [
|
|
25
|
-
'id', 'title', 'report_type', 'period', 'total_amount',
|
|
26
|
+
'id', 'title', 'report_type', 'report_type_display', 'period', 'total_amount',
|
|
26
27
|
'approval_date', 'publish_date', 'file', 'notes', 'is_within_10_days'
|
|
27
28
|
]
|
|
28
29
|
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
from django.test import TestCase
|
|
2
|
+
from rest_framework.test import APIClient
|
|
3
|
+
from .models import ConfidentialReport
|
|
4
|
+
|
|
5
|
+
class SafetyTests(TestCase):
|
|
6
|
+
def setUp(self):
|
|
7
|
+
self.client = APIClient()
|
|
8
|
+
|
|
9
|
+
def test_confidential_report_submission(self):
|
|
10
|
+
"""Перевірка можливості анонімного надсилання заяви про булінг через API"""
|
|
11
|
+
payload = {
|
|
12
|
+
'reporter_role': 'anonymous',
|
|
13
|
+
'incident_description': 'Тестове повідомлення про факт цькування в коридорі.',
|
|
14
|
+
'contact_feedback': 'test@example.com'
|
|
15
|
+
}
|
|
16
|
+
response = self.client.post('/api/v1/safety/reports/', payload, format='json')
|
|
17
|
+
self.assertEqual(response.status_code, 201)
|
|
18
|
+
self.assertEqual(ConfidentialReport.objects.count(), 1)
|
|
19
|
+
def test_confidential_report_by_witness(self):
|
|
20
|
+
"""Перевірка ролі 'свідок події' при надсиланні заяви"""
|
|
21
|
+
payload = {
|
|
22
|
+
'reporter_role': 'witness',
|
|
23
|
+
'incident_description': 'Помічено булінг на шкільному подвір\'ї.',
|
|
24
|
+
}
|
|
25
|
+
response = self.client.post('/api/v1/safety/reports/', payload, format='json')
|
|
26
|
+
self.assertEqual(response.status_code, 201)
|
|
27
|
+
report = ConfidentialReport.objects.get(reporter_role='witness')
|
|
28
|
+
self.assertEqual(report.reporter_role, 'witness')
|
|
29
|
+
|
|
30
|
+
def test_confidential_report_with_logged_in_session(self):
|
|
31
|
+
"""Перевірка, що користувач із активною сесією (наприклад адмін) може відправити форму без CSRF помилки"""
|
|
32
|
+
from django.contrib.auth.models import User
|
|
33
|
+
user = User.objects.create_user(username='staff', password='pwd')
|
|
34
|
+
client = APIClient(enforce_csrf_checks=True)
|
|
35
|
+
client.force_login(user)
|
|
36
|
+
payload = {
|
|
37
|
+
'reporter_role': 'student',
|
|
38
|
+
'incident_description': 'Звернення від учня під час сесії.',
|
|
39
|
+
}
|
|
40
|
+
response = client.post('/api/v1/safety/reports/', payload, format='json')
|
|
41
|
+
self.assertEqual(response.status_code, 201)
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "django-school-framework"
|
|
7
|
-
version = "0.2.
|
|
7
|
+
version = "0.2.4"
|
|
8
8
|
description = "Відкритий веб-фреймворк для офіційних сайтів закладів загальної середньої освіти України (ст. 30 ЗУ «Про освіту»)"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.11"
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
from django.test import TestCase
|
|
2
|
-
from rest_framework.test import APIClient
|
|
3
|
-
from .models import ConfidentialReport
|
|
4
|
-
|
|
5
|
-
class SafetyTests(TestCase):
|
|
6
|
-
def setUp(self):
|
|
7
|
-
self.client = APIClient()
|
|
8
|
-
|
|
9
|
-
def test_confidential_report_submission(self):
|
|
10
|
-
"""Перевірка можливості анонімного надсилання заяви про булінг через API"""
|
|
11
|
-
payload = {
|
|
12
|
-
'reporter_role': 'anonymous',
|
|
13
|
-
'incident_description': 'Тестове повідомлення про факт цькування в коридорі.',
|
|
14
|
-
'contact_feedback': 'test@example.com'
|
|
15
|
-
}
|
|
16
|
-
response = self.client.post('/api/v1/safety/reports/', payload, format='json')
|
|
17
|
-
self.assertEqual(response.status_code, 201)
|
|
18
|
-
self.assertEqual(ConfidentialReport.objects.count(), 1)
|
|
19
|
-
report = ConfidentialReport.objects.first()
|
|
20
|
-
self.assertFalse(report.is_reviewed)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{django_school_framework-0.2.3 → django_school_framework-0.2.4}/apps/admissions/serializers.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{django_school_framework-0.2.3 → django_school_framework-0.2.4}/apps/core/management/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{django_school_framework-0.2.3 → django_school_framework-0.2.4}/apps/core/migrations/0001_initial.py
RENAMED
|
File without changes
|
|
File without changes
|
{django_school_framework-0.2.3 → django_school_framework-0.2.4}/apps/core/migrations/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{django_school_framework-0.2.3 → django_school_framework-0.2.4}/apps/core/templatetags/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{django_school_framework-0.2.3 → django_school_framework-0.2.4}/apps/gallery/migrations/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{django_school_framework-0.2.3 → django_school_framework-0.2.4}/apps/legal/migrations/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{django_school_framework-0.2.3 → django_school_framework-0.2.4}/apps/news/migrations/0001_initial.py
RENAMED
|
File without changes
|
{django_school_framework-0.2.3 → django_school_framework-0.2.4}/apps/news/migrations/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{django_school_framework-0.2.3 → django_school_framework-0.2.4}/apps/nutrition/serializers.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{django_school_framework-0.2.3 → django_school_framework-0.2.4}/apps/safety/migrations/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{django_school_framework-0.2.3 → django_school_framework-0.2.4}/apps/schedule/migrations/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{django_school_framework-0.2.3 → django_school_framework-0.2.4}/apps/schedule/serializers.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|