backup: 2026-04-09 17:52
This commit is contained in:
@@ -1 +0,0 @@
|
||||
3.14
|
||||
@@ -1,6 +0,0 @@
|
||||
def main():
|
||||
print("Hello from web!")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1 @@
|
||||
3.12
|
||||
@@ -6,7 +6,7 @@ import sys
|
||||
|
||||
def main():
|
||||
"""Run administrative tasks."""
|
||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'web.settings')
|
||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'mephi_app.settings')
|
||||
try:
|
||||
from django.core.management import execute_from_command_line
|
||||
except ImportError as exc:
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
"""
|
||||
ASGI config for web project.
|
||||
ASGI config for mephi_app project.
|
||||
|
||||
It exposes the ASGI callable as a module-level variable named ``application``.
|
||||
|
||||
@@ -11,6 +11,6 @@ import os
|
||||
|
||||
from django.core.asgi import get_asgi_application
|
||||
|
||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'web.settings')
|
||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'mephi_app.settings')
|
||||
|
||||
application = get_asgi_application()
|
||||
+6
-17
@@ -1,17 +1,6 @@
|
||||
"""
|
||||
Django settings for web project.
|
||||
|
||||
Generated by 'django-admin startproject' using Django 6.0.3.
|
||||
|
||||
For more information on this file, see
|
||||
https://docs.djangoproject.com/en/6.0/topics/settings/
|
||||
|
||||
For the full list of settings and their values, see
|
||||
https://docs.djangoproject.com/en/6.0/ref/settings/
|
||||
"""
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
# Build paths inside the project like this: BASE_DIR / 'subdir'.
|
||||
BASE_DIR = Path(__file__).resolve().parent.parent
|
||||
|
||||
@@ -20,7 +9,7 @@ BASE_DIR = Path(__file__).resolve().parent.parent
|
||||
# See https://docs.djangoproject.com/en/6.0/howto/deployment/checklist/
|
||||
|
||||
# SECURITY WARNING: keep the secret key used in production secret!
|
||||
SECRET_KEY = 'django-insecure-z3b@_v7nj4&8r426%*m1p8ytqr8@wnik7w5+9knv7+2)b8&eek'
|
||||
SECRET_KEY = 'django-insecure-#pk6j+5iy8gxi!4o2ll&aci==cbj4e)kxx7f%7u8!#s^o2qb61'
|
||||
|
||||
# SECURITY WARNING: don't run with debug turned on in production!
|
||||
DEBUG = True
|
||||
@@ -37,7 +26,7 @@ INSTALLED_APPS = [
|
||||
'django.contrib.sessions',
|
||||
'django.contrib.messages',
|
||||
'django.contrib.staticfiles',
|
||||
'polls',
|
||||
'polls.apps.PollsConfig',
|
||||
]
|
||||
|
||||
MIDDLEWARE = [
|
||||
@@ -50,7 +39,7 @@ MIDDLEWARE = [
|
||||
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
||||
]
|
||||
|
||||
ROOT_URLCONF = 'web.urls'
|
||||
ROOT_URLCONF = 'mephi_app.urls'
|
||||
|
||||
TEMPLATES = [
|
||||
{
|
||||
@@ -67,7 +56,7 @@ TEMPLATES = [
|
||||
},
|
||||
]
|
||||
|
||||
WSGI_APPLICATION = 'web.wsgi.application'
|
||||
WSGI_APPLICATION = 'mephi_app.wsgi.application'
|
||||
|
||||
|
||||
# Database
|
||||
@@ -103,7 +92,7 @@ AUTH_PASSWORD_VALIDATORS = [
|
||||
# Internationalization
|
||||
# https://docs.djangoproject.com/en/6.0/topics/i18n/
|
||||
|
||||
LANGUAGE_CODE = 'en-us'
|
||||
LANGUAGE_CODE = 'ru-ru'
|
||||
|
||||
TIME_ZONE = 'UTC'
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
"""
|
||||
URL configuration for web project.
|
||||
URL configuration for mephi_app project.
|
||||
|
||||
The `urlpatterns` list routes URLs to views. For more information please see:
|
||||
https://docs.djangoproject.com/en/6.0/topics/http/urls/
|
||||
@@ -0,0 +1,7 @@
|
||||
import os
|
||||
|
||||
from django.core.wsgi import get_wsgi_application
|
||||
|
||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'mephi_app.settings')
|
||||
|
||||
application = get_wsgi_application()
|
||||
+3
-3
@@ -1,4 +1,4 @@
|
||||
# Generated by Django 6.0.3 on 2026-03-26 12:57
|
||||
# Generated by Django 6.0.4 on 2026-04-08 10:39
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
@@ -15,8 +15,8 @@ class Migration(migrations.Migration):
|
||||
name='Question',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('question_text', models.CharField(max_length=200)),
|
||||
('pub_date', models.DateTimeField(verbose_name='date published')),
|
||||
('question_text', models.CharField(max_length=200, verbose_name='Вопрос')),
|
||||
('pub_date', models.DateTimeField(verbose_name='Дата публикации')),
|
||||
],
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,23 @@
|
||||
# Generated by Django 6.0.4 on 2026-04-08 10:43
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('polls', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Choice',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('choice_text', models.CharField(max_length=200, verbose_name='Ответ')),
|
||||
('votes', models.IntegerField(default=0, verbose_name='Количество голосов')),
|
||||
('question', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='polls.question')),
|
||||
],
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,11 @@
|
||||
from django.db import models
|
||||
|
||||
# Create your models here.
|
||||
class Question(models.Model):
|
||||
question_text = models.CharField(verbose_name='Вопрос', max_length=200)
|
||||
pub_date = models.DateTimeField(verbose_name='Дата публикации')
|
||||
|
||||
class Choice(models.Model):
|
||||
question = models.ForeignKey(Question, on_delete=models.CASCADE)
|
||||
choice_text = models.CharField(verbose_name='Ответ', max_length=200)
|
||||
votes = models.IntegerField(verbose_name='Количество голосов', default=0)
|
||||
@@ -0,0 +1,3 @@
|
||||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
||||
@@ -1,10 +1,9 @@
|
||||
[project]
|
||||
name = "web"
|
||||
name = "web-django"
|
||||
version = "0.1.0"
|
||||
description = "Add your description here"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.14"
|
||||
requires-python = ">=3.12"
|
||||
dependencies = [
|
||||
"django>=6.0.3",
|
||||
"jupyterlab>=4.5.6",
|
||||
"django>=6.0.4",
|
||||
]
|
||||
Generated
+55
@@ -0,0 +1,55 @@
|
||||
version = 1
|
||||
revision = 3
|
||||
requires-python = ">=3.12"
|
||||
|
||||
[[package]]
|
||||
name = "asgiref"
|
||||
version = "3.11.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/63/40/f03da1264ae8f7cfdbf9146542e5e7e8100a4c66ab48e791df9a03d3f6c0/asgiref-3.11.1.tar.gz", hash = "sha256:5f184dc43b7e763efe848065441eac62229c9f7b0475f41f80e207a114eda4ce", size = 38550, upload-time = "2026-02-03T13:30:14.33Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/5c/0a/a72d10ed65068e115044937873362e6e32fab1b7dce0046aeb224682c989/asgiref-3.11.1-py3-none-any.whl", hash = "sha256:e8667a091e69529631969fd45dc268fa79b99c92c5fcdda727757e52146ec133", size = 24345, upload-time = "2026-02-03T13:30:13.039Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "django"
|
||||
version = "6.0.4"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "asgiref" },
|
||||
{ name = "sqlparse" },
|
||||
{ name = "tzdata", marker = "sys_platform == 'win32'" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/60/b9/4155091ad1788b38563bd77a7258c0834e8c12a7f56f6975deaf54f8b61d/django-6.0.4.tar.gz", hash = "sha256:8cfa2572b3f2768b2e84983cf3c4811877a01edb64e817986ec5d60751c113ac", size = 10907407, upload-time = "2026-04-07T13:55:44.961Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/e9/47/3d61d611609764aa71a37f7037b870e7bfb22937366974c4fd46cada7bab/django-6.0.4-py3-none-any.whl", hash = "sha256:14359c809fc16e8f81fd2b59d7d348e4d2d799da6840b10522b6edf7b8afc1da", size = 8368342, upload-time = "2026-04-07T13:55:37.999Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sqlparse"
|
||||
version = "0.5.5"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/90/76/437d71068094df0726366574cf3432a4ed754217b436eb7429415cf2d480/sqlparse-0.5.5.tar.gz", hash = "sha256:e20d4a9b0b8585fdf63b10d30066c7c94c5d7a7ec47c889a2d83a3caa93ff28e", size = 120815, upload-time = "2025-12-19T07:17:45.073Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/49/4b/359f28a903c13438ef59ebeee215fb25da53066db67b305c125f1c6d2a25/sqlparse-0.5.5-py3-none-any.whl", hash = "sha256:12a08b3bf3eec877c519589833aed092e2444e68240a3577e8e26148acc7b1ba", size = 46138, upload-time = "2025-12-19T07:17:46.573Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tzdata"
|
||||
version = "2026.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/19/f5/cd531b2d15a671a40c0f66cf06bc3570a12cd56eef98960068ebbad1bf5a/tzdata-2026.1.tar.gz", hash = "sha256:67658a1903c75917309e753fdc349ac0efd8c27db7a0cb406a25be4840f87f98", size = 197639, upload-time = "2026-04-03T11:25:22.002Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/b0/70/d460bd685a170790ec89317e9bd33047988e4bce507b831f5db771e142de/tzdata-2026.1-py2.py3-none-any.whl", hash = "sha256:4b1d2be7ac37ceafd7327b961aa3a54e467efbdb563a23655fbfe0d39cfc42a9", size = 348952, upload-time = "2026-04-03T11:25:20.313Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "web-django"
|
||||
version = "0.1.0"
|
||||
source = { virtual = "." }
|
||||
dependencies = [
|
||||
{ name = "django" },
|
||||
]
|
||||
|
||||
[package.metadata]
|
||||
requires-dist = [{ name = "django", specifier = ">=6.0.4" }]
|
||||
@@ -1,12 +0,0 @@
|
||||
from django.db import models
|
||||
|
||||
class Question(models.Model):
|
||||
question_text = models.CharField(max_length=200)
|
||||
pub_date = models.DateTimeField('date published')
|
||||
|
||||
def __str__(self):
|
||||
return self.question_text
|
||||
|
||||
# q1 = Question.objects.create(question_text="What's new?")
|
||||
|
||||
# print(Question.objects.all())
|
||||
@@ -1,13 +0,0 @@
|
||||
|
||||
from django.test import TestCase
|
||||
from .models import Question
|
||||
|
||||
from django.utils import timezone
|
||||
|
||||
class QuestionTest(TestCase):
|
||||
def test_create_object(self):
|
||||
obj = Question.objects.create(
|
||||
question_text="What's new?",
|
||||
pub_date=timezone.now()
|
||||
)
|
||||
self.assertTrue(Question.objects.filter(id=obj.id).exists())
|
||||
@@ -1,16 +0,0 @@
|
||||
"""
|
||||
WSGI config for web 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/6.0/howto/deployment/wsgi/
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
from django.core.wsgi import get_wsgi_application
|
||||
|
||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'web.settings')
|
||||
|
||||
application = get_wsgi_application()
|
||||
Generated
-1327
File diff suppressed because it is too large
Load Diff
@@ -7,11 +7,13 @@ BASE_URL = "http://127.0.0.1:5000"
|
||||
|
||||
|
||||
def ask_format() -> str:
|
||||
"""Запрашивает желаемый формат ответа и возвращает json или wsdl."""
|
||||
raw = input("Формат ответа (json/wsdl) [json]: ").strip().lower()
|
||||
return raw if raw in {"json", "wsdl"} else "json"
|
||||
|
||||
|
||||
def print_response(resp: requests.Response) -> None:
|
||||
"""Печатает HTTP-статус и тело ответа в JSON или сыром виде."""
|
||||
print(f"\nHTTP {resp.status_code}")
|
||||
try:
|
||||
print(json.dumps(resp.json(), ensure_ascii=False, indent=2))
|
||||
@@ -20,6 +22,7 @@ def print_response(resp: requests.Response) -> None:
|
||||
|
||||
|
||||
def input_payload() -> dict:
|
||||
"""Собирает поля объекта из консоли и возвращает payload для POST/PUT."""
|
||||
print("Оставьте поле пустым, если не хотите его передавать")
|
||||
payload = {}
|
||||
|
||||
@@ -51,6 +54,7 @@ def input_payload() -> dict:
|
||||
|
||||
|
||||
def list_calls() -> None:
|
||||
"""Запрашивает и выводит пагинированный список объектов calls."""
|
||||
page = input("page [1]: ").strip() or "1"
|
||||
per_page = input("per_page [20]: ").strip() or "20"
|
||||
fmt = ask_format()
|
||||
@@ -64,6 +68,7 @@ def list_calls() -> None:
|
||||
|
||||
|
||||
def get_call() -> None:
|
||||
"""Запрашивает и выводит один объект calls по ID."""
|
||||
call_id = input("ID объекта: ").strip()
|
||||
fmt = ask_format()
|
||||
resp = requests.get(
|
||||
@@ -73,6 +78,7 @@ def get_call() -> None:
|
||||
|
||||
|
||||
def create_call() -> None:
|
||||
"""Создает новый объект calls из введенных пользователем данных."""
|
||||
payload = input_payload()
|
||||
fmt = ask_format()
|
||||
resp = requests.post(
|
||||
@@ -82,6 +88,7 @@ def create_call() -> None:
|
||||
|
||||
|
||||
def update_call() -> None:
|
||||
"""Обновляет существующий объект calls по ID введенными полями."""
|
||||
call_id = input("ID объекта: ").strip()
|
||||
payload = input_payload()
|
||||
fmt = ask_format()
|
||||
@@ -95,6 +102,7 @@ def update_call() -> None:
|
||||
|
||||
|
||||
def delete_call() -> None:
|
||||
"""Удаляет объект calls по ID и выводит результат операции."""
|
||||
call_id = input("ID объекта: ").strip()
|
||||
fmt = ask_format()
|
||||
resp = requests.delete(
|
||||
@@ -104,6 +112,7 @@ def delete_call() -> None:
|
||||
|
||||
|
||||
def stats_by_hour() -> None:
|
||||
"""Запрашивает статистику количества обращений по выбранному часу."""
|
||||
hour = input("Час (0-23): ").strip()
|
||||
fmt = ask_format()
|
||||
resp = requests.get(
|
||||
@@ -113,11 +122,13 @@ def stats_by_hour() -> None:
|
||||
|
||||
|
||||
def show_wsdl() -> None:
|
||||
"""Получает и выводит WSDL-описание API."""
|
||||
resp = requests.get(f"{BASE_URL}/api/wsdl", timeout=30)
|
||||
print_response(resp)
|
||||
|
||||
|
||||
def menu() -> None:
|
||||
"""Запускает интерактивное меню CLI и обрабатывает выбор пользователя."""
|
||||
actions = {
|
||||
"1": ("Список объектов", list_calls),
|
||||
"2": ("Один объект по ID", get_call),
|
||||
|
||||
@@ -13,17 +13,33 @@ app = Flask(__name__)
|
||||
ALLOWED_FIELDS = ("lat", "lng", "title", "timeStamp", "town", "hour")
|
||||
|
||||
|
||||
def parse_positive_int(raw_value: str, field_name: str) -> tuple[int | None, str | None]:
|
||||
"""Преобразует строку в положительное целое число или возвращает текст ошибки."""
|
||||
try:
|
||||
value = int(raw_value)
|
||||
except (TypeError, ValueError):
|
||||
return None, f"{field_name} должен быть целым числом"
|
||||
|
||||
if value <= 0:
|
||||
return None, f"{field_name} должен быть положительным целым числом"
|
||||
|
||||
return value, None
|
||||
|
||||
|
||||
def get_db_connection() -> sqlite3.Connection:
|
||||
"""Создает и возвращает подключение к SQLite с доступом к колонкам по имени."""
|
||||
conn = sqlite3.connect(DB_PATH)
|
||||
conn.row_factory = sqlite3.Row
|
||||
return conn
|
||||
|
||||
|
||||
def wants_wsdl() -> bool:
|
||||
"""Проверяет, запросил ли клиент XML/WSDL-формат через query-параметр format."""
|
||||
return request.args.get("format", "json").lower() == "wsdl"
|
||||
|
||||
|
||||
def dict_to_xml(payload: dict) -> bytes:
|
||||
"""Преобразует словарь (включая вложенные dict/list) в XML-документ."""
|
||||
root = Element("response")
|
||||
|
||||
for key, value in payload.items():
|
||||
@@ -53,6 +69,7 @@ def dict_to_xml(payload: dict) -> bytes:
|
||||
|
||||
|
||||
def format_response(payload: dict, status: int = 200) -> Response:
|
||||
"""Формирует ответ API: XML при format=wsdl, иначе JSON."""
|
||||
if wants_wsdl():
|
||||
xml_payload = dict_to_xml(payload)
|
||||
return Response(xml_payload, status=status, mimetype="application/wsdl+xml")
|
||||
@@ -62,6 +79,7 @@ def format_response(payload: dict, status: int = 200) -> Response:
|
||||
|
||||
|
||||
def fetch_call_by_id(conn: sqlite3.Connection, call_id: int):
|
||||
"""Возвращает запись calls по rowid или None, если запись не найдена."""
|
||||
query = """
|
||||
SELECT rowid AS id, lat, lng, title, timeStamp, town, hour
|
||||
FROM calls
|
||||
@@ -71,6 +89,7 @@ def fetch_call_by_id(conn: sqlite3.Connection, call_id: int):
|
||||
|
||||
|
||||
def parse_payload() -> tuple[dict, str | None]:
|
||||
"""Читает JSON-тело запроса, валидирует и оставляет только разрешенные поля."""
|
||||
if not request.is_json:
|
||||
return {}, "Body должен быть в формате JSON"
|
||||
|
||||
@@ -87,6 +106,7 @@ def parse_payload() -> tuple[dict, str | None]:
|
||||
|
||||
@app.get("/api/calls")
|
||||
def list_calls() -> Response:
|
||||
"""Возвращает пагинированный список объектов calls."""
|
||||
page = max(request.args.get("page", default=1, type=int), 1)
|
||||
per_page = min(max(request.args.get("per_page", default=20, type=int), 1), 100)
|
||||
offset = (page - 1) * per_page
|
||||
@@ -115,6 +135,10 @@ def list_calls() -> Response:
|
||||
|
||||
@app.get("/api/calls/<int:call_id>")
|
||||
def get_call(call_id: int) -> Response:
|
||||
"""Возвращает один объект calls по его идентификатору."""
|
||||
if call_id <= 0:
|
||||
return format_response({"error": "id должен быть положительным целым числом"}, status=400)
|
||||
|
||||
conn = get_db_connection()
|
||||
row = fetch_call_by_id(conn, call_id)
|
||||
conn.close()
|
||||
@@ -127,6 +151,7 @@ def get_call(call_id: int) -> Response:
|
||||
|
||||
@app.post("/api/calls")
|
||||
def create_call() -> Response:
|
||||
"""Создает новый объект calls из данных JSON-тела запроса."""
|
||||
payload, error = parse_payload()
|
||||
if error:
|
||||
return format_response({"error": error}, status=400)
|
||||
@@ -157,6 +182,10 @@ def create_call() -> Response:
|
||||
|
||||
@app.put("/api/calls/<int:call_id>")
|
||||
def update_call(call_id: int) -> Response:
|
||||
"""Обновляет существующий объект calls по id частичным набором полей."""
|
||||
if call_id <= 0:
|
||||
return format_response({"error": "id должен быть положительным целым числом"}, status=400)
|
||||
|
||||
payload, error = parse_payload()
|
||||
if error:
|
||||
return format_response({"error": error}, status=400)
|
||||
@@ -184,6 +213,10 @@ def update_call(call_id: int) -> Response:
|
||||
|
||||
@app.delete("/api/calls/<int:call_id>")
|
||||
def delete_call(call_id: int) -> Response:
|
||||
"""Удаляет объект calls по id и возвращает статус операции."""
|
||||
if call_id <= 0:
|
||||
return format_response({"error": "id должен быть положительным целым числом"}, status=400)
|
||||
|
||||
conn = get_db_connection()
|
||||
exists = fetch_call_by_id(conn, call_id)
|
||||
if exists is None:
|
||||
@@ -199,6 +232,7 @@ def delete_call(call_id: int) -> Response:
|
||||
|
||||
@app.get("/api/stats/hour/<int:hour>")
|
||||
def stats_by_hour(hour: int) -> Response:
|
||||
"""Возвращает количество обращений в таблице calls для заданного часа (0-23)."""
|
||||
if hour < 0 or hour > 23:
|
||||
return format_response({"error": "Час должен быть от 0 до 23"}, status=400)
|
||||
|
||||
@@ -215,8 +249,32 @@ def stats_by_hour(hour: int) -> Response:
|
||||
return format_response(payload)
|
||||
|
||||
|
||||
@app.route("/api/calls/<call_id>", methods=["GET", "PUT", "DELETE"])
|
||||
def invalid_call_id(call_id: str) -> Response:
|
||||
"""Возвращает понятную ошибку, если id в URL не является корректным числом."""
|
||||
_, error = parse_positive_int(call_id, "id")
|
||||
if error:
|
||||
return format_response({"error": error}, status=400)
|
||||
return format_response({"error": "Некорректный запрос"}, status=400)
|
||||
|
||||
|
||||
@app.get("/api/stats/hour/<hour>")
|
||||
def invalid_hour(hour: str) -> Response:
|
||||
"""Возвращает понятную ошибку, если hour в URL задан некорректно."""
|
||||
try:
|
||||
parsed_hour = int(hour)
|
||||
except (TypeError, ValueError):
|
||||
return format_response({"error": "Час должен быть целым числом"}, status=400)
|
||||
|
||||
if parsed_hour < 0 or parsed_hour > 23:
|
||||
return format_response({"error": "Час должен быть от 0 до 23"}, status=400)
|
||||
|
||||
return format_response({"error": "Некорректный запрос"}, status=400)
|
||||
|
||||
|
||||
@app.get("/api/wsdl")
|
||||
def wsdl_description() -> Response:
|
||||
"""Отдает статическое WSDL-описание доступных операций сервиса."""
|
||||
wsdl = """<?xml version=\"1.0\" encoding=\"UTF-8\"?>
|
||||
<definitions name=\"CallsService\"
|
||||
targetNamespace=\"http://localhost:5000/calls\"
|
||||
|
||||
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 197 KiB |
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user