Add graphql queries. Update handlers

main
Ernest Litvinenko 2024-07-08 18:35:04 +03:00
parent 7bb4be9211
commit 9d01a17453
14 changed files with 451 additions and 473 deletions

12
.idea/dataSources.xml Normal file
View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="DataSourceManagerImpl" format="xml" multifile-model="true">
<data-source source="LOCAL" name="NETDBS_2@10.2.100.126" uuid="87379102-4799-4d65-9872-24e48c29e1fc">
<driver-ref>firebird</driver-ref>
<synchronize>true</synchronize>
<jdbc-driver>org.firebirdsql.jdbc.FBDriver</jdbc-driver>
<jdbc-url>jdbc:firebirdsql://10.2.100.126:3050/NETDBS_2?lc_ctype=WIN1251</jdbc-url>
<working-dir>$ProjectFileDir$</working-dir>
</data-source>
</component>
</project>

6
.idea/vcs.xml Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

View File

@ -4,7 +4,7 @@ from fastapi.security import OAuth2PasswordBearer
from core.config import Config
from core.model.profile.db import ProfileDB
from core.storage import profile
from core.storage import profile_storage
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/api/v1/auth/phone")
@ -18,4 +18,4 @@ def get_user_from_token(token: str = Depends(oauth2_scheme)) -> ProfileDB:
except jwt.exceptions.InvalidSignatureError as err:
raise HTTPException(status_code=401, detail=str(err))
return profile.get_profile_by_id(data['profile_id'])
return profile_storage.get_profile_by_id(data['profile_id'])

10
core/model/note/db.py Normal file
View File

@ -0,0 +1,10 @@
from pydantic import BaseModel
class AppNoteDB(BaseModel):
id: int
user_id: int
task_id: int
note_status: int
tip: int
text: str

View File

