From bf33cdc95c5324272e44640f71ca76bc327d9941 Mon Sep 17 00:00:00 2001 From: Untone Date: Tue, 15 Oct 2024 11:12:09 +0300 Subject: [PATCH] fixed-coales --- resolvers/rating.py | 6 +++-- resolvers/reaction.py | 6 ++--- resolvers/stat.py | 23 +++++++----------- services/db.py | 2 +- services/search.py | 2 +- services/viewed.py | 56 +++++++++++++++++++++++-------------------- 6 files changed, 47 insertions(+), 48 deletions(-) diff --git a/resolvers/rating.py b/resolvers/rating.py index 2607edee..6a7e82f4 100644 --- a/resolvers/rating.py +++ b/resolvers/rating.py @@ -192,7 +192,8 @@ def add_author_rating_columns(q, group_list): (shout_reaction.kind == ReactionKind.DISLIKE.value, -1), else_=0, ) - ) + ), + 0, ).label("shouts_rating"), ) .select_from(shout_reaction) @@ -226,7 +227,8 @@ def add_author_rating_columns(q, group_list): (reaction_2.kind == ReactionKind.DISLIKE.value, -1), else_=0, ) - ) + ), + 0, ).label("comments_rating"), ) .select_from(reaction_2) diff --git a/resolvers/reaction.py b/resolvers/reaction.py index 4072a528..672d169b 100644 --- a/resolvers/reaction.py +++ b/resolvers/reaction.py @@ -52,9 +52,9 @@ def add_reaction_stat_columns(q): ), ).add_columns( # Count unique comments - func.count(aliased_reaction.id) - .filter(aliased_reaction.kind == ReactionKind.COMMENT.value) - .label("comments_stat"), + func.coalesce( + func.count(aliased_reaction.id).filter(aliased_reaction.kind == ReactionKind.COMMENT.value), 0 + ).label("comments_stat"), # Calculate rating as the difference between likes and dislikes func.sum( case( diff --git a/resolvers/stat.py b/resolvers/stat.py index 424c4164..5372732b 100644 --- a/resolvers/stat.py +++ b/resolvers/stat.py @@ -173,7 +173,7 @@ def get_topic_comments_stat(topic_id: int) -> int: sub_comments = ( select( Shout.id.label("shout_id"), - func.coalesce(func.count(Reaction.id)).label("comments_count"), + func.coalesce(func.count(Reaction.id), 0).label("comments_count"), ) .join(ShoutTopic, ShoutTopic.shout == Shout.id) .join(Topic, ShoutTopic.topic == Topic.id) @@ -257,17 +257,10 @@ def get_author_followers_stat(author_id: int) -> int: return result[0] if result else 0 -def get_author_comments_stat(author_id: int) -> int: - """ - Получает количество комментариев, оставленных указанным автором. - - :param author_id: Идентификатор автора. - :return: Количество комментариев, оставленных автором. - """ - # Подзапрос для получения количества комментариев, оставленных автором - sub_comments = ( - select(Author.id, func.coalesce(func.count(Reaction.id)).label("comments_count")) - .select_from(Author) # явно указываем левый элемент join'а +def get_author_comments_stat(author_id): + q = ( + select(func.coalesce(func.count(Reaction.id), 0).label("comments_count")) + .select_from(Author) .outerjoin( Reaction, and_( @@ -276,13 +269,13 @@ def get_author_comments_stat(author_id: int) -> int: Reaction.deleted_at.is_(None), ), ) + .where(Author.id == author_id) .group_by(Author.id) - .subquery() ) - q = select(sub_comments.c.comments_count).filter(sub_comments.c.id == author_id) + with local_session() as session: result = session.execute(q).first() - return result[0] if result else 0 + return result.comments_count if result else 0 def get_with_stat(q): diff --git a/services/db.py b/services/db.py index 5b60f431..5a689da4 100644 --- a/services/db.py +++ b/services/db.py @@ -39,7 +39,7 @@ def create_table_if_not_exists(engine, table): table.__table__.create(engine) logger.info(f"Table '{table.__tablename__}' created.") else: - logger.info(f"Table '{table.__tablename__}' already exists.") + logger.info(f"Table '{table.__tablename__}' ok.") # noinspection PyUnusedLocal diff --git a/services/search.py b/services/search.py index d19731fd..e1e9e0de 100644 --- a/services/search.py +++ b/services/search.py @@ -108,7 +108,7 @@ class SearchService: logger.error(f"Ошибка подключения к OpenSearch: {exc}") self.client = None else: - logger.warning("Задайте переменные среды для подключения к серверу поиска") + logger.warning("env var ELASTIC_HOST is not set") async def info(self): if isinstance(self.client, OpenSearch): diff --git a/services/viewed.py b/services/viewed.py index cc2730bc..34557853 100644 --- a/services/viewed.py +++ b/services/viewed.py @@ -55,40 +55,46 @@ class ViewedStorage: # Запуск фоновой задачи _task = asyncio.create_task(self.worker()) else: - logger.warning(" * Пожалуйста, добавьте ключевой файл Google Analytics") + logger.warning(" * please, add Google Analytics credentials file") self.disabled = True @staticmethod def load_precounted_views(): """Загрузка предварительно подсчитанных просмотров из файла JSON""" self = ViewedStorage + viewfile_path = VIEWS_FILEPATH + if not os.path.exists(viewfile_path): + viewfile_path = os.path.join(os.path.curdir, "views.json") + if not os.path.exists(viewfile_path): + logger.warning(" * views.json not found") + return + + logger.info(f" * loading views from {viewfile_path}") try: - if os.path.exists(VIEWS_FILEPATH): - start_date_int = os.path.getmtime(VIEWS_FILEPATH) - start_date_str = datetime.fromtimestamp(start_date_int).strftime("%Y-%m-%d") - self.start_date = start_date_str - now_date = datetime.now().strftime("%Y-%m-%d") + start_date_int = os.path.getmtime(viewfile_path) + start_date_str = datetime.fromtimestamp(start_date_int).strftime("%Y-%m-%d") + self.start_date = start_date_str + now_date = datetime.now().strftime("%Y-%m-%d") - if now_date == self.start_date: - logger.info(" * Данные актуализованы!") - else: - logger.warn(f" * Файл просмотров {VIEWS_FILEPATH} устарел: {self.start_date}") - - with open(VIEWS_FILEPATH, "r") as file: - precounted_views = json.load(file) - self.views_by_shout.update(precounted_views) - logger.info(f" * {len(precounted_views)} публикаций с просмотрами успешно загружены.") + if now_date == self.start_date: + logger.info(" * views data is up to date!") else: - logger.warning(" * Файл просмотров не найден.") + logger.warn(f" * {viewfile_path} is too old: {self.start_date}") + + with open(viewfile_path, "r") as file: + precounted_views = json.load(file) + self.views_by_shout.update(precounted_views) + logger.info(f" * {len(precounted_views)} shouts with views was loaded.") + except Exception as e: - logger.error(f"Ошибка загрузки предварительно подсчитанных просмотров: {e}") + logger.error(f"precounted views loading error: {e}") # noinspection PyTypeChecker @staticmethod async def update_pages(): """Запрос всех страниц от Google Analytics, отсортированных по количеству просмотров""" self = ViewedStorage - logger.info(" ⎧ Обновление данных просмотров от Google Analytics ---") + logger.info(" ⎧ views update from Google Analytics ---") if not self.disabled: try: start = time.time() @@ -122,10 +128,10 @@ class ViewedStorage: # Запись путей страниц для логирования slugs.add(slug) - logger.info(f" ⎪ Собрано страниц: {len(slugs)} ") + logger.info(f" ⎪ collected pages: {len(slugs)} ") end = time.time() - logger.info(" ⎪ Обновление страниц заняло %fs " % (end - start)) + logger.info(" ⎪ views update time: %fs " % (end - start)) except Exception as error: logger.error(error) self.disabled = True @@ -189,17 +195,15 @@ class ViewedStorage: except Exception as exc: failed += 1 logger.debug(exc) - logger.info(" - Обновление не удалось #%d, ожидание 10 секунд" % failed) + logger.info(" - update failed #%d, wait 10 secs" % failed) if failed > 3: - logger.info(" - Больше не пытаемся обновить") + logger.info(" - views update failed, not trying anymore") break if failed == 0: when = datetime.now(timezone.utc) + timedelta(seconds=self.period) t = format(when.astimezone().isoformat()) - logger.info( - " ⎩ Следующее обновление: %s" % (t.split("T")[0] + " " + t.split("T")[1].split(".")[0]) - ) + logger.info(" ⎩ next update: %s" % (t.split("T")[0] + " " + t.split("T")[1].split(".")[0])) await asyncio.sleep(self.period) else: await asyncio.sleep(10) - logger.info(" - Попытка снова обновить данные") + logger.info(" - try to update views again")