Skip to content

Commit 722917c

Browse files
No notification MVP v0.1
No notification MVP v0.1
2 parents d4f184c + 751d9d9 commit 722917c

24 files changed

+473
-66
lines changed

.env.example

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
APP_NAME='Critical Value API v1'
2+
SECRET_KEY=supersecretkey
3+
ACCESS_TOKEN_EXPIRE_MINUTES=30
4+
LOG_LEVEL=INFO
5+
16
DB_HOST=localhost
27
DB_PORT=5432
38
DB_NAME=db_name

.github/workflows/ci.yaml

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
name: PySATL CI
2-
31
on:
42
push:
53
branches:
@@ -35,10 +33,10 @@ jobs:
3533
run: poetry install --with dev
3634

3735
- name: Run Ruff lint
38-
run: poetry run ruff check --output-format=github
36+
run: poetry run ruff check --output-format=github --extend-exclude "migrations"
3937

4038
- name: Check formatting with Ruff
41-
run: poetry run ruff format --check
39+
run: poetry run ruff format pysatl_knowledge --check
4240

4341
- name: Mypy
4442
run: |

.pre-commit-config.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ repos:
66
hooks:
77
- id: flake8
88
additional_dependencies: [Flake8-pyproject]
9+
exclude: migrations
910
# stages: [push]
1011

1112
- repo: https://github.com/pre-commit/mirrors-mypy

docker-compose.yml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
version: "3.9"
2+
3+
services:
4+
db:
5+
image: postgres:15
6+
container_name: knowledge-db
7+
ports:
8+
- "${DB_PORT}:5432"
9+
environment:
10+
POSTGRES_USER: ${DB_USER}
11+
POSTGRES_PASSWORD: ${DB_PASS}
12+
POSTGRES_DB: ${DB_NAME}
13+
volumes:
14+
- postgres_data:/var/lib/postgresql/data
15+
healthcheck:
16+
test: ["CMD-SHELL", "pg_isready -U ${DB_USER} -d ${DB_NAME}"]
17+
interval: 5s
18+
retries: 5
19+
20+
volumes:
21+
postgres_data:

migrations/versions/9a61883b84c3_create_users_and_experiments_tables.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
"""Create users and experiments tables
22
33
Revision ID: 9a61883b84c3
4-
Revises:
4+
Revises:
55
Create Date: 2025-07-28 20:58:42.261092
66
77
"""
88
from typing import Sequence, Union
99

10-
from alembic import op
1110
import sqlalchemy as sa
11+
from alembic import op
1212

1313

1414
# revision identifiers, used by Alembic.

poetry.lock

Lines changed: 42 additions & 15 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,9 @@ dependencies = [
2020
"pydantic>=2.7.0",
2121
"python-dotenv>=1.0.1",
2222
"loguru>=0.7.2",
23-
"httpx>=0.27.0"
23+
"httpx>=0.27.0",
24+
"python-multipart (>=0.0.20,<0.0.21)",
25+
"pydantic-settings (>=2.10.1,<3.0.0)"
2426
]
2527

2628
[build-system]
@@ -61,6 +63,7 @@ lines_after_imports=2
6163
skip_glob = ["**/.env*", "**/env/*", "**/.venv/*", "**/docs/*", "**/user_data/*"]
6264