@ -1,414 +1,241 @@
import functools
import json
from datetime import date, datetime
from typing import Self, Optional, Union, Any, Callable
from firebird.driver import Cursor
from pydantic import BaseModel, Field, Json
from pypika import Query, Table
from pypika.queries import QueryBuilder
from core.database.db import redis_cache_obj
from core.storage.base import BaseStorage
from redis_cache import RedisCache
from typing import Optional
from sqlalchemy import BIGINT, Column, ForeignKey
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, relationship, aliased
class __BaseDB(BaseModel, BaseStorage):
__tablename__: str
__read_only: bool = False
__table: Table | None = None
__state_changed = {}
__query: QueryBuilder | None = None
@classmethod
def cache(cls) -> RedisCache:
return redis_cache_obj()
@classmethod
def get_keys(cls):
return [x for x in cls.__dict__['__annotations__'].keys() if not x.startswith("__")]
@classmethod
def get_table(cls) -> Self:
return Table(cls.__tablename__)
@classmethod
def fetch_one(cls, id: int) -> type[Self]:
t = cls.get_table()
keys = cls.get_keys()
stmt = Query.from_(t).select(*keys).where(getattr(t, keys[0]) == id)
with cls.get_cursor() as cur:
cur: Cursor
data = cur.execute(stmt.get_sql()).fetchone()
return cls(**{key: val for key, val in zip(keys, data)})
@classmethod
def parse_orm(cls, row):
return cls(**{key: val for key, val in zip(cls.get_keys(), row)})
@classmethod
def fetch_all(cls, where_query: QueryBuilder = None) -> list['Self']:
table = cls.get_table()
if not where_query:
stmt = Query.from_(table).select(*cls.get_keys())
else:
stmt = Query.from_(table).select(*cls.get_keys()).where(where_query)
with cls.get_cursor() as cursor:
cursor: Cursor
stmt: QueryBuilder
print(stmt)
data = cursor.execute(stmt.get_sql()).fetchall()
return [cls.parse_orm(d) for d in data]
@classmethod
def fetch_related(cls) -> 'Self':
table = cls.get_table()
cls.__query = Query.from_(table)
return cls
@classmethod
def create(cls, model: Self) -> Self:
if model.__read_only:
raise Exception("This model is read only")
# table = self.get_table()
# data = self.model_dump(mode='json', exclude_none=True, exclude={'id'}, exclude_unset=True)
# stmt = Query.into(table).columns(*[self.get_keys()]).insert(
# *[kwargs[key] for key in self.get_keys() if key in kwargs.keys()])
# with self.get_cursor() as cursor:
# cursor: Cursor
# cursor.execute(stmt.get_sql())
@classmethod
def update(cls, model: Self) -> Self:
if model.__read_only:
raise Exception("This model is read only")
class __Base(DeclarativeBase):
pass
class MPLSTDB(__BaseDB):
__tablename__: str = "LST"
ID_LST: int
LST_ID_VLST: int
LST_NAME: str
LST_NAME_SH: str
@classmethod
def fetch_one(cls, id: int) -> type[Self]:
cache = cls.cache()
@cache.cache()
def wrapper(id):
return super(MPLSTDB, cls).fetch_one(id)
return wrapper(id)
class MPLSTDB(__Base):
__tablename__ = "LST"
ID_LST: Mapped[int] = mapped_column(BIGINT, primary_key=True)
LST_ID_VLST: Mapped[int]
LST_NAME: Mapped[str]
LST_NAME_SH: Mapped[str]
class MPAppTaskDB(__BaseDB):
class MPAppTaskDB(__Base):
__tablename__: str = "APP_TASK"
ID_APP_TASK: int | None = None
APP_TASK_ID_SOTR: int = None
APP_TASK_ID_APP_TASK: int = None
APP_TASK_DT_START_PLN: datetime = None
APP_TASK_DT_END_PLN: datetime = None
APP_TASK_DT_START_FACT: datetime | None = None
APP_TASK_DT_END_FACT: datetime | None = None
APP_TASK_STATUS: int = None
APP_TASK_TIP: int = None
APP_TASK_TEXT: str = None
APP_TASK_DEL: int = None
ID_APP_TASK: Mapped[int] = mapped_column(BIGINT, primary_key=True)
APP_TASK_ID_SOTR: Mapped[int] = mapped_column(BIGINT)
APP_TASK_ID_APP_TASK: Mapped[int] = mapped_column(BIGINT, ForeignKey("APP_TASK.ID_APP_TASK"))
APP_TASK_DT_START_PLN: Mapped[datetime]
APP_TASK_DT_END_PLN: Mapped[datetime]
APP_TASK_DT_START_FACT: Mapped[datetime | None]
APP_TASK_DT_END_FACT: Mapped[datetime | None]
APP_TASK_STATUS: Mapped[int]
APP_TASK_TIP: Mapped[int]
APP_TASK_TEXT: Mapped[str]
APP_TASK_DEL: Mapped[int]
subtasks: Mapped['MPAppTaskDB'] = relationship('MPAppTaskDB', remote_side=[ID_APP_TASK])
@property
def status(self) -> MPLSTDB:
return MPLSTDB.fetch_one(self.APP_TASK_STATUS)
@property
def is_subtask(self) -> bool:
return self.ID_APP_TASK != self.APP_TASK_ID_APP_TASK
@property
def task_type(self) -> MPLSTDB:
return MPLSTDB.fetch_one(self.APP_TASK_TIP)
@property
def subtasks(self) -> list['MPAppTaskDB']:
t: MPAppTaskDB = MPAppTaskDB.get_table()
return MPAppTaskDB.fetch_all(
(t.APP_TASK_ID_APP_TASK == self.ID_APP_TASK) & (t.APP_TASK_ID_APP_TASK != t.ID_APP_TASK) & (
t.APP_TASK_DEL == 0))
@property
def events(self) -> list['MPAppEventDB']:
return MPAppEventDB.fetch_all(MPAppEventDB.get_table().APP_EVENT_ID_REC == self.ID_APP_TASK)
@property
def params(self) -> list['MPAppParamDB']:
return MPAppParamDB.fetch_all(MPAppParamDB.get_table().APP_PARAM_ID_REC == self.ID_APP_TASK)
class EventData(__BaseDB):
key: MPLSTDB
value: Any
class MPAppEventDB(__BaseDB):
class MPAppEventDB(__Base):
__tablename__: str = "APP_EVENT"
ID_APP_EVENT: int = None
APP_EVENT_ID_SOTR: int = None
APP_EVENT_ID_REC: int = None
APP_EVENT_VID: int = None
APP_EVENT_TIP: int = None
APP_EVENT_DT: datetime | None = None
APP_EVENT_TEXT: str | None = None
APP_EVENT_DATA: Json
APP_EVENT_DEL: int = 0
@property
def event_type(self) -> MPLSTDB:
return MPLSTDB.fetch_one(self.APP_EVENT_TIP)
@property
def event_data(self) -> list[EventData]:
return [EventData(key=MPLSTDB.fetch_one(key), value=value) for d in list(self.APP_EVENT_DATA) for key, value in
d.items()]
ID_APP_EVENT: Mapped[int] = mapped_column(primary_key=True)
APP_EVENT_ID_SOTR: Mapped[int]
APP_EVENT_ID_REC: Mapped[int]
APP_EVENT_VID: Mapped[int]
APP_EVENT_TIP: Mapped[int]
APP_EVENT_DT: Mapped[datetime | None]
APP_EVENT_TEXT: Mapped[str | None]
APP_EVENT_DATA: Mapped[str]
APP_EVENT_DEL: Mapped[int]
class MPMSTDB(__BaseDB):
__read_only = True
class MPMSTDB(__Base):
__tablename__ = "MST"
ID_MST: int = Field(default=0)
MST_PR_OTHER: int = Field(default=0)
MST_ID_KG: int = Field(default=0)
MST_ID_SRV: int = Field(default=0)
MST_ID_SETTLEMENT: Optional[int] = Field(default=0)
MST_SID: Optional[str] = Field(default=None)
MST_NAME: Optional[str] = Field(default=None)
MST_CLI_NAME: Optional[str] = Field(default=None)
MST_CODE: int = Field(default=0)
MST_CODE_PODR_NDS: int | None = Field(default=0)
MST_CODE_PODR_BN: int | None = Field(default=0)
MST_PR_TTNINPUT: int = Field(default=0)
MST_PR_TTNOUTPUT: int = Field(default=0)
MST_PR_AEX: int = Field(default=0)
MST_PR_AEX_ADR: Optional[int] = Field(default=0)
MST_ID_MST_TTNOUTPUT: int = Field(default=0)
MST_PR_SORT: int = Field(default=0)
MST_PR_PVZ: int = Field(default=0)
MST_PR_VIRT: int = Field(default=0)
MST_PR_INOTHER: int = Field(default=0)
MST_PR_ZAKG: int = Field(default=0)
MST_PR_FAR: int = Field(default=0)
MST_PR_KKT: int = Field(default=0)
MST_PR_CC: int = Field(default=0)
MST_PR_AS: int = Field(default=0)
MST_KM: int = Field(default=0)
MST_MP: int = Field(default=0)
MST_ID_AGENT_AS: int = Field(default=0)
MST_PR_NOLIM_AS: int = Field(default=0)
MST_PR_WC_AS: int = Field(default=0)
MST_PR_TRS: int = Field(default=0)
MST_ID_REGION: int = Field(default=0)
MST_ADDRESS_CODE: int = Field(default=0)
MST_ID_KLADR_DOM: Optional[int] = Field(default=0)
MST_SHIR: float = Field(default=0.0)
MST_DOLG: float = Field(default=0.0)
MST_ADR_STOR: Optional[str] = Field(default=None)
MST_FUNC_MASK: int = Field(default=0)
MST_ID_SRV_CALL: int = Field(default=0)
MST_ID_MST_CALL: int = Field(default=0)
MST_PR_DIRECT: int = Field(default=0)
MST_NAME_DIRECT: Optional[str] = Field(default=None)
MST_PR_NOTE: int = Field(default=0)
MST_PR_NOTSITE: int = Field(default=0)
MST_PR_GREEN: int = Field(default=0)
MST_PR_GREENORK: int = Field(default=0)
MST_PR_GREENPRINTER: int = Field(default=0)
MST_PR_VID_TR_VD: int = Field(default=0)
MST_PR_BAN_IN: int = Field(default=0)
MST_PR_NO_CLIENT_CODES: int = Field(default=0)
MST_PR_NO_STTN02: int = Field(default=0)
MST_PR_NO_EEU: int = Field(default=0)
MST_VID_CALC_FOBYOM: int = Field(default=0)
MST_TXT: Optional[str] = Field(default=None)
MST_DEL: int = Field(default=0)
MST_CH: Optional[datetime] = Field(default=None)
MST_WCH: int = Field(default=0)
MST_IMP: Optional[datetime] = Field(default=None)
MST_MPOST: int = Field(default=0)
MST_SEANS: int = Field(default=0)
MST_OWNERMST: int = Field(default=0)
MST_CR: Optional[datetime] = Field(default=None)
MST_WCR: int = Field(default=0)
MST_FIMP: Optional[datetime] = Field(default=None)
MST_ID_MST_SYNONYM: int = Field(default=0)
MST_NAME_OLD: Optional[str] = Field(default=None)
MST_SRC_OLD: int = Field(default=0)
MST_UPPERNAME_OLD: Optional[str] = Field(default=None)
MST_TXT_AEX: Optional[str] = Field(default=None)
MST_PR_NODOOR_AEX: int = Field(default=0)
@classmethod
def fetch_one(cls, id: int) -> type[Self]:
cache = cls.cache()
@cache.cache()
def wrapper(id):
return super(MPMSTDB, cls).fetch_one(id)
return wrapper(id)
ID_MST: Mapped[int] = mapped_column(primary_key=True)
MST_PR_OTHER: Mapped[int]
MST_ID_KG: Mapped[int]
MST_ID_SRV: Mapped[int]
MST_ID_SETTLEMENT: Mapped[Optional[int]]
MST_SID: Mapped[Optional[str]]
MST_NAME: Mapped[Optional[str]]
MST_CLI_NAME: Mapped[Optional[str]]
MST_CODE: Mapped[int]
MST_CODE_PODR_NDS: Mapped[int | None]
MST_CODE_PODR_BN: Mapped[int | None]
MST_PR_TTNINPUT: Mapped[int]
MST_PR_TTNOUTPUT: Mapped[int]
MST_PR_AEX: Mapped[int]
MST_PR_AEX_ADR: Mapped[Optional[int]]
MST_ID_MST_TTNOUTPUT: Mapped[int]
MST_PR_SORT: Mapped[int]
MST_PR_PVZ: Mapped[int]
MST_PR_VIRT: Mapped[int]
MST_PR_INOTHER: Mapped[int]
MST_PR_ZAKG: Mapped[int]
MST_PR_FAR: Mapped[int]
MST_PR_KKT: Mapped[int]
MST_PR_CC: Mapped[int]
MST_PR_AS: Mapped[int]
MST_KM: Mapped[int]
MST_MP: Mapped[int]
MST_ID_AGENT_AS: Mapped[int]
MST_PR_NOLIM_AS: Mapped[int]
MST_PR_WC_AS: Mapped[int]
MST_PR_TRS: Mapped[int]
MST_ID_REGION: Mapped[int]
MST_ADDRESS_CODE: Mapped[int]
MST_ID_KLADR_DOM: Mapped[Optional[int]]
MST_SHIR: Mapped[float]
MST_DOLG: Mapped[float]
MST_ADR_STOR: Mapped[Optional[str]]
MST_FUNC_MASK: Mapped[int]
MST_ID_SRV_CALL: Mapped[int]
MST_ID_MST_CALL: Mapped[int]
MST_PR_DIRECT: Mapped[int]
MST_NAME_DIRECT: Mapped[Optional[str]]
MST_PR_NOTE: Mapped[int]
MST_PR_NOTSITE: Mapped[int]
MST_PR_GREEN: Mapped[int]
MST_PR_GREENORK: Mapped[int]
MST_PR_GREENPRINTER: Mapped[int]
MST_PR_VID_TR_VD: Mapped[int]
MST_PR_BAN_IN: Mapped[int]
MST_PR_NO_CLIENT_CODES: Mapped[int]
MST_PR_NO_STTN02: Mapped[int]
MST_PR_NO_EEU: Mapped[int]
MST_VID_CALC_FOBYOM: Mapped[int]
MST_TXT: Mapped[Optional[str]]
MST_DEL: Mapped[int]
MST_CH: Mapped[Optional[datetime]]
MST_WCH: Mapped[int]
MST_IMP: Mapped[Optional[datetime]]
MST_MPOST: Mapped[int]
MST_SEANS: Mapped[int]
MST_OWNERMST: Mapped[int]
MST_CR: Mapped[Optional[datetime]]
MST_WCR: Mapped[int]
MST_FIMP: Mapped[Optional[datetime]]
MST_ID_MST_SYNONYM: Mapped[int]
MST_NAME_OLD: Mapped[Optional[str]]
MST_SRC_OLD: Mapped[int]
MST_UPPERNAME_OLD: Mapped[Optional[str]]
MST_TXT_AEX: Mapped[Optional[str]]
MST_PR_NODOOR_AEX: Mapped[int]
class MPTRSDB(__BaseDB):
class MPTRSDB(__Base):
__tablename__ = "TRS"
__read_only = True
ID_TRS: int = Field(default=0)
TRS_PR_TEST: int = Field(default=0)
TRS_PR_TEST_ID_SOTR: int = Field(default=0)
TRS_PR_TEST_DT: Optional[datetime] = Field(default=None)
TRS_PR: int = Field(default=0)
TRS_PR_UP: int = Field(default=0)
TRS_ID_LST_PR: int = Field(default=0)
TRS_ID_LST_VID: int = Field(default=0)
TRS_ID_LSTU_TIP: int = Field(default=0)
TRS_SID: Optional[str] = Field(default=None)
TRS_SID_GOST: Optional[str] = Field(default=None)
TRS_SID_OLD: Optional[str] = Field(default=None)
TRS_SRC_OLD: int = Field(default=0)
TRS_PR_VLAD: int = Field(default=0)
TRS_ID_AGENT_AS: int = Field(default=0)
TRS_VES: float = Field(default=0.0)
TRS_OBYOM: float = Field(default=0.0)
TRS_PR_LTOR: int = Field(default=0)
TRS_PR_LLEN: int = Field(default=0)
TRS_PR_LTOP: int = Field(default=0)
TRS_PR_TEPL: int = Field(default=0)
TRS_PR_TEPL_WHERE: int = Field(default=0)
TRS_OBYOM_TEPL: float = Field(default=0.0)
TRS_PR_NOZAGRGRUZ: int = Field(default=0)
TRS_CNT_AXIS: int = Field(default=0)
TRS_PRIM: Optional[str] = Field(default=None)
TRS_INFO: Optional[str] = Field(default=None)
TRS_TARA: Optional[float] = Field(default=0.0)
TRS_TYPEPROPERTY: int = Field(default=0)
TRS_DOGAREND: Optional[str] = Field(default=None)
TRS_1C_D_AKT: Optional[date] = Field(default=None)
TRS_1C_NOMMSG: int = Field(default=0)
TRS_1C_DEL: int = Field(default=0)
TRS_1C_DATEEND: Optional[date] = Field(default=None)
TRS_DEL: int = Field(default=0)
TRS_CR: Optional[datetime] = Field(default=None)
TRS_WCR: int = Field(default=0)
TRS_CH: Optional[datetime] = Field(default=None)
TRS_WCH: int = Field(default=0)
TRS_OWNERMST: int = Field(default=0)
TRS_SEANS: int = Field(default=0)
TRS_IMP: Optional[datetime] = Field(default=None)
TRS_FIMP: Optional[datetime] = Field(default=None)
TRS_MPOST: int = Field(default=0)
@classmethod
def fetch_one(cls, id: int) -> type[Self]:
cache = cls.cache()
@cache.cache()
def wrapper(id):
return super(MPTRSDB, cls).fetch_one(id)
return wrapper(id)
ID_TRS: Mapped[int] = mapped_column(primary_key=True)
TRS_PR_TEST: Mapped[int]
TRS_PR_TEST_ID_SOTR: Mapped[int]
TRS_PR_TEST_DT: Mapped[Optional[datetime]]
TRS_PR: Mapped[int]
TRS_PR_UP: Mapped[int]
TRS_ID_LST_PR: Mapped[int]
TRS_ID_LST_VID: Mapped[int]
TRS_ID_LSTU_TIP: Mapped[int]
TRS_SID: Mapped[Optional[str]]
TRS_SID_GOST: Mapped[Optional[str]]
TRS_SID_OLD: Mapped[Optional[str]]
TRS_SRC_OLD: Mapped[int]
TRS_PR_VLAD: Mapped[int]
TRS_ID_AGENT_AS: Mapped[int]
TRS_VES: Mapped[float]
TRS_OBYOM: Mapped[float]
TRS_PR_LTOR: Mapped[int]
TRS_PR_LLEN: Mapped[int]
TRS_PR_LTOP: Mapped[int]
TRS_PR_TEPL: Mapped[int]
TRS_PR_TEPL_WHERE: Mapped[int]
TRS_OBYOM_TEPL: Mapped[float]
TRS_PR_NOZAGRGRUZ: Mapped[int]
TRS_CNT_AXIS: Mapped[int]
TRS_PRIM: Mapped[Optional[str]]
TRS_INFO: Mapped[Optional[str]]
TRS_TARA: Mapped[Optional[float]]
TRS_TYPEPROPERTY: Mapped[int]
TRS_DOGAREND: Mapped[Optional[str]]
TRS_1C_D_AKT: Mapped[Optional[date]]
TRS_1C_NOMMSG: Mapped[int]
TRS_1C_DEL: Mapped[int]
TRS_1C_DATEEND: Mapped[Optional[date]]
TRS_DEL: Mapped[int]
TRS_CR: Mapped[Optional[datetime]]
TRS_WCR: Mapped[int]
TRS_CH: Mapped[Optional[datetime]]
TRS_WCH: Mapped[int]
TRS_OWNERMST: Mapped[int]
TRS_SEANS: Mapped[int]
TRS_IMP: Mapped[Optional[datetime]]
TRS_FIMP: Mapped[Optional[datetime]]
TRS_MPOST: Mapped[int]
class MPMarshDB(__BaseDB):
__read_only = True
class MPMarshDB(__Base):
__tablename__ = "MARSH"
ID_MARSH: int = Field(default=0)
MARSH_PR: int = Field(default=0)
MARSH_PR_PLAN: int = Field(default=0)
MARSH_PR_VLAD: int = Field(default=0)
MARSH_PR_DOP: int = Field(default=0)
MARSH_PR_TEPL: int = Field(default=0)
MARSH_KEY_GPREF: Optional[str] = Field(default=None, min_length=1, max_length=16)
MARSH_KEY_PREF: Optional[str] = Field(default=None, min_length=1, max_length=32)
MARSH_NAME: Optional[str] = Field(default=None, min_length=1, max_length=128)
MARSH_D_N: Optional[date] = Field(default=None)
MARSH_D_K: Optional[date] = Field(default=None)
MARSH_ID_MST_OTPR: int = Field(default=0)
MARSH_ID_MST_NAZN: int = Field(default=0)
MARSH_DAYS_WEEK: int = Field(default=0)
MARSH_T_OTPR: float = Field(default=0.0)
MARSH_DATE_OUT: Optional[date] = Field(default=None)
MARSH_PRICE: Optional[float] = Field(default=0.0)
MARSH_KM: Optional[float] = Field(default=0.0)
MARSH_TXT: Optional[str] = Field(default=None, min_length=1, max_length=512)
MARSH_DEL: int = Field(default=0)
ID_MARSH: Mapped[int] = mapped_column(primary_key=True)
MARSH_PR: Mapped[int]
MARSH_PR_PLAN: Mapped[int]
MARSH_PR_VLAD: Mapped[int]
MARSH_PR_DOP: Mapped[int]
MARSH_PR_TEPL: Mapped[int]
MARSH_KEY_GPREF: Mapped[Optional[str]]
MARSH_KEY_PREF: Mapped[Optional[str]]
MARSH_NAME: Mapped[Optional[str]]
MARSH_D_N: Mapped[Optional[date]]
MARSH_D_K: Mapped[Optional[date]]
MARSH_ID_MST_OTPR: Mapped[int]
MARSH_ID_MST_NAZN: Mapped[int]
MARSH_DAYS_WEEK: Mapped[int]
MARSH_T_OTPR: Mapped[float]
MARSH_DATE_OUT: Mapped[Optional[date]]
MARSH_PRICE: Mapped[Optional[float]]
MARSH_KM: Mapped[Optional[float]]
MARSH_TXT: Mapped[Optional[str]]
MARSH_DEL: Mapped[int]
class MPMarshTRSDB(__BaseDB):
class MPMarshTRSDB(__Base):
__tablename__ = "MARSH_TRS"
__read_only = True
ID_MARSH_TRS: int = Field(default=0)
MARSH_TRS_ID_MARSH: int = Field(default=0)
MARSH_TRS_DATE: Optional[date] = Field(default=None)
MARSH_TRS_ID_TRS: int = Field(default=0)
MARSH_TRS_TRS_PR_COLDONLY: Optional[int] = Field(default=0)
MARSH_TRS_ID_PRIC: int = Field(default=0)
MARSH_TRS_PRIC_PR_COLDONLY: Optional[int] = Field(default=0)
MARSH_TRS_ID_SOTR: int = Field(default=0)
MARSH_TRS_DT_DELIVERY: Optional[datetime] = Field(default=None)
MARSH_TRS_PR: int = Field(default=0)
MARSH_TRS_COMMENT: Optional[str] = Field(default=None, max_length=4096)
MARSH_TRS_TARIFF: float = Field(default=0.0)
MARSH_TRS_DEL: int = Field(default=0)
MARSH_TRS_OWNERMST: int = Field(default=0)
MARSH_TRS_MPOST: Optional[int] = Field(default=0)
MARSH_TRS_CR: Optional[datetime] = Field(default=None)
MARSH_TRS_WCR: int = Field(default=0)
MARSH_TRS_IMP: Optional[datetime] = Field(default=None)
MARSH_TRS_CH: Optional[datetime] = Field(default=None)
MARSH_TRS_WCH: int = Field(default=0)
MARSH_TRS_SEANS: int = Field(default=0)
MARSH_TRS_FIMP: Optional[datetime] = Field(default=None)
@property
def marsh(self) -> MPMarshDB:
return MPMarshDB.fetch_one(self.MARSH_TRS_ID_MARSH)
@property
def trs(self) -> MPTRSDB:
return MPTRSDB.fetch_one(self.MARSH_TRS_ID_TRS)
@property
def trailer(self) -> MPTRSDB:
return MPTRSDB.fetch_one(self.MARSH_TRS_ID_PRIC)
ID_MARSH_TRS: Mapped[int] = mapped_column(primary_key=True)
MARSH_TRS_ID_MARSH: Mapped[int]
MARSH_TRS_DATE: Mapped[Optional[date]]
MARSH_TRS_ID_TRS: Mapped[int]
MARSH_TRS_TRS_PR_COLDONLY: Mapped[Optional[int]]
MARSH_TRS_ID_PRIC: Mapped[int]
MARSH_TRS_PRIC_PR_COLDONLY: Mapped[Optional[int]]
MARSH_TRS_ID_SOTR: Mapped[int]
MARSH_TRS_DT_DELIVERY: Mapped[Optional[datetime]]
MARSH_TRS_PR: Mapped[int]
MARSH_TRS_COMMENT: Mapped[Optional[str]]
MARSH_TRS_TARIFF: Mapped[float]
MARSH_TRS_DEL: Mapped[int]
MARSH_TRS_OWNERMST: Mapped[int]
MARSH_TRS_MPOST: Mapped[Optional[int]]
MARSH_TRS_CR: Mapped[Optional[datetime]]
MARSH_TRS_WCR: Mapped[int]
MARSH_TRS_IMP: Mapped[Optional[datetime]]
MARSH_TRS_CH: Mapped[Optional[datetime]]
MARSH_TRS_WCH: Mapped[int]
MARSH_TRS_SEANS: Mapped[int]
MARSH_TRS_FIMP: Mapped[Optional[datetime]]
class MPAppParamDB(__BaseDB):
class MPAppParamDB(__Base):
__tablename__ = "APP_PARAM"
__read_only = True
APP_PARAM_ID_REC: int = Field(default=0)
APP_PARAM_STR: Optional[str] = Field(default=None, min_length=1, max_length=1024)
APP_PARAM_VID: int = Field(default=0)
APP_PARAM_TIP: int = Field(default=0)
APP_PARAM_DEL: int = Field(default=0)
APP_PARAM_CR: Optional[datetime] = Field(default=None)
APP_PARAM_WCR: int = Field(default=0)
APP_PARAM_CH: Optional[datetime] = Field(default=None)
APP_PARAM_WCH: int = Field(default=0)
APP_PARAM_ID_REC: Mapped[int] = mapped_column(primary_key=True)
APP_PARAM_STR: Mapped[Optional[str]]
APP_PARAM_VID: Mapped[int]
APP_PARAM_TIP: Mapped[int]
APP_PARAM_DEL: Mapped[int]
APP_PARAM_CR: Mapped[Optional[datetime]]
APP_PARAM_WCR: Mapped[int]
APP_PARAM_CH: Mapped[Optional[datetime]]
APP_PARAM_WCH: Mapped[int]
@property
def param_type(self) -> MPLSTDB:
return MPLSTDB.fetch_one(self.APP_PARAM_TIP)
@property
def related_table(self) -> Union['MPMSTDB', 'MPMarshTRSDB', 'MPTRSDB', 'MPMarshDB']:
query = {
"ID_MARSH_TRS": MPMarshTRSDB,
"ID_MST": MPMSTDB,
"ID_TRS": MPTRSDB,
"ID_MARSH": MPMarshDB
}
return query[self.param_type.LST_NAME_SH].fetch_one(self.APP_PARAM_STR)
# SET RELATIONS

