סיימתי את זה

Signed-off-by: Dmitri Shimanski <dshiafeed@gmail.com>
This commit is contained in:
Dmitri Shimanski
2025-04-08 17:09:27 +03:00
parent d63b8d70e9
commit e12fd15e71
20 changed files with 532 additions and 14 deletions

View File

@@ -0,0 +1,131 @@
import sqlite3
from typing import Type, Optional, List
from pydantic import BaseModel
from threading import Lock
class BaseRepository:
def __init__(self, mutex: Lock, db_path: str = "app.db"):
self.mutex = mutex
self.db_path = db_path
self._init_db()
@property
def _connection(self) -> sqlite3.Connection:
"""Создает новое соединение для каждого запроса"""
return sqlite3.connect(self.db_path)
def _init_db(self):
"""Инициализация таблиц в базе данных"""
with self.mutex:
with self._connection as conn:
cursor = conn.cursor()
statements = self.create_table_sql.split(';')
for stmt in statements:
stmt = stmt.strip()
if stmt:
cursor.execute(stmt)
conn.commit()
@property
def create_table_sql(self) -> str:
"""Должен быть переопределен в дочерних классах"""
raise NotImplementedError
def _execute(
self,
sql: str,
params: tuple = (),
fetch: bool = False
) -> Optional[List[dict]]:
"""Общий метод для выполнения запросов с возвратом словарей"""
with self._connection as conn:
cursor = conn.cursor()
cursor.execute(sql, params)
if fetch:
columns = [col[0] for col in cursor.description]
results = [dict(zip(columns, row)) for row in cursor.fetchall()]
return results
conn.commit()
return None
class BaseModelSchema(BaseModel):
id: Optional[int] = None
class BaseCRUDRepository(BaseRepository):
table_name: str = ""
schema: Type[BaseModelSchema] = BaseModelSchema
@property
def create_table_sql(self) -> str:
return f"""
CREATE TABLE IF NOT EXISTS {self.table_name} (
id INTEGER PRIMARY KEY AUTOINCREMENT,
{self._get_columns_definition()}
);
"""
def _get_columns_definition(self) -> str:
"""Генерирует SQL-определение колонок на основе модели Pydantic"""
fields = self.schema.model_fields
columns = []
for name, field in fields.items():
if name == "id":
continue
sql_type = "TEXT" if field.annotation == str else "INTEGER"
nullable = "NOT NULL" if not field.is_required else ""
columns.append(f"{name} {sql_type} {nullable}")
return ", ".join(columns)
def create(self, item: BaseModelSchema) -> int:
fields = item.dict(exclude={"id"})
columns = ", ".join(fields.keys())
placeholders = ", ".join(["?"] * len(fields))
sql = f"""
INSERT INTO {self.table_name} ({columns})
VALUES ({placeholders})
"""
self._execute(sql, tuple(fields.values()))
return self._get_last_insert_id()
def get(self, item_id: int) -> Optional[BaseModelSchema]:
sql = f"SELECT * FROM {self.table_name} WHERE id = ?"
result = self._execute(sql, (item_id,), fetch=True)
return self.schema(**dict(result[0])) if result else None
def get_by_name(self, item_id: str) -> Optional[BaseModelSchema]:
sql = f"SELECT * FROM {self.table_name} WHERE name = ?"
result = self._execute(sql, (item_id,), fetch=True)
return self.schema(**dict(result[0])) if result else None
def get_all(self) -> List[BaseModelSchema]:
sql = f"SELECT * FROM {self.table_name}"
results = self._execute(sql, fetch=True)
return [self.schema(**dict(row)) for row in results]
def update(self, item_id: int, item: BaseModelSchema) -> bool:
fields = item.dict(exclude={"id"})
set_clause = ", ".join([f"{key} = ?" for key in fields.keys()])
sql = f"""
UPDATE {self.table_name}
SET {set_clause}
WHERE id = ?
"""
params = (*fields.values(), item_id)
self._execute(sql, params)
return True
def delete(self, item_id: int) -> bool:
sql = f"DELETE FROM {self.table_name} WHERE id = ?"
self._execute(sql, (item_id,))
return True
def _get_last_insert_id(self) -> int:
result = self._execute("SELECT last_insert_rowid()", fetch=True)
return result[0]["last_insert_rowid()"] if result else 0

View File

@@ -0,0 +1,14 @@
from typing import Optional
from pydantic import BaseModel
from db_repository.BaseRepositories import BaseCRUDRepository
from models.Volunteer import Volunteer
class VolunteerCreate(BaseModel, Volunteer):
id: int
class VolunteerSchema(VolunteerCreate):
id: int = None

View File

View File

