From 18fc08f6c80d4c75993e98e1af6a47d8f90108d8 Mon Sep 17 00:00:00 2001 From: Untone Date: Mon, 29 Jan 2024 00:28:04 +0300 Subject: [PATCH] virtual-score-column-fix --- resolvers/reader.py | 102 +++++++++----------------------------------- services/viewed.py | 33 +++----------- 2 files changed, 28 insertions(+), 107 deletions(-) diff --git a/resolvers/reader.py b/resolvers/reader.py index 8cfdec88..a4371f0f 100644 --- a/resolvers/reader.py +++ b/resolvers/reader.py @@ -1,7 +1,7 @@ import logging -from sqlalchemy import bindparam, distinct, or_ -from sqlalchemy.orm import aliased, joinedload, selectinload +from sqlalchemy import bindparam, distinct, literal_column, or_ +from sqlalchemy.orm import aliased, column_property, joinedload, selectinload from sqlalchemy.sql.expression import and_, asc, case, desc, func, nulls_last, select from starlette.exceptions import HTTPException @@ -82,9 +82,7 @@ async def get_shout(_, _info, slug=None, shout_id=None): 'rating': int(likes_stat or 0) - int(dislikes_stat or 0), } - for author_caption in ( - session.query(ShoutAuthor).join(Shout).where(Shout.slug == slug) - ): + for author_caption in session.query(ShoutAuthor).join(Shout).where(Shout.slug == slug): for author in shout.authors: if author.id == author_caption.author: author.caption = author_caption.caption @@ -105,9 +103,7 @@ async def get_shout(_, _info, slug=None, shout_id=None): shout.main_topic = main_topic[0] return shout except Exception: - raise HTTPException( - status_code=404, detail=f'shout {slug or shout_id} not found' - ) + raise HTTPException(status_code=404, detail=f'shout {slug or shout_id} not found') @query.field('load_shouts_by') @@ -153,9 +149,7 @@ async def load_shouts_by(_, _info, options): # order order_by = options.get('order_by', Shout.published_at) - query_order_by = ( - desc(order_by) if options.get('order_by_desc', True) else asc(order_by) - ) + query_order_by = desc(order_by) if options.get('order_by_desc', True) else asc(order_by) q = q.order_by(nulls_last(query_order_by)) # limit offset @@ -248,20 +242,15 @@ async def load_shouts_feed(_, info, options): with local_session() as session: reader = session.query(Author).filter(Author.user == user_id).first() if reader: - reader_followed_authors = select(AuthorFollower.author).where( - AuthorFollower.follower == reader.id - ) - reader_followed_topics = select(TopicFollower.topic).where( - TopicFollower.follower == reader.id - ) + reader_followed_authors = select(AuthorFollower.author).where(AuthorFollower.follower == reader.id) + reader_followed_topics = select(TopicFollower.topic).where(TopicFollower.follower == reader.id) subquery = ( select(Shout.id) .where(Shout.id == ShoutAuthor.shout) .where(Shout.id == ShoutTopic.shout) .where( - (ShoutAuthor.author.in_(reader_followed_authors)) - | (ShoutTopic.topic.in_(reader_followed_topics)) + (ShoutAuthor.author.in_(reader_followed_authors)) | (ShoutTopic.topic.in_(reader_followed_topics)) ) ) @@ -286,24 +275,15 @@ async def load_shouts_feed(_, info, options): order_by = options.get('order_by', Shout.published_at) - query_order_by = ( - desc(order_by) if options.get('order_by_desc', True) else asc(order_by) - ) + query_order_by = desc(order_by) if options.get('order_by_desc', True) else asc(order_by) offset = options.get('offset', 0) limit = options.get('limit', 10) - q = ( - q.group_by(Shout.id) - .order_by(nulls_last(query_order_by)) - .limit(limit) - .offset(offset) - ) + q = q.group_by(Shout.id).order_by(nulls_last(query_order_by)).limit(limit).offset(offset) # print(q.compile(compile_kwargs={"literal_binds": True})) - for [shout, reacted_stat, commented_stat, _last_comment] in session.execute( - q - ).unique(): + for [shout, reacted_stat, commented_stat, _last_comment] in session.execute(q).unique(): main_topic = ( session.query(Topic.slug) .join( @@ -335,12 +315,12 @@ async def load_shouts_search(_, _info, text, limit=50, offset=0): results = await SearchService.search(text, limit, offset) results_dict = {r['slug']: r for r in results} found_keys = list(results_dict.keys()) - - shouts_data = [] with local_session() as session: + # Define a virtual column 'score' using column_property + Shout.score = column_property(literal_column(f"({results_dict.get(Shout.slug, {}).get('score', 0)})")) + results = ( session.query(Shout) - .join(ShoutAuthor, Shout.id == ShoutAuthor.shout) .join(ShoutTopic, Shout.id == ShoutTopic.shout) .options( joinedload(Shout.authors), @@ -354,50 +334,14 @@ async def load_shouts_search(_, _info, text, limit=50, offset=0): ) .limit(limit) .offset(offset) + .order_by(desc(Shout.score)) # Order by the virtual 'score' column .all() ) + logger.debug(f'search found {len(results)} results') - for shout in results: - shout_data = shout.dict() - shout_slug = shout_data.get('slug', '') - topic = ( - session.query(Topic) - .join( - ShoutTopic, - and_( - ShoutTopic.topic == Topic.id, - ShoutTopic.shout == shout.id, - ShoutTopic.main == True, - ), - ) - .first() - ) - if topic: - shout_data['main_topic'] = topic - authors = ( - session.query(Author, ShoutAuthor, ShoutTopic) - .join( - ShoutTopic, - and_( - ShoutAuthor.author == Author.id, - ShoutTopic.shout == shout.id, - ), - ) - .all() - ) - if authors: - shout_data['authors'] = authors - score = results_dict.get(shout_slug, {}).get('score', 0) - shout_data['score'] = score # Add the score to the dictionary - shouts_data.append(shout_data) - shouts_data = sorted( - shouts_data, - key=lambda x: float(x.get('score', '0')), - reverse=True, - ) - return shouts_data - else: - return [] + + return results + return [] @login_required @@ -414,9 +358,7 @@ async def load_shouts_unrated(_, info, limit: int = 50, offset: int = 0): and_( Reaction.shout == Shout.id, Reaction.replyTo.is_(None), - Reaction.kind.in_( - [ReactionKind.LIKE.value, ReactionKind.DISLIKE.value] - ), + Reaction.kind.in_([ReactionKind.LIKE.value, ReactionKind.DISLIKE.value]), ), ) .outerjoin(Author, Author.user == bindparam('user_id')) @@ -485,9 +427,7 @@ async def load_shouts_random_top(_, _info, options): aliased_reaction = aliased(Reaction) - subquery = ( - select(Shout.id).outerjoin(aliased_reaction).where(Shout.deleted_at.is_(None)) - ) + subquery = select(Shout.id).outerjoin(aliased_reaction).where(Shout.deleted_at.is_(None)) subquery = apply_filters(subquery, options.get('filters', {})) subquery = subquery.group_by(Shout.id).order_by( diff --git a/services/viewed.py b/services/viewed.py index e70d6ccc..75436b2c 100644 --- a/services/viewed.py +++ b/services/viewed.py @@ -60,9 +60,7 @@ class ViewedStorage: if os.path.exists(VIEWS_FILEPATH): file_timestamp = os.path.getctime(VIEWS_FILEPATH) - self.start_date = datetime.fromtimestamp(file_timestamp).strftime( - '%Y-%m-%d' - ) + self.start_date = datetime.fromtimestamp(file_timestamp).strftime('%Y-%m-%d') # Запуск фоновой задачи asyncio.create_task(self.worker()) @@ -78,9 +76,7 @@ class ViewedStorage: 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)} публикаций с просмотрами успешно загружены.' - ) + logger.info(f' * {len(precounted_views)} публикаций с просмотрами успешно загружены.') except Exception as e: logger.error(f'Ошибка загрузки предварительно подсчитанных просмотров: {e}') @@ -98,9 +94,7 @@ class ViewedStorage: property=f'properties/{GOOGLE_PROPERTY_ID}', dimensions=[Dimension(name='pagePath')], metrics=[Metric(name='screenPageViews')], - date_ranges=[ - DateRange(start_date=self.start_date, end_date='today') - ], + date_ranges=[DateRange(start_date=self.start_date, end_date='today')], ) response = self.analytics_client.run_report(request) if response and isinstance(response.rows, list): @@ -117,9 +111,7 @@ class ViewedStorage: views_count = int(row.metric_values[0].value) # Обновление данных в хранилище - self.views_by_shout[slug] = self.views_by_shout.get( - slug, 0 - ) + self.views_by_shout[slug] = self.views_by_shout.get(slug, 0) self.views_by_shout[slug] += views_count self.update_topics(slug) @@ -178,20 +170,12 @@ class ViewedStorage: # Обновление тем и авторов с использованием вспомогательной функции for [_shout_topic, topic] in ( - session.query(ShoutTopic, Topic) - .join(Topic) - .join(Shout) - .where(Shout.slug == shout_slug) - .all() + session.query(ShoutTopic, Topic).join(Topic).join(Shout).where(Shout.slug == shout_slug).all() ): update_groups(self.shouts_by_topic, topic.slug, shout_slug) for [_shout_topic, author] in ( - session.query(ShoutAuthor, Author) - .join(Author) - .join(Shout) - .where(Shout.slug == shout_slug) - .all() + session.query(ShoutAuthor, Author).join(Author).join(Shout).where(Shout.slug == shout_slug).all() ): update_groups(self.shouts_by_author, author.slug, shout_slug) @@ -216,10 +200,7 @@ class ViewedStorage: 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(' ⎩ Следующее обновление: %s' % (t.split('T')[0] + ' ' + t.split('T')[1].split('.')[0])) await asyncio.sleep(self.period) else: await asyncio.sleep(10)