View File

@ -1,6 +1,10 @@
from .base import BaseStorage
from .profile_storage import Storage as ProfileStorage
from .task_storage import Storage as TaskStorage
from .note_storage import Storage as NoteStorage
base_storage = BaseStorage()
profile_storage = ProfileStorage()
task_storage = TaskStorage()
note_storage = NoteStorage()
profile = ProfileStorage()
task = TaskStorage()

View File

@ -1,6 +1,6 @@
import socket
from contextlib import contextmanager
from typing import ContextManager
from typing import ContextManager, NewType, TypeVar
import sqlalchemy
from firebird.driver import Cursor, Connection
@ -9,6 +9,11 @@ from sqlalchemy import text
from core.database.db import engine
def row_to_type(self: sqlalchemy.Row):
return type("KeyedROW", (), {key.upper(): val for key, val in self._mapping.items()})
class BaseStorage:
_pool = engine
@ -56,3 +61,6 @@ where ID_SEANS = RDB$GET_CONTEXT('USER_SESSION', 'ID_SEANS');
# yield cursor
# finally:
# cursor.close()
sqlalchemy.Row.row_to_type = row_to_type

View File

@ -0,0 +1,58 @@
import datetime
import json
from typing import Optional
import sqlalchemy
from sqlalchemy import text
from .base import BaseStorage
from ..model.note.db import AppNoteDB
class Storage(BaseStorage):
def fetch_all_notes_for_user(self, user_id: int):
stmt = text("""
select * from APP_NOTE where APP_NOTE_ID_SOTR = :user_id and APP_NOTE_DEL = 0;
""")
with self.get_session() as session:
session: sqlalchemy.Connection
res = [x.row_to_type() for x in session.execute(stmt, {
"user_id": user_id
}).fetchall()]
return [AppNoteDB(id=row.ID_APP_NOTE, user_id=row.APP_NOTE_ID_SOTR, task_id=row.APP_NOTE_ID_APP_TASK,
note_status=row.APP_NOTE_STATUS, tip=row.APP_NOTE_TIP, text=row.APP_NOTE_TEXT) for row in res]
def update_note(self, id: int, status: int, user_id: int):
stmt = text("""
insert into APP_EVENT (APP_EVENT_DT, APP_EVENT_ID_SOTR, APP_EVENT_TEXT, APP_EVENT_DATA, APP_EVENT_VID, APP_EVENT_ID_REC) values (current_timestamp, :user_id, 'Изменен статус уведомлений', :event_data, 8797, :note_id)
""")
event_data = json.dumps([{"8794": str(status)}], separators=(',', ":"))
with self.get_session() as session:
session: sqlalchemy.Connection
session.execute(stmt, {
"user_id": user_id,
"event_data": event_data,
"note_id": id
})
session.commit()
def create_note(self, user_id: int, note_text: str, time_created: datetime.datetime, task_id: Optional[int] = None):
stmt = text("""
insert into APP_EVENT (APP_EVENT_DT, APP_EVENT_ID_SOTR, APP_EVENT_TEXT, APP_EVENT_DATA, APP_EVENT_VID) values (:time_created, :user_id, :text, :event_data, 8795)
""")
if (task_id is not None):
event_data = json.dumps([{"ID_APP_TASK": str(task_id)}], separators=(",", ":"))
else:
event_data = None
with self.get_session() as session:
session: sqlalchemy.Connection
session.execute(stmt, {
"time_created": time_created,
"user_id": user_id,
"text": note_text,
"event_data": event_data
})
session.commit()

