Coverage for polar/organization/tasks.py: 27%
98 statements
« prev ^ index » next coverage.py v7.10.6, created at 2025-12-05 15:52 +0000
« prev ^ index » next coverage.py v7.10.6, created at 2025-12-05 15:52 +0000
1import uuid 1a
3from sqlalchemy.orm import joinedload 1a
5from polar.account.repository import AccountRepository 1a
6from polar.email.react import render_email_template 1a
7from polar.email.schemas import ( 1a
8 OrganizationReviewedEmail,
9 OrganizationReviewedProps,
10 OrganizationUnderReviewEmail,
11 OrganizationUnderReviewProps,
12)
13from polar.email.sender import enqueue_email 1a
14from polar.exceptions import PolarTaskError 1a
15from polar.held_balance.service import held_balance as held_balance_service 1a
16from polar.integrations.plain.service import plain as plain_service 1a
17from polar.models import Organization 1a
18from polar.models.organization import OrganizationStatus 1a
19from polar.user.repository import UserRepository 1a
20from polar.worker import AsyncSessionMaker, TaskPriority, actor 1a
22from .repository import OrganizationRepository 1a
25class OrganizationTaskError(PolarTaskError): ... 1a
28class OrganizationDoesNotExist(OrganizationTaskError): 1a
29 def __init__(self, organization_id: uuid.UUID) -> None: 1a
30 self.organization_id = organization_id
31 message = f"The organization with id {organization_id} does not exist."
32 super().__init__(message)
35class OrganizationAccountNotSet(OrganizationTaskError): 1a
36 def __init__(self, organization_id: uuid.UUID) -> None: 1a
37 self.organization_id = organization_id
38 message = (
39 f"The organization with id {organization_id} does not have an account set."
40 )
41 super().__init__(message)
44class AccountDoesNotExist(OrganizationTaskError): 1a
45 def __init__(self, account_id: uuid.UUID) -> None: 1a
46 self.account_id = account_id
47 message = f"The account with id {account_id} does not exist."
48 super().__init__(message)
51class UserDoesNotExist(OrganizationTaskError): 1a
52 def __init__(self, user_id: uuid.UUID) -> None: 1a
53 self.user_id = user_id
54 message = f"The user with id {user_id} does not exist."
55 super().__init__(message)
58@actor(actor_name="organization.created", priority=TaskPriority.LOW) 1a
59async def organization_created(organization_id: uuid.UUID) -> None: 1a
60 async with AsyncSessionMaker() as session:
61 repository = OrganizationRepository.from_session(session)
62 organization = await repository.get_by_id(organization_id)
63 if organization is None:
64 raise OrganizationDoesNotExist(organization_id)
67@actor(actor_name="organization.account_set", priority=TaskPriority.LOW) 1a
68async def organization_account_set(organization_id: uuid.UUID) -> None: 1a
69 async with AsyncSessionMaker() as session:
70 repository = OrganizationRepository.from_session(session)
71 organization = await repository.get_by_id(organization_id)
72 if organization is None:
73 raise OrganizationDoesNotExist(organization_id)
75 if organization.account_id is None:
76 raise OrganizationAccountNotSet(organization_id)
78 account_repository = AccountRepository.from_session(session)
79 account = await account_repository.get_by_id(organization.account_id)
80 if account is None:
81 raise AccountDoesNotExist(organization.account_id)
83 await held_balance_service.release_account(session, account)
86@actor(actor_name="organization.under_review", priority=TaskPriority.LOW) 1a
87async def organization_under_review(organization_id: uuid.UUID) -> None: 1a
88 async with AsyncSessionMaker() as session:
89 repository = OrganizationRepository.from_session(session)
90 organization = await repository.get_by_id(
91 organization_id, options=(joinedload(Organization.account),)
92 )
93 if organization is None:
94 raise OrganizationDoesNotExist(organization_id)
96 await plain_service.create_organization_review_thread(session, organization)
98 # Send an email for the initial review
99 if organization.status == OrganizationStatus.INITIAL_REVIEW:
100 admin_user = await repository.get_admin_user(session, organization)
101 if admin_user:
102 email = OrganizationUnderReviewEmail(
103 props=OrganizationUnderReviewProps.model_validate(
104 {"email": admin_user.email, "organization": organization}
105 )
106 )
107 enqueue_email(
108 to_email_addr=admin_user.email,
109 subject="Your organization is under review",
110 html_content=render_email_template(email),
111 )
114@actor(actor_name="organization.reviewed", priority=TaskPriority.LOW) 1a
115async def organization_reviewed( 1a
116 organization_id: uuid.UUID, initial_review: bool = False
117) -> None:
118 async with AsyncSessionMaker() as session:
119 repository = OrganizationRepository.from_session(session)
120 organization = await repository.get_by_id(organization_id)
121 if organization is None:
122 raise OrganizationDoesNotExist(organization_id)
124 # Release held balance if account exists
125 if organization.account_id:
126 account_repository = AccountRepository.from_session(session)
127 account = await account_repository.get_by_id(organization.account_id)
128 if account:
129 await held_balance_service.release_account(session, account)
131 # Send an email after the initial review
132 if initial_review:
133 admin_user = await repository.get_admin_user(session, organization)
134 if admin_user:
135 email = OrganizationReviewedEmail(
136 props=OrganizationReviewedProps.model_validate(
137 {"email": admin_user.email, "organization": organization}
138 )
139 )
140 enqueue_email(
141 to_email_addr=admin_user.email,
142 subject="Your organization review is complete",
143 html_content=render_email_template(email),
144 )
147@actor(actor_name="organization.deletion_requested", priority=TaskPriority.HIGH) 1a
148async def organization_deletion_requested( 1a
149 organization_id: uuid.UUID,
150 user_id: uuid.UUID,
151 blocked_reasons: list[str],
152) -> None:
153 """Handle organization deletion request that requires support review."""
154 async with AsyncSessionMaker() as session:
155 repository = OrganizationRepository.from_session(session)
156 organization = await repository.get_by_id(organization_id)
157 if organization is None:
158 raise OrganizationDoesNotExist(organization_id)
160 user_repository = UserRepository.from_session(session)
161 user = await user_repository.get_by_id(user_id)
162 if user is None:
163 raise UserDoesNotExist(user_id)
165 # Create Plain ticket for support handling
166 await plain_service.create_organization_deletion_thread(
167 session, organization, user, blocked_reasons
168 )