Coverage for app/backend/src/couchers/moderation/utils.py: 100%
25 statements
« prev ^ index » next coverage.py v7.16.1, created at 2026-09-19 15:47 +0000
« prev ^ index » next coverage.py v7.16.1, created at 2026-09-19 15:47 +0000
1"""
2Utility functions for the Unified Moderation System (UMS)
3"""
5from collections.abc import Callable
6from typing import Any
8from sqlalchemy.orm import Session
10from couchers.metrics import observe_moderation_action, observe_moderation_queue_item_created
11from couchers.models import (
12 ModerationAction,
13 ModerationLog,
14 ModerationObjectType,
15 ModerationQueueItem,
16 ModerationState,
17 ModerationTrigger,
18 ModerationVisibility,
19 get_moderated_models,
20)
23def create_moderation(
24 session: Session,
25 object_type: ModerationObjectType,
26 object_id: int | Callable[[int], int],
27 # None for objects that are their own creator, whose id isn't known until the callback has run
28 creator_user_id: int | None = None,
29 data: Any | None = None,
30) -> ModerationState:
31 has_own_visibility_mechanism = get_moderated_models()[object_type].has_own_visibility_mechanism
32 visibility = None if has_own_visibility_mechanism else ModerationVisibility.shadowed
34 # Handle callback pattern for circular dependencies
35 if callable(object_id):
36 moderation_state = ModerationState(
37 object_type=object_type,
38 object_id=0, # Placeholder
39 visibility=visibility,
40 )
41 session.add(moderation_state)
42 session.flush()
44 # Call the callback to create the object and get its ID
45 actual_object_id = object_id(moderation_state.id)
46 moderation_state.object_id = actual_object_id
47 else:
48 actual_object_id = object_id
49 moderation_state = ModerationState(
50 object_type=object_type,
51 object_id=object_id,
52 visibility=visibility,
53 )
54 session.add(moderation_state)
55 session.flush()
57 session.add(
58 ModerationLog(
59 moderation_state_id=moderation_state.id,
60 action=ModerationAction.create,
61 moderator_user_id=creator_user_id if creator_user_id is not None else actual_object_id,
62 new_visibility=visibility,
63 reason="Object created.",
64 )
65 )
67 if not has_own_visibility_mechanism:
68 session.add(
69 ModerationQueueItem(
70 moderation_state_id=moderation_state.id,
71 trigger=ModerationTrigger.initial_review,
72 reason="Object created.",
73 data=data,
74 )
75 )
76 observe_moderation_queue_item_created(ModerationTrigger.initial_review, object_type)
78 observe_moderation_action(ModerationAction.create, object_type)
80 session.flush()
82 return moderation_state