View File

@ -1,13 +1,17 @@
import typing
from dataclasses import field
from core.model.note.db import AppNoteDB
from core.model.task.db import DBAppTask, DBSubTask, DBEvent, DBMarsh, DBTRS, DBMST, Location
from datetime import datetime
from enum import Enum
from typing import Union
import strawberry
from strawberry.types import Info
from sqlalchemy import select
from core.model.task.enums import MarshTemperatureProperty
from core.storage import task
from core.model.task.enums import StatusEnum
from core.storage import base_storage, task_storage, note_storage
@strawberry.enum
@ -30,87 +34,136 @@ class MarshTemperaturePropertyQl(Enum):
UNDEFINED = 0
@strawberry.enum
class MarshTemperaturePropertyQL(Enum):
HOT = 1
COLD = 2
UNDEFINED = 0
@strawberry.type
class Query:
@strawberry.field
def tasks(self, user_id: str) -> list['AppTaskQL']:
tasks = task.fetch_tasks_with_subtasks(user_id=int(user_id))
returned_list: list['AppTaskQL'] = []
for t in tasks:
updated_task = AppTaskQL(
id=str(t.id),
start_pln=t.start_pln,
end_pln=t.end_pln,
start_fact=t.start_fact,
end_fact=t.end_fact,
status=t.status.value,
task_type=t.task_type.value,
text=t.text,
route=MarshQL(
id=str(t.route.id),
temperature_property=t.route.temperature_property.value,
name=t.route.name,
trailer=TRSQL(
id=str(t.route.trailer.id),
gost=t.route.trailer.gost
) if t.route.trailer else None,
truck=TRSQL(
id=str(t.route.truck.id),
gost=t.route.truck.gost
) if t.route.truck else None,
) if t.route else None,
events=[EventQl(
id=e.id,
type=e.type,
text=e.text,
event_data=e.event_data,
event_datetime=e.event_datetime
) for e in t.events]
)
returned_list.append(updated_task)
def tasks(self, user_id: str, is_planned: typing.Optional[bool] = False, is_completed: typing.Optional[bool] = False) -> list['AppTaskQL']:
tasks = task_storage.fetch_tasks_with_subtasks(user_id)
return returned_list
if is_planned:
return [x for x in tasks if x.status == StatusEnum.NOT_DEFINED]
if is_completed:
return [x for x in tasks if x.status == StatusEnum.COMPLETED]
return tasks
@strawberry.field
def task(self, user_id: str, task_id: typing.Optional[str] = None, is_active: typing.Optional[bool] = None) -> \
typing.Optional[
'AppTaskQL']:
tasks = task_storage.fetch_tasks_with_subtasks(user_id)
if task_id is not None:
try:
return next(t for t in tasks if t.id == int(task_id))
except StopIteration:
return None
if is_active is not None:
try:
return next(t for t in tasks if t.status == StatusEnum.IN_PROGRESS)
except StopIteration:
return None
@strawberry.field
def notes(self, user_id: str) -> list['AppNoteQL']:
return note_storage.fetch_all_notes_for_user(int(user_id))
@strawberry.field
def subtask(self, user_id: str, subtask_id: str) -> typing.Optional['SubtaskQL']:
try:
return next(s for t in task_storage.fetch_tasks_with_subtasks(int(user_id)) for s in t.subtasks if
s.id == int(subtask_id))
except StopIteration:
return None
@strawberry.type
class AppTaskQL:
@strawberry.experimental.pydantic.type(model=Location)
class LocationQL:
lat: float
lon: float
@strawberry.experimental.pydantic.type(model=DBMST)
class MSTQL:
name: str
location: LocationQL
@strawberry.experimental.pydantic.type(model=DBSubTask)
class SubtaskQL:
id: str
start_pln: datetime
end_pln: datetime
start_fact: datetime | None
end_fact: datetime | None
start_pln: strawberry.auto
end_pln: strawberry.auto
start_fact: strawberry.auto
end_fact: strawberry.auto
status: StatusEnumQl
task_type: TaskTypeEnumQl
task_type: int
text: str
events = []
subtasks = []
route = None
station: typing.Optional[MSTQL] = None
@strawberry.type
class MarshQL:
@strawberry.experimental.pydantic.type(model=DBEvent)
class AppEventQL:
id: str
temperature_property: MarshTemperaturePropertyQl
name: str
trailer: typing.Optional['TRSQL'] = None
truck: typing.Optional['TRSQL'] = None
@strawberry.type
class TRSQL:
id: str | None
gost: str | None
@strawberry.type
class EventQl:
id: int
type: str
text: str
event_data: dict
event_datetime: datetime
@strawberry.experimental.pydantic.type(model=DBTRS)
class TRSQL:
gost: str | None
@strawberry.experimental.pydantic.type(model=DBMarsh)
class AppRouteQL:
temperature_property: MarshTemperaturePropertyQL
name: str
trailer: typing.Optional[TRSQL]
truck: typing.Optional[TRSQL]
@strawberry.experimental.pydantic.type(model=AppNoteDB)
class AppNoteQL:
id: str
user_id: str
task_id: str
note_status: int
tip: int
text: str
@strawberry.experimental.pydantic.type(model=DBAppTask)
class AppTaskQL:
id: str
profile_id: strawberry.auto
start_pln: strawberry.auto
end_pln: strawberry.auto
start_fact: strawberry.auto
end_fact: strawberry.auto
status: StatusEnumQl
task_type: int
text: strawberry.auto
events: list[AppEventQL]
subtasks: list[SubtaskQL]
route: 'AppRouteQL'
@strawberry.field
def active_subtask(self) -> typing.Optional[SubtaskQL]:
try:
return next(x for x in self.subtasks if x.status == x.status.IN_PROGRESS)
except StopIteration:
return None
schema = strawberry.Schema(query=Query)

