From 6ba041a839cd294580536212fc582b03e2579c66 Mon Sep 17 00:00:00 2001 From: fakz9 Date: Tue, 19 Mar 2024 09:01:46 +0300 Subject: [PATCH] crappy --- backend/session.py | 6 ++-- main.py | 3 +- models/__init__.py | 6 ++++ models/base.py | 1 + models/client.py | 4 +-- models/deal.py | 8 +++++- models/secondary.py | 10 +++++++ models/service.py | 21 ++++++++++++++ requirements.txt | 3 +- routers/__init__.py | 1 + routers/client.py | 6 +++- routers/deal.py | 10 +++++++ routers/service.py | 59 +++++++++++++++++++++++++++++++++++++++ schemas/base.py | 4 +++ schemas/client.py | 11 ++++---- schemas/deal.py | 49 ++++++++++++++++++++++++-------- schemas/services.py | 49 ++++++++++++++++++++++++++++++++ services/client.py | 6 ++-- services/deal.py | 26 ++++++++++++++++- services/service.py | 56 +++++++++++++++++++++++++++++++++++++ test_main.http | 11 -------- utils/import_services.py | 58 ++++++++++++++++++++++++++++++++++++++ utils/services.xlsx | Bin 0 -> 32012 bytes 23 files changed, 369 insertions(+), 39 deletions(-) create mode 100644 models/secondary.py create mode 100644 models/service.py create mode 100644 routers/service.py create mode 100644 schemas/services.py create mode 100644 services/service.py delete mode 100644 test_main.http create mode 100644 utils/import_services.py create mode 100644 utils/services.xlsx diff --git a/backend/session.py b/backend/session.py index 2d328ac..3f3cf81 100644 --- a/backend/session.py +++ b/backend/session.py @@ -1,3 +1,5 @@ +from typing import AsyncGenerator + from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession from sqlalchemy.orm import sessionmaker @@ -10,10 +12,10 @@ session_maker = sessionmaker( class_=AsyncSession, expire_on_commit=False, autocommit=False, - autoflush=False + autoflush=False, ) -async def get_session() -> AsyncSession: +async def get_session() -> AsyncGenerator[AsyncSession, None]: async with session_maker() as session: yield session diff --git a/main.py b/main.py index 124d4f1..dfa54ef 100644 --- a/main.py +++ b/main.py @@ -19,7 +19,8 @@ app.add_middleware( routers_list = [ routers.auth_router, routers.deal_router, - routers.client_router + routers.client_router, + routers.service_router ] for router in routers_list: app.include_router(router) diff --git a/models/__init__.py b/models/__init__.py index dbd6ad6..7827e82 100644 --- a/models/__init__.py +++ b/models/__init__.py @@ -1,3 +1,9 @@ +from sqlalchemy.orm import configure_mappers + from .auth import * from .deal import * from .client import * +from .service import * +from .secondary import * + +configure_mappers() diff --git a/models/base.py b/models/base.py index ebdfe6c..6e3aeab 100644 --- a/models/base.py +++ b/models/base.py @@ -1,3 +1,4 @@ from sqlalchemy.orm import declarative_base BaseModel = declarative_base() +metadata = BaseModel.metadata \ No newline at end of file diff --git a/models/client.py b/models/client.py index ff9c1e0..deb0fe7 100644 --- a/models/client.py +++ b/models/client.py @@ -1,5 +1,5 @@ from sqlalchemy import Column, Integer, String, DateTime, ForeignKey, BigInteger -from sqlalchemy.orm import relationship +from sqlalchemy.orm import relationship, backref from models import BaseModel @@ -17,7 +17,7 @@ class ClientDetails(BaseModel): id = Column(Integer, autoincrement=True, primary_key=True, index=True) client_id = Column(Integer, ForeignKey('clients.id'), unique=True, nullable=False, comment='ID клиента') - client = relationship('Client', backref='details', uselist=False) + client = relationship('Client', backref=backref('details', uselist=False)) address = Column(String) phone_number = Column(String) diff --git a/models/deal.py b/models/deal.py index ae05d42..b231482 100644 --- a/models/deal.py +++ b/models/deal.py @@ -1,9 +1,10 @@ from enum import IntEnum, unique -from sqlalchemy import Column, Integer, String, DateTime, ForeignKey +from sqlalchemy import Column, Integer, String, DateTime, ForeignKey, Boolean from sqlalchemy.orm import relationship from models.base import BaseModel +from models.secondary import deal_services @unique @@ -29,6 +30,11 @@ class Deal(BaseModel): status_history = relationship('DealStatusHistory', back_populates='deal', cascade="all, delete-orphan") + is_deleted = Column(Boolean, nullable=False, server_default='0', default=False, comment='Удалена') + is_completed = Column(Boolean, nullable=False, server_default='0', default=False, comment='Завершена') + + services = relationship('Service', secondary=deal_services) + class DealStatusHistory(BaseModel): __tablename__ = 'deals_status_history' diff --git a/models/secondary.py b/models/secondary.py new file mode 100644 index 0000000..b5a7c68 --- /dev/null +++ b/models/secondary.py @@ -0,0 +1,10 @@ +from sqlalchemy import Table, Column, Integer, ForeignKey + +from models.base import metadata + +deal_services = Table( + 'deal_services', metadata, + Column('deal_id', Integer, ForeignKey('deals.id')), + Column('service_id', Integer, ForeignKey('services.id')), + Column('quantity', Integer) +) diff --git a/models/service.py b/models/service.py new file mode 100644 index 0000000..3a81d37 --- /dev/null +++ b/models/service.py @@ -0,0 +1,21 @@ +from sqlalchemy import Column, Integer, String, ForeignKey, Double +from sqlalchemy.orm import relationship + +from models import BaseModel + + +class Service(BaseModel): + __tablename__ = 'services' + id = Column(Integer, autoincrement=True, primary_key=True, index=True) + name = Column(String, nullable=False, comment='Название услуги') + + category_id = Column(Integer, ForeignKey('service_categories.id'), nullable=False, comment='ID категории услуги') + category = relationship('ServiceCategory') + + price = Column(Double, nullable=False, comment='Стоимость услуги') + + +class ServiceCategory(BaseModel): + __tablename__ = 'service_categories' + id = Column(Integer, autoincrement=True, primary_key=True, index=True) + name = Column(String, nullable=False) diff --git a/requirements.txt b/requirements.txt index 0424121..c55941f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -16,4 +16,5 @@ alembic # Other python-dotenv aiohttp -aiohttp[speedups] \ No newline at end of file +aiohttp[speedups] +openpyxl \ No newline at end of file diff --git a/routers/__init__.py b/routers/__init__.py index 43c00c0..0788b9a 100644 --- a/routers/__init__.py +++ b/routers/__init__.py @@ -1,4 +1,5 @@ from .auth import auth_router from .deal import deal_router from .client import client_router +from .service import service_router diff --git a/routers/client.py b/routers/client.py index 8de30c9..5713c52 100644 --- a/routers/client.py +++ b/routers/client.py @@ -39,7 +39,11 @@ async def update_client_details( return ClientUpdateDetailsResponse(ok=True) -@client_router.get('/get-all', operation_id='get_all_clients', response_model=ClientGetAllResponse) +@client_router.get( + '/get-all', + operation_id='get_all_clients', + response_model=ClientGetAllResponse +) async def get_all_clients( session: Annotated[AsyncSession, Depends(get_session)], ): diff --git a/routers/deal.py b/routers/deal.py index f714524..b34154c 100644 --- a/routers/deal.py +++ b/routers/deal.py @@ -40,3 +40,13 @@ async def change_status( user: Annotated[User, Depends(get_current_user)] ): return await DealService(session).change_status(request, user) + + +@deal_router.get('/summaries', + response_model=DealSummaryResponse, + operation_id='getDealSummaries' + ) +async def get_summary( + session: Annotated[AsyncSession, Depends(get_session)] +): + return await DealService(session).get_summary() diff --git a/routers/service.py b/routers/service.py new file mode 100644 index 0000000..8f4b86a --- /dev/null +++ b/routers/service.py @@ -0,0 +1,59 @@ +from typing import Annotated + +from fastapi import APIRouter, Depends +from sqlalchemy.ext.asyncio import AsyncSession + +from backend.session import get_session +from schemas.services import * +from services.service import ServiceService + +service_router = APIRouter( + prefix="/service", + tags=['service'] +) + + +@service_router.get( + '/get-all', + response_model=ServiceGetAllResponse, + operation_id="get_all_services" +) +async def get_all( + session: Annotated[AsyncSession, Depends(get_session)] +): + return await ServiceService(session).get_all() + + +@service_router.post( + '/create', + response_model=ServiceCreateResponse, + operation_id="create_service" +) +async def create( + session: Annotated[AsyncSession, Depends(get_session)], + request: ServiceCreateRequest +): + return await ServiceService(session).create(request) + + +@service_router.get( + '/categories/get-all', + response_model=ServiceGetAllCategoriesResponse, + operation_id="get_all_service_categories" +) +async def get_all_categories( + session: Annotated[AsyncSession, Depends(get_session)] +): + return await ServiceService(session).get_all_categories() + + +@service_router.post( + '/categories/create', + response_model=ServiceCreateCategoryResponse, + operation_id="create_service_category" +) +async def create_category( + session: Annotated[AsyncSession, Depends(get_session)], + request: ServiceCreateCategoryRequest +): + return await ServiceService(session).create_category(request) diff --git a/schemas/base.py b/schemas/base.py index 43250b7..4e5be92 100644 --- a/schemas/base.py +++ b/schemas/base.py @@ -10,3 +10,7 @@ class CustomModel(BaseModel): class Config: from_attributes = True + +class OkMessageSchema(BaseModel): + ok: bool + message: str diff --git a/schemas/client.py b/schemas/client.py index 6934b58..070f6bc 100644 --- a/schemas/client.py +++ b/schemas/client.py @@ -3,18 +3,17 @@ from typing import List from schemas.base import CustomModel -class ClientSchema(CustomModel): - id: int - name: str - - class ClientDetailsSchema(CustomModel): address: str | None = None phone_number: str | None = None inn: int | None = None email: str | None = None - # TODO add email validation + +class ClientSchema(CustomModel): + id: int + name: str + details: ClientDetailsSchema | None = None class ClientSearchRequest(CustomModel): diff --git a/schemas/deal.py b/schemas/deal.py index 271f9b1..6fe7faa 100644 --- a/schemas/deal.py +++ b/schemas/deal.py @@ -1,18 +1,34 @@ import datetime +from typing import List from schemas.base import CustomModel from schemas.client import ClientSchema +# region Entities +class FastDeal(CustomModel): + name: str + client: ClientSchema + comment: str + acceptance_date: datetime.datetime + + +class DealSummary(CustomModel): + id: int + name: str + client_name: str + changed_at: datetime.datetime + status: int + + +# endregion Entities + +# region Requests class DealChangeStatusRequest(CustomModel): deal_id: int new_status: int -class DealChangeStatusResponse(CustomModel): - ok: bool - - class DealCreateRequest(CustomModel): name: str @@ -25,16 +41,27 @@ class DealQuickCreateRequest(CustomModel): acceptance_date: datetime.datetime -class DealQuickCreateResponse(CustomModel): - deal_id: int +class DealSummaryRequest(CustomModel): + pass + + +# endregion Requests + +# region Responses + +class DealChangeStatusResponse(CustomModel): + ok: bool class DealCreateResponse(CustomModel): ok: bool -class FastDeal(CustomModel): - name: str - client: ClientSchema - comment: str - acceptance_date: datetime.datetime +class DealQuickCreateResponse(CustomModel): + deal_id: int + + +class DealSummaryResponse(CustomModel): + summaries: List[DealSummary] + +# endregion Responses diff --git a/schemas/services.py b/schemas/services.py new file mode 100644 index 0000000..39b98c0 --- /dev/null +++ b/schemas/services.py @@ -0,0 +1,49 @@ +from typing import List + +from schemas.base import CustomModel, OkMessageSchema + + +# region Entities +class ServiceCategorySchema(CustomModel): + id: int + name: str + + +class ServiceSchema(CustomModel): + id: int + name: str + category: ServiceCategorySchema + price: float + + +# endregion + + +# region Requests +class ServiceCreateRequest(CustomModel): + service: ServiceSchema + + +class ServiceCreateCategoryRequest(CustomModel): + category: ServiceCategorySchema + + +# endregion + + +# region Responses +class ServiceGetAllResponse(CustomModel): + services: List[ServiceSchema] + + +class ServiceGetAllCategoriesResponse(CustomModel): + categories: List[ServiceCategorySchema] + + +class ServiceCreateResponse(OkMessageSchema): + pass + + +class ServiceCreateCategoryResponse(OkMessageSchema): + pass +# endregion diff --git a/services/client.py b/services/client.py index 16b1e66..e096d72 100644 --- a/services/client.py +++ b/services/client.py @@ -3,6 +3,7 @@ from typing import Union, Annotated from fastapi import Depends from sqlalchemy import select, update +from sqlalchemy.orm import joinedload from models import Client, ClientDetails, User from services.auth import get_current_user @@ -24,7 +25,7 @@ class ClientService(BaseService): return details async def get_all(self) -> ClientGetAllResponse: - clients_query = await self.session.scalars(select(Client)) + clients_query = await self.session.scalars(select(Client).options(joinedload(Client.details))) clients = clients_query.all() result = [] for client in clients: @@ -65,7 +66,8 @@ class ClientService(BaseService): async def search_clients(self, request: ClientSearchRequest) -> ClientSearchResponse: query = await self.session.scalars(select(Client) - .where(Client.name.ilike(f'%{request.name}%'))) + .where(Client.name.ilike(f'%{request.name}%')) + .options(joinedload(Client.details))) clients = [] for client in query.all(): clients.append(ClientSchema.model_validate(client)) diff --git a/services/deal.py b/services/deal.py index 44e9231..20b3caf 100644 --- a/services/deal.py +++ b/services/deal.py @@ -2,6 +2,7 @@ import datetime from typing import Type, Union from sqlalchemy import select +from sqlalchemy.orm import joinedload, selectinload from models import User, Deal from models.deal import * @@ -16,7 +17,9 @@ class DealService(BaseService): async def _get_deal_by_id(self, deal_id) -> Union[Deal, None]: return await self.session.get(Deal, deal_id) - async def change_status(self, deal: Deal, status: DealStatus, user: User, + async def change_status(self, deal: Deal, + status: DealStatus, + user: User, deadline: datetime.datetime = None) -> DealStatusHistory: deadline = deadline status_change = DealStatusHistory( @@ -75,3 +78,24 @@ class DealService(BaseService): await self.change_status(deal, DealStatus(request.new_status), user) await self.session.commit() return DealChangeStatusResponse(ok=True) + + async def get_summary(self) -> DealSummaryResponse: + deals_query = await self.session.scalars(select(Deal) + .options(selectinload(Deal.status_history), + joinedload(Deal.client)) + .where(Deal.is_deleted == False, + Deal.is_completed == False)) + summaries = [] + for deal in deals_query.all(): + deal: Deal + last_status: DealStatusHistory = max(deal.status_history, key=lambda status: status.changed_at) + summaries.append( + DealSummary( + id=deal.id, + client_name=deal.client.name, + name=deal.name, + changed_at=last_status.changed_at, + status=last_status.to_status + ) + ) + return DealSummaryResponse(summaries=summaries) \ No newline at end of file diff --git a/services/service.py b/services/service.py new file mode 100644 index 0000000..5a7cf92 --- /dev/null +++ b/services/service.py @@ -0,0 +1,56 @@ +from sqlalchemy import select +from sqlalchemy.orm import joinedload + +from models import Service, ServiceCategory +from services.base import BaseService +from schemas.services import ServiceGetAllResponse, ServiceSchema, ServiceGetAllCategoriesResponse, \ + ServiceCategorySchema, ServiceCreateRequest, ServiceCreateResponse, ServiceCreateCategoryRequest, \ + ServiceCreateCategoryResponse + + +class ServiceService(BaseService): + async def get_all(self) -> ServiceGetAllResponse: + query = await (self.session + .scalars(select(Service) + .options(joinedload(Service.category)) + .order_by(Service.category_id, Service.id))) + services = [] + for service in query.all(): + services.append(ServiceSchema.model_validate(service)) + return ServiceGetAllResponse(services=services) + + async def create(self, request: ServiceCreateRequest) -> ServiceCreateResponse: + try: + raw_service = request.service + service_dict = raw_service.model_dump() + service_dict['category_id'] = raw_service.category.id + del service_dict['id'] + del service_dict['category'] + service = Service(**service_dict) + self.session.add(service) + await self.session.commit() + return ServiceCreateResponse(ok=True, message="Услуга успешно создана") + except Exception as e: + return ServiceCreateResponse(ok=False, message=f"Неудалось создать услугу, ошибка: {e}") + + async def create_category(self, request: ServiceCreateCategoryRequest) -> ServiceCreateCategoryResponse: + try: + raw_category = request.category + category_dict = raw_category.model_dump() + del category_dict['id'] + category = ServiceCategory(**category_dict) + self.session.add(category) + await self.session.commit() + return ServiceCreateCategoryResponse(ok=True, message="Категория успешно создана") + + except Exception as e: + return ServiceCreateCategoryResponse(ok=False, message=f"Неудалось создать категорию, ошибка: {e}") + + async def get_all_categories(self) -> ServiceGetAllCategoriesResponse: + query = await (self.session + .scalars(select(ServiceCategory) + .order_by(ServiceCategory.id))) + categories = [] + for category in query.all(): + categories.append(ServiceCategorySchema.model_validate(category)) + return ServiceGetAllCategoriesResponse(categories=categories) diff --git a/test_main.http b/test_main.http deleted file mode 100644 index a2d81a9..0000000 --- a/test_main.http +++ /dev/null @@ -1,11 +0,0 @@ -# Test your FastAPI endpoints - -GET http://127.0.0.1:8000/ -Accept: application/json - -### - -GET http://127.0.0.1:8000/hello/User -Accept: application/json - -### diff --git a/utils/import_services.py b/utils/import_services.py new file mode 100644 index 0000000..b7162dd --- /dev/null +++ b/utils/import_services.py @@ -0,0 +1,58 @@ +import asyncio + +import openpyxl +from openpyxl.worksheet.worksheet import Worksheet +from sqlalchemy.ext.asyncio import AsyncSession + +from backend.session import session_maker +from models import ServiceCategory, Service + + +async def write_services(services: dict): + async with session_maker() as session: + session: AsyncSession + + for raw_category in services.keys(): + category = ServiceCategory(name=raw_category) + session.add(category) + await session.flush() + print(category.id) + for raw_service in services[raw_category]: + service = Service(name=raw_service['name'], + price=raw_service['price'], + category_id=category.id) + session.add(service) + await session.flush() + await session.commit() + + +async def main(): + workbook = openpyxl.load_workbook('services.xlsx') + sheet: Worksheet = workbook.active + START_ROW = 12 + current_category = None + services = {} + merged_cells = sheet.merged_cells.ranges + for row in range(START_ROW, sheet.max_row): + first_column = sheet.cell(row, 1) + first_column_value = first_column.value.strip() + + second_column = sheet.cell(row, 2) + second_column_value = second_column.value + + is_category = any([second_column.coordinate in merged_cell for merged_cell in merged_cells]) + if is_category: + current_category = first_column_value + services[current_category] = [] + continue + price = second_column_value + name = first_column_value + services[current_category].append({ + 'name': name, + 'price': price + }) + await write_services(services) + + +if __name__ == '__main__': + asyncio.run(main()) diff --git a/utils/services.xlsx b/utils/services.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..8553cc76ba4e654998538a4f7bb95850e0654c76 GIT binary patch literal 32012 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 Oc!Wsrhd}RNrT+yHzr6PV literal 0 HcmV?d00001