Api reference
components.clinic.public.business_logic ¶
gdpr_compliance ¶
HEALTH_SERVICES_DELETION_CONTRACT
module-attribute
¶
HEALTH_SERVICES_DELETION_CONTRACT = DeletionContract(
producer=health_services,
owns=frozenset(
{
medical_conversation_part,
therapist_booking_session,
}
),
reads_as_discriminator=frozenset(),
)
delete_member_data ¶
Delete all clinic data related to a specific member.
Source code in components/clinic/public/business_logic/gdpr_compliance.py
get_profiles_ready_for_deletion ¶
Report Clinic's GDPR eligibility per the BucketEligibilityResult contract. We use global profile IDs, because user IDs are not unique between apps (FR/ES/BE)
in the clinic, we define a member as "ready for deletion" in https://github.com/alan-eu/Topics/discussions/31845?sort=old#discussioncomment-15755603 ⧉
The summary of the properties we are interested in: +----------------------------+-----------------------------------------------+-----------------------------------------------+ | Entity Name | Properties Checked | Rationale for Properties Checked | +============================+===============================================+===============================================+ | Medical Conversations | created_at (time of last medical conversation | The user sending a message clearly denotes an | | | part sent by user) | intentional interaction with our health | | | | services. | +----------------------------+-----------------------------------------------+-----------------------------------------------+ | Therapist Booking Sessions | ends_at (time of most recent therapist booking | A session is a clear intention of interaction | | | session where the user was the attending member | with our health services; the time it ends is | | | (clinic_user field)) | when that interaction ended. Sometimes followed| | | | by a medical conversation (covered above). | +----------------------------+-----------------------------------------------+-----------------------------------------------+ | Health Programs | last_updated_at (time of most recent health | This property is updated with any interaction | | | program progress created by the user; denoted by| with a health program. Some interactions may | | | app_id and user_id) | not truly be “program” interactions (e.g., a | | | | rating), but we don’t have a better way to | | | | differentiate between actions, and this catches| | | | them all. | +----------------------------+-----------------------------------------------+-----------------------------------------------+
Source code in components/clinic/public/business_logic/gdpr_compliance.py
84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 | |
get_subset_of_clinic_user_ids_with_recent_health_program_interactions ¶
get_subset_of_clinic_user_ids_with_recent_health_program_interactions(
feature_users, feature_user_key_to_clinic_user_id
)
Return the subset of clinic user IDs whose corresponding feature users have had health program interactions in the last CLINIC_RETENTION_PERIOD_IN_YEARS years.
Checks the last_updated_at time of the most recent health program progress for each feature user, then maps active feature users back to clinic user IDs.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
feature_users
|
list[FeatureUser]
|
List of feature users to check |
required |
feature_user_key_to_clinic_user_id
|
dict[tuple[str, str], UUID]
|
Mapping of (app_id, app_user_id) to clinic user ID |
required |
Source code in components/clinic/public/business_logic/gdpr_compliance.py
get_subset_of_clinic_user_ids_with_recent_medical_conversation_interactions ¶
Return clinic user IDs that had medical conversation interactions within retention period.
Uses batched IN clauses to avoid overwhelming PostgreSQL with large queries.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
clinic_user_ids
|
list[UUID]
|
List of clinic user IDs to check |
required |
Source code in components/clinic/public/business_logic/gdpr_compliance.py
get_subset_of_clinic_user_ids_with_recent_session_interactions ¶
Return clinic user IDs that had therapist booking sessions within retention period.
Uses batched IN clauses to avoid overwhelming PostgreSQL with large queries.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
clinic_user_ids
|
list[UUID]
|
List of clinic user IDs to check |
required |
Source code in components/clinic/public/business_logic/gdpr_compliance.py
get_user_ids_ready_for_deletion ¶
Return inactive feature users ready for deletion, bypassing global profiles.
Same retention logic as get_profiles_ready_for_deletion but operates entirely with app user IDs, avoiding the N+1 profile→feature_user resolution.
Returns:
| Type | Description |
|---|---|
list[FeatureUser]
|
Feature users whose clinic data is ready for deletion. |
Source code in components/clinic/public/business_logic/gdpr_compliance.py
250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 | |
insi_identity ¶
is_identity_verified_for_user ¶
Check if the user's identity is verified.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
app_user
|
FeatureUser
|
The application user whose identity is to be checked. |
required |
Returns: bool: True if the user's identity is verified, False otherwise.
Source code in components/clinic/public/business_logic/insi_identity.py
medical_conversation ¶
get_first_medical_conversation_for_member ¶
Retrieves the first medical conversation for a given member.
This function acts as a proxy to the internal business logic function
get_first_medical_conversation_for_member to fetch the first medical
conversation associated with the provided app_user.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
app_user
|
FeatureUser
|
The user for whom the first medical conversation is to be retrieved. |
required |
Returns:
| Type | Description |
|---|---|
Optional[MedicalConversation]
|
Optional[MedicalConversation]: The first medical conversation for the given member, or None if no conversation is found. |
Source code in components/clinic/public/business_logic/medical_conversation.py
mark_conversation_as_deleted ¶
Source code in components/clinic/public/business_logic/medical_conversation.py
therapist_booking_session ¶
get_past_sessions_that_need_invoice_generation_count_for_user ¶
Counts the number of past reimbursed therapist booking sessions waiting for invoicing for a specific user and session type. sessions that are waiting for invoicing are those that are not refunded, are confirmed, and have a valid credit. They ended between now and 2 days ago. (default_delay_in_hours is 48)
Arguments: feature_user (FeatureUser): The user for whom upcoming sessions need to be counted. session_type (TherapistBookingSessionType): The type of therapist session to filter by.
Returns: int: The count of done reimbursed therapist booking sessions matching the provided user and session type.
Source code in components/clinic/public/business_logic/therapist_booking_session.py
get_upcoming_reimbursed_therapist_booking_session_count_for_user ¶
Counts the number of upcoming reimbursed therapist booking sessions for a specific user and session type. Only counts sessions that are not refunded, are confirmed, have a valid credit, and are scheduled for a future date.
Arguments: feature_user (FeatureUser): The user for whom upcoming sessions need to be counted. session_type (TherapistBookingSessionType): The type of therapist session to filter by.
Returns: int: The count of upcoming reimbursed therapist booking sessions matching the provided user and session type.
Source code in components/clinic/public/business_logic/therapist_booking_session.py
user ¶
app_user_has_upcoming_availability_for_alan_consultations ¶
app_user_has_upcoming_availability_for_alan_consultations(
member_app_user,
locale=None,
availability_threshold_in_hours=24,
medical_admins_ids=None,
)
Returns true if the given member is able to book an Alan consultation within the following upcoming parameters: - next availability is within the next availability threshold in hours (24 hours by default)
Source code in components/clinic/public/business_logic/user.py
get_clinic_user_for_french_user ¶
Source code in components/clinic/public/business_logic/user.py
get_medical_admin_for_user ¶
Source code in components/clinic/public/business_logic/user.py
has_app_user_access_to_orientation ¶
Source code in components/clinic/public/business_logic/user.py
update_clinic_consent_for_user ¶
Update the clinic user consent
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
actor_user
|
FeatureUser
|
The actor feature user |
required |
has_given_consent
|
bool
|
The user has given his consent |
required |
delete_existing_data
|
Optional[bool]
|
Has to delete existing data status |
True
|
Source code in components/clinic/public/business_logic/user.py
validate_child_dependent ¶
validate_child_dependent_and_get_feature_user ¶
Validate that a child is a dependent of the current user and return a FeatureUser for the child.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
parent_user
|
FeatureUser
|
The current user (parent) |
required |
child_app_user_id
|
str
|
The ID of the child to validate |
required |
Source code in components/clinic/public/business_logic/validate_child_dependent.py
components.clinic.public.commands ¶
app_group ¶
register_clinic_commands ¶
Source code in components/clinic/public/commands/app_group.py
billing_assurance_maladie ¶
setup_test_data_for_billing_assurance_maladie ¶
setup_test_data_for_billing_assurance_maladie(
medical_admin_id,
operator_email,
password,
sessions_per_user,
dry_run,
)
Seed members + Intellio patients + past consultations to test Assurance Maladie billing.
Local (dev) and staging only (live Intellio Editeurs sandbox). Idempotent. See the business-logic module for the full flow.
Source code in components/clinic/public/commands/billing_assurance_maladie.py
booking ¶
notifications ¶
notify_all_scheduled_messages ¶
Source code in components/clinic/public/commands/booking/notifications.py
sessions ¶
cancel_all_future_sessions_for_medical_admin_as_medical_admin ¶
Cancels all future sessions for a given medical admin as if the same medical admin is cancelling them themselves. This is useful when a medical admin is departing Alan, and we want all the members with booked sessions to receive the appropriate notifications for the cancellation.
Source code in components/clinic/public/commands/booking/sessions.py
delete_test_sessions ¶
Source code in components/clinic/public/commands/booking/sessions.py
expire_booking_invites ¶
Source code in components/clinic/public/commands/booking/sessions.py
generate_past_sessions_invoice ¶
Source code in components/clinic/public/commands/booking/sessions.py
generate_sessions_invoices ¶
Source code in components/clinic/public/commands/booking/sessions.py
push_metadata_to_doctor_ai_for_recent_ended_sessions_booked_from_mo ¶
Push the metadata of recent ended sessions (<48 hours ago) booked from Mo to Doctor AI.
Source code in components/clinic/public/commands/booking/sessions.py
refund_payment_not_linked_to_session ¶
Source code in components/clinic/public/commands/booking/sessions.py
therapists ¶
update_cached_next_availability ¶
Source code in components/clinic/public/commands/booking/therapists.py
clinic_invoices ¶
generate_fake_clinic_invoices ¶
Source code in components/clinic/public/commands/clinic_invoices.py
sync_clinic_monthly_invoice_lines_from_turing ¶
Source code in components/clinic/public/commands/clinic_invoices.py
end_to_end_test ¶
gdpr_compliance ¶
get_user_ids_ready_for_deletion ¶
Return feature users whose clinic data is ready for GDPR deletion.
Source code in components/clinic/public/commands/gdpr_compliance.py
medical_admins ¶
backfill_medical_admin_synced_ans_data ¶
This temporary command creates ANS data for medical admins that have a medical_identifier but don't have associated ANS data yet.
TODO: @sarah.louahem Remove this command once run in prod - we now create ANS data automatically.
Source code in components/clinic/public/commands/medical_admins.py
create_medical_admins ¶
Truncate and recreate every MedicalAdmin from the local dataset.
Source code in components/clinic/public/commands/medical_admins.py
create_medical_chat_bot ¶
Source code in components/clinic/public/commands/medical_admins.py
set_offline_inactive_medical_admin ¶
Source code in components/clinic/public/commands/medical_admins.py
sync_medical_admin_ans_data ¶
Sync medical admin ANS data with the ANS FHIR API.
This command checks all stored medical admin ANS data and updates records that have older versions than what's available in the ANS FHIR API.
Source code in components/clinic/public/commands/medical_admins.py
medical_admins_dataset ¶
MedicalAdminData
dataclass
¶
MedicalAdminData(
first_name,
last_name,
onboarding_status,
accessible_conversation_specialties,
apps_displayed_in,
has_access_to_app_ids,
access_types,
specialty,
country,
languages,
description=None,
experiences=None,
answers_to_proactive_conversation_topics=None,
prod_id=None,
dato_id=None,
first_message_body=None,
avatar=None,
clinic_role=None,
)
answers_to_proactive_conversation_topics
class-attribute
instance-attribute
¶
MedicalAdminExperienceData
dataclass
¶
RAW_MEDICAL_ADMINS
module-attribute
¶
RAW_MEDICAL_ADMINS = [
MedicalAdminData(
first_name="Augustin",
last_name="Beaucote",
specialty=GENERAL_PRACTITIONER,
avatar="http://eu.alan.uploads.s3.eu-central-1.amazonaws.com/manual_upload/9d775ac221a84812a3cc18bae63b7766_doctor_beaucote_headshot.jpg",
description="Je suis médecin généraliste et immunologue et j’ai rejoint Alan début 2020 pour développer les services de santé.\nJ’ai fait mes études en région parisienne à la faculté du Kremlin Bicêtre avant de rejoindre la région de Montpellier en 2015, où j’ai été interne pendant 3 ans.\nJ’ai choisi de me spécialiser en immunologie et j’ai fait des remplacements en cabinet de médecine générale et aux urgences adultes et pédiatriques.",
experiences=[
MedicalAdminExperienceData(
title="Immunothérapies ciblées des maladies",
subtitle="Faculté de médecine de Montpellier",
index=3,
),
MedicalAdminExperienceData(
title="Master 2, Génome et différenciation cellulaire - Hématopoïèse",
subtitle="Université Paris Diderot et IGMM",
index=2,
),
MedicalAdminExperienceData(
title="DESC 2 d'immunologie clinique",
subtitle="Université de Montpellier",
index=1,
),
MedicalAdminExperienceData(
title="Doctorat en Médecine Générale",
subtitle="Faculté de médecine de Montpellier",
index=0,
),
],
apps_displayed_in=[alan_insurance],
accessible_conversation_specialties=[
GENERAL_MEDICINE,
PEDIATRICS,
CHILDCARE,
DIETETICS,
DERMATOLOGY,
PHYSIOTHERAPY,
GYNECOLOGY,
],
has_access_to_app_ids=[ALAN_FR],
access_types=[CHAT],
answers_to_proactive_conversation_topics=[
SLEEP,
STRESS,
],
country=france,
languages=[french, english],
onboarding_status=COMPLETED,
),
MedicalAdminData(
first_name="Clara",
last_name="Poncelet",
specialty=MIDWIFE,
first_message_body="Je suis Clara, je suis sage-femme, j’exerce en Suisse dans une structure hospitalière et en France comme sage-femme libérale. Je suis aussi maman d’une petite fille depuis fin 2020 !",
avatar="http://eu.alan.uploads.s3.eu-central-1.amazonaws.com/manual_upload/3fec273f3a484072b50029a7ee7a0440_clara_poncelet.jpg",
description="Je suis sage-femme depuis 2015 et heureuse nouvelle arrivée chez Alan.\nCes cinq dernières années sont marquées par mes voyages régionaux car j’ai choisi d’exercer dans différents grands centres hospitaliers afin d’enrichir ma formation.\nDepuis 2019 je fais des remplacements en libéral pour un accompagnement semi-global et j’exerce en Suisse dans une structure hospitalière. Je me forme dans les domaines ouverts aux sages-femmes dès que j’en ai l’occasion pour adapter mes pratiques.\nJe suis maman d’une petite fille depuis fin 2020.",
experiences=[
MedicalAdminExperienceData(
title="Diplôme de sage-femme en 2015",
subtitle="Université de Bourgogne à Dijon",
index=0,
)
],
accessible_conversation_specialties=[GYNECOLOGY],
apps_displayed_in=[alan_insurance],
has_access_to_app_ids=[ALAN_FR],
access_types=[CHAT],
country=france,
languages=[french, english],
onboarding_status=COMPLETED,
),
MedicalAdminData(
first_name="Barbara",
last_name="Dezileaux",
specialty=GENERAL_PRACTITIONER,
avatar="http://eu.alan.uploads.s3.eu-central-1.amazonaws.com/manual_upload/8b28ef4ffbea476c93565ad2d3cd4eb7_doctor_dezileaux_headshot.jpg",
description="Médecin généraliste, j’ai choisi de rejoindre les équipes d’Alan car j’apprécie leur démarche d’aide et de conseil envers leurs utilisateurs.\nJe suis diplômée depuis 2015, après avoir réalisé mes études à Bordeaux puis mon internat à Lille.\nJ’exerce depuis en cabinet libéral et en clinique dans différentes régions de France.\nJ’ai à cœur de me former en continu, en particulier sur les sujets de la maternité et de la périnatalité qui me passionnent.",
experiences=[
MedicalAdminExperienceData(
title="Doctorat en Médecine Générale",
subtitle="Faculté de médecine de Lille",
index=0,
)
],
accessible_conversation_specialties=[
GENERAL_MEDICINE,
PEDIATRICS,
CHILDCARE,
DIETETICS,
DERMATOLOGY,
PHYSIOTHERAPY,
GYNECOLOGY,
],
apps_displayed_in=[alan_insurance],
access_types=[CHAT],
has_access_to_app_ids=[ALAN_FR],
country=france,
languages=[french, english],
onboarding_status=COMPLETED,
),
MedicalAdminData(
first_name="Marion",
last_name="Cosson",
specialty=GENERAL_PRACTITIONER,
clinic_role=CLINIC_ADMIN,
apps_displayed_in=[],
has_access_to_app_ids=[ALAN_FR],
accessible_conversation_specialties=[
GENERAL_MEDICINE,
PEDIATRICS,
CHILDCARE,
DIETETICS,
DERMATOLOGY,
PHYSIOTHERAPY,
GYNECOLOGY,
],
access_types=[CHAT],
country=france,
languages=[french, english, spanish],
onboarding_status=COMPLETED,
),
MedicalAdminData(
first_name="Pauline",
last_name="Lotte",
specialty=CHILDCARE_NURSE,
avatar="http://eu.alan.uploads.s3.eu-central-1.amazonaws.com/manual_upload/9a0297eaf92f4f88a763c9fe3c954983_pauline-lotte.jpg",
description="Je suis infirmière puéricultrice depuis 2007 et installée en libéral depuis 2018. J'interviens chez Alan en tant que spécialiste du sommeil.\nAprès 10 ans de pratique en hôpital pédiatrique et 1 an en direction de crèche, j'ai choisi de travailler en indépendante pour accompagner les parents au plus près de leur quotidien avec bébé.\nJe me suis spécialisée dans plusieurs domaines, et plus spécialement le sommeil du tout-petit.\nJe suis maman d'une petite fille depuis 2019.",
experiences=[
MedicalAdminExperienceData(
title="2006 - Diplôme d'Etat d'Infirmière",
subtitle="IFSI de Reims",
index=10,
),
MedicalAdminExperienceData(
title="2007 - Diplôme d'Etat de Puéricultrice",
subtitle="Ecole de puéricultrice de Reims",
index=9,
),
MedicalAdminExperienceData(
title="2013 - Diplôme Universitaire de prise en charge de la douleur de l'enfant",
subtitle=None,
index=8,
),
MedicalAdminExperienceData(
title="2019 - Instructrice Dunstan Baby Langage",
subtitle="Dunstan Baby Langage France",
index=7,
),
MedicalAdminExperienceData(
title="2019 - Educateur Montessori",
subtitle="Enfance Positive",
index=6,
),
MedicalAdminExperienceData(
title="2020 - Animateur de Signes Associés à la Parole",
subtitle="Eveil et Signes",
index=5,
),
MedicalAdminExperienceData(
title="2020 - Formation Nutrition pédiatrique",
subtitle="EPM nutrition",
index=4,
),
MedicalAdminExperienceData(
title="2020 - Formation au sommeil du bébé de 0 à 5 ans",
subtitle="M. Bilodeau",
index=3,
),
MedicalAdminExperienceData(
title="2020 - Comprendre et accompagner le sommeil de l'enfant",
subtitle="Mandy Roman",
index=2,
),
MedicalAdminExperienceData(
title="2021 - Formation consultation du sommeil, alimentation et rythmes de 0 à 6 ans en avril 2021",
subtitle="Prosom",
index=1,
),
MedicalAdminExperienceData(
title="2021 - Formation sommeil du tout petit et accompagnement parental",
subtitle="Ingrid Bayot",
index=0,
),
],
apps_displayed_in=[alan_insurance],
accessible_conversation_specialties=[CHILDCARE],
has_access_to_app_ids=[ALAN_FR],
access_types=[CHAT],
country=france,
languages=[dutch, english],
onboarding_status=COMPLETED,
),
MedicalAdminData(
first_name="Margaux",
last_name="Degen",
specialty=PSYCHOLOGIST,
avatar="http://eu.alan.uploads.s3.eu-central-1.amazonaws.com/manual_upload/9d775ac221a84812a3cc18bae63b7766_doctor_beaucote_headshot.jpg",
description="Je suis psychologue clinicienne et j'ai rejoint Alan pour aider les membres à prendre soin de leur santé mentale au quotidien.\nFormée à Paris et spécialisée en thérapies cognitivo-comportementales (TCC), j'accompagne mes patients sur les sujets de stress, sommeil, anxiété et bien-être au travail.\nJe propose des séances d'orientation et un suivi thérapeutique en visio.",
first_message_body="Bonjour, je suis Margaux, psychologue chez Alan. Dites-moi ce qui vous amène et on regardera ensemble comment je peux vous aider.",
experiences=[
MedicalAdminExperienceData(
title="DU Thérapies cognitivo-comportementales",
subtitle="Université Paris Descartes",
index=2,
),
MedicalAdminExperienceData(
title="Master 2 Psychologie clinique",
subtitle="Université Paris Cité",
index=1,
),
MedicalAdminExperienceData(
title="Licence de Psychologie",
subtitle="Université Paris Nanterre",
index=0,
),
],
accessible_conversation_specialties=[
PSYCHOLOGY,
THERAPY,
],
clinic_role=CLINIC_ADMIN,
apps_displayed_in=[alan_insurance],
has_access_to_app_ids=[ALAN_FR],
access_types=[CHAT, VIDEO],
answers_to_proactive_conversation_topics=[
MENTAL_HEALTH,
MENTAL_WELLBEING,
STRESS,
SLEEP,
],
country=france,
languages=[french, english],
onboarding_status=COMPLETED,
dato_id="148187763",
),
MedicalAdminData(
first_name="Emile",
last_name="Montrois",
specialty=PSYCHOLOGIST,
accessible_conversation_specialties=[
PSYCHOLOGY,
THERAPY,
],
access_types=[VIDEO],
apps_displayed_in=[alan_insurance],
has_access_to_app_ids=[ALAN_FR],
country=france,
languages=[french, english],
onboarding_status=COMPLETED,
dato_id="148184344",
),
MedicalAdminData(
first_name="Florian",
last_name="Ghiazza",
specialty=PHYSIOTHERAPIST,
accessible_conversation_specialties=[
PHYSIOTHERAPY,
BACK_PAIN,
EMERGENCY_PHYSIOTHERAPY,
],
access_types=[CHAT, VIDEO],
apps_displayed_in=[alan_insurance],
has_access_to_app_ids=[ALAN_FR],
country=france,
languages=[french, english],
onboarding_status=COMPLETED,
dato_id="OTiPilFASx-0yBrP2HtLrQ",
prod_id="b65af61b-c455-4c5f-a678-164d165d1f45",
),
MedicalAdminData(
first_name="Diego",
last_name="Ferral-Toro",
specialty=PHYSIOTHERAPIST,
accessible_conversation_specialties=[
PHYSIOTHERAPY,
BACK_PAIN,
EMERGENCY_PHYSIOTHERAPY,
],
access_types=[CHAT, VIDEO],
apps_displayed_in=[alan_insurance],
has_access_to_app_ids=[ALAN_ES],
country=spain,
languages=[spanish, english],
onboarding_status=COMPLETED,
dato_id="XSdL4ehETM-owb_20xEmhg",
prod_id="18eb92d9-3271-4286-91dc-a4e1a8ac5de2",
),
MedicalAdminData(
first_name="Diane",
last_name="Coomans",
specialty=PHYSIOTHERAPIST,
accessible_conversation_specialties=[
PHYSIOTHERAPY,
BACK_PAIN,
EMERGENCY_PHYSIOTHERAPY,
],
access_types=[CHAT, VIDEO],
apps_displayed_in=[alan_insurance],
has_access_to_app_ids=[ALAN_BE],
country=belgium,
languages=[dutch, english],
onboarding_status=COMPLETED,
dato_id="OXi_GJK7TziPCWlkkFWQCw",
prod_id="59ce8029-2aa1-4ff5-81b1-25156ebcf5f1",
),
MedicalAdminData(
first_name="Robin",
last_name="Vervaeke",
specialty=PHYSIOTHERAPIST,
accessible_conversation_specialties=[
PHYSIOTHERAPY,
BACK_PAIN,
EMERGENCY_PHYSIOTHERAPY,
],
access_types=[CHAT, VIDEO],
apps_displayed_in=[alan_insurance],
has_access_to_app_ids=[ALAN_FR, ALAN_ES, ALAN_BE],
country=france,
languages=[french, english],
onboarding_status=COMPLETED,
dato_id="R03pRAdAR1-Hq7_B4A0pAw",
prod_id="59584887-47a7-464d-86c7-4f1a15299eaa",
),
MedicalAdminData(
first_name="Olivier",
last_name="Delarras",
specialty=GENERAL_PRACTITIONER,
accessible_conversation_specialties=[
GENERAL_MEDICINE,
CONSULTATION,
PEDIATRICS,
CHILDCARE,
DIETETICS,
DERMATOLOGY,
PHYSIOTHERAPY,
GYNECOLOGY,
],
access_types=[CHAT, VIDEO],
apps_displayed_in=[alan_insurance],
has_access_to_app_ids=[ALAN_FR, ALAN_ES, ALAN_BE],
country=france,
languages=[french, english],
onboarding_status=COMPLETED,
dato_id="RunHY9MqRXOl4ti7fuhoTw",
prod_id="3c7c88e9-0794-4d23-9a32-002e75ca1f60",
),
MedicalAdminData(
first_name="Sophie",
last_name="Vandenberghe",
specialty=PSYCHOLOGIST,
accessible_conversation_specialties=[
PSYCHOLOGY,
THERAPY,
],
access_types=[CHAT, VIDEO],
apps_displayed_in=[alan_insurance],
has_access_to_app_ids=[ALAN_BE],
country=belgium,
languages=[dutch, french, english],
onboarding_status=COMPLETED,
dato_id="141232141",
),
MedicalAdminData(
first_name="Lucas",
last_name="De Smet",
specialty=GENERAL_PRACTITIONER,
accessible_conversation_specialties=[
GENERAL_MEDICINE
],
access_types=[CHAT],
apps_displayed_in=[alan_insurance],
has_access_to_app_ids=[ALAN_BE],
country=belgium,
languages=[dutch, french, english],
onboarding_status=COMPLETED,
dato_id="142690430",
),
MedicalAdminData(
first_name="Lucía",
last_name="Martínez",
specialty=PSYCHOLOGIST,
accessible_conversation_specialties=[
PSYCHOLOGY,
THERAPY,
],
access_types=[CHAT, VIDEO],
apps_displayed_in=[alan_insurance],
has_access_to_app_ids=[ALAN_ES],
country=spain,
languages=[spanish, english],
onboarding_status=COMPLETED,
dato_id="121815108",
),
MedicalAdminData(
first_name="Maxime",
last_name="Tremblay",
specialty=PSYCHOLOGIST,
accessible_conversation_specialties=[
PSYCHOLOGY,
THERAPY,
],
access_types=[CHAT, VIDEO],
apps_displayed_in=[alan_insurance],
has_access_to_app_ids=[ALAN_CA],
country=canada,
languages=[french, english],
onboarding_status=COMPLETED,
dato_id="122092169",
),
MedicalAdminData(
first_name="Patrick",
last_name="Pediatre",
specialty=PEDIATRICIAN,
accessible_conversation_specialties=[
PEDIATRICS,
CHILDCARE,
],
apps_displayed_in=[alan_insurance],
has_access_to_app_ids=[ALAN_FR],
access_types=[CHAT],
country=france,
languages=[french, english],
onboarding_status=COMPLETED,
dato_id="149004586",
),
MedicalAdminData(
first_name="Delphine",
last_name="Dietetique",
specialty=DIETITIAN,
accessible_conversation_specialties=[DIETETICS],
apps_displayed_in=[alan_insurance],
has_access_to_app_ids=[ALAN_FR],
access_types=[CHAT],
answers_to_proactive_conversation_topics=[
NUTRITION
],
country=france,
languages=[french, english],
onboarding_status=COMPLETED,
dato_id="122579720",
),
MedicalAdminData(
first_name="Damien",
last_name="Derma",
specialty=DERMATOLOGIST,
accessible_conversation_specialties=[DERMATOLOGY],
apps_displayed_in=[alan_insurance],
has_access_to_app_ids=[ALAN_FR],
access_types=[CHAT],
country=france,
languages=[french, english],
onboarding_status=COMPLETED,
dato_id="141710814",
),
MedicalAdminData(
first_name="Gisèle",
last_name="Gyneco",
specialty=GYNECOLOGIST,
accessible_conversation_specialties=[GYNECOLOGY],
apps_displayed_in=[alan_insurance],
has_access_to_app_ids=[ALAN_FR],
access_types=[CHAT],
country=france,
languages=[french, english],
onboarding_status=COMPLETED,
dato_id="147383847",
),
MedicalAdminData(
first_name="Nora",
last_name="Infirmiere",
specialty=NURSE,
accessible_conversation_specialties=[
GENERAL_MEDICINE
],
apps_displayed_in=[alan_insurance],
has_access_to_app_ids=[ALAN_FR],
access_types=[CHAT],
country=france,
languages=[french, english],
onboarding_status=COMPLETED,
dato_id="81587618",
),
MedicalAdminData(
first_name="Charles",
last_name="Coach",
specialty=COACH,
accessible_conversation_specialties=[],
apps_displayed_in=[alan_insurance],
has_access_to_app_ids=[ALAN_FR],
access_types=[VIDEO],
country=france,
languages=[french, english],
onboarding_status=COMPLETED,
dato_id="109915791",
),
MedicalAdminData(
first_name="Odile",
last_name="Opticien",
specialty=OPTICIAN,
accessible_conversation_specialties=[OPTIC],
apps_displayed_in=[alan_insurance],
has_access_to_app_ids=[ALAN_FR],
access_types=[CHAT],
country=france,
languages=[french, english],
onboarding_status=COMPLETED,
dato_id="141518431",
),
MedicalAdminData(
first_name="Sarah",
last_name="Sexologue",
specialty=SEXOLOGIST,
accessible_conversation_specialties=[SEXOLOGY],
apps_displayed_in=[alan_insurance],
has_access_to_app_ids=[ALAN_FR],
access_types=[CHAT],
country=france,
languages=[french, english],
onboarding_status=COMPLETED,
dato_id="120171054",
),
MedicalAdminData(
first_name="Olivia",
last_name="OpsDemo",
specialty=GENERAL_PRACTITIONER,
accessible_conversation_specialties=[
GENERAL_MEDICINE
],
apps_displayed_in=[alan_insurance],
has_access_to_app_ids=[ALAN_FR],
access_types=[CHAT],
country=france,
languages=[french, english],
onboarding_status=COMPLETED,
dato_id="122939312",
clinic_role=CLINIC_OPS,
),
]
medical_conversation ¶
close_old_answered_medical_conversations ¶
Source code in components/clinic/public/commands/medical_conversation.py
close_old_therapy_session_conversations ¶
Mark inactive therapy/orientation conversations as closable for inactivity.
Source code in components/clinic/public/commands/medical_conversation.py
create_fake_conversations ¶
Create fake conversations for testing purposes only - count: number of fake conversations to create - specialty: medical specialty of the fake conversations to create - app: app of the fake conversations to create - is_closed: whether the fake conversations should be closed or not
Source code in components/clinic/public/commands/medical_conversation.py
erase_all_conversation_data ¶
Source code in components/clinic/public/commands/medical_conversation.py
mark_conversations_for_retention_deletion ¶
Set deleted_at on BE GP video-consultation conversations (5-year retention).
Source code in components/clinic/public/commands/medical_conversation.py
notify_stale_consultation_conversations ¶
Notify members in stale consultation conversations (36h without HP reply post-TLC).
Source code in components/clinic/public/commands/medical_conversation.py
resolve_closable_conversations ¶
Source code in components/clinic/public/commands/medical_conversation.py
send_pending_proactive_conversations ¶
Source code in components/clinic/public/commands/medical_conversation.py
update_active_after_for_conversations ¶
This command will update the active_after field on conversations. It's helpful for: - computing it the first time - resetting it from time to time, if we have a bug and we want to fix the values
Source code in components/clinic/public/commands/medical_conversation.py
setup ¶
flask clinic setup — bootstrap the local Alan Clinic environment.
This file is the orchestrator: it owns the Click command and the order of
phases. The phase functions themselves live in the setup_steps/ package
as siblings:
setup_steps.helpers— shared utilities (run_step, email parsing, User lookup)setup_steps.printer— pretty progress blocks (success / action / error / info)setup_steps.preflight— Click-callback pre-flight checks (e.g. Postgres up)setup_steps.country_config—CountryConfigregistry (used by upstack PRs)setup_steps.init_steps— encryption, dataset admins, chat bot, booking cachesetup_steps.preconfigure_booking_hp— assign mock dato_id to the local Clinic Admin (booking HP)setup_steps.test_data_generator— hand off protagonist creation to the TDG + poll for the usersetup_steps.fake_conversations— optional fake convs (volume test)setup_steps.post_setup— final manual-steps checklist
setup ¶
setup(
country,
password,
alaner_email,
dato_id_mock,
company_name,
colleague_count,
with_therapist_oauth,
with_protagonist_seeds,
with_fake_conversations,
fake_conversations_count,
yes,
)
Bootstrap the local Alan Clinic environment.
Wipes and re-creates encryption keys, the medical admin dataset (with one admin per specialty + 1 CLINIC_OPS, plus-tagged off your AWS IAM identity), the medical chat bot, the booking next-availability cache, and pre-configures the Clinic Admin as the bookable therapy HP. Optionally generates fake conversations.
Source code in components/clinic/public/commands/setup.py
58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 | |
setup_steps ¶
country_config ¶
Country abstraction for flask clinic setup.
Every piece of country-specific information lives here, so the orchestrator
and the phase steps stay country-agnostic. Each phase function takes a
CountryConfig and reads what it needs from it; the orchestrator picks
the right config from the --country flag.
Adding a country = adding one more CountryConfig instance and registering
it in COUNTRIES. The Click --country flag's Choice is auto-derived
from this dict, so newly registered countries appear in --help
immediately.
CountryConfig
dataclass
¶
CountryConfig(
name,
app_name,
default_timezone,
medical_admin_country,
default_admin_languages,
tdg_base_url="",
marmot_base_url="",
tdg_protagonist_yaml_template="",
)
Everything country-specific the setup needs, in one declarative block.
Medical admins always live behind the FR User model (doctor-chat runs on
fr_api), so there is no per-country override for the User class or the
create_or_assign helper. The fields here are what genuinely varies
between countries: app id, timezone, languages, and the TDG URLs.
render_tdg_protagonist_yaml ¶
Render the TDG YAML with all caller-supplied substitutions filled in.
Source code in components/clinic/public/commands/setup_steps/country_config.py
tdg_protagonist_yaml_template
class-attribute
instance-attribute
¶
FR
module-attribute
¶
FR = CountryConfig(
name="fr",
app_name=ALAN_FR,
default_timezone="Europe/Paris",
medical_admin_country=france,
default_admin_languages=[french, english],
tdg_base_url="http://localhost:8001",
marmot_base_url="http://localhost:4001/marmot/fr/alan-clinic",
tdg_protagonist_yaml_template=dedent(
' - ref: "alan_demo"\n company:\n name: "{company_name}"\n - ref: "protagonist_enrollment"\n enrollment:\n contract: "ref:alan_demo.contracts[0]"\n user__email: "{protagonist_email}"\n user__first_name: "Demo"\n user__last_name: "Member"\n - ref: "colleague_employments"\n count: {colleague_count}\n employment:\n company: "ref:alan_demo"\n '
),
)
fake_conversations ¶
Optional conversation-domain step: bulk-seed random fake medical conversations
(--with-fake-conversations).
step_create_fake_conversations ¶
Generate count random fake medical conversations across existing members (volume test).
Source code in components/clinic/public/commands/setup_steps/fake_conversations.py
helpers ¶
Shared helpers for the flask clinic setup command package: step runner,
email parsing, and FR User resolution.
aws_user_email ¶
Return <iam-username>@alan.eu, or empty string if AWS is unavailable.
Source code in components/clinic/public/commands/setup_steps/helpers.py
default_protagonist_email ¶
Best-effort default protagonist email: AWS IAM identity + +main.member tag.
Returns empty string if AWS credentials are not available or the IAM identity does not look like a valid email — callers should fall back to a required-flag error in that case.
Source code in components/clinic/public/commands/setup_steps/helpers.py
get_or_create_clinic_user_with_consent ¶
Find the user → return their ClinicUser, creating one with consent if missing.
Raises if the underlying FR User doesn't exist yet (the caller is expected
to have run the TDG step first). Stamps consent_given_at so the privacy
toggle is ON out-of-the-box. The pivot row uses AppName.ALAN_FR —
medical admins (and the local clinic protagonist) always live behind the
FR User model.
Source code in components/clinic/public/commands/setup_steps/helpers.py
get_user_by_email ¶
Resolve a FR User by email.
Medical admins (and the local clinic protagonist) always sit behind the
FR User model — doctor-chat runs on fr_api. We expire_all() before
querying so subsequent polling loops see rows just inserted by an
out-of-process operation (e.g. the TDG).
Source code in components/clinic/public/commands/setup_steps/helpers.py
open_browser ¶
Try to open url in the default browser. Returns False on failure.
run_step ¶
Run one labelled phase of the setup; failures are reported but don't abort.
Source code in components/clinic/public/commands/setup_steps/helpers.py
split_email_local_domain ¶
Split local+tag@domain into (local, domain) — drops any pre-existing +tag.
Source code in components/clinic/public/commands/setup_steps/helpers.py
init_steps ¶
Phase: bootstrap. Always-run baseline steps — encryption keys, the medical admin dataset, the chat bot, and the booking next-availability cache.
step_create_medical_admins ¶
Truncate + re-create every medical admin from the local dataset.
Each admin gets a plus-tagged login email derived from alaner_email
(<local>+<lastname>@<domain>) so mails land in the developer's inbox.
Medical admins always live behind the FR User model — doctor-chat runs
on fr_api.
Source code in components/clinic/public/commands/setup_steps/init_steps.py
step_create_medical_chat_bot ¶
Idempotently create the system clinic_user backing the medical chat bot.
Source code in components/clinic/public/commands/setup_steps/init_steps.py
step_encryption ¶
Regenerate the encryption keys + doctor group from scratch.
step_update_booking_cache ¶
Refresh cached_next_availability on every TherapistBookingTherapist.
Source code in components/clinic/public/commands/setup_steps/init_steps.py
post_setup ¶
Phase: end-of-run checklist. Prints the manual steps still required after
flask clinic setup finishes — RQ worker, mailer, frontend, marmot HP
booking config, doctor-chat login, and the member-app credentials.
print_manual_post_setup_checklist ¶
print_manual_post_setup_checklist(
*,
country,
password,
protagonist_email,
alaner_email,
therapist_oauth_done
)
Print the post-setup checklist with concrete credentials per HP and member.
Source code in components/clinic/public/commands/setup_steps/post_setup.py
preconfigure_booking_hp ¶
Phase: pre-configure the local "super-admin" HP for booking flows.
Assigns a mock dato_id to the therapy-capable Clinic Admin so the marmot card hydrates from DatoCMS.
Also exposes a standalone flask clinic preconfigure-booking-hp so the
preconfig can be run on its own — identical surface as the flask clinic setup
phase but reusable from scripts/CI.
preconfigure_booking_hp ¶
Run the booking-HP preconfig step on its own, without the full flask clinic setup.
Source code in components/clinic/public/commands/setup_steps/preconfigure_booking_hp.py
step_preconfigure_booking_for_therapy_hp ¶
Set the therapy Clinic Admin's dato_id so their card is hydrated from DatoCMS.
Therapy-only because it's the only booking flow that needs explicit
activation (TherapistBookingTherapist + Google OAuth). Consultation
(GP) and back_pain (physio) work out-of-the-box against the existing
dataset admins — no preconfig needed.
To complete OAuth on this admin (or any other) afterwards, use
flask clinic setup_therapist_oauth --admin-email <pro_email>.
Targets the first medical admin with clinic_role=CLINIC_ADMIN whose
accessible_conversation_specialties includes THERAPY.
Source code in components/clinic/public/commands/setup_steps/preconfigure_booking_hp.py
preflight ¶
Pre-flight checks applied to the flask clinic setup Click callback BEFORE
the shared audit decorator runs. Lives outside the regular phase steps
(which run via run_step inside the command body) because the wrapping
has to happen at module import time, on setup.callback itself.
require_local_postgres ¶
Wrap the Click callback with a TCP pre-check against local Postgres.
A stopped local DB surfaces as a one-line friendly message with the
exact devbox command. Without this wrapper, the shared audit
decorator flushes a CommandLog row before our body runs and we get
a 200-line SQLAlchemy traceback upstream of anything we could catch
from inside setup().
Source code in components/clinic/public/commands/setup_steps/preflight.py
printer ¶
Pretty-printing helpers shared across setup_steps phases.
All blocks have the same shape: bold colored title → body lines (indented by two spaces) → colored separator line. The semantic functions pick the color so callers don't have to think about it:
print_success → green ("this went well")
print_action → yellow ("you need to do something")
print_error → red ("this failed")
print_info → cyan ("here's a status / task in a guided checklist")
print_action ¶
Bold yellow title + body + yellow = separator. For action-required items.
Source code in components/clinic/public/commands/setup_steps/printer.py
print_error ¶
Bold red title + body + red = separator. For failures.
print_info ¶
Bold cyan title + body + cyan - separator. For neutral checklist items.
print_section_footer ¶
print_section_header ¶
Open a multi-block section: bold title + = separator below.
Source code in components/clinic/public/commands/setup_steps/printer.py
print_success ¶
Bold green title + body + green = separator. For positive outcomes.
seed_user_data ¶
Phase: seed demo conversations + booking sessions for a member user.
Wired into flask clinic setup (the setup derives the user email from your
alaner identity), and exposed standalone as flask clinic seed_user_data
--user-email <email> so you can re-seed any member after the fact.
Prerequisites:
- The user already exists in the DB (run create_protagonist_via_tdg first
or have the TDG run elsewhere).
- The medical admin dataset has been created (flask clinic setup).
The seed graph (GP / physio / therapy + specific MedicalConversationSpecialty
values) is FR-centric by design — FR is the only country wired in
CountryConfig today. When BE / ES / CA get added, lift the specialty
specs into CountryConfig.seed_specialty_specs (new field) and have each
step read from there. Single-source-of-truth follows the same pattern as
tdg_protagonist_yaml_template.
seed_user_data ¶
Seed historical conversations + future teleconsultations for the user.
Source code in components/clinic/public/commands/setup_steps/seed_user_data.py
step_seed_open_teleconsultations ¶
Create future confirmed TherapistBookingSession rows with the therapy admin.
Covers 1 orientation + 2 therapy + 1 consultation so testing each session
type screen has data. created_at / confirmed_at antedated 6h so the
derived chat appears active immediately when the dev logs in.
Skipped silently if the therapy admin has no TherapistBookingTherapist
yet (real Google OAuth not done) — re-run after OAuth to populate them.
Source code in components/clinic/public/commands/setup_steps/seed_user_data.py
step_seed_user_conversations ¶
Create historical conversations for the user, mixed states + specialties.
GP closed (35d), GP open recent, therapy + orientation session (75d, past),
therapy + therapy session (50d, past), physio open recent. The therapy
sessions are skipped if the therapy admin has no TherapistBookingTherapist
yet (i.e. real OAuth not done) — the therapy convs are still created, just
without a linked session.
Source code in components/clinic/public/commands/setup_steps/seed_user_data.py
101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 | |
setup_therapist_oauth ¶
Phase: guide the dev through the Google OAuth + booking activation for an HP.
This is the one step we can't bypass programmatically — marmot's "Activate
booking" wizard opens a Google OAuth flow in the browser, and the dev has
to manually create a calendar named "Alan Therapy" with at least one event
matching the regex in booking/business_logic/google_calendar.py. After
that, refreshing cached_next_availability exposes the HP as bookable in
the member app.
Exposed standalone as flask clinic setup_therapist_oauth and wired into
flask clinic setup via the --with-therapist-oauth flag.
setup_therapist_oauth ¶
Open marmot for the dev to complete Google OAuth + create a calendar slot.
Source code in components/clinic/public/commands/setup_steps/setup_therapist_oauth.py
step_setup_therapist_oauth_interactive ¶
step_setup_therapist_oauth_interactive(
*,
country,
admin_email=None,
timeout_secs=DEFAULT_TIMEOUT_SECS,
poll_interval_secs=DEFAULT_POLL_INTERVAL_SECS,
heartbeat_interval_secs=DEFAULT_HEARTBEAT_INTERVAL_SECS
)
Open marmot, then poll until the HP has a valid cached_next_availability.
The dev finishes booking activation in the browser (Google OAuth, calendar
name "Alan Therapy", at least one event matching the slot regex). The
polling refreshes the cache every poll_interval_secs and exits once a
real availability is found — meaning OAuth is connected AND a slot is
visible in the calendar.
Skipped silently if the country has no marmot_base_url configured.
Source code in components/clinic/public/commands/setup_steps/setup_therapist_oauth.py
64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 | |
test_data_generator ¶
Phase: hand off the protagonist creation to the Test Data Generator (TDG).
The TDG is a web tool (<base>/admin_tools/test_data_generator/new?fixture=...)
that consumes a YAML and runs the matching factories to build the requested
graph (company + employees + protagonist user). We can't drive it
programmatically, but we can:
- Build the country-specific YAML with the protagonist email substituted in.
- base64-encode it and stuff it into the
?fixture=query param so the editor opens pre-filled. - Copy that URL to the dev's clipboard and print it.
- Poll the DB until the protagonist user appears (TDG ran successfully).
- Backfill the
ClinicUserrow +consent_given_atso the privacy toggle is on out-of-the-box.
Also exposes flask clinic generate-tdg-url as a standalone command so the
URL can be re-generated without re-running the full setup (e.g. lost it from
the clipboard).
create_protagonist_via_tdg ¶
Open the TDG pre-filled with the protagonist YAML and wait for the user.
Source code in components/clinic/public/commands/setup_steps/test_data_generator.py
derive_company_name ¶
Derive a demo company name from the protagonist email: <Local Capitalized> Co.
alex.temina+main.member@alan.eu → Alex Temina Co.
Source code in components/clinic/public/commands/setup_steps/test_data_generator.py
derive_protagonist_email ¶
Derive the protagonist email as <local>+main.member@<domain>.
Source code in components/clinic/public/commands/setup_steps/test_data_generator.py
step_create_protagonist_via_tdg ¶
step_create_protagonist_via_tdg(
*,
country,
protagonist_email,
company_name="",
colleague_count=DEFAULT_COLLEAGUE_COUNT,
timeout_secs=DEFAULT_TIMEOUT_SECS,
poll_interval_secs=DEFAULT_POLL_INTERVAL_SECS,
heartbeat_interval_secs=DEFAULT_HEARTBEAT_INTERVAL_SECS
)
Hand off protagonist creation to the TDG and block until the user appears.
Builds the country-specific YAML, opens the TDG editor in the browser
pre-filled with it, and then polls the DB until the dev clicks 'Generate'
and the User shows up. No-ops silently if the user is already in the DB,
so re-running a partial setup doesn't ask you to redo fixtures.
Skipped silently if the country has no tdg_base_url configured. An empty
company_name is auto-derived from protagonist_email (Alex Temina Co).
Source code in components/clinic/public/commands/setup_steps/test_data_generator.py
step_ensure_protagonist_clinic_user ¶
Create the protagonist's ClinicUser if missing, and stamp consent_given_at.
The TDG creates the country User but not the ClinicUser pivot row;
without it, none of the clinic-side flows attach to the protagonist.
TODO alex.temina: migrate this to a clinic_user factory in the TDG so the YAML can
express - clinic_user: {user: "ref:protagonist", consent_given_at: now}
directly, and drop this step. Tracked in the TDG Linear board.
Source code in components/clinic/public/commands/setup_steps/test_data_generator.py
unread_message_reminders ¶
send_medical_conversations_unread_message_reminders ¶
Source code in components/clinic/public/commands/unread_message_reminders.py
user ¶
archive_clinic_user_data ¶
Archive a clinic user's data to S3 without deleting them.
Runs collect → encrypt → upload → mapping in dry-run mode by default. Pass --execute to perform the actual archive.
Usage
flask clinic archive_clinic_user_data
Source code in components/clinic/public/commands/user.py
clear_stripe_customer_ids ¶
Clears all stripe_customer_id values from clinic_user table. Only runs in non-production environments. Usage: flask clinic clear_stripe_customer_ids flask clinic clear_stripe_customer_ids --execute
Source code in components/clinic/public/commands/user.py
delete_clinic_data_for_user ¶
Delete a clinic user and all associated data for non-medical admin users.
Source code in components/clinic/public/commands/user.py
inspect_archive ¶
Fetch, decrypt, and print a mapping file and its referenced archive. Dev only.
Usage
flask clinic inspect_archive
Example
flask clinic inspect_archive mapping_clinic-456_20260331_120000.csv.enc
Source code in components/clinic/public/commands/user.py
components.clinic.public.dependencies ¶
TherapySessionConfiguration
dataclass
¶
TherapySessionConfiguration(
duration,
price_multiplier,
credit_multiplier,
force_same_length_slot=False,
available_for_booking=True,
)
Bases: DataClassJsonMixin
Configuration for a therapy session duration variant.
Attributes:
| Name | Type | Description |
|---|---|---|
duration |
int
|
Session duration in minutes |
price_multiplier |
int
|
Multiplier applied to the base session price |
credit_multiplier |
int
|
Multiplier applied to the session credit count |
force_same_length_slot |
bool
|
Force google calendar slots to match session duration |
available_for_booking |
bool
|
Whether this configuration is visible for booking |
default_session_configurations ¶
Single-entry default configuration from SESSION_TYPE_CONFIG.
Source code in components/clinic/public/dependencies.py
components.clinic.public.entities ¶
app_company ¶
available_health_service ¶
AvailableHealthService
dataclass
¶
AvailableHealthService(
name,
has_access=None,
is_recommended=None,
has_upcoming_availability=None,
)
Bases: DataClassJsonMixin
Available health service available for a clinic user
external_onboarding_user ¶
ExternalOnboardingUserData
dataclass
¶
ExternalOnboardingUserData(
email,
first_name,
last_name,
phone_number,
password,
prehashed_password,
gender_str,
date_of_birth,
social_security_number,
place_of_birth_name,
place_of_birth_postal_code,
terms_accepted,
refresh_token_type,
client_id=None,
birth_last_name=None,
)
External onboarding user data. Attributes: email: The email of the user. first_name: The first name of the user. last_name: The last name of the user. phone_number: The phone number of the user. password: The password of the user. prehashed_password: The prehashed password of the user. gender_str: The gender of the user. date_of_birth: The date of birth of the user. social_security_number: The social security number of the user. place_of_birth_name: The name of the place of birth of the user. place_of_birth_postal_code: The postal code of the place of birth of the user. terms_accepted: Whether the user has accepted the terms and conditions. refresh_token_type: The refresh token type of the user. client_id: The Keycloak client ID for email verification flow. birth_last_name: The birth last name of the user (maiden name). Defaults to last_name if not provided.
components.clinic.public.enums ¶
available_health_service_name ¶
available_shop_product_types ¶
pricing ¶
ClinicInvoiceAmountCurrency ¶
Bases: AlanBaseEnum
format_amount ¶
Source code in components/clinic/internal/enums/clinic_invoice_amount_currency.py
regions ¶
CaAdministrativeAreas ¶
Bases: AlanBaseEnum
Canadian provinces and territories.
Used to specify which administrative regions a medical admin is licensed to operate in. Values use ISO 3166-2:CA codes (CA- prefix + 2-letter region code). -
components.clinic.public.events ¶
subscription ¶
subscribe_to_events ¶
All event subscriptions for the Clinic should be done here.