View File

@ -8,7 +8,7 @@ from pydantic_extra_types.phone_numbers import PhoneNumber
from core.config import Config
from core.errors.auth.errors import profile_not_founded, incorrect_phone_number
from core.storage import profile
from core.storage import profile_storage
router = APIRouter(prefix="/auth")
@ -34,19 +34,19 @@ async def get_authorization_code(req: PhoneNumberRequest):
if not 10 <= len(req.phoneNumber) <= 12:
incorrect_phone_number()
p = profile.get_profile_by_phone("".join(char for char in req.phoneNumber if char.isdigit()))
p = profile_storage.get_profile_by_phone("".join(char for char in req.phoneNumber if char.isdigit()))
if not p:
profile_not_founded()
return {"code": profile.generate_profile_auth_code(p.id, p.phone_number)}
return {"code": profile_storage.generate_profile_auth_code(p.id, p.phone_number)}
@router.post("/phone/code")
async def get_access_token(form_data: OAuth2PasswordRequestForm = Depends()) -> Token:
p = profile.get_profile_by_phone(''.join(char for char in form_data.username if char.isdigit()))
p = profile_storage.get_profile_by_phone(''.join(char for char in form_data.username if char.isdigit()))
if not p:
profile_not_founded()
check = profile.check_profile_code(p.id, form_data.password)
check = profile_storage.check_profile_code(p.id, form_data.password)
if not check:
raise HTTPException(status_code=403, detail="Incorrect code")

