matplobbot-shared 0.1.25__tar.gz → 0.1.27__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of matplobbot-shared might be problematic. Click here for more details.

@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: matplobbot-shared
3
- Version: 0.1.25
3
+ Version: 0.1.27
4
4
  Summary: Shared library for the Matplobbot ecosystem (database, services, i18n).
5
5
  Author: Ackrome
6
6
  Author-email: ivansergeyevich@gmail.com
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: matplobbot-shared
3
- Version: 0.1.25
3
+ Version: 0.1.27
4
4
  Summary: Shared library for the Matplobbot ecosystem (database, services, i18n).
5
5
  Author: Ackrome
6
6
  Author-email: ivansergeyevich@gmail.com
@@ -2,7 +2,7 @@ from setuptools import setup, find_packages
2
2
 
3
3
  setup(
4
4
  name="matplobbot-shared",
5
- version="0.1.25", # Let's use the version from your requirements.txt
5
+ version="0.1.27", # Let's use the version from your requirements.txt
6
6
  packages=find_packages(include=['shared_lib', 'shared_lib.*']),
7
7
  description="Shared library for the Matplobbot ecosystem (database, services, i18n).",
8
8
  author="Ackrome",
@@ -262,24 +262,149 @@ async def get_activity_over_time_data_from_db(db_conn, period='day'):
262
262
  rows = await db_conn.fetch(query)
263
263
  return [{"period": row['period_start'], "count": row['actions_count']} for row in rows]
264
264
 
265
- async def get_user_profile_data_from_db(db_conn, user_id: int, page: int = 1, page_size: int = 50):
266
- offset = (page - 1) * page_size
267
- query = """
265
+
266
+ async def get_user_profile_data_from_db(
267
+ db_conn,
268
+ user_id: int,
269
+ page: int = 1,
270
+ page_size: int = 50,
271
+ sort_by: str = 'timestamp',
272
+ sort_order: str = 'desc'
273
+ ):
274
+ """Извлекает детали профиля пользователя и пагинированный список его действий."""
275
+ # --- Безопасная сортировка ---
276
+ allowed_sort_columns = ['id', 'action_type', 'action_details', 'timestamp'] # These are ua columns
277
+ if sort_by not in allowed_sort_columns:
278
+ sort_by = 'timestamp' # Значение по умолчанию
279
+ sort_order = 'ASC' if sort_order.lower() == 'asc' else 'DESC' # Безопасное определение порядка
280
+
281
+ # --- Единый запрос для получения всех данных ---
282
+ # Используем CTE и оконные функции для эффективности.
283
+ # 1. Выбираем все действия пользователя.
284
+ # 2. С помощью оконной функции COUNT(*) OVER () получаем общее количество действий без дополнительного запроса.
285
+ # 3. Присоединяем информацию о пользователе.
286
+ # 4. Применяем пагинацию и сортировку.
287
+ query = f"""
268
288
  WITH UserActions AS (
269
- SELECT id, action_type, action_details, TO_CHAR(timestamp, 'YYYY-MM-DD HH24:MI:SS') AS timestamp
270
- FROM user_actions WHERE user_id = $1
289
+ SELECT
290
+ id,
291
+ action_type,
292
+ action_details,
293
+ TO_CHAR(timestamp, 'YYYY-MM-DD HH24:MI:SS') AS timestamp
294
+ FROM user_actions
295
+ WHERE user_id = $1
271
296
  )
272
- SELECT u.user_id, u.full_name, COALESCE(u.username, 'N/A') AS username, u.avatar_pic_url,
273
- (SELECT COUNT(*) FROM UserActions) as total_actions,
274
- ua.id as action_id, ua.action_type, ua.action_details, ua.timestamp
275
- FROM users u LEFT JOIN UserActions ua ON 1=1
276
- WHERE u.user_id = $2 ORDER BY ua.timestamp DESC LIMIT $3 OFFSET $4;
297
+ SELECT
298
+ u.user_id,
299
+ u.full_name,
300
+ COALESCE(u.username, 'Нет username') AS username,
301
+ u.avatar_pic_url,
302
+ (SELECT COUNT(*) FROM UserActions) as total_actions,
303
+ ua.id as action_id,
304
+ ua.action_type,
305
+ ua.action_details,
306
+ ua.timestamp
307
+ FROM users u
308
+ LEFT JOIN UserActions ua ON 1=1
309
+ WHERE u.user_id = $2
310
+ ORDER BY ua.{sort_by} {sort_order}
311
+ LIMIT $3 OFFSET $4;
277
312
  """
313
+ offset = (page - 1) * page_size
278
314
  rows = await db_conn.fetch(query, user_id, user_id, page_size, offset)
279
- if not rows: return None
280
-
315
+ if not rows:
316
+ # If user exists but has no actions, we might get no rows. Check user existence separately.
317
+ user_exists = await db_conn.fetchrow("SELECT 1 FROM users WHERE user_id = $1", user_id)
318
+ if not user_exists:
319
+ return None # User not found
320
+ # User exists but has no actions, return empty actions list
321
+ user_details_row = await db_conn.fetchrow("SELECT user_id, full_name, COALESCE(username, 'Нет username') AS username, avatar_pic_url FROM users WHERE user_id = $1", user_id)
322
+ return {
323
+ "user_details": dict(user_details_row),
324
+ "actions": [],
325
+ "total_actions": 0
326
+ }
327
+
281
328
  first_row = dict(rows[0])
282
- user_details = {k: first_row[k] for k in ["user_id", "full_name", "username", "avatar_pic_url"]}
283
- actions = [dict(r) for r in rows if r["action_id"] is not None]
284
-
285
- return {"user_details": user_details, "actions": actions, "total_actions": first_row["total_actions"]}
329
+ user_details = {
330
+ "user_id": first_row["user_id"],
331
+ "full_name": first_row["full_name"],
332
+ "username": first_row["username"],
333
+ "avatar_pic_url": first_row["avatar_pic_url"]
334
+ }
335
+ total_actions = first_row["total_actions"]
336
+
337
+ # Собираем действия, если они есть (может быть пользователь без действий)
338
+ actions = []
339
+ for row in rows:
340
+ row_dict = dict(row)
341
+ if row_dict["action_id"] is not None: # action_id не NULL
342
+ actions.append({
343
+ "id": row_dict["action_id"],
344
+ "action_type": row_dict["action_type"],
345
+ "action_details": row_dict["action_details"],
346
+ "timestamp": row_dict["timestamp"]
347
+ })
348
+
349
+ return {
350
+ "user_details": user_details,
351
+ "actions": actions,
352
+ "total_actions": total_actions
353
+ }
354
+
355
+ async def get_users_for_action(db_conn, action_type: str, action_details: str, page: int = 1, page_size: int = 15, sort_by: str = 'full_name', sort_order: str = 'asc'):
356
+ """Извлекает пагинированный список уникальных пользователей, совершивших определенное действие."""
357
+ # Note: action_type for messages is 'text_message' in the DB
358
+ db_action_type = 'text_message' if action_type == 'message' else action_type
359
+ offset = (page - 1) * page_size
360
+
361
+ # --- Безопасная сортировка ---
362
+ allowed_sort_columns = ['user_id', 'full_name', 'username'] # These are u columns
363
+ if sort_by not in allowed_sort_columns:
364
+ sort_by = 'full_name' # Значение по умолчанию
365
+ sort_order = 'DESC' if sort_order.lower() == 'desc' else 'ASC' # Безопасное определение порядка
366
+ order_by_clause = f"ORDER BY u.{sort_by} {sort_order}"
367
+
368
+ # Query for total count of distinct users
369
+ count_query = """
370
+ SELECT COUNT(DISTINCT u.user_id)
371
+ FROM users u
372
+ JOIN user_actions ua ON u.user_id = ua.user_id
373
+ WHERE ua.action_type = $1 AND ua.action_details = $2;
374
+ """
375
+ total_users = await db_conn.fetchval(count_query, db_action_type, action_details)
376
+
377
+ # Query for the paginated list of users
378
+ users_query = f"""
379
+ SELECT DISTINCT
380
+ u.user_id,
381
+ u.full_name,
382
+ COALESCE(u.username, 'Нет username') AS username
383
+ FROM users u
384
+ JOIN user_actions ua ON u.user_id = ua.user_id
385
+ WHERE ua.action_type = $1 AND ua.action_details = $2
386
+ {order_by_clause}
387
+ LIMIT $3 OFFSET $4;
388
+ """
389
+ rows = await db_conn.fetch(users_query, db_action_type, action_details, page_size, offset)
390
+ users = [dict(row) for row in rows]
391
+
392
+ return {
393
+ "users": users,
394
+ "total_users": total_users
395
+ }
396
+
397
+ async def get_all_user_actions(db_conn, user_id: int):
398
+ """Извлекает ВСЕ действия для указанного пользователя без пагинации."""
399
+ query = """
400
+ SELECT
401
+ id,
402
+ action_type,
403
+ action_details,
404
+ TO_CHAR(timestamp, 'YYYY-MM-DD HH24:MI:SS') AS timestamp
405
+ FROM user_actions
406
+ WHERE user_id = $1
407
+ ORDER BY timestamp DESC;
408
+ """
409
+ rows = await db_conn.fetch(query, user_id)
410
+ return [dict(row) for row in rows]