import typing import sqlmodel import sqlmodel.sql._expression_select_cls from .product import product from .product import product_factory from .validator import barcode_validator from .validator import name_validator from .validator import description_validator from .validator import author_validator from .exception import validator_exception from .exception import exists_exception class product_loader(sqlmodel.Session): """ This is loader for product. It help in work with product database, and manage of the product of course. """ def get_by_barcode(self, target: str) -> product | None: """ It load product by barcode. When product with given barcode not exists, return None. It also validate barcode, and when it is not property, raise validation exception. Parameters: target (str): Barcode of product to load Returns: (product): Product with given barcode from database (None): When product not exists """ if barcode_validator(target).invalid: raise validator_exception("get.barcode") query = self.__select.where(product.barcode == target) result = self.exec(query) return result.first() def get_by_name(self, target: str) -> product | None: """ It load product by name. When product with given name not exists, returns None. It also validate name, and if it is not correct, raise validation_exception. Parameters: target (str): Name of the product to load Returns: (product): Product with given name (None): When product with that name not exists """ if name_validator(target).invalid: raise validator_exception("get.name") query = self.__select.where(product.name == target) result = self.exec(query) return result.first() def search_by_name(self, target: str) -> typing.Iterable[product]: """ This search by name. To start searching phrase with could be valid name must be given, that mean it could not be too short or too long. If phrase is not valid, then validator_exception is raised. Parameters: target (str): Phrase to search in names Returns: (Iterable[product]): Generator with products found by name """ if name_validator(target).invalid: raise validator_exception("search.name") target = "%" + target + "%" query = self.__select.where(product.name.like(target)) result = self.exec(query) for item in result: yield item def search_by_author(self, target: str) -> typing.Iterable[product]: """ This search products by author. Author must be valid, when it is not then validation_exception would be raised. Parameters: target (str): Author to search product for Returns: (Iterable[product]): Generator with products with author """ if author_validator(target).invalid: raise validator_exception("search.author") target = "%" + target + "%" query = self.__select.where(product.author.like(target)) result = self.exec(query) for item in result: yield item def load_all(self) -> typing.Iterable[product]: """ This load all products from database. Returns: (Iterable[produdct]): Generator with products from database """ query = self.__select result = self.exec(query) for item in result: yield item def barcode_in_use(self, target: str) -> bool: """ It check that barcode is already in use by any product. Parameters: target (str): Barcode to check Returns: (bool): True when item exists, false when not """ return self.get_by_barcode(target) is not None def name_in_use(self, target: str) -> bool: """ It check that name is already in use by any element. Parameters: target (str): Name to check that is in use Returns: (bool): True when name in use, False when not """ return self.get_by_name(target) is not None def __append(self, target: product) -> bool: """ This insert new product into database, when it is not stored yet. It check that item is ready, also barcode and name can not be in use. Parameters: target (product): Product to insert into database Returns: (bool): True when success, False when not """ if self.barcode_in_use(target.barcode): raise exists_exception("barcode") if self.name_in_use(target.name): raise exists_exception("name") self.add(target) self.commit() self.refresh(target) return True def store(self, target: product) -> bool: """ This save product into database. When product is not in database yet, then it inserting. If product is in database, then updating it. It check that new barcode and name is not already in use by another product. Parameters: target (product): Product to save or insert Returns: (bool): True when save success, False when not """ if not target.ready: raise RuntimeError("Target is not ready yet.") if not target.in_database: return self.__append(target) name_use = self.get_by_name(target.name) if name_use is not None and name_use.id != target.id: raise exists_exception("name") barcode_use = self.get_by_barcode(target.barcode) if barcode_use is not None and barcode_use.id != target.id: raise exists_exception("barcode") try: target.sqlmodel_update(target.model_dump(exclude = "id")) self.commit() self.refresh(target) return True except: return False def drop(self, target: product) -> bool: """ This remove product from database. Product must exists, to delete it from storage. Parameters: target (product): Item to delete Returns: (bool): True when deleted successfull, False if not """ if not target.in_database: return False try: for count in target.reservations: self.delete(count) self.delete(target) self.commit() return True except: return False @property def __select(self) -> sqlmodel.sql._expression_select_cls.SelectOfScalar: """ New product selector. """ return sqlmodel.select(product)