View File

View File

View File

@ -10,21 +10,21 @@ from core.model.profile.db import ProfileDB
from core.model.task.db import DBAppTask, DBSubTask, DBEvent
from core.model.task.enums import StatusEnum
from core.model.task.requests import SetTaskStatusActiveRequest, SetSubtaskStatusRequest, UpdTaskRequest, UpdTaskData
from core.storage import task
from core.storage import task_storage
from core.model.task.db2 import MPAppTaskDB
router = APIRouter(prefix="/tasks", tags=["Tasks and subtasks"])
@router.get("/test", description="Fetch Task for authenticated user")
async def get_task_test(user: ProfileDB = Depends(get_user_from_token)) -> list[MPAppTaskDB]:
return MPAppTaskDB.fetch_all()
# @router.get("/test", description="Fetch Task for authenticated user")
# async def get_task_test(user: ProfileDB = Depends(get_user_from_token)) -> list[MPAppTaskDB]:
# return MPAppTaskDB.fetch_all()
@router.get("", description="Fetch Task for authenticated user")
async def get_tasks(user: ProfileDB = Depends(get_user_from_token)) -> list[DBAppTask]:
return task.fetch_tasks_with_subtasks(user.id)
return task_storage.fetch_tasks_with_subtasks(user.id)
@router.post("")
@ -83,7 +83,7 @@ async def upd_task(req: UpdTaskRequest, user: ProfileDB = Depends(get_user_from_
}
}
tasks = task.fetch_tasks_with_subtasks(user.id)
tasks = task_storage.fetch_tasks_with_subtasks(user.id)
available_tasks_ids = [x.id for x in tasks] + [sbt.id for t in tasks for sbt in t.subtasks]
req.data.sort(key=lambda u: u.dt)
@ -123,21 +123,21 @@ async def upd_task(req: UpdTaskRequest, user: ProfileDB = Depends(get_user_from_
except KeyError as exc:
update_task_by_chain_failed(exc)
[task.update_task(event, user.id) for event in req.data]
return task.fetch_tasks_with_subtasks(user.id)
[task_storage.update_task(event, user.id) for event in req.data]
return task_storage.fetch_tasks_with_subtasks(user.id)
@router.get("/planned")
async def get_planned_tasks(user: ProfileDB = Depends(get_user_from_token)) -> list[DBAppTask]:
# TODO Rebuild this method to fetch only planned tasks
tasks = task.fetch_tasks_with_subtasks(user_id=user.id)
tasks = task_storage.fetch_tasks_with_subtasks(user_id=user.id)
return [x for x in tasks if x.status == StatusEnum.NOT_DEFINED]
@router.get("/active")
async def get_active_task(user: ProfileDB = Depends(get_user_from_token)) -> DBAppTask | dict:
# TODO Rebuild this method to fetch only active tasks
tasks = task.fetch_tasks_with_subtasks(user_id=user.id)
tasks = task_storage.fetch_tasks_with_subtasks(user_id=user.id)
try:
return next(x for x in tasks if x.status == StatusEnum.IN_PROGRESS)
except StopIteration:
@ -147,7 +147,7 @@ async def get_active_task(user: ProfileDB = Depends(get_user_from_token)) -> DBA
@router.get("/completed")
async def get_active_task(user: ProfileDB = Depends(get_user_from_token)) -> list[DBAppTask]:
# TODO Rebuild this method to fetch only active tasks
tasks = task.fetch_tasks_with_subtasks(user_id=user.id)
tasks = task_storage.fetch_tasks_with_subtasks(user_id=user.id)
return [x for x in tasks if x.status == StatusEnum.COMPLETED]
@ -170,7 +170,7 @@ async def get_active_task(user: ProfileDB = Depends(get_user_from_token)) -> lis
@router.get("/{task_id}/subtasks")
async def get_subtasks(user: ProfileDB = Depends(get_user_from_token), task_id: int = Path()) -> list[DBSubTask]:
# TODO Rebuild this method to fetch only subtasks
tasks = task.fetch_tasks_with_subtasks(user_id=user.id)
tasks = task_storage.fetch_tasks_with_subtasks(user_id=user.id)
try:
t = next(x for x in tasks if x.id == task_id)
return t.subtasks
@ -181,11 +181,11 @@ async def get_subtasks(user: ProfileDB = Depends(get_user_from_token), task_id:
@router.post("/subtask")
async def set_status_to_subtask(req_data: SetSubtaskStatusRequest,
user: ProfileDB = Depends(get_user_from_token)) -> DBSubTask:
tasks = task.fetch_tasks_with_subtasks(user_id=user.id)
tasks = task_storage.fetch_tasks_with_subtasks(user_id=user.id)
try:
subtask = next(subtask for t in tasks for subtask in t.subtasks if subtask.id == req_data.subtask_id)
task.set_subtask_to_completed(subtask_id=subtask.id, profile_id=user.id, dt=req_data.finished_dt)
task_storage.set_subtask_to_completed(subtask_id=subtask.id, profile_id=user.id, dt=req_data.finished_dt)
subtask.status = StatusEnum.COMPLETED
return subtask
@ -196,7 +196,7 @@ async def set_status_to_subtask(req_data: SetSubtaskStatusRequest,
@router.get("/{task_id}/events")
async def get_events(user: ProfileDB = Depends(get_user_from_token), task_id: int = Path()) -> list[DBEvent]:
# TODO Rebuild this method to fetch only events
tasks = task.fetch_tasks_with_subtasks(user_id=user.id)
tasks = task_storage.fetch_tasks_with_subtasks(user_id=user.id)
try:
t = next(x for x in tasks if x.id == task_id)
return [x for x in t.events if x.type == "Change"]