core-framework 0.3.0__py3-none-any.whl
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.
- core_framework/__init__.py +0 -0
- core_framework/alembic/comment/alembic/README +1 -0
- core_framework/alembic/comment/alembic/env.py +72 -0
- core_framework/alembic/comment/alembic/script.py.mako +28 -0
- core_framework/alembic/comment/alembic/versions/30334fd1347b_init.py +59 -0
- core_framework/alembic/comment/alembic/versions/a2b3c4d5e6f7_improve_comment_indexes.py +54 -0
- core_framework/alembic/comment/alembic/versions/bcc8e00cfc8b_add_extra_tables.py +64 -0
- core_framework/alembic/comment/alembic/versions/d1e2f3a4b5c6_add_comment_stats_dirty_table.py +29 -0
- core_framework/alembic/comment/alembic/versions/e3f4a5b6c7d8_cascade_delete_comment_descendants.py +49 -0
- core_framework/alembic/comment/alembic/versions/f7e6d5c4b3a2_comments_path_to_ltree.py +60 -0
- core_framework/alembic/comment/alembic.ini +52 -0
- core_framework/alembic/extension/alembic/README +1 -0
- core_framework/alembic/extension/alembic/env.py +98 -0
- core_framework/alembic/extension/alembic/script.py.mako +28 -0
- core_framework/alembic/extension/alembic/versions/0389226049cb_add_pg_trgm_extension.py +25 -0
- core_framework/alembic/extension/alembic/versions/5dc58b016cf5_add_citext_extension.py +25 -0
- core_framework/alembic/extension/alembic/versions/b0ba0d8a284e_add_pg_stat_statements_extension.py +25 -0
- core_framework/alembic/extension/alembic/versions/c9d0e1f2a3b4_add_ltree_extension.py +25 -0
- core_framework/alembic/extension/alembic.ini +147 -0
- core_framework/alembic/moderation/alembic/README +1 -0
- core_framework/alembic/moderation/alembic/env.py +98 -0
- core_framework/alembic/moderation/alembic/script.py.mako +28 -0
- core_framework/alembic/moderation/alembic/versions/085ba9021850_add_category_to_user_restrictions.py +93 -0
- core_framework/alembic/moderation/alembic/versions/5f9e4fc14a41_create_moderation_appeals_table.py +69 -0
- core_framework/alembic/moderation/alembic/versions/63e37381e73b_add_user_reports_table.py +33 -0
- core_framework/alembic/moderation/alembic/versions/6a2ae31b7ac6_add_moderation_actions_table.py +34 -0
- core_framework/alembic/moderation/alembic/versions/716aa1735c03_improve_indexes.py +36 -0
- core_framework/alembic/moderation/alembic/versions/7d243ddbfde1_add_post_reports_table.py +35 -0
- core_framework/alembic/moderation/alembic/versions/8fba1f72dd46_add_indexes.py +64 -0
- core_framework/alembic/moderation/alembic/versions/95cc35a51984_update_restriction_history.py +91 -0
- core_framework/alembic/moderation/alembic/versions/9ad79d0af730_add_unique_constraint_user_reports_.py +28 -0
- core_framework/alembic/moderation/alembic/versions/a5e569f5df1a_create_user_restrictions_table.py +38 -0
- core_framework/alembic/moderation/alembic/versions/b2c3d4e5f6a7_add_indexes.py +42 -0
- core_framework/alembic/moderation/alembic/versions/c3d4e5f6a7b8_improve_report_indexes.py +48 -0
- core_framework/alembic/moderation/alembic/versions/d4af74643ff5_add_internal_notes_table.py +38 -0
- core_framework/alembic/moderation/alembic/versions/db20f2fb7390_add_comment_reports_table.py +35 -0
- core_framework/alembic/moderation/alembic/versions/e66226952ea6_add_report_category_to_user_reports_.py +54 -0
- core_framework/alembic/moderation/alembic/versions/f5e8cb275c30_enforce_1_pending_appeal.py +29 -0
- core_framework/alembic/moderation/alembic/versions/fe1faad2832d_create_restriction_history_table.py +69 -0
- core_framework/alembic/moderation/alembic.ini +147 -0
- core_framework/alembic/post/alembic/README +1 -0
- core_framework/alembic/post/alembic/env.py +97 -0
- core_framework/alembic/post/alembic/script.py.mako +28 -0
- core_framework/alembic/post/alembic/versions/51542673f5c8_add_tables_for_muted_banned_users.py +41 -0
- core_framework/alembic/post/alembic/versions/5beeeae40a4a_add_post_views_table.py +45 -0
- core_framework/alembic/post/alembic/versions/620971509a8b_init.py +55 -0
- core_framework/alembic/post/alembic/versions/a1b2c3d4e5f6_add_indexes.py +44 -0
- core_framework/alembic/post/alembic/versions/c1d2e3f4a5b6_add_post_hashtags_table.py +36 -0
- core_framework/alembic/post/alembic/versions/e56723f2afff_add_post_stats_table.py +39 -0
- core_framework/alembic/post/alembic/versions/fbc723ac58cc_add_post_likes_table.py +32 -0
- core_framework/alembic/post/alembic.ini +149 -0
- core_framework/alembic/user/alembic/README +1 -0
- core_framework/alembic/user/alembic/env.py +98 -0
- core_framework/alembic/user/alembic/script.py.mako +28 -0
- core_framework/alembic/user/alembic/versions/1a8bb99726ed_remove_avatar_id_from_users.py +81 -0
- core_framework/alembic/user/alembic/versions/2ccacf455941_improve_indexes.py +34 -0
- core_framework/alembic/user/alembic/versions/47f47ce2110e_create_user_deletions_table.py +31 -0
- core_framework/alembic/user/alembic/versions/5976db3f0175_drop_user_states.py +26 -0
- core_framework/alembic/user/alembic/versions/62417002cf32_add_indexes.py +46 -0
- core_framework/alembic/user/alembic/versions/6f7ccf3c226b_refactor_user_login_events.py +66 -0
- core_framework/alembic/user/alembic/versions/73432817015b_add_user_preferences_table.py +33 -0
- core_framework/alembic/user/alembic/versions/765bc01a7a59_create_user_blocks_table.py +33 -0
- core_framework/alembic/user/alembic/versions/7a56631f9927_create_user_login_events_table.py +49 -0
- core_framework/alembic/user/alembic/versions/831611e589bc_create_user_state.py +31 -0
- core_framework/alembic/user/alembic/versions/83c98ab2a779_add_user_profiles_table.py +88 -0
- core_framework/alembic/user/alembic/versions/8a94362cad6d_create_user_role.py +31 -0
- core_framework/alembic/user/alembic/versions/94b973923895_add_user_change_history_table.py +97 -0
- core_framework/alembic/user/alembic/versions/cbc0f4efe84f_add_avatar_id_column_to_users_table.py +31 -0
- core_framework/alembic/user/alembic/versions/d8b98ac6b073_add_index_for_get_admin_user_ids_query.py +29 -0
- core_framework/alembic/user/alembic/versions/ddb70cc09d16_create_user_states_table.py +34 -0
- core_framework/alembic/user/alembic/versions/f9ba10815ecd_add_users_table.py +33 -0
- core_framework/alembic/user/alembic.ini +147 -0
- core_framework/api/__init__.py +0 -0
- core_framework/api/admin/__init__.py +0 -0
- core_framework/api/admin/comments/router.py +69 -0
- core_framework/api/admin/comments/schemas.py +53 -0
- core_framework/api/admin/moderation/__init__.py +0 -0
- core_framework/api/admin/moderation/router.py +205 -0
- core_framework/api/admin/moderation/schemas.py +110 -0
- core_framework/api/admin/posts/router.py +62 -0
- core_framework/api/admin/posts/schemas.py +29 -0
- core_framework/api/admin/router.py +17 -0
- core_framework/api/admin/users/__init__.py +0 -0
- core_framework/api/admin/users/router.py +181 -0
- core_framework/api/admin/users/schemas.py +137 -0
- core_framework/api/auth/__init__.py +0 -0
- core_framework/api/auth/router.py +21 -0
- core_framework/api/auth/schemas.py +28 -0
- core_framework/api/comments/authenticated/router.py +126 -0
- core_framework/api/comments/authenticated/schemas.py +27 -0
- core_framework/api/comments/public/router.py +103 -0
- core_framework/api/comments/public/schemas.py +36 -0
- core_framework/api/comments/router.py +9 -0
- core_framework/api/comments/schemas.py +17 -0
- core_framework/api/dependencies.py +168 -0
- core_framework/api/events/router.py +39 -0
- core_framework/api/events/schemas.py +20 -0
- core_framework/api/posts/authenticated/router.py +83 -0
- core_framework/api/posts/authenticated/schemas.py +37 -0
- core_framework/api/posts/public/router.py +100 -0
- core_framework/api/posts/public/schemas.py +39 -0
- core_framework/api/posts/router.py +9 -0
- core_framework/api/posts/schemas.py +39 -0
- core_framework/api/router.py +19 -0
- core_framework/api/schemas.py +9 -0
- core_framework/api/system/__init__.py +0 -0
- core_framework/api/system/router.py +108 -0
- core_framework/api/users/__init__.py +0 -0
- core_framework/api/users/authenticated/__init__.py +0 -0
- core_framework/api/users/authenticated/router.py +244 -0
- core_framework/api/users/authenticated/schemas.py +81 -0
- core_framework/api/users/public/__init__.py +0 -0
- core_framework/api/users/public/router.py +25 -0
- core_framework/api/users/public/schemas.py +7 -0
- core_framework/api/users/router.py +9 -0
- core_framework/api/users/shared/schemas.py +174 -0
- core_framework/application/__init__.py +0 -0
- core_framework/application/auth/__init__.py +0 -0
- core_framework/application/auth/access_service.py +26 -0
- core_framework/application/auth/auth_service.py +10 -0
- core_framework/application/auth/models.py +10 -0
- core_framework/application/bootstrap.py +19 -0
- core_framework/application/comments/admin_service.py +236 -0
- core_framework/application/comments/aggregation_service.py +28 -0
- core_framework/application/comments/authenticated_service.py +89 -0
- core_framework/application/comments/public_service.py +218 -0
- core_framework/application/events/README.md +26 -0
- core_framework/application/events/event_service.py +51 -0
- core_framework/application/events/event_token.py +46 -0
- core_framework/application/events/models.py +9 -0
- core_framework/application/moderation/__init__.py +0 -0
- core_framework/application/moderation/appeal_service.py +98 -0
- core_framework/application/moderation/moderator_service.py +46 -0
- core_framework/application/moderation/report_service.py +180 -0
- core_framework/application/moderation/scheduled_service.py +5 -0
- core_framework/application/moderation/user_service.py +180 -0
- core_framework/application/posts/admin_service.py +104 -0
- core_framework/application/posts/aggregation_service.py +28 -0
- core_framework/application/posts/authenticated_service.py +72 -0
- core_framework/application/posts/public_service.py +197 -0
- core_framework/application/shared/__init__.py +0 -0
- core_framework/application/shared/enums.py +16 -0
- core_framework/application/shared/exceptions.py +16 -0
- core_framework/application/shared/user_agent.py +24 -0
- core_framework/application/users/__init__.py +0 -0
- core_framework/application/users/admin_service.py +298 -0
- core_framework/application/users/authenticated_service.py +179 -0
- core_framework/application/users/public_service.py +7 -0
- core_framework/application/users/scheduled_service.py +5 -0
- core_framework/bundled_alembic.py +57 -0
- core_framework/core/__init__.py +37 -0
- core_framework/core/cache.py +234 -0
- core_framework/core/context.py +14 -0
- core_framework/core/database.py +111 -0
- core_framework/core/exception_handlers/__init__.py +3 -0
- core_framework/core/exception_handlers/comment.py +99 -0
- core_framework/core/exception_handlers/common.py +5 -0
- core_framework/core/exception_handlers/moderation.py +104 -0
- core_framework/core/exception_handlers/post.py +54 -0
- core_framework/core/exception_handlers/setup.py +80 -0
- core_framework/core/exception_handlers/user.py +72 -0
- core_framework/core/http_client.py +64 -0
- core_framework/core/logging.py +99 -0
- core_framework/core/middleware.py +64 -0
- core_framework/core/observability.py +36 -0
- core_framework/core/pagination.py +203 -0
- core_framework/core/redis.py +135 -0
- core_framework/core/runtime.py +66 -0
- core_framework/core/settings.py +189 -0
- core_framework/domains/__init__.py +0 -0
- core_framework/domains/comment/README.md +243 -0
- core_framework/domains/comment/__init__.py +25 -0
- core_framework/domains/comment/constants.py +3 -0
- core_framework/domains/comment/dependencies.py +29 -0
- core_framework/domains/comment/enums.py +11 -0
- core_framework/domains/comment/exceptions.py +31 -0
- core_framework/domains/comment/models.py +54 -0
- core_framework/domains/comment/repository.py +947 -0
- core_framework/domains/comment/service.py +259 -0
- core_framework/domains/moderation/README.md +138 -0
- core_framework/domains/moderation/__init__.py +47 -0
- core_framework/domains/moderation/dependencies.py +29 -0
- core_framework/domains/moderation/enums.py +62 -0
- core_framework/domains/moderation/exceptions.py +31 -0
- core_framework/domains/moderation/models.py +94 -0
- core_framework/domains/moderation/repository.py +828 -0
- core_framework/domains/moderation/service.py +334 -0
- core_framework/domains/post/README.md +182 -0
- core_framework/domains/post/__init__.py +22 -0
- core_framework/domains/post/constants.py +3 -0
- core_framework/domains/post/dependencies.py +29 -0
- core_framework/domains/post/enums.py +18 -0
- core_framework/domains/post/exceptions.py +21 -0
- core_framework/domains/post/models.py +53 -0
- core_framework/domains/post/repository.py +791 -0
- core_framework/domains/post/service.py +204 -0
- core_framework/domains/user/README.md +74 -0
- core_framework/domains/user/__init__.py +39 -0
- core_framework/domains/user/constants.py +8 -0
- core_framework/domains/user/dependencies.py +29 -0
- core_framework/domains/user/enums.py +19 -0
- core_framework/domains/user/exceptions.py +31 -0
- core_framework/domains/user/models.py +124 -0
- core_framework/domains/user/repository.py +612 -0
- core_framework/domains/user/service.py +257 -0
- core_framework/domains/user/utils.py +182 -0
- core_framework/main.py +104 -0
- core_framework/worker/__init__.py +0 -0
- core_framework/worker/main.py +56 -0
- core_framework/worker/schedules/__init__.py +35 -0
- core_framework/worker/schedules/schedule_aggregate_comment_stats.py +32 -0
- core_framework/worker/schedules/schedule_aggregate_post_view_counts.py +28 -0
- core_framework/worker/schedules/schedule_expired_account_deletions.py +24 -0
- core_framework/worker/schedules/schedule_expired_mute_lifts.py +24 -0
- core_framework/worker/tasks/__init__.py +11 -0
- core_framework/worker/tasks/process_account_deletion.py +13 -0
- core_framework/worker/tasks/process_aggregate_comment_stats.py +19 -0
- core_framework/worker/tasks/process_aggregate_post_stats.py +12 -0
- core_framework/worker/tasks/process_mute_lift.py +13 -0
- core_framework-0.3.0.dist-info/METADATA +22 -0
- core_framework-0.3.0.dist-info/RECORD +222 -0
- core_framework-0.3.0.dist-info/WHEEL +4 -0
|
File without changes
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
from datetime import datetime
|
|
2
|
+
from typing import Any
|
|
3
|
+
|
|
4
|
+
from loguru import logger
|
|
5
|
+
|
|
6
|
+
import core_framework.domains.moderation.dependencies as moderation_deps
|
|
7
|
+
import core_framework.domains.user.dependencies as user_deps
|
|
8
|
+
from core_framework.application.moderation.user_service import clear_user_restriction
|
|
9
|
+
from core_framework.domains.moderation import AppealDecision, ModerationActionType
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
async def decide_appeal(
|
|
13
|
+
*,
|
|
14
|
+
actor_id: str,
|
|
15
|
+
appeal_id: int,
|
|
16
|
+
decision: AppealDecision,
|
|
17
|
+
reason: str,
|
|
18
|
+
) -> dict[str, Any]:
|
|
19
|
+
raw_appeal = await moderation_deps.moderation_service.decide_appeal(
|
|
20
|
+
actor_id=actor_id,
|
|
21
|
+
appeal_id=appeal_id,
|
|
22
|
+
decision=decision,
|
|
23
|
+
reason=reason,
|
|
24
|
+
)
|
|
25
|
+
await moderation_deps.moderation_service.record_moderation_action(
|
|
26
|
+
actor_id=actor_id,
|
|
27
|
+
action_type=ModerationActionType.DECIDE_APPEAL,
|
|
28
|
+
target_user_id=raw_appeal.user_id,
|
|
29
|
+
action_metadata={
|
|
30
|
+
"appeal_id": raw_appeal.id,
|
|
31
|
+
"decision": decision,
|
|
32
|
+
"reason": reason,
|
|
33
|
+
},
|
|
34
|
+
)
|
|
35
|
+
user_ids = {raw_appeal.user_id} if raw_appeal.reviewer_id is None else {raw_appeal.user_id, raw_appeal.reviewer_id}
|
|
36
|
+
user_identity_mapping = await user_deps.user_service.retrieve_user_identity_mapping(user_ids=user_ids)
|
|
37
|
+
|
|
38
|
+
if raw_appeal.status == AppealDecision.APPROVED:
|
|
39
|
+
try:
|
|
40
|
+
await clear_user_restriction(actor_id=actor_id, user_id=raw_appeal.user_id)
|
|
41
|
+
except Exception as e:
|
|
42
|
+
logger.error(f"Failed to clear restriction after appeal approval {appeal_id=} {raw_appeal.user_id=}: {e}")
|
|
43
|
+
|
|
44
|
+
return {
|
|
45
|
+
"id": raw_appeal.id,
|
|
46
|
+
"user": {
|
|
47
|
+
"user_id": raw_appeal.user_id,
|
|
48
|
+
"username": user_identity_mapping[raw_appeal.user_id].username,
|
|
49
|
+
"display_name": user_identity_mapping[raw_appeal.user_id].display_name,
|
|
50
|
+
},
|
|
51
|
+
"justification": raw_appeal.justification,
|
|
52
|
+
"reviewer": {
|
|
53
|
+
"user_id": raw_appeal.reviewer_id,
|
|
54
|
+
"username": user_identity_mapping[raw_appeal.reviewer_id].username,
|
|
55
|
+
"display_name": user_identity_mapping[raw_appeal.reviewer_id].display_name,
|
|
56
|
+
}
|
|
57
|
+
if raw_appeal.reviewer_id is not None
|
|
58
|
+
else None,
|
|
59
|
+
"decision_reason": raw_appeal.decision_reason,
|
|
60
|
+
"status": raw_appeal.status,
|
|
61
|
+
"created_at": raw_appeal.created_at,
|
|
62
|
+
"updated_at": raw_appeal.updated_at,
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
async def retrieve_appeals(*, status: AppealDecision | None, cursor: datetime, limit: int) -> list[dict[str, Any]]:
|
|
67
|
+
raw_appeals = await moderation_deps.moderation_service.retrieve_appeals(
|
|
68
|
+
status=status,
|
|
69
|
+
cursor=cursor,
|
|
70
|
+
limit=limit,
|
|
71
|
+
)
|
|
72
|
+
user_ids = {appeal.user_id for appeal in raw_appeals} | {
|
|
73
|
+
appeal.reviewer_id for appeal in raw_appeals if appeal.reviewer_id is not None
|
|
74
|
+
}
|
|
75
|
+
user_identity_mapping = await user_deps.user_service.retrieve_user_identity_mapping(user_ids=user_ids)
|
|
76
|
+
return [
|
|
77
|
+
{
|
|
78
|
+
"id": appeal.id,
|
|
79
|
+
"user": {
|
|
80
|
+
"user_id": appeal.user_id,
|
|
81
|
+
"username": user_identity_mapping[appeal.user_id].username,
|
|
82
|
+
"display_name": user_identity_mapping[appeal.user_id].display_name,
|
|
83
|
+
},
|
|
84
|
+
"justification": appeal.justification,
|
|
85
|
+
"reviewer": {
|
|
86
|
+
"user_id": appeal.reviewer_id,
|
|
87
|
+
"username": user_identity_mapping[appeal.reviewer_id].username,
|
|
88
|
+
"display_name": user_identity_mapping[appeal.reviewer_id].display_name,
|
|
89
|
+
}
|
|
90
|
+
if appeal.reviewer_id is not None
|
|
91
|
+
else None,
|
|
92
|
+
"decision_reason": appeal.decision_reason,
|
|
93
|
+
"status": appeal.status,
|
|
94
|
+
"created_at": appeal.created_at,
|
|
95
|
+
"updated_at": appeal.updated_at,
|
|
96
|
+
}
|
|
97
|
+
for appeal in raw_appeals
|
|
98
|
+
]
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
from datetime import datetime
|
|
2
|
+
from typing import Any
|
|
3
|
+
|
|
4
|
+
import core_framework.domains.moderation.dependencies as moderation_deps
|
|
5
|
+
import core_framework.domains.user.dependencies as user_deps
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
async def retrieve_moderation_actions_strong(
|
|
9
|
+
*,
|
|
10
|
+
actor_id: str,
|
|
11
|
+
cursor: datetime,
|
|
12
|
+
limit: int,
|
|
13
|
+
) -> list[dict[str, Any]]:
|
|
14
|
+
actions = await moderation_deps.moderation_service.retrieve_moderation_actions_strong(
|
|
15
|
+
actor_id=actor_id,
|
|
16
|
+
cursor=cursor,
|
|
17
|
+
limit=limit,
|
|
18
|
+
)
|
|
19
|
+
actor_ids = {action.actor_id for action in actions}
|
|
20
|
+
target_user_ids = {action.target_user_id for action in actions if action.target_user_id is not None}
|
|
21
|
+
user_ids = actor_ids | target_user_ids
|
|
22
|
+
user_identity_mapping = await user_deps.user_service.retrieve_user_identity_mapping(user_ids=user_ids)
|
|
23
|
+
|
|
24
|
+
return [
|
|
25
|
+
{
|
|
26
|
+
"id": action.id,
|
|
27
|
+
"actor": {
|
|
28
|
+
"user_id": action.actor_id,
|
|
29
|
+
"username": user_identity_mapping[action.actor_id].username,
|
|
30
|
+
"display_name": user_identity_mapping[action.actor_id].display_name,
|
|
31
|
+
},
|
|
32
|
+
"target_user": (
|
|
33
|
+
{
|
|
34
|
+
"user_id": action.target_user_id,
|
|
35
|
+
"username": user_identity_mapping[action.target_user_id].username,
|
|
36
|
+
"display_name": user_identity_mapping[action.target_user_id].display_name,
|
|
37
|
+
}
|
|
38
|
+
if action.target_user_id is not None
|
|
39
|
+
else None
|
|
40
|
+
),
|
|
41
|
+
"action_type": action.action_type,
|
|
42
|
+
"action_metadata": action.action_metadata,
|
|
43
|
+
"created_at": action.created_at,
|
|
44
|
+
}
|
|
45
|
+
for action in actions
|
|
46
|
+
]
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
from asyncio import TaskGroup
|
|
2
|
+
from datetime import datetime
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
import core_framework.domains.comment.dependencies as comment_deps
|
|
6
|
+
import core_framework.domains.moderation.dependencies as moderation_deps
|
|
7
|
+
import core_framework.domains.post.dependencies as post_deps
|
|
8
|
+
import core_framework.domains.user.dependencies as user_deps
|
|
9
|
+
from core_framework.domains.moderation import Report, ReportType
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def _build_report_item(
|
|
13
|
+
report: Report,
|
|
14
|
+
report_type: ReportType,
|
|
15
|
+
reporter: dict[str, Any],
|
|
16
|
+
preview: list[dict[str, str]],
|
|
17
|
+
) -> dict[str, Any]:
|
|
18
|
+
return {
|
|
19
|
+
"type": report_type.value,
|
|
20
|
+
"id": report.id,
|
|
21
|
+
"reporter": reporter,
|
|
22
|
+
"target_id": report.target_id,
|
|
23
|
+
"category": report.category,
|
|
24
|
+
"reason": report.reason,
|
|
25
|
+
"created_at": report.created_at,
|
|
26
|
+
"preview": preview,
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
async def _retrieve_user_reports(
|
|
31
|
+
*,
|
|
32
|
+
reporter_id: str | None,
|
|
33
|
+
target_id: str | None,
|
|
34
|
+
cursor: datetime,
|
|
35
|
+
limit: int,
|
|
36
|
+
) -> list[dict[str, Any]]:
|
|
37
|
+
raw_reports = await moderation_deps.moderation_service.retrieve_user_reports(
|
|
38
|
+
reporter_id=reporter_id,
|
|
39
|
+
target_id=target_id,
|
|
40
|
+
cursor=cursor,
|
|
41
|
+
limit=limit,
|
|
42
|
+
)
|
|
43
|
+
if not raw_reports:
|
|
44
|
+
return []
|
|
45
|
+
|
|
46
|
+
reporter_ids = {r.reporter_id for r in raw_reports}
|
|
47
|
+
target_ids = {r.target_id for r in raw_reports}
|
|
48
|
+
user_identity_mapping = await user_deps.user_service.retrieve_user_identity_mapping(
|
|
49
|
+
user_ids=reporter_ids | target_ids
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
result: list[dict[str, Any]] = []
|
|
53
|
+
for report in raw_reports:
|
|
54
|
+
reporter_identity = user_identity_mapping[report.reporter_id]
|
|
55
|
+
reporter = {
|
|
56
|
+
"user_id": report.reporter_id,
|
|
57
|
+
"username": reporter_identity.username,
|
|
58
|
+
"display_name": reporter_identity.display_name,
|
|
59
|
+
}
|
|
60
|
+
target_identity = user_identity_mapping[report.target_id]
|
|
61
|
+
preview = [
|
|
62
|
+
{"key": "username", "value": target_identity.username},
|
|
63
|
+
{"key": "display_name", "value": target_identity.display_name or ""},
|
|
64
|
+
{"key": "role", "value": target_identity.role.value},
|
|
65
|
+
]
|
|
66
|
+
result.append(_build_report_item(report, ReportType.USER, reporter, preview))
|
|
67
|
+
|
|
68
|
+
return result
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
async def _retrieve_post_reports(
|
|
72
|
+
*,
|
|
73
|
+
reporter_id: str | None,
|
|
74
|
+
target_id: str | None,
|
|
75
|
+
cursor: datetime,
|
|
76
|
+
limit: int,
|
|
77
|
+
) -> list[dict[str, Any]]:
|
|
78
|
+
raw_reports = await moderation_deps.moderation_service.retrieve_post_reports(
|
|
79
|
+
reporter_id=reporter_id,
|
|
80
|
+
target_id=target_id,
|
|
81
|
+
cursor=cursor,
|
|
82
|
+
limit=limit,
|
|
83
|
+
)
|
|
84
|
+
if not raw_reports:
|
|
85
|
+
return []
|
|
86
|
+
|
|
87
|
+
reporter_ids = {r.reporter_id for r in raw_reports}
|
|
88
|
+
target_ids = {r.target_id for r in raw_reports}
|
|
89
|
+
async with TaskGroup() as tg:
|
|
90
|
+
identity_task = tg.create_task(user_deps.user_service.retrieve_user_identity_mapping(user_ids=reporter_ids))
|
|
91
|
+
content_task = tg.create_task(post_deps.post_service.retrieve_post_content_mapping(post_ids=target_ids))
|
|
92
|
+
user_identity_mapping = identity_task.result()
|
|
93
|
+
post_content_mapping = content_task.result()
|
|
94
|
+
|
|
95
|
+
result: list[dict[str, Any]] = []
|
|
96
|
+
for report in raw_reports:
|
|
97
|
+
reporter_identity = user_identity_mapping[report.reporter_id]
|
|
98
|
+
reporter = {
|
|
99
|
+
"user_id": report.reporter_id,
|
|
100
|
+
"username": reporter_identity.username,
|
|
101
|
+
"display_name": reporter_identity.display_name,
|
|
102
|
+
}
|
|
103
|
+
content = post_content_mapping[report.target_id]
|
|
104
|
+
preview = [{"key": "post_content", "value": content}]
|
|
105
|
+
result.append(_build_report_item(report, ReportType.POST, reporter, preview))
|
|
106
|
+
|
|
107
|
+
return result
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
async def _retrieve_comment_reports(
|
|
111
|
+
*,
|
|
112
|
+
reporter_id: str | None,
|
|
113
|
+
target_id: str | None,
|
|
114
|
+
cursor: datetime,
|
|
115
|
+
limit: int,
|
|
116
|
+
) -> list[dict[str, Any]]:
|
|
117
|
+
raw_reports = await moderation_deps.moderation_service.retrieve_comment_reports(
|
|
118
|
+
reporter_id=reporter_id,
|
|
119
|
+
target_id=target_id,
|
|
120
|
+
cursor=cursor,
|
|
121
|
+
limit=limit,
|
|
122
|
+
)
|
|
123
|
+
if not raw_reports:
|
|
124
|
+
return []
|
|
125
|
+
|
|
126
|
+
reporter_ids = {r.reporter_id for r in raw_reports}
|
|
127
|
+
target_ids = {r.target_id for r in raw_reports}
|
|
128
|
+
async with TaskGroup() as tg:
|
|
129
|
+
identity_task = tg.create_task(user_deps.user_service.retrieve_user_identity_mapping(user_ids=reporter_ids))
|
|
130
|
+
content_task = tg.create_task(
|
|
131
|
+
comment_deps.comment_service.retrieve_comment_content_mapping(comment_ids=target_ids)
|
|
132
|
+
)
|
|
133
|
+
user_identity_mapping = identity_task.result()
|
|
134
|
+
comment_content_mapping = content_task.result()
|
|
135
|
+
|
|
136
|
+
result: list[dict[str, Any]] = []
|
|
137
|
+
for report in raw_reports:
|
|
138
|
+
reporter_identity = user_identity_mapping[report.reporter_id]
|
|
139
|
+
reporter = {
|
|
140
|
+
"user_id": report.reporter_id,
|
|
141
|
+
"username": reporter_identity.username,
|
|
142
|
+
"display_name": reporter_identity.display_name,
|
|
143
|
+
}
|
|
144
|
+
content = comment_content_mapping[report.target_id]
|
|
145
|
+
preview = [{"key": "comment_content", "value": content}]
|
|
146
|
+
result.append(_build_report_item(report, ReportType.COMMENT, reporter, preview))
|
|
147
|
+
|
|
148
|
+
return result
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
async def retrieve_reports(
|
|
152
|
+
*,
|
|
153
|
+
report_type: ReportType,
|
|
154
|
+
reporter_id: str | None,
|
|
155
|
+
target_id: str | None,
|
|
156
|
+
cursor: datetime,
|
|
157
|
+
limit: int,
|
|
158
|
+
) -> list[dict[str, Any]]:
|
|
159
|
+
match report_type:
|
|
160
|
+
case ReportType.USER:
|
|
161
|
+
return await _retrieve_user_reports(
|
|
162
|
+
reporter_id=reporter_id,
|
|
163
|
+
target_id=target_id,
|
|
164
|
+
cursor=cursor,
|
|
165
|
+
limit=limit,
|
|
166
|
+
)
|
|
167
|
+
case ReportType.POST:
|
|
168
|
+
return await _retrieve_post_reports(
|
|
169
|
+
reporter_id=reporter_id,
|
|
170
|
+
target_id=target_id,
|
|
171
|
+
cursor=cursor,
|
|
172
|
+
limit=limit,
|
|
173
|
+
)
|
|
174
|
+
case ReportType.COMMENT:
|
|
175
|
+
return await _retrieve_comment_reports(
|
|
176
|
+
reporter_id=reporter_id,
|
|
177
|
+
target_id=target_id,
|
|
178
|
+
cursor=cursor,
|
|
179
|
+
limit=limit,
|
|
180
|
+
)
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
from asyncio import TaskGroup
|
|
2
|
+
from datetime import datetime
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
import core_framework.domains.comment.dependencies as comment_deps
|
|
6
|
+
import core_framework.domains.moderation.dependencies as moderation_deps
|
|
7
|
+
import core_framework.domains.post.dependencies as post_deps
|
|
8
|
+
import core_framework.domains.user.dependencies as user_deps
|
|
9
|
+
from core_framework.application.shared.enums import RedisKeys
|
|
10
|
+
from core_framework.application.shared.exceptions import ForbiddenException, UserNotFoundException
|
|
11
|
+
from core_framework.core.cache import invalidate_cache
|
|
12
|
+
from core_framework.domains.moderation import ModerationActionType, RestrictionCategory, RestrictionType
|
|
13
|
+
from core_framework.domains.user import UserIdentity, UserRole
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
async def ban_user(
|
|
17
|
+
*,
|
|
18
|
+
actor_id: str,
|
|
19
|
+
user_id: str,
|
|
20
|
+
category: RestrictionCategory,
|
|
21
|
+
reason: str,
|
|
22
|
+
) -> None:
|
|
23
|
+
identity = await user_deps.user_service.retrieve_user_identity_strong(user_id=user_id)
|
|
24
|
+
if identity == UserIdentity.DEFAULT:
|
|
25
|
+
raise UserNotFoundException()
|
|
26
|
+
if identity.role == UserRole.ADMIN:
|
|
27
|
+
raise ForbiddenException("Admin users cannot be banned")
|
|
28
|
+
restriction = await moderation_deps.moderation_service.retrieve_user_restriction_strong(user_id=user_id)
|
|
29
|
+
if restriction.status == RestrictionType.BANNED:
|
|
30
|
+
await _sync_user_restriction_lookups(user_id=user_id, restriction_status=restriction.status)
|
|
31
|
+
return
|
|
32
|
+
await moderation_deps.moderation_service.ban_user(
|
|
33
|
+
actor_id=actor_id,
|
|
34
|
+
user_id=user_id,
|
|
35
|
+
reason=reason,
|
|
36
|
+
category=category,
|
|
37
|
+
)
|
|
38
|
+
await _sync_user_restriction_lookups(user_id=user_id, restriction_status=RestrictionType.BANNED)
|
|
39
|
+
await moderation_deps.moderation_service.record_moderation_action(
|
|
40
|
+
actor_id=actor_id,
|
|
41
|
+
action_type=ModerationActionType.BAN,
|
|
42
|
+
target_user_id=user_id,
|
|
43
|
+
action_metadata={"reason": reason, "category": category},
|
|
44
|
+
)
|
|
45
|
+
await invalidate_cache(RedisKeys.USER_DETAIL, user_id=user_id)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
async def mute_user(*, actor_id: str, user_id: str, category: RestrictionCategory, reason: str) -> None:
|
|
49
|
+
identity = await user_deps.user_service.retrieve_user_identity_strong(user_id=user_id)
|
|
50
|
+
if identity == UserIdentity.DEFAULT:
|
|
51
|
+
raise UserNotFoundException()
|
|
52
|
+
restriction = await moderation_deps.moderation_service.retrieve_user_restriction_strong(user_id=user_id)
|
|
53
|
+
if restriction.status in (RestrictionType.MUTED, RestrictionType.BANNED):
|
|
54
|
+
await _sync_user_restriction_lookups(user_id=user_id, restriction_status=restriction.status)
|
|
55
|
+
return
|
|
56
|
+
await moderation_deps.moderation_service.mute_user(
|
|
57
|
+
actor_id=actor_id,
|
|
58
|
+
user_id=user_id,
|
|
59
|
+
reason=reason,
|
|
60
|
+
category=category,
|
|
61
|
+
)
|
|
62
|
+
await _sync_user_restriction_lookups(user_id=user_id, restriction_status=RestrictionType.MUTED)
|
|
63
|
+
await moderation_deps.moderation_service.record_moderation_action(
|
|
64
|
+
actor_id=actor_id,
|
|
65
|
+
action_type=ModerationActionType.MUTE,
|
|
66
|
+
target_user_id=user_id,
|
|
67
|
+
action_metadata={"reason": reason, "category": category},
|
|
68
|
+
)
|
|
69
|
+
await invalidate_cache(RedisKeys.USER_DETAIL, user_id=user_id)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
async def warn_user(*, actor_id: str, user_id: str, category: RestrictionCategory, reason: str) -> None:
|
|
73
|
+
identity = await user_deps.user_service.retrieve_user_identity_strong(user_id=user_id)
|
|
74
|
+
if identity == UserIdentity.DEFAULT:
|
|
75
|
+
raise UserNotFoundException()
|
|
76
|
+
restriction = await moderation_deps.moderation_service.retrieve_user_restriction_strong(user_id=user_id)
|
|
77
|
+
if restriction.status in (RestrictionType.WARNED, RestrictionType.MUTED, RestrictionType.BANNED):
|
|
78
|
+
await _sync_user_restriction_lookups(user_id=user_id, restriction_status=restriction.status)
|
|
79
|
+
return
|
|
80
|
+
await moderation_deps.moderation_service.warn_user(
|
|
81
|
+
actor_id=actor_id,
|
|
82
|
+
user_id=user_id,
|
|
83
|
+
reason=reason,
|
|
84
|
+
category=category,
|
|
85
|
+
)
|
|
86
|
+
await _sync_user_restriction_lookups(user_id=user_id, restriction_status=RestrictionType.WARNED)
|
|
87
|
+
await moderation_deps.moderation_service.record_moderation_action(
|
|
88
|
+
actor_id=actor_id,
|
|
89
|
+
action_type=ModerationActionType.WARN,
|
|
90
|
+
target_user_id=user_id,
|
|
91
|
+
action_metadata={"reason": reason, "category": category},
|
|
92
|
+
)
|
|
93
|
+
await invalidate_cache(RedisKeys.USER_DETAIL, user_id=user_id)
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
async def clear_user_restriction(*, actor_id: str, user_id: str) -> None:
|
|
97
|
+
identity = await user_deps.user_service.retrieve_user_identity_strong(user_id=user_id)
|
|
98
|
+
if identity == UserIdentity.DEFAULT:
|
|
99
|
+
raise UserNotFoundException()
|
|
100
|
+
restriction = await moderation_deps.moderation_service.retrieve_user_restriction_strong(user_id=user_id)
|
|
101
|
+
if restriction.status == RestrictionType.ACTIVE:
|
|
102
|
+
await _sync_user_restriction_lookups(user_id=user_id, restriction_status=restriction.status)
|
|
103
|
+
return
|
|
104
|
+
await moderation_deps.moderation_service.clear_user_restriction(actor_id=actor_id, user_id=user_id)
|
|
105
|
+
await _sync_user_restriction_lookups(user_id=user_id, restriction_status=RestrictionType.ACTIVE)
|
|
106
|
+
await moderation_deps.moderation_service.record_moderation_action(
|
|
107
|
+
actor_id=actor_id,
|
|
108
|
+
action_type=ModerationActionType.UNRESTRICT,
|
|
109
|
+
target_user_id=user_id,
|
|
110
|
+
)
|
|
111
|
+
await invalidate_cache(RedisKeys.USER_DETAIL, user_id=user_id)
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
async def remove_user_report(*, report_id: int) -> None:
|
|
115
|
+
await moderation_deps.moderation_service.remove_user_report(report_id=report_id)
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
async def remove_post_report(*, report_id: int) -> None:
|
|
119
|
+
await moderation_deps.moderation_service.remove_post_report(report_id=report_id)
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
async def remove_comment_report(*, report_id: int) -> None:
|
|
123
|
+
await moderation_deps.moderation_service.remove_comment_report(report_id=report_id)
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
async def retrieve_restriction_history(
|
|
127
|
+
*,
|
|
128
|
+
user_id: str,
|
|
129
|
+
cursor: datetime,
|
|
130
|
+
limit: int,
|
|
131
|
+
) -> list[dict[str, Any]]:
|
|
132
|
+
restriction_history = await moderation_deps.moderation_service.retrieve_restriction_history(
|
|
133
|
+
user_id=user_id,
|
|
134
|
+
cursor=cursor,
|
|
135
|
+
limit=limit,
|
|
136
|
+
)
|
|
137
|
+
actor_ids = {entry.actor_id for entry in restriction_history if entry.actor_id is not None}
|
|
138
|
+
user_identity_mapping = await user_deps.user_service.retrieve_user_identity_mapping(user_ids=actor_ids)
|
|
139
|
+
return [
|
|
140
|
+
{
|
|
141
|
+
"user_id": entry.user_id,
|
|
142
|
+
"action": entry.action,
|
|
143
|
+
"restriction_type": entry.restriction_type,
|
|
144
|
+
"category": entry.category,
|
|
145
|
+
"reason": entry.reason,
|
|
146
|
+
"actor": {
|
|
147
|
+
"user_id": entry.actor_id,
|
|
148
|
+
"username": user_identity_mapping[entry.actor_id].username,
|
|
149
|
+
"display_name": user_identity_mapping[entry.actor_id].display_name,
|
|
150
|
+
}
|
|
151
|
+
if entry.actor_id is not None
|
|
152
|
+
else None,
|
|
153
|
+
"created_at": entry.created_at,
|
|
154
|
+
}
|
|
155
|
+
for entry in restriction_history
|
|
156
|
+
]
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
async def _sync_user_restriction_lookups(*, user_id: str, restriction_status: RestrictionType) -> None:
|
|
160
|
+
# Keep post- and comment-domain lookups aligned with moderation truth, including no-op
|
|
161
|
+
# moderation calls (early returns) so retries can heal cross-domain drift from prior partial failures.
|
|
162
|
+
if restriction_status in (RestrictionType.MUTED, RestrictionType.BANNED):
|
|
163
|
+
restriction_type = restriction_status.value
|
|
164
|
+
async with TaskGroup() as tg:
|
|
165
|
+
tg.create_task(
|
|
166
|
+
post_deps.post_service.add_user_restriction_lookup(
|
|
167
|
+
user_id=user_id,
|
|
168
|
+
restriction_type=restriction_type,
|
|
169
|
+
)
|
|
170
|
+
)
|
|
171
|
+
tg.create_task(
|
|
172
|
+
comment_deps.comment_service.add_user_restriction_lookup(
|
|
173
|
+
user_id=user_id,
|
|
174
|
+
restriction_type=restriction_type,
|
|
175
|
+
)
|
|
176
|
+
)
|
|
177
|
+
return
|
|
178
|
+
async with TaskGroup() as tg:
|
|
179
|
+
tg.create_task(post_deps.post_service.remove_user_restriction_lookup(user_id=user_id))
|
|
180
|
+
tg.create_task(comment_deps.comment_service.remove_user_restriction_lookup(user_id=user_id))
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
from asyncio import TaskGroup
|
|
2
|
+
from collections import defaultdict
|
|
3
|
+
from datetime import datetime
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
import core_framework.domains.comment.dependencies as comment_deps
|
|
7
|
+
import core_framework.domains.moderation.dependencies as moderation_deps
|
|
8
|
+
import core_framework.domains.post.dependencies as post_deps
|
|
9
|
+
import core_framework.domains.user.dependencies as user_deps
|
|
10
|
+
from core_framework.domains.comment import CommentSubjectType
|
|
11
|
+
from core_framework.domains.post import PostStats, PostStatus, PostWithMetadata
|
|
12
|
+
from core_framework.domains.user import UserIdentity
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def _serialize_post(
|
|
16
|
+
post: PostWithMetadata,
|
|
17
|
+
post_stats_mapping: defaultdict[str, PostStats],
|
|
18
|
+
hashtags_mapping: defaultdict[str, list[str]],
|
|
19
|
+
user_identity_mapping: defaultdict[str, UserIdentity],
|
|
20
|
+
) -> dict[str, Any]:
|
|
21
|
+
stats = post_stats_mapping[post.id]
|
|
22
|
+
hashtags = hashtags_mapping[post.id]
|
|
23
|
+
author_identity = user_identity_mapping[post.author_id]
|
|
24
|
+
return {
|
|
25
|
+
"id": post.id,
|
|
26
|
+
"author": {
|
|
27
|
+
"user_id": post.author_id,
|
|
28
|
+
"username": author_identity.username,
|
|
29
|
+
"display_name": author_identity.display_name,
|
|
30
|
+
},
|
|
31
|
+
"content": post.content,
|
|
32
|
+
"hashtags": hashtags,
|
|
33
|
+
"visibility": post.visibility,
|
|
34
|
+
"status": post.status,
|
|
35
|
+
"edited_count": post.edited_count,
|
|
36
|
+
"stats": {
|
|
37
|
+
"like_count": stats.like_count,
|
|
38
|
+
"comment_count": stats.comment_count,
|
|
39
|
+
"view_count": stats.view_count,
|
|
40
|
+
"report_count": stats.report_count,
|
|
41
|
+
},
|
|
42
|
+
"edited_at": post.edited_at,
|
|
43
|
+
"created_at": post.created_at,
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
async def retrieve_all_posts(*, cursor: datetime, limit: int) -> list[dict[str, Any]]:
|
|
48
|
+
posts = await post_deps.post_service.retrieve_posts_unfiltered(cursor=cursor, limit=limit)
|
|
49
|
+
post_ids_set = {post.id for post in posts}
|
|
50
|
+
author_ids = {post.author_id for post in posts}
|
|
51
|
+
async with TaskGroup() as tg:
|
|
52
|
+
stats_task = tg.create_task(post_deps.post_service.retrieve_post_stats_mapping(post_ids=post_ids_set))
|
|
53
|
+
hashtags_task = tg.create_task(post_deps.post_service.retrieve_hashtags_mapping(post_ids=post_ids_set))
|
|
54
|
+
identity_task = tg.create_task(user_deps.user_service.retrieve_user_identity_mapping(user_ids=author_ids))
|
|
55
|
+
return [
|
|
56
|
+
_serialize_post(
|
|
57
|
+
post,
|
|
58
|
+
stats_task.result(),
|
|
59
|
+
hashtags_task.result(),
|
|
60
|
+
identity_task.result(),
|
|
61
|
+
)
|
|
62
|
+
for post in posts
|
|
63
|
+
]
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
async def retrieve_post_by_id(*, post_id: str) -> dict[str, Any]:
|
|
67
|
+
post = await post_deps.post_service.retrieve_post_with_metadata_by_id(post_id=post_id)
|
|
68
|
+
post_ids_set = {post.id}
|
|
69
|
+
author_ids = {post.author_id}
|
|
70
|
+
async with TaskGroup() as tg:
|
|
71
|
+
stats_task = tg.create_task(post_deps.post_service.retrieve_post_stats_mapping(post_ids=post_ids_set))
|
|
72
|
+
hashtags_task = tg.create_task(post_deps.post_service.retrieve_hashtags_mapping(post_ids=post_ids_set))
|
|
73
|
+
identity_task = tg.create_task(user_deps.user_service.retrieve_user_identity_mapping(user_ids=author_ids))
|
|
74
|
+
return _serialize_post(
|
|
75
|
+
post,
|
|
76
|
+
stats_task.result(),
|
|
77
|
+
hashtags_task.result(),
|
|
78
|
+
identity_task.result(),
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
async def set_post_inactive(*, post_id: str) -> None:
|
|
83
|
+
await post_deps.post_service.set_post_status_by_id(post_id=post_id, status=PostStatus.DELETED)
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
async def restore_post(post_id: str) -> None:
|
|
87
|
+
await post_deps.post_service.set_post_status_by_id(post_id=post_id, status=PostStatus.ACTIVE)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
async def remove_post(*, post_id: str) -> None:
|
|
91
|
+
comment_ids = await comment_deps.comment_service.retrieve_comment_ids_by_subject_strong(
|
|
92
|
+
subject_type=CommentSubjectType.POST,
|
|
93
|
+
subject_id=post_id,
|
|
94
|
+
)
|
|
95
|
+
async with TaskGroup() as tg:
|
|
96
|
+
tg.create_task(moderation_deps.moderation_service.remove_post_reports_by_target_id(target_id=post_id))
|
|
97
|
+
tg.create_task(moderation_deps.moderation_service.remove_comment_reports_by_target_ids(target_ids=comment_ids))
|
|
98
|
+
tg.create_task(
|
|
99
|
+
comment_deps.comment_service.remove_comments_by_subject(
|
|
100
|
+
subject_type=CommentSubjectType.POST,
|
|
101
|
+
subject_id=post_id,
|
|
102
|
+
)
|
|
103
|
+
)
|
|
104
|
+
await post_deps.post_service.delete_post_by_id(post_id=post_id)
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
from asyncio import TaskGroup
|
|
2
|
+
|
|
3
|
+
import core_framework.domains.comment.dependencies as comment_deps
|
|
4
|
+
import core_framework.domains.moderation.dependencies as moderation_deps
|
|
5
|
+
import core_framework.domains.post.dependencies as post_deps
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
async def aggregate_post_view_counts() -> list[str]:
|
|
9
|
+
return await post_deps.post_service.aggregate_post_view_counts()
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
async def aggregate_post_stats(*, post_id: str) -> None:
|
|
13
|
+
async with TaskGroup() as tg:
|
|
14
|
+
like_task = tg.create_task(post_deps.post_service.retrieve_post_like_count(post_id=post_id))
|
|
15
|
+
report_task = tg.create_task(moderation_deps.moderation_service.retrieve_post_report_count(post_id=post_id))
|
|
16
|
+
comment_count_task = tg.create_task(comment_deps.comment_service.retrieve_post_comment_count(post_id=post_id))
|
|
17
|
+
|
|
18
|
+
counts = {
|
|
19
|
+
"like_count": like_task.result(),
|
|
20
|
+
"comment_count": comment_count_task.result(),
|
|
21
|
+
"report_count": report_task.result(),
|
|
22
|
+
}
|
|
23
|
+
await post_deps.post_service.update_post_stats(
|
|
24
|
+
post_id=post_id,
|
|
25
|
+
like_count=counts["like_count"],
|
|
26
|
+
comment_count=counts["comment_count"],
|
|
27
|
+
report_count=counts["report_count"],
|
|
28
|
+
)
|