6365
[tool.ruff]
66+
ignore = ["B008"]
6467
line-length = 100
6568
extend-exclude = [".env", ".venv"]
6669
exclude = [
@@ -72,7 +75,7 @@ extend-select = [
7275
"C90", "B", "F", "E", "W", "UP", "I", "A", "TID", "YTT", "S", "PTH", "ASYNC", "NPY"
7376
]
7477
extend-ignore = [
75-
"E241", "E272", "E221", "B007", "B904", "S603", "S607", "S608", "NPY002"
78+
"E241", "E272", "E221", "B007", "B904", "S603", "S607", "S608", "NPY002", "UP007"
7679
]
7780

7881
[tool.mypy]

pysatl_knowledge/api/auth.py

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,23 @@
1-
from fastapi import APIRouter
1+
from fastapi import APIRouter, Depends, HTTPException
2+
from fastapi.security import OAuth2PasswordRequestForm
23

4+
from pysatl_knowledge.schemas.auth_schema import LoginResponse
5+
from pysatl_knowledge.services.auth_service import AuthService
6+
from pysatl_knowledge.services.dependencies import get_auth_service
37

4-
router = APIRouter(prefix="/hello", tags=["Hello"])
58

9+
router = APIRouter(prefix="/auth", tags=["Authentication"])
610

7-
@router.get("")
8-
async def say_hello():
9-
return {"message": "Hello World!"}
11+
12+
@router.post("/login", response_model=LoginResponse)
13+
async def login(
14+
form_data: OAuth2PasswordRequestForm = Depends(),
15+
service: AuthService = Depends(get_auth_service),
16+
):
17+
"""
18+
Получить JWT токен.
19+
"""
20+
token = await service.login(form_data.username, form_data.password)
21+
if not token:
22+
raise HTTPException(status_code=401, detail="Invalid credentials")
23+
return token
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
from typing import Optional
2+
3+
from fastapi import APIRouter, Depends, HTTPException, Path
4+
5+
from pysatl_knowledge.core.security.jwt_utils import get_current_admin, get_current_user
6+
from pysatl_knowledge.schemas.critical_value_schema import (
7+
CriticalValueCreate,
8+
CriticalValueResponse,
9+
CriticalValueVerify,
10+
)
11+
from pysatl_knowledge.services.critical_value_service import CriticalValuesService
12+
from pysatl_knowledge.services.dependencies import get_cv_service
13+
14+
15+
router = APIRouter(prefix="/critical_values", tags=["Critical Values"])
16+
17+
18+
@router.get("", response_model=CriticalValueResponse)
19+
async def get_critical_values(
20+
criterion_code: str,
21+
size: int,
22+
iterations: int,
23+
service: CriticalValuesService = Depends(get_cv_service),
24+
):
25+
return (
26+
await service.get_cv_by_params(
27+
criterion_code=criterion_code,
28+
sample_size=size,
29+
iterations=iterations,
30+
status="verified",
31+
)
32+
)[0]
33+
34+
35+
@router.get("/all", response_model=list[CriticalValueResponse])
36+
async def get_all_critical_values(
37+
criterion_code: Optional[str] = None,
38+
size: Optional[int] = None,
39+
iterations: Optional[int] = None,
40+
status: Optional[str] = None,
41+
current_user=Depends(get_current_user),
42+
service: CriticalValueCreate = Depends(get_cv_service),
43+
):
44+
return await service.get_cv_by_params(
45+
criterion_code=criterion_code,
46+
sample_size=size,
47+
iterations=iterations,
48+
status=status,
49+
)
50+
51+
52+
@router.post("", response_model=CriticalValueResponse)
53+
async def create_critical_value(
54+
data: CriticalValueCreate, service: CriticalValuesService = Depends(get_cv_service)
55+
):
56+
"""
57+
Создать новое критическое значение.
58+
"""
59+
try:
60+
return await service.create_cv(data)
61+
except ValueError as e:
62+
raise HTTPException(status_code=422, detail=str(e))
63+
64+
65+
@router.patch("/{critical_value_id}/verify", response_model=CriticalValueResponse)
66+
async def verify_critical_value(
67+
data: CriticalValueVerify,
68+
critical_value_id: int = Path(..., ge=1),
69+
service: CriticalValuesService = Depends(get_cv_service),
70+
current_admin=Depends(get_current_admin),
71+
):
72+
"""
73+
Верифицировать или отклонить критическое значение (только для admin).
74+
"""
75+
result = await service.verify_cv(critical_value_id, data.status)
76+
if not result:
77+
raise HTTPException(status_code=404, detail="Critical value not found")
78+
return result

pysatl_knowledge/core/config.py

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,20 @@
1-
from pydantic_settings import BaseSettings, SettingsConfigDict
1+
from pydantic_settings import BaseSettings
22

33

44
class Settings(BaseSettings):
5-
DB_HOST: str
6-
DB_PORT: int
7-
DB_NAME: str
8-
DB_USER: str
9-
DB_PASS: str
5+
app_name: str = "test"
6+
secret_key: str = "supersecretkey"
7+
access_token_expire_minutes: int = 30
8+
log_level: str = "INFO"
9+
DB_HOST: str = "localhost"
10+
DB_PORT: int = 5432
11+
DB_NAME: str = "test"
12+
DB_USER: str = "test"
13+
DB_PASS: str = "<PASSWORD>"
14+
15+
class Config:
16+
env_file = ".env"
17+
extra = "ignore"
1018

1119
@property
1220
def DATABASE_URL(self) -> str:
@@ -19,7 +27,5 @@ def DATABASE_URL(self) -> str:
1927
f"{self.DB_NAME}"
2028
)
2129

22-
model_config = SettingsConfigDict(env_file=".env")
23-
2430

2531
settings = Settings()

0 commit comments

Comments
 (0)