начало БД
This commit is contained in:
+115
@@ -0,0 +1,115 @@
|
||||
from sqlalchemy import delete, update
|
||||
from sqlalchemy.ext.declarative import declarative_base
|
||||
from sqlalchemy.ext.asyncio import create_async_engine, async_sessionmaker
|
||||
from sqlalchemy.exc import InvalidRequestError
|
||||
from sqlalchemy.pool import NullPool
|
||||
import config
|
||||
from utils import loggerDB
|
||||
|
||||
|
||||
DATABASE_URL = f"postgresql+asyncpg://{config.DB_USER}:{config.DB_PASS}@{config.DB_HOST}:{config.DB_PORT}/{config.DB_NAME}"
|
||||
|
||||
engine = create_async_engine(DATABASE_URL, poolclass=NullPool)
|
||||
SessionLocal = async_sessionmaker(engine)
|
||||
|
||||
Base = declarative_base()
|
||||
|
||||
|
||||
class CRUD:
|
||||
|
||||
async def create(db_data, refresh: bool = False):
|
||||
try:
|
||||
is_lst = isinstance(db_data, list)
|
||||
async with SessionLocal() as db:
|
||||
if is_lst:
|
||||
loggerDB.info(f"Создаю {len(db_data)} записей")
|
||||
try:
|
||||
db.add_all(db_data)
|
||||
except InvalidRequestError:
|
||||
for data in db_data:
|
||||
await db.merge(data)
|
||||
else:
|
||||
loggerDB.info("Создаю запись")
|
||||
db.add(db_data)
|
||||
await db.commit()
|
||||
if refresh:
|
||||
if is_lst:
|
||||
loggerDB.info(f"Обновляю {len(db_data)} записей")
|
||||
for data in db_data:
|
||||
await db.refresh(data)
|
||||
else:
|
||||
loggerDB.info("Обновляю запись")
|
||||
await db.refresh(db_data)
|
||||
loggerDB.info("Запись создана")
|
||||
return db_data if refresh else None
|
||||
except Exception as e:
|
||||
loggerDB.error(f"Ошибка создания: {str(e)}", exc_info=True)
|
||||
return None
|
||||
|
||||
async def read(query, all: bool = False):
|
||||
try:
|
||||
async with SessionLocal() as db:
|
||||
loggerDB.info(f"Чтение записей. Все: {all}")
|
||||
results = await db.execute(query)
|
||||
loggerDB.info(f"Чтение завершено")
|
||||
return (
|
||||
results.unique().scalars().all()
|
||||
if all
|
||||
else results.unique().scalars().first()
|
||||
)
|
||||
except Exception as e:
|
||||
loggerDB.error(f"Ошибка чтения: {str(e)}", exc_info=True)
|
||||
return None
|
||||
|
||||
async def delete(db_data) -> bool:
|
||||
def itemInfo(instance):
|
||||
from sqlalchemy import inspect
|
||||
|
||||
state = inspect(instance)
|
||||
|
||||
if state.identity is None:
|
||||
pKey = None
|
||||
pValue = None
|
||||
else:
|
||||
mapper = state.mapper
|
||||
pKey = mapper.primary_key[0].name
|
||||
pValue = getattr(instance, pKey)
|
||||
|
||||
return {"key": pKey, "value": pValue, "class": instance.__class__}
|
||||
|
||||
async def deleteFromDB(data, db):
|
||||
itemData = itemInfo(data)
|
||||
query = delete(itemData["class"]).where(
|
||||
getattr(itemData["class"], itemData["key"]) == itemData["value"]
|
||||
)
|
||||
await db.execute(query)
|
||||
|
||||
async with SessionLocal() as db:
|
||||
try:
|
||||
if isinstance(db_data, list):
|
||||
loggerDB.info(f"Удаляю записей: {len(db_data)}")
|
||||
for data in db_data:
|
||||
await deleteFromDB(data, db)
|
||||
else:
|
||||
loggerDB.info("Удаляю запись")
|
||||
await deleteFromDB(db_data, db)
|
||||
await db.commit()
|
||||
loggerDB.info("Запись удалена")
|
||||
return True
|
||||
except Exception as e:
|
||||
await db.rollback()
|
||||
loggerDB.error(f"Ошибка удаления: {str(e)}", exc_info=True)
|
||||
return False
|
||||
|
||||
async def update(db_data, id, **kwargs):
|
||||
async with SessionLocal() as db:
|
||||
try:
|
||||
query = update(db_data).where(db_data.id == id).values(**kwargs)
|
||||
item = await db.execute(query)
|
||||
await db.commit()
|
||||
loggerDB.info("Запись обновлена")
|
||||
return item
|
||||
except Exception as e:
|
||||
await db.rollback()
|
||||
loggerDB.error(f"Ошибка обновления: {str(e)}", exc_info=True)
|
||||
return None
|
||||
@@ -0,0 +1,26 @@
|
||||
from sqlalchemy import select
|
||||
from utils import logger
|
||||
from db import CRUD
|
||||
from db.schemas import AccessLevel
|
||||
|
||||
|
||||
async def getAccessdata(accessId: int) -> dict:
|
||||
query = select(AccessLevel).where(AccessLevel.id == accessId)
|
||||
accessData = await CRUD.read(query)
|
||||
if not accessData:
|
||||
logger.error("Уровень доступа не найден")
|
||||
return {}
|
||||
return accessData.toDict()
|
||||
|
||||
|
||||
async def editAccessData(accessId: int, **kwargs):
|
||||
query = select(AccessLevel).where(AccessLevel.id == accessId)
|
||||
accessData = await CRUD.read(query)
|
||||
if not accessData:
|
||||
logger.error("Уровень доступа не найден")
|
||||
return {}
|
||||
editedAccessData = await accessData.edit(**kwargs)
|
||||
logger.info(
|
||||
f"Уровень доступа {editedAccessData.title} успешно обновлен, изменены данные: {kwargs.keys()}"
|
||||
)
|
||||
return editedAccessData.toDict()
|
||||
@@ -0,0 +1,19 @@
|
||||
from utils import logger
|
||||
from db import CRUD
|
||||
from db.schemas import Toolbox
|
||||
from sqlalchemy import select
|
||||
|
||||
|
||||
async def addNewToolbox(toolboxData: dict):
|
||||
title = toolboxData.get("title", None)
|
||||
if not title:
|
||||
logger.error("Не указано Назавание тулбокса")
|
||||
return {}
|
||||
query = select(Toolbox).where(Toolbox.title == title)
|
||||
toolbox = await CRUD.read(query)
|
||||
if toolbox:
|
||||
logger.error("Тулбокс с таким названием уже существует")
|
||||
return {}
|
||||
newToolbox = await Toolbox(**toolboxData).save()
|
||||
logger.info(f"Тулбокс {newToolbox.title} успешно создан")
|
||||
return newToolbox.toDict()
|
||||
@@ -0,0 +1,81 @@
|
||||
from sqlalchemy import or_, select
|
||||
from db import CRUD
|
||||
from db.handlers.toolbox import addNewToolbox
|
||||
from db.schemas import User
|
||||
from utils import logger, pwd_hash
|
||||
|
||||
|
||||
async def addNewUser(userData: dict) -> dict:
|
||||
login = userData.get("login", None)
|
||||
if not login:
|
||||
logger.error("Не указан логин")
|
||||
return {}
|
||||
userName = userData.get("username")
|
||||
if not userName:
|
||||
logger.error("Не указано имя пользователя")
|
||||
return {}
|
||||
query = select(User).where(or_(User.login == login, User.username == userName))
|
||||
user = await CRUD.read(query)
|
||||
if user:
|
||||
logger.error("Пользователь с таким логином или именем уже существует")
|
||||
return {}
|
||||
if "access_level_id" not in userData:
|
||||
logger.error("Не указан уровень доступа")
|
||||
return {}
|
||||
if "password" not in userData:
|
||||
logger.error("Не указан пароль")
|
||||
return {}
|
||||
userData["hashed_password"] = pwd_hash(userData.pop("password"))
|
||||
newUser = await User(**userData).save()
|
||||
if not newUser:
|
||||
logger.error("Ошибка сохранения пользователя")
|
||||
return {}
|
||||
logger.info(f"Пользователь {newUser.username} успешно добавлен, id: {newUser.id}")
|
||||
if newUser.available_own_toolbox:
|
||||
newToolboxData = {
|
||||
"title": f"Тулбокс {newUser.username}",
|
||||
"description": f"Оборудование, полученное сотрудником {newUser.username}, под личную материальную ответственность",
|
||||
"owner_id": newUser.id,
|
||||
}
|
||||
newToolbox = await addNewToolbox(newToolboxData)
|
||||
logger.info(
|
||||
f"Тулбокс {newToolbox['title']} успешно создан и закреплен за пользователем {newUser.username}"
|
||||
)
|
||||
return newUser.toDict()
|
||||
|
||||
|
||||
async def editUser(userData: dict) -> dict:
|
||||
id = userData.get("id", None)
|
||||
if not id:
|
||||
logger.error("Не указан id пользователя")
|
||||
return {}
|
||||
query = select(User).where(User.id == id)
|
||||
user = await CRUD.read(query)
|
||||
if not user:
|
||||
logger.error("Пользователь с таким id не найден")
|
||||
return {}
|
||||
changedUserData = userData.get("changedUserData", {})
|
||||
if len(changedUserData.keys()) == 0:
|
||||
logger.error("Не указаны изменяемые данные")
|
||||
return {}
|
||||
if "password" in changedUserData:
|
||||
userData["hashed_password"] = pwd_hash(changedUserData.pop("password"))
|
||||
editedUser = await user.edit(**changedUserData)
|
||||
if not editedUser:
|
||||
logger.error("Ошибка обновления пользователя")
|
||||
return {}
|
||||
logger.info(
|
||||
f"Пользователь {editedUser.username} успешно обновлен, изменены данные: {changedUserData.keys()}"
|
||||
)
|
||||
if not user.available_own_toolbox:
|
||||
if editedUser.available_own_toolbox:
|
||||
newToolboxData = {
|
||||
"title": f"Тулбокс {editedUser.username}",
|
||||
"description": f"Оборудование, полученное сотрудником {editedUser.username}, под личную материальную ответственность",
|
||||
"owner_id": editedUser.id,
|
||||
}
|
||||
newToolbox = await addNewToolbox(newToolboxData)
|
||||
logger.info(
|
||||
f"Тулбокс {newToolbox['title']} успешно создан и закреплен за пользователем {editedUser.username}"
|
||||
)
|
||||
return editedUser.toDict()
|
||||
@@ -0,0 +1,6 @@
|
||||
from user import *
|
||||
from access import *
|
||||
from toolkit import *
|
||||
from categories import *
|
||||
from db.schemas.toolbox import *
|
||||
from stock import *
|
||||
@@ -0,0 +1,53 @@
|
||||
from datetime import datetime
|
||||
from sqlalchemy import Boolean, Column, DateTime, Integer, String, Text
|
||||
from db import Base
|
||||
import utils
|
||||
|
||||
|
||||
class AccessLevel(Base):
|
||||
__tablename__ = "access_level"
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
title = Column(String, unique=True, index=True)
|
||||
description = Column(Text)
|
||||
created_at = Column(DateTime, default=datetime.now)
|
||||
updated_at = Column(DateTime, default=datetime.now, onupdate=datetime.now)
|
||||
|
||||
# permissions
|
||||
receiving_edit = Column(Boolean, default=False)
|
||||
refund_request_edit = Column(Boolean, default=False)
|
||||
refund_request_confirm = Column(Boolean, default=False)
|
||||
debit_request_edit = Column(Boolean, default=False)
|
||||
debit_request_confirm = Column(Boolean, default=False)
|
||||
tools_creation = Column(Boolean, default=False)
|
||||
tools_registration = Column(Boolean, default=False)
|
||||
tools_registration_edit = Column(Boolean, default=False)
|
||||
tools_edit = Column(Boolean, default=False)
|
||||
tools_achievement = Column(Boolean, default=False)
|
||||
users_creation = Column(Boolean, default=False)
|
||||
users_edit = Column(Boolean, default=False)
|
||||
users_disabling = Column(Boolean, default=False)
|
||||
users_view = Column(Boolean, default=False)
|
||||
available_own_toolbox = Column(Boolean, default=False)
|
||||
view_all_toolboxes = Column(Boolean, default=False)
|
||||
view_requests = Column(Boolean, default=False)
|
||||
access_level_view = Column(Boolean, default=False)
|
||||
access_level_edit = Column(Boolean, default=False)
|
||||
manage_toolboxes = Column(Boolean, default=False)
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
for key, value in kwargs.items():
|
||||
setattr(self, key, value)
|
||||
|
||||
def toDict(self):
|
||||
return utils.toDict(self)
|
||||
|
||||
async def save(self):
|
||||
from db import CRUD
|
||||
|
||||
return await CRUD.create(self, refresh=True)
|
||||
|
||||
async def edit(self, **kwargs):
|
||||
from db import CRUD
|
||||
|
||||
return await CRUD.update(AccessLevel, self.id, **kwargs)
|
||||
@@ -0,0 +1,31 @@
|
||||
from datetime import datetime
|
||||
from sqlalchemy import Column, DateTime, Integer, String, Text
|
||||
from db import Base
|
||||
import utils
|
||||
|
||||
|
||||
class Category(Base):
|
||||
__tablename__ = "categories"
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
title = Column(String, unique=True, index=True)
|
||||
description = Column(Text)
|
||||
created_at = Column(DateTime, default=datetime.now)
|
||||
updated_at = Column(DateTime, default=datetime.now, onupdate=datetime.now)
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
for key, value in kwargs.items():
|
||||
setattr(self, key, value)
|
||||
|
||||
def toDict(self):
|
||||
return utils.toDict(self)
|
||||
|
||||
async def save(self):
|
||||
from db import CRUD
|
||||
|
||||
return await CRUD.create(self, refresh=True)
|
||||
|
||||
async def edit(id: int, **kwargs):
|
||||
from db import CRUD
|
||||
|
||||
return await CRUD.update(Category, id, **kwargs)
|
||||
@@ -0,0 +1,45 @@
|
||||
from datetime import datetime
|
||||
from sqlalchemy import Column, DateTime, Float, ForeignKey, Integer
|
||||
from sqlalchemy.orm import relationship
|
||||
|
||||
from db import Base
|
||||
import utils
|
||||
|
||||
|
||||
class Stock(Base):
|
||||
__tablename__ = "stocks"
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
toolkit_id = Column(
|
||||
Integer, ForeignKey("toolkits.id", ondelete="CASCADE"), nullable=False
|
||||
)
|
||||
toolkit_data = relationship(
|
||||
"Toolkit", cascade="all, delete-orphan", lazy="joined", uselist=False
|
||||
)
|
||||
toolbox_id = Column(
|
||||
Integer, ForeignKey("toolboxes.id", ondelete="CASCADE"), nullable=False
|
||||
)
|
||||
toolbox_data = relationship(
|
||||
"Toolbox", cascade="all, delete-orphan", lazy="joined", uselist=False
|
||||
)
|
||||
quantity = Column(Integer, nullable=False)
|
||||
price = Column(Float, nullable=False)
|
||||
created_at = Column(DateTime, default=datetime.now)
|
||||
updated_at = Column(DateTime, default=datetime.now, onupdate=datetime.now)
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
for key, value in kwargs.items():
|
||||
setattr(self, key, value)
|
||||
|
||||
def toDict(self):
|
||||
return utils.toDict(self)
|
||||
|
||||
async def save(self):
|
||||
from db import CRUD
|
||||
|
||||
return await CRUD.create(self, refresh=True)
|
||||
|
||||
async def edit(self, **kwargs):
|
||||
from db import CRUD
|
||||
|
||||
return await CRUD.update(Stock, self.id, **kwargs)
|
||||
@@ -0,0 +1,34 @@
|
||||
from datetime import datetime
|
||||
from sqlalchemy import Column, DateTime, ForeignKey, Integer, String, Text
|
||||
from db import Base
|
||||
import utils
|
||||
|
||||
|
||||
class Toolbox(Base):
|
||||
__tablename__ = "toolboxes"
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
title = Column(String, unique=True, index=True)
|
||||
description = Column(Text)
|
||||
owner_id = Column(
|
||||
Integer, ForeignKey("users.id", ondelete="CASCADE"), nullable=True
|
||||
)
|
||||
created_at = Column(DateTime, default=datetime.now)
|
||||
updated_at = Column(DateTime, default=datetime.now, onupdate=datetime.now)
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
for key, value in kwargs.items():
|
||||
setattr(self, key, value)
|
||||
|
||||
def toDict(self):
|
||||
return utils.toDict(self)
|
||||
|
||||
async def save(self):
|
||||
from db import CRUD
|
||||
|
||||
return await CRUD.create(self, refresh=True)
|
||||
|
||||
async def edit(id: int, **kwargs):
|
||||
from db import CRUD
|
||||
|
||||
return await CRUD.update(Toolbox, id, **kwargs)
|
||||
@@ -0,0 +1,45 @@
|
||||
from datetime import datetime
|
||||
from sqlalchemy import Column, DateTime, ForeignKey, Integer, String, Text
|
||||
from sqlalchemy.dialects.postgresql import JSONB
|
||||
from sqlalchemy.orm import relationship
|
||||
from db import Base
|
||||
import utils
|
||||
|
||||
|
||||
class Toolkit(Base):
|
||||
__tablename__ = "toolkits"
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
title = Column(String, unique=True, index=True)
|
||||
description = Column(Text)
|
||||
category_id = Column(Integer, ForeignKey("categories.id", ondelete="CASCADE"))
|
||||
category_data = relationship(
|
||||
"Category", cascade="all, delete-orphan", lazy="joined", uselist=False
|
||||
)
|
||||
image = Column(
|
||||
JSONB, default={"main": "images/tools/default.png", "additional": []}
|
||||
)
|
||||
quantity_min = Column(Integer, nullable=True)
|
||||
quantity_min_extra = Column(Integer, nullable=True)
|
||||
external_link = Column(String, nullable=True)
|
||||
created_at = Column(DateTime, default=datetime.now)
|
||||
updated_at = Column(DateTime, default=datetime.now, onupdate=datetime.now)
|
||||
refilled_at = Column(DateTime, default=datetime.now)
|
||||
moved_at = Column(DateTime, default=datetime.now)
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
for key, value in kwargs.items():
|
||||
setattr(self, key, value)
|
||||
|
||||
def toDict(self):
|
||||
return utils.toDict(self)
|
||||
|
||||
async def save(self):
|
||||
from db import CRUD
|
||||
|
||||
return await CRUD.create(self, refresh=True)
|
||||
|
||||
async def edit(id: int, **kwargs):
|
||||
from db import CRUD
|
||||
|
||||
return await CRUD.update(Toolkit, id, **kwargs)
|
||||
@@ -0,0 +1,35 @@
|
||||
from datetime import datetime
|
||||
from sqlalchemy import Boolean, Column, DateTime, ForeignKey, Integer, String
|
||||
from db import Base
|
||||
import utils
|
||||
|
||||
|
||||
class User(Base):
|
||||
__tablename__ = "users"
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
login = Column(String, unique=True, index=True)
|
||||
username = Column(String, unique=True, index=True)
|
||||
photo = Column(String, default="images/users/default.png")
|
||||
hashed_password = Column(String)
|
||||
access_level_id = Column(Integer, ForeignKey("access_level.id", ondelete="CASCADE"))
|
||||
is_active = Column(Boolean, default=True)
|
||||
created_at = Column(DateTime, default=datetime.now)
|
||||
updated_at = Column(DateTime, default=datetime.now, onupdate=datetime.now)
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
for key, value in kwargs.items():
|
||||
setattr(self, key, value)
|
||||
|
||||
def toDict(self):
|
||||
return utils.toDict(self)
|
||||
|
||||
async def save(self) -> "User":
|
||||
from db import CRUD
|
||||
|
||||
return await CRUD.create(self, refresh=True)
|
||||
|
||||
async def edit(self, **kwargs) -> "User":
|
||||
from db import CRUD
|
||||
|
||||
return await CRUD.update(User, self.id, **kwargs)
|
||||
Reference in New Issue
Block a user