@@ -0,0 +1,67 @@
import array
from typing import Optional, List
from db_repository.BaseRepositories import BaseCRUDRepository
from db_repository.schemas.apartment import ApartmentSchema
import json
class ApartmentRepository(BaseCRUDRepository):
table_name = "apartments"
schema = ApartmentSchema
def _get_columns_definition(self) -> str:
base_columns = super()._get_columns_definition()
# Добавляем кастомные поля, которые не обрабатываются автоматически
return (
"owner TEXT NOT NULL, "
"location TEXT NOT NULL, "
"rooms INTEGER NOT NULL, "
"has_mamad TEXT NOT NULL, "
"price TEXT NOT NULL, "
"accepted_regions TEXT NOT NULL, "
"is_available INTEGER NOT NULL"
)
def create(self, item: ApartmentSchema) -> int:
item_dict = item.model_dump()
return super().create(self.schema(**item_dict))
def get(self, item_id: int) -> Optional[ApartmentSchema]:
result = super().get(item_id)
if result:
result_dict = result.dict()
result_dict["accepted_regions"] = result_dict["accepted_regions"]
return ApartmentSchema(**result_dict)
return None
def search_available(
self,
region: str,
min_rooms: int = 0,
) -> List[ApartmentSchema]:
query = f"""
SELECT * FROM {self.table_name}
WHERE is_available = 1
AND rooms >= ?
"""
params = (min_rooms,)
results = self._execute(query, params, fetch=True)
return [
self._parse_result(row)
for row in results
if (region in row["accepted_regions"] or "all" in row["accepted_regions"])
]
def _parse_result(self, row: tuple) -> ApartmentSchema:
return ApartmentSchema(
id=row["id"],
owner=row["owner"],
location=row["location"],
rooms=row["rooms"],
has_mamad=row["has_mamad"],
price=row["price"],
accepted_regions=row["accepted_regions"],
is_available=bool(row["is_available"]),
)

View File

@@ -0,0 +1,22 @@
from typing import List
from db_repository.BaseRepositories import BaseCRUDRepository
from db_repository.schemas.needy import NeedySchema
class NeedyRepository(BaseCRUDRepository):
table_name = "needies"
schema = NeedySchema
def _get_columns_definition(self) -> str:
return (
"name TEXT NOT NULL, "
"contact TEXT NOT NULL, "
"region TEXT NOT NULL, "
"how_much_peoples INTEGER NOT NULL"
)
def get_by_region(self, region: str) -> List[NeedySchema]:
query = f"SELECT * FROM {self.table_name} WHERE region = ?"
results = self._execute(query, (region,), fetch=True)
return [self.schema(**dict(row)) for row in results]

View File

View File

@@ -0,0 +1,12 @@
from .base import BaseSchema
from typing import List
class ApartmentSchema(BaseSchema):
owner: str
location: str
rooms: int
has_mamad: str
price: str
accepted_regions: str
is_available: bool = True

View File

@@ -0,0 +1,6 @@
from pydantic import BaseModel
from typing import List, Optional
class BaseSchema(BaseModel):
id: Optional[int] = None

View File

@@ -0,0 +1,6 @@
from .user import UserSchema
class NeedySchema(UserSchema):
region: str
how_much_peoples: int

View File

@@ -0,0 +1,6 @@
from .base import BaseSchema
class UserSchema(BaseSchema):
name: str
contact: str

View File

@@ -0,0 +1,5 @@
from .user import UserSchema
class VolunteerSchema(UserSchema):
pass

View File

@@ -0,0 +1,45 @@
from db_repository.BaseRepositories import BaseCRUDRepository
from db_repository.schemas.volunteer import VolunteerSchema
from typing import List
class VolunteerRepository(BaseCRUDRepository):
table_name = "volunteers"
schema = VolunteerSchema
def _get_columns_definition(self) -> str:
return (
"name TEXT NOT NULL, "
"contact TEXT NOT NULL"
)
@property
def create_table_sql(self) -> str:
return f"""
CREATE TABLE IF NOT EXISTS {self.table_name} (
id INTEGER PRIMARY KEY AUTOINCREMENT,
{self._get_columns_definition()}
);
CREATE TABLE IF NOT EXISTS volunteer_apartments (
volunteer_id INTEGER NOT NULL,
apartment_id INTEGER NOT NULL,
FOREIGN KEY(volunteer_id) REFERENCES volunteers(id),
FOREIGN KEY(apartment_id) REFERENCES apartments(id)
);
"""
def add_apartment(self, volunteer_id: int, apartment_id: int):
query = """
INSERT INTO volunteer_apartments (volunteer_id, apartment_id)
VALUES (?, ?)
"""
self._execute(query, (volunteer_id, apartment_id))
def get_apartments(self, volunteer_id: int) -> List[int]:
query = """
SELECT apartment_id FROM volunteer_apartments
WHERE volunteer_id = ?
"""
results = self._execute(query, (volunteer_id,), fetch=True)
return [row[0] for row in results] if results else []