mirror of
https://github.com/yawaflua/BiahadHakatonProject.git
synced 2025-12-09 19:59:29 +02:00
131
db_repository/BaseRepositories.py
Normal file
131
db_repository/BaseRepositories.py
Normal 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
|
||||
14
db_repository/Volunteer.py
Normal file
14
db_repository/Volunteer.py
Normal 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
|
||||
0
db_repository/__init__.py
Normal file
0
db_repository/__init__.py
Normal file
67
db_repository/apartment_repository.py
Normal file
67
db_repository/apartment_repository.py
Normal 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"]),
|
||||
)
|
||||
22
db_repository/needy_repository.py
Normal file
22
db_repository/needy_repository.py
Normal 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]
|
||||
0
db_repository/schemas/__init__.py
Normal file
0
db_repository/schemas/__init__.py
Normal file
12
db_repository/schemas/apartment.py
Normal file
12
db_repository/schemas/apartment.py
Normal 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
|
||||
6
db_repository/schemas/base.py
Normal file
6
db_repository/schemas/base.py
Normal file
@@ -0,0 +1,6 @@
|
||||
from pydantic import BaseModel
|
||||
from typing import List, Optional
|
||||
|
||||
|
||||
class BaseSchema(BaseModel):
|
||||
id: Optional[int] = None
|
||||
6
db_repository/schemas/needy.py
Normal file
6
db_repository/schemas/needy.py
Normal file
@@ -0,0 +1,6 @@
|
||||
from .user import UserSchema
|
||||
|
||||
|
||||
class NeedySchema(UserSchema):
|
||||
region: str
|
||||
how_much_peoples: int
|
||||
6
db_repository/schemas/user.py
Normal file
6
db_repository/schemas/user.py
Normal file
@@ -0,0 +1,6 @@
|
||||
from .base import BaseSchema
|
||||
|
||||
|
||||
class UserSchema(BaseSchema):
|
||||
name: str
|
||||
contact: str
|
||||
5
db_repository/schemas/volunteer.py
Normal file
5
db_repository/schemas/volunteer.py
Normal file
@@ -0,0 +1,5 @@
|
||||
from .user import UserSchema
|
||||
|
||||
|
||||
class VolunteerSchema(UserSchema):
|
||||
pass
|
||||
45
db_repository/volunteer_repository.py
Normal file
45
db_repository/volunteer_repository.py
Normal 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 []
|
||||
Reference in New Issue
Block a user