This commit is contained in:
parent
76aeddbde2
commit
bf33cdc95c
|
@ -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)
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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")
|
||||
|
|
Loading…
Reference in New Issue
Block a user