from aiohttp import web from typing import Dict, Optional, List, Union, Any, Mapping, Tuple from sqlalchemy.exc import NoResultFound, IntegrityError, DBAPIError from config import ( POSTGRES_PASSWORD, POSTGRES_HOST, POSTGRES_USER, POSTGRES_DB, POSTGRES_PORT, POSTGRES_DATA_USER, POSTGRES_DATA_PASSWORD, POSTGRES_DATA_HOST, POSTGRES_DATA_DB, POSTGRES_DATA_PORT, ) from server.artist.constants import COUNTRIES_MAPPER from server.db.constants import DEFAULT_ERROR_MSG, DB_ERROR_MAPPER from server.db.pg_connector import get_postgres_async_session from server.track.constants import GENRES_MAPPER ENTITY_TYPE_MAPPER: Mapping[str, str] = {"genres": "genre", "countries": "country"} GENRE_COUNTRY_MAPPER: Mapping[str, List[Dict[str, Any]]] = {"country": COUNTRIES_MAPPER, "genre": GENRES_MAPPER} class BaseQueryWrapper: _session = None DB_CONNECTION_PARAMS: Dict[str, Any] @classmethod def session(cls): if not cls._session: cls._session = get_postgres_async_session(**cls.DB_CONNECTION_PARAMS) return cls._session @staticmethod async def handle_exceptions(err): db_error_msg = err.args[0] errors_text = list(DB_ERROR_MAPPER.keys()) current_error = [i for i in errors_text if i in db_error_msg] return { "error": DB_ERROR_MAPPER.get(current_error[0]) if current_error else DEFAULT_ERROR_MSG, } @classmethod async def _execute(cls, query, commit=True): query = query.execution_options(synchronize_session="fetch") async with cls.session()() as session: result = await session.execute(query) if commit: await session.commit() return result @classmethod async def insert(cls, query): try: result = await cls._execute(query) except (IntegrityError, DBAPIError) as err: return await cls.handle_exceptions(err) else: return {"id": result.inserted_primary_key[0]} @classmethod async def update(cls, query): try: updated_result = await cls._execute(query) result = updated_result.context.compiled_parameters[0] except IntegrityError as err: return await cls.handle_exceptions(err) else: return {"id": result.get("id")} @classmethod async def delete(cls, query): await cls._execute(query) @classmethod async def select(cls, query, many=False): try: result = await cls._execute(query, commit=False) result = getattr(result.scalars(), "all" if many else "one")() except NoResultFound: result = {} return result @classmethod async def fetchall(cls, query, commit: bool = False) -> list: results = await cls._execute(query, commit=commit) results = results.fetchall() results = [c._asdict() for c in results] return results class QueryWrapper(BaseQueryWrapper): DB_CONNECTION_PARAMS: Dict[str, Any] = { "user": POSTGRES_USER, "password": POSTGRES_PASSWORD, "host": POSTGRES_HOST, "database": POSTGRES_DB, "port": POSTGRES_PORT, } class DataQueryWrapper(BaseQueryWrapper): DB_CONNECTION_PARAMS: Dict[str, Any] = { "user": POSTGRES_DATA_USER, "password": POSTGRES_DATA_PASSWORD, "host": POSTGRES_DATA_HOST, "database": POSTGRES_DATA_DB, "port": POSTGRES_DATA_PORT, } class Utils: @staticmethod async def prepare_profile_response(request, id: Optional[int] = None): user_id = request["user_id"] atlas_api = request.app["atlas_api"] response: Union[Dict, List] = {} if id else [] labels = await atlas_api.get_label_names(user_id, id) labels_list = [label_id for label_id in labels] users_with_same_labels = await atlas_api.get_users_with_same_labels(labels_list) for label_id, label_name in labels.items(): user_data_results = users_with_same_labels.get(label_id).get("results") users = [] for user in user_data_results: users.append({"name": user["family_name"] + " " + user["given_name"], "email": user["email"]}) response.__getattribute__("update" if id else "append")( { "id": label_id, "name": label_name, "users": users, } ) return response @staticmethod async def get_account_favorites(request: web.Request, entity_type) -> List[Dict[str, Any]]: def form_response(favorites: Dict[str, List[Dict[str, Any]]]): result = sorted( [ {**item} for entity in favorites["data"] for item in GENRE_COUNTRY_MAPPER[entity["entity_type"]] if entity["entity_id"] == item["code"] ], key=lambda d: d["name"], ) return result user_id = request["user_id"] user_service_api = request.app["user_service_api"] data = dict(entity=dict(type=ENTITY_TYPE_MAPPER[entity_type])) favorites = await user_service_api.list_favorites(user_id, data) return form_response(favorites) @classmethod def _get_params(cls, request: web.Request, entity_type: str, entity_id: str) -> Tuple: user_id = request["user_id"] user_service_api = request.app["user_service_api"] data = dict(entity_type=ENTITY_TYPE_MAPPER[entity_type], entity_id=entity_id) return user_id, user_service_api, data @classmethod async def delete_entity_from_account_favorites(cls, request: web.Request, entity_type: str, entity_id: str) -> None: user_id, user_service_api, data = cls._get_params(request, entity_type, entity_id) await user_service_api.delete_entity_from_account_favorites(user_id, data) return None @classmethod async def add_entity_to_account_favorites(cls, request: web.Request, entity_type: str, entity_id: str) -> None: user_id, user_service_api, data = cls._get_params(request, entity_type, entity_id) await user_service_api.add_entity_to_account_favorites(user_id, data) return None