Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
150 changes: 149 additions & 1 deletion hw1/app.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,130 @@
from typing import Any, Awaitable, Callable
import json

#_________Отправка HTTP ответа_________
async def send_response(send, status_code, data):
await send({
"type": "http.response.start",
"status": status_code,
"headers": [[b"content-type", b"application/json"]]
})

await send({
"type": "http.response.body",
"body": json.dumps(data).encode()
})


#_________Вычисление n-го числа Фибоначчи_________
def fibonacci(n):
if n <= 1:
return n
a, b = 0, 1
for _ in range(n-1):
a, b = b, a + b
return b

#_________Вычисление n-го факториала________
def factorial(n):
if n < 0:
raise ValueError("n must be non-negative")
result = 1
for i in range(n):
result *= i
return result

#_________Вычисление среднего значения списка_________
def mean(json_data):
if not isinstance(json_data, list):
raise ValueError("json must be a list")
if not all(isinstance(item, (int, float)) for item in json_data):
raise ValueError("json must contain only numbers")
return sum(json_data) / len(json_data)


#_________Обработка /fibonacci/{n}_________
async def handle_fibonacci(send, path):
try:
n_str = path.split("/fibonacci/")[1]
n = int(n_str)

if n < 0:
await send_response(send, 400, {"error": "Bad Request"})
return

result = fibonacci(n)
await send_response(send, 200, {"result": result})

except ValueError:
await send_response(send, 422, {"error": "Unprocessable Entity"})
except Exception:
await send_response(send, 422, {"error": "Unprocessable Entity"})


#_________Обработка /factorial?n={n}_________
async def handle_factorial(send, scope):
try:
query_string = scope.get("query_string", b"").decode()

if not query_string:
await send_response(send, 422, {"error": "Unprocessable Entity"})
return

if "=" in query_string:
key, value = query_string.split("=", 1)
else:
await send_response(send, 422, {"error": "Unprocessable Entity"})

if key != "n":
await send_response(send, 422, {"error": "Unprocessable Entity"})
return

if not value:
await send_response(send, 422, {"error": "Unprocessable Entity"})
return

n = int(value)

if n < 0:
await send_response(send, 400, {"error": "Bad Request"})
return

result = factorial(n)
await send_response(send, 200, {"result": result})

except ValueError:
await send_response(send, 422, {"error": "Unprocessable Entity"})
except Exception:
await send_response(send, 422, {"error": "Unprocessable Entity"})


#_________Обработка /mean_________
async def handle_mean(send, receive):
try:
message = await receive()
body = message.get("body", b"")

if not body:
await send_response(send, 422, {"error": "Unprocessable Entity"})
return

data = json.loads(body.decode())

if not isinstance(data, list):
await send_response(send, 422, {"error": "Unprocessable Entity"})
return

if len(data) == 0:
await send_response(send, 400, {"error": "Bad Request"})
return

result = mean(data)
await send_response(send, 200, {"result": result})

except json.JSONDecodeError:
await send_response(send, 422, {"error": "Unprocessable Entity"})
except Exception:
await send_response(send, 422, {"error": "Unprocessable Entity"})


async def application(
Expand All @@ -12,7 +138,29 @@ async def application(
receive: Корутина для получения сообщений от клиента
send: Корутина для отправки сообщений клиенту
"""
# TODO: Ваша реализация здесь

if scope["type"] == "lifespan":
while True:
message = await receive()
if message["type"] == "lifespan.startup":
await send({"type": "lifespan.startup.complete"})
elif message["type"] == "lifespan.shutdown":
await send({"type": "lifespan.shutdown.complete"})
break
return

method = scope["method"]
path = scope["path"]

if path.startswith("/fibonacci/"):
await handle_fibonacci(send, path)
elif path.startswith("/factorial"):
await handle_factorial(send, scope)
elif path.startswith("/mean"):
await handle_mean(send, receive)
else:
await send_response(send, 404, {"error": "Not Found"})


if __name__ == "__main__":
import uvicorn
Expand Down
25 changes: 25 additions & 0 deletions hw2/hw/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
FROM python:3.12 AS base

ARG PYTHONFAULTHANDLER=1 \
PYTHONUNBUFFERED=1 \
PYTHONHASHSEED=random \
PIP_NO_CACHE_DIR=on \
PIP_DISABLE_PIP_VERSION_CHECK=on \
PIP_DEFAULT_TIMEOUT=500

RUN apt-get update && apt-get install -y gcc
RUN python -m pip install --upgrade pip

WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt

COPY . .

EXPOSE 8000

FROM base as local

ENV PYTHONPATH=/app:$PYTHONPATH

CMD ["uvicorn", "shop_api.main:app", "--port", "8000", "--host", "0.0.0.0"]
59 changes: 59 additions & 0 deletions hw2/hw/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
version: "3"

services:
postgres:
image: postgres:16-alpine
environment:
POSTGRES_USER: shop_user
POSTGRES_PASSWORD: shop_password
POSTGRES_DB: shop_db
ports:
- 5445:5432
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U shop_user -d shop_db"]
interval: 10s
timeout: 5s
retries: 5
restart: always

shop_api:
build:
context: .
dockerfile: Dockerfile
target: local
restart: always
ports:
- 8000:8000
environment:
DATABASE_URL: postgresql+asyncpg://shop_user:shop_password@postgres:5445/shop_db
depends_on:
postgres:
condition: service_healthy

grafana:
image: grafana/grafana:latest
ports:
- 3000:3000
restart: always
depends_on:
- prometheus

prometheus:
image: prom/prometheus
volumes:
- ./settings/prometheus/:/etc/prometheus/
command:
- "--config.file=/etc/prometheus/prometheus.yml"
- "--storage.tsdb.path=/prometheus"
- "--web.console.libraries=/usr/share/prometheus/console_libraries"
- "--web.console.templates=/usr/share/prometheus/consoles"
ports:
- 9090:9090
restart: always
depends_on:
- shop_api

volumes:
postgres_data:
Loading
Loading