Skip to content

Api reference

components.clinic.public.business_logic

insi_identity

is_identity_verified_for_user

is_identity_verified_for_user(app_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
def is_identity_verified_for_user(app_user: FeatureUser) -> bool:
    """
    Check if the user's identity is verified.

    Args:
        app_user (FeatureUser): The application user whose identity is to be checked.
    Returns:
        bool: True if the user's identity is verified, False otherwise.
    """
    clinic_user = get_or_create_clinic_user(app_user=app_user)

    return is_identity_validated(clinic_user_id=clinic_user.id)

medical_conversation

get_first_medical_conversation_for_member

get_first_medical_conversation_for_member(app_user)

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
def get_first_medical_conversation_for_member(
    app_user: FeatureUser,
) -> Optional[MedicalConversation]:
    """
    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`.

    Args:
        app_user (FeatureUser): The user for whom the first medical conversation
                                is to be retrieved.

    Returns:
        Optional[MedicalConversation]: The first medical conversation for the
                                       given member, or None if no conversation
                                       is found.
    """
    from components.clinic.internal.business_logic.medical_conversation import (
        get_first_medical_conversation_for_member,
    )

    return get_first_medical_conversation_for_member(app_user=app_user)

mark_conversation_as_deleted

mark_conversation_as_deleted(medical_conversation_id)
Source code in components/clinic/public/business_logic/medical_conversation.py
def mark_conversation_as_deleted(medical_conversation_id: UUID) -> None:  # noqa: D103
    from components.clinic.internal.business_logic.medical_chat_bot import (
        get_medical_chat_bot_clinic_user_id,
    )
    from components.clinic.internal.business_logic.medical_conversation import (
        mark_as_deleted,
    )

    mark_as_deleted(
        medical_conversation_id=medical_conversation_id,
        actor_clinic_user_id=get_medical_chat_bot_clinic_user_id(),
    )

therapist_booking_session

get_past_sessions_that_need_invoice_generation_count_for_user

get_past_sessions_that_need_invoice_generation_count_for_user(
    feature_user, session_type
)

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
def get_past_sessions_that_need_invoice_generation_count_for_user(
    feature_user: FeatureUser, session_type: TherapistBookingSessionType
) -> int:
    """
    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.
    """
    clinic_user = get_or_create_clinic_user(app_user=feature_user)

    return int(
        get_past_sessions_that_need_invoices_query(
            clinic_user_id=clinic_user.id,
            session_type=session_type,
            min_date=(datetime.now() - timedelta(hours=DEFAULT_DELAY_IN_HOURS)),
            delay_in_hours=0,
        ).count()
    )

get_upcoming_reimbursed_therapist_booking_session_count_for_user

get_upcoming_reimbursed_therapist_booking_session_count_for_user(
    feature_user, session_type
)

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
def get_upcoming_reimbursed_therapist_booking_session_count_for_user(
    feature_user: FeatureUser, session_type: TherapistBookingSessionType
) -> int:
    """
    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.
    """
    from components.clinic.internal.booking.models.therapist_booking_session import (
        TherapistBookingSession,
    )

    clinic_user = get_or_create_clinic_user(app_user=feature_user)

    return int(
        current_session.query(TherapistBookingSession)  # noqa: ALN085
        .filter(
            ~TherapistBookingSession.is_refunded,
            TherapistBookingSession.confirmed_at.isnot(None),
            TherapistBookingSession.clinic_user_id == clinic_user.id,
            TherapistBookingSession.session_type == session_type,
            TherapistBookingSession.starts_at > datetime.now(),
            TherapistBookingSession.therapist_booking_user_credit_id.isnot(None),
        )
        .count()
    )

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
def app_user_has_upcoming_availability_for_alan_consultations(
    member_app_user: FeatureUser,
    locale: Optional[str] = None,
    availability_threshold_in_hours: Optional[int] = 24,
    medical_admins_ids: Optional[
        list[str]
    ] = None,  # if this argument is passed, we filter the medical admins to only choose those with an id in this list
) -> bool:
    """
    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)
    """
    from components.clinic.internal.business_logic.medical_admin import (
        get_medical_admins_for_member,
    )

    # general practitioners that are bookable online are the only ones that do Alan consultations
    available_medical_admins = get_medical_admins_for_member(
        app_user=member_app_user,
        is_bookable_online=True,
        locale=locale,
        medical_specialties=[MedicalAdminSpecialty.GENERAL_PRACTITIONER],
        medical_admins_ids=medical_admins_ids,
        availability_threshold_in_hours=availability_threshold_in_hours,
    )

    # since we applied the availability threshold filter, if there are no available medical admins, it means that the member is not able to book an Alan consultation within the given upcoming availability
    return len(available_medical_admins) > 0

get_clinic_user_for_french_user

get_clinic_user_for_french_user(user_id)
Source code in components/clinic/public/business_logic/user.py
def get_clinic_user_for_french_user(user_id: int) -> ClinicUser | None:  # noqa: D103
    from components.clinic.internal.business_logic.clinic_user import (
        get_clinic_user_for_french_user,
    )

    return get_clinic_user_for_french_user(user_id)

get_medical_admin_for_user

get_medical_admin_for_user(user_id, not_deleted=True)
Source code in components/clinic/public/business_logic/user.py
def get_medical_admin_for_user(  # noqa: D103
    user_id: int, not_deleted: Optional[bool] = True
) -> MedicalAdmin | None:
    from components.clinic.internal.business_logic.medical_admin import (
        get_medical_admin_by_app_user,
    )

    return get_medical_admin_by_app_user(
        medical_admin_app_user=FeatureUser(
            app_user_id=str(user_id),
            app_id=get_current_app_name(),
        ),
        not_deleted=not_deleted,
    )

has_app_user_access_to_orientation

has_app_user_access_to_orientation(app_user)
Source code in components/clinic/public/business_logic/user.py
def has_app_user_access_to_orientation(app_user: FeatureUser) -> bool:  # noqa: D103
    from components.clinic.internal.booking.business_logic.sessions_queries import (
        has_app_user_access_to_orientation,
    )

    return has_app_user_access_to_orientation(app_user=app_user)
update_clinic_consent_for_user(
    actor_user, has_given_consent, delete_existing_data=True
)

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
def update_clinic_consent_for_user(
    actor_user: FeatureUser,
    has_given_consent: bool,
    delete_existing_data: Optional[bool] = True,
) -> None:
    """
    Update the clinic user consent

    Args:
        actor_user: The actor feature user
        has_given_consent: The user has given his consent
        delete_existing_data: Has to delete existing data status
    """
    from components.clinic.internal.business_logic.clinic_user import (
        update_clinic_consent,
    )

    update_clinic_consent(
        actor_app_user=actor_user,
        has_given_consent=has_given_consent,
        delete_existing_data=delete_existing_data,
    )

validate_child_dependent

validate_child_dependent_and_get_feature_user

validate_child_dependent_and_get_feature_user(
    parent_user, child_app_user_id
)

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
def validate_child_dependent_and_get_feature_user(
    parent_user: FeatureUser, child_app_user_id: str
) -> FeatureUser:
    """
    Validate that a child is a dependent of the current user and return a FeatureUser for the child.

    Args:
        parent_user: The current user (parent)
        child_app_user_id: The ID of the child to validate

    Raises:
        BaseErrorCode.forbidden if user has no dependents or child is not a dependent of current user
    """
    # Get the app user data of the current user
    user_data = get_app_dependency(parent_user.app_id).get_app_user_data(
        app_user_id=parent_user.app_user_id
    )

    if not user_data.dependents:
        raise BaseErrorCode.forbidden(message="User has no dependents")

    # Get the list of dependent app_user_ids
    dependent_ids = [
        dependent.app_user_id
        for dependent in user_data.dependents
        if dependent.app_user_id
    ]

    if child_app_user_id not in dependent_ids:
        raise BaseErrorCode.forbidden(
            message="Child is not a dependent of the current user"
        )

    # Create and return feature user for the child
    return FeatureUser(
        app_user_id=child_app_user_id,
        app_id=parent_user.app_id,
    )

components.clinic.public.commands

app_group

clinic_commands module-attribute

clinic_commands = AppGroup('clinic')

register_clinic_commands

register_clinic_commands()
Source code in components/clinic/public/commands/app_group.py
def register_clinic_commands() -> None:  # noqa: D103
    from components.clinic.public.commands import (  # noqa: F401
        clinic_invoices,
        end_to_end_test,
        medical_admins,
        medical_conversation,
        unread_message_reminders,
        user,
    )

booking

app_group

booking_commands module-attribute
booking_commands = AppGroup('booking')
register_booking_commands
register_booking_commands()
Source code in components/clinic/public/commands/booking/app_group.py
def register_booking_commands() -> None:  # noqa: D103
    from components.clinic.public.commands.booking import (  # noqa: F401
        notifications,
        sessions,
        therapists,
    )

notifications

notify_all_scheduled_messages
notify_all_scheduled_messages()
Source code in components/clinic/public/commands/booking/notifications.py
@booking_commands.command(requires_authentication=False)
@command_with_dry_run
def notify_all_scheduled_messages() -> None:  # noqa: D103
    from components.clinic.internal.booking.business_logic.notifications import (
        notify_all_scheduled_messages,
    )

    notify_all_scheduled_messages()

sessions

cancel_all_future_sessions_for_medical_admin_as_medical_admin
cancel_all_future_sessions_for_medical_admin_as_medical_admin(
    dry_run, medical_admin_id
)

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
@booking_commands.command(requires_authentication=False)
@click.option(
    "--medical-admin-id",
    type=str,
    required=True,
)
@command_with_dry_run
def cancel_all_future_sessions_for_medical_admin_as_medical_admin(
    dry_run: bool,
    medical_admin_id: str,
) -> None:
    """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.
    """
    from components.clinic.internal.booking.business_logic.sessions_actions import (
        cancel_session_for_expert,
    )
    from components.clinic.internal.booking.business_logic.sessions_queries import (
        get_therapist_upcoming_sessions,
    )
    from components.clinic.internal.models.medical_admin import MedicalAdmin
    from shared.helpers.get_or_else import get_or_raise_missing_resource
    from shared.helpers.logging.logger import current_logger

    # get the feature user for the medical admin
    medical_admin = get_or_raise_missing_resource(MedicalAdmin, medical_admin_id)
    medical_admin_feature_user = medical_admin.clinic_user.feature_user

    # get all the sessions for the medical admin
    sessions = get_therapist_upcoming_sessions(
        actor_app_user=medical_admin_feature_user,
    )

    current_logger.info(
        f"Found {len(sessions)} upcoming sessions for medical admin {medical_admin_id}"
    )

    num_cancelled = 0

    # cancel each session as the medical admin
    for session in sessions:
        if dry_run:
            current_logger.info(f"DRY RUN: Would have cancelled session {session.id}")
        else:
            current_logger.info(f"Cancelling session {session.id}")

        cancel_session_for_expert(
            session_id=session.id,
            actor_app_user=medical_admin_feature_user,
            dry_run=dry_run,
        )

        num_cancelled += 1
        if num_cancelled % 5 == 0:
            current_logger.info(f"Cancelled {num_cancelled}/{len(sessions)} sessions")

    current_logger.info(f"Completed. Cancelled {len(sessions)} sessions")
create_conversation_with_messages_for_future_sessions
create_conversation_with_messages_for_future_sessions(
    dry_run, limit
)

Create missing conversation messages for booked sessions that haven't occurred yet.

Source code in components/clinic/public/commands/booking/sessions.py
@booking_commands.command(requires_authentication=False)
@click.option(
    "--limit",
    type=int,
    required=False,
    default=1000,
)
@command_with_dry_run
def create_conversation_with_messages_for_future_sessions(
    dry_run: bool, limit: int
) -> None:
    """Create missing conversation messages for booked sessions that haven't occurred yet."""
    from components.clinic.internal.business_logic.medical_conversation_for_session import (
        create_conversation_with_messages_for_future_sessions,
    )

    create_conversation_with_messages_for_future_sessions(dry_run=dry_run, limit=limit)
delete_test_sessions
delete_test_sessions()
Source code in components/clinic/public/commands/booking/sessions.py
@booking_commands.command(requires_authentication=False)
@do_not_run_in_prod
def delete_test_sessions() -> None:  # noqa: D103
    from components.clinic.internal.booking.business_logic.end_to_end_test import (
        delete_test_sessions,
    )

    delete_test_sessions()
expire_booking_invites
expire_booking_invites(dry_run)
Source code in components/clinic/public/commands/booking/sessions.py
@booking_commands.command(requires_authentication=False)
@command_with_dry_run
def expire_booking_invites(dry_run: bool) -> None:  # noqa: D103
    from components.clinic.internal.booking.business_logic.sessions_actions import (
        cancel_all_expired_invites,
    )

    cancel_all_expired_invites(dry_run=dry_run)
generate_past_sessions_invoice
generate_past_sessions_invoice(dry_run=False)
Source code in components/clinic/public/commands/booking/sessions.py
@booking_commands.command(requires_authentication=False)
@command_with_dry_run
def generate_past_sessions_invoice(dry_run: bool = False) -> None:  # noqa: D103
    from components.clinic.internal.booking.business_logic.sessions_invoicing import (
        generate_past_sessions_invoice,
    )

    generate_past_sessions_invoice(dry_run=dry_run)
generate_sessions_invoices
generate_sessions_invoices(dry_run)
Source code in components/clinic/public/commands/booking/sessions.py
@booking_commands.command(requires_authentication=False)
@command_with_dry_run
def generate_sessions_invoices(dry_run: bool) -> None:  # noqa: D103
    from components.clinic.internal.booking.business_logic.sessions_invoicing import (
        generate_sessions_invoices,
    )

    generate_sessions_invoices(dry_run=dry_run)
link_sessions_to_conversations(dry_run, limit)

Link sessions to existing conversations of their type when it has not been done yet. This will ease to retrieve the conversation for a given session.

Parameters:

Name Type Description Default
dry_run bool

Whether to perform a dry run or not.

required
limit int

The maximum number of sessions to link to conversations.

required
Source code in components/clinic/public/commands/booking/sessions.py
@booking_commands.command(requires_authentication=False)
@click.option(
    "--limit",
    type=int,
    required=False,
    default=1000,
)
@command_with_dry_run
def link_sessions_to_conversations(dry_run: bool, limit: int) -> None:
    """Link sessions to existing conversations of their type when it has not been done yet.
    This will ease to retrieve the conversation for a given session.

    Arguments:
        dry_run (bool): Whether to perform a dry run or not.
        limit (int): The maximum number of sessions to link to conversations.
    """
    from components.clinic.internal.business_logic.medical_conversation_for_session import (
        link_sessions_to_existing_conversations_of_their_type,
    )

    link_sessions_to_existing_conversations_of_their_type(dry_run=dry_run, limit=limit)
refund_payment_not_linked_to_session
refund_payment_not_linked_to_session(dry_run)
Source code in components/clinic/public/commands/booking/sessions.py
@booking_commands.command(requires_authentication=False)
@command_with_dry_run
def refund_payment_not_linked_to_session(dry_run: bool) -> None:  # noqa: D103
    from components.clinic.internal.booking.business_logic.credits import (
        refund_payment_not_linked_to_session,
    )

    refund_payment_not_linked_to_session(dry_run=dry_run)

therapists

update_cached_next_availability
update_cached_next_availability(dry_run)
Source code in components/clinic/public/commands/booking/therapists.py
@booking_commands.command(requires_authentication=False)
@command_with_dry_run
def update_cached_next_availability(dry_run: bool) -> None:  # noqa: D103
    from components.clinic.internal.booking.business_logic.therapists import (
        update_cached_next_availability_of_all_therapists,
    )

    update_cached_next_availability_of_all_therapists(commit=not dry_run)

clinic_invoices

generate_fake_clinic_invoices

generate_fake_clinic_invoices(dry_run)
Source code in components/clinic/public/commands/clinic_invoices.py
@clinic_commands.command(requires_authentication=False)
@command_with_dry_run
@do_not_run_in_prod
def generate_fake_clinic_invoices(dry_run: bool) -> None:  # noqa: D103
    from components.clinic.internal.business_logic.clinic_invoice import (
        generate_fake_clinic_invoices,
    )

    generate_fake_clinic_invoices(dry_run=dry_run)

sync_clinic_monthly_invoice_lines_from_turing

sync_clinic_monthly_invoice_lines_from_turing(
    dry_run, date, previous_month, mapped_ids
)
Source code in components/clinic/public/commands/clinic_invoices.py
@clinic_commands.command(requires_authentication=False)
@command_with_dry_run
@click.option(
    "--date",
    type=click.DateTime(formats=["%Y-%m-%d"]),
    required=False,
    help="Date in YYYY-MM-DD format (e.g., 2021-09-01)",
)
@click.option(
    "--previous-month",
    is_flag=True,
    default=False,
    help="Run invoice generation for the previous month",
)
@click.option(
    "--mapped_ids",
    type=str,
    required=False,
    help="Optional mapping of origin IDs to target IDs in the format 'originid1:targetid1,originid2:targetid2'. If provided, only the specified origin IDs will be queried from Turing, and the corresponding target IDs will be used when inserting into the backend database.",
)
def sync_clinic_monthly_invoice_lines_from_turing(  # noqa: D103
    dry_run: bool,
    date: Optional[datetime.date],
    previous_month: bool,
    mapped_ids: Optional[str],
) -> None:
    from components.clinic.internal.business_logic.clinic_invoice import (
        sync_clinic_monthly_invoice_lines_from_turing,
    )

    if date and previous_month:
        raise click.BadParameter(
            "You can't specify both --date and --previous-month options"
        )

    if previous_month:
        date = (
            datetime.date.today().replace(day=1) - datetime.timedelta(days=1)
        ).replace(day=1)
        click.echo(f"Invoicing date: {date}")

    result = sync_clinic_monthly_invoice_lines_from_turing(
        dry_run=dry_run,
        date=date,
        mapped_ids=mapped_ids,
    )

    if not dry_run:
        current_app.slack_web_client.chat_postMessage(  # type: ignore[attr-defined]
            channel=(
                SlackChannel.alan_clinic_ops_channel
                if is_production_mode()
                else SlackChannel.test
            ),
            username="Clinic Bot",
            icon_emoji=":moneybag:",
            text=(
                f"👋 I just finished generating {result.created_invoices_count} invoices (for the {result.invoicing_date} date)."
                "\nHead over <https://alan.com/marmot-v2/alan-clinic/invoices|the billing page> in Marmot and send the invoices for validation 🍔"
                + (
                    f"\nnote: {result.removed_invoices_count} draft invoice(s) were first removed when generating today's invoices."
                    if result.removed_invoices_count
                    else ""
                )
            ),
        )

end_to_end_test

delete_test_conversations

delete_test_conversations()
Source code in components/clinic/public/commands/end_to_end_test.py
@clinic_commands.command(requires_authentication=False)
@do_not_run_in_prod
def delete_test_conversations() -> None:  # noqa: D103
    from components.clinic.internal.business_logic.end_to_end_test import (
        delete_test_conversations,
    )

    delete_test_conversations()

keep_last_part_from_test_conversations

keep_last_part_from_test_conversations()
Source code in components/clinic/public/commands/end_to_end_test.py
@clinic_commands.command(requires_authentication=False)
@do_not_run_in_prod
def keep_last_part_from_test_conversations() -> None:  # noqa: D103
    from components.clinic.internal.business_logic.end_to_end_test import (
        keep_last_part_from_test_conversations,
    )

    keep_last_part_from_test_conversations()

medical_admins

create_medical_admins

create_medical_admins(password, dry_run)
Source code in components/clinic/public/commands/medical_admins.py
@clinic_commands.command(requires_authentication=False)
@click.option(
    "--password", help="Password for all the accounts", type=str, default="azerty"
)
@command_with_dry_run
def create_medical_admins(password: str, dry_run: bool) -> None:  # noqa: D103
    _create_medical_admins(password=password, dry_run=dry_run)

create_medical_chat_bot

create_medical_chat_bot()
Source code in components/clinic/public/commands/medical_admins.py
@clinic_commands.command(requires_authentication=False)
def create_medical_chat_bot() -> None:  # noqa: D103
    from components.clinic.internal.business_logic.medical_chat_bot import (
        create_medical_chat_bot_clinic_user,
        medical_chat_bot_clinic_user_exists,
    )

    if not is_development_mode():
        click.echo("This command is only available in development mode!")
        return

    if medical_chat_bot_clinic_user_exists():
        click.echo("The Medical Chat Bot already exists.")
    else:
        create_medical_chat_bot_clinic_user()

set_offline_inactive_medical_admin

set_offline_inactive_medical_admin(minutes, dry_run)
Source code in components/clinic/public/commands/medical_admins.py
@clinic_commands.command(requires_authentication=False)
@click.option(
    "--minutes",
    type=int,
    default=30,
    help="Number of minutes before medical admin is inactive",
)
@command_with_dry_run
def set_offline_inactive_medical_admin(minutes: int, dry_run: bool) -> None:  # noqa: D103
    from components.clinic.internal.business_logic.medical_admin import (
        set_offline_inactive_medical_admins,
    )

    set_offline_inactive_medical_admins(
        minutes=minutes,
        dry_run=dry_run,
    )

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,
)
access_types instance-attribute
access_types
accessible_conversation_specialties instance-attribute
accessible_conversation_specialties
answers_to_proactive_conversation_topics class-attribute instance-attribute
answers_to_proactive_conversation_topics = None
apps_displayed_in instance-attribute
apps_displayed_in
avatar class-attribute instance-attribute
avatar = None
clinic_role class-attribute instance-attribute
clinic_role = None
country instance-attribute
country
dato_id class-attribute instance-attribute
dato_id = None
description class-attribute instance-attribute
description = None
experiences class-attribute instance-attribute
experiences = None
first_message_body class-attribute instance-attribute
first_message_body = None
first_name instance-attribute
first_name
has_access_to_app_ids instance-attribute
has_access_to_app_ids
languages instance-attribute
languages
last_name instance-attribute
last_name
onboarding_status instance-attribute
onboarding_status
prod_id class-attribute instance-attribute
prod_id = None
specialty instance-attribute
specialty

MedicalAdminExperienceData dataclass

MedicalAdminExperienceData(title, subtitle, index)
index instance-attribute
index
subtitle instance-attribute
subtitle
title instance-attribute
title

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,
        ],
        clinic_role=CLINIC_ADMIN,
        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,
        accessible_conversation_specialties=[
            PSYCHOLOGY,
            THERAPY,
        ],
        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="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",
    ),
]

medical_conversation

close_old_answered_medical_conversations

close_old_answered_medical_conversations(
    age_for_gp, age_for_non_gp, dry_run
)
Source code in components/clinic/public/commands/medical_conversation.py
@clinic_commands.command(requires_authentication=False)
@command_with_dry_run
@click.option(
    "--age-for-gp", type=int, help="Minimum age in days to close gp conversations"
)
@click.option(
    "--age-for-non-gp",
    type=int,
    help="Minimum age in days to close non gp conversations",
)
def close_old_answered_medical_conversations(  # noqa: D103
    age_for_gp: int,
    age_for_non_gp: int,
    dry_run: bool,
) -> None:
    from components.clinic.internal.business_logic.medical_conversation import (
        close_old_answered_medical_conversations,
    )

    close_old_answered_medical_conversations(
        age_for_gp=age_for_gp,
        age_for_non_gp=age_for_non_gp,
        dry_run=dry_run,
    )

create_fake_conversations

create_fake_conversations(count, specialty, app, is_closed)

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
@clinic_commands.command(requires_authentication=False)
@click.option(
    "-c", "--count", default=50, help="Number of fake conversations to create"
)
@click.option(
    "-s",
    "--specialty",
    default=None,
    help="Medical specialty of fake conversations to create",
)
@click.option(
    "-a",
    "--app",
    default=None,
    help="App of fake conversations to create",
)
@click.option(
    "--is-closed",
    type=bool,
    default=False,
    help="App of fake conversations to create",
)
@do_not_run_in_prod
def create_fake_conversations(
    count: int, specialty: str, app: str, is_closed: bool
) -> None:
    """
    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
    """
    import random

    from sqlalchemy.sql.expression import func

    from components.clinic.internal.enums.medical_conversation_specialty import (
        MedicalConversationSpecialty,
    )
    from components.clinic.internal.models.clinic_user import ClinicUser
    from components.clinic.internal.models.medical_admin import MedicalAdmin
    from components.clinic.internal.models.medical_conversation import (
        MedicalConversation,
    )
    from components.clinic.internal.models.medical_conversation_attachment import (
        MedicalConversationAttachment,
    )
    from components.clinic.internal.models.medical_conversation_part import (
        MedicalConversationPart,
    )
    from shared.helpers.app_name import AppName

    # re-use existing encrypted body for new parts
    # they can be decrypted by medical admin
    parts_with_internal_encrypted_body = (
        current_session.query(MedicalConversationPart)  # noqa: ALN085
        .filter(
            MedicalConversationPart.internal_encrypted_body.is_not(None),
        )
        .all()
    )
    internal_encrypted_bodies = [
        p.internal_encrypted_body for p in parts_with_internal_encrypted_body
    ]

    # re-use existing attachments for new attachments
    # they can be decrypted by medical admin
    internally_encrypted_attachments = (
        current_session.query(MedicalConversationAttachment)  # noqa: ALN085
        .filter(MedicalConversationAttachment.is_internally_encrypted.is_(True))
        .all()
    )

    medical_specialty = MedicalConversationSpecialty.validate(specialty)
    app_id = AppName.validate(app)

    medical_admins = current_session.query(MedicalAdmin).filter(  # noqa: ALN085
        MedicalAdmin.clinic_role != MedicalAdminRole.CLINIC_OPS
    )

    for _ in range(count):
        medical_specialty_for_conversation = (
            medical_specialty
            if medical_specialty
            else random.choice(list(MedicalConversationSpecialty))  # noqa: S311
        )

        medical_admins_for_specialty = [
            medical_admin
            for medical_admin in medical_admins
            if medical_admin.default_conversation_specialty
            == medical_specialty_for_conversation
        ]
        if not medical_admins_for_specialty:
            click.echo(
                f"No medical admin found to handle specialty {medical_specialty_for_conversation}, skiping for this conversation"
            )
            continue

        app_id = (
            app_id if app_id else random.choice([AppName.ALAN_FR, AppName.ALAN_BE])  # noqa: S311
        )

        clinic_user = (
            current_session.query(ClinicUser)  # noqa: ALN085
            .filter(ClinicUser.app_id == app_id)
            .order_by(func.random())
            .first()
        )
        # 20% chance to create a new user
        has_to_create_user = (
            clinic_user is None or random.randint(0, 4) == 0  # noqa: S311
        )

        if has_to_create_user:
            gender = random.choice([UserGender.male, UserGender.female])  # noqa: S311
            first_name = (
                faker.first_name_male()
                if gender == UserGender.male
                else faker.first_name_female()
            )
            last_name = faker.last_name()
            birth_date = faker.date_of_birth(minimum_age=18, maximum_age=80)
            user = create_profile_with_user(
                first_name=first_name,
                last_name=last_name,
                gender=gender,
                birth_date=birth_date,
            )
            clinic_user = ClinicUser(
                app_user_id=str(user.id),
                app_id=app_id,
            )
            current_session.add(clinic_user)

        medical_admin = random.choice(medical_admins_for_specialty)  # noqa: S311

        conversation = MedicalConversation(
            creator_clinic_user=clinic_user,
            member_clinic_user=clinic_user,
            medical_specialty=medical_specialty_for_conversation,
            has_been_closed_by_medical_admin=is_closed,
        )
        current_session.add(conversation)
        current_session.flush()

        # create between 2 and 30 user parts
        nb_parts = random.randint(2, 30)  # noqa: S311
        for _ in range(nb_parts):
            internal_encrypted_body = (
                random.choice(internal_encrypted_bodies)  # noqa: S311
                if len(internal_encrypted_bodies) > 0
                else None
            )
            member_part = MedicalConversationPart(
                medical_conversation=conversation,
                author=clinic_user,
                body=faker.text(),
                internal_encrypted_body=internal_encrypted_body,
            )
            current_session.add(member_part)
            # 25% chance to have an attachment
            if random.randint(0, 3) == 0:  # noqa: S311
                internally_encrypted_attachment = (
                    random.choice(internally_encrypted_attachments)  # noqa: S311
                    if len(internally_encrypted_attachments) > 0
                    else None
                )
                if internally_encrypted_attachment:
                    member_attachment = MedicalConversationAttachment(
                        medical_conversation_part=member_part,
                        filename=internally_encrypted_attachment.filename,
                        mime_type=internally_encrypted_attachment.mime_type,
                        s3_key=internally_encrypted_attachment.s3_key,  # gitleaks:allow
                        is_internally_encrypted=True,
                    )
                    current_session.add(member_attachment)
            # for each user part, create between 2 and 5 medical admin parts
            nb_admin_parts = random.randint(2, 5)  # noqa: S311
            for _ in range(nb_admin_parts):
                admin_part = MedicalConversationPart(
                    author=medical_admin,
                    medical_conversation=conversation,
                    body=faker.text(),
                    internal_encrypted_body=random.choice(  # noqa: S311
                        internal_encrypted_bodies
                    )
                    if len(internal_encrypted_bodies) > 0
                    else None,
                )
                current_session.add(admin_part)
                # 25% chance to have an attachment
                if random.randint(0, 3) == 0:  # noqa: S311
                    internally_encrypted_attachment = (
                        random.choice(internally_encrypted_attachments)  # noqa: S311
                        if len(internally_encrypted_attachments) > 0
                        else None
                    )
                    if internally_encrypted_attachment:
                        admin_attachment = MedicalConversationAttachment(
                            medical_conversation_part=admin_part,
                            filename=internally_encrypted_attachment.filename,
                            mime_type=internally_encrypted_attachment.mime_type,
                            s3_key=internally_encrypted_attachment.s3_key,  # gitleaks:allow
                            is_internally_encrypted=True,
                        )
                        current_session.add(admin_attachment)
        # 50% chance to add 1 last user part to the conversation
        # have similar proportion of inactive and active conversations
        if random.randint(0, 1) == 0:  # noqa: S311
            internal_encrypted_body = (
                random.choice(internal_encrypted_bodies)  # noqa: S311
                if len(internal_encrypted_bodies) > 0
                else None
            )
            last_member_part = MedicalConversationPart(
                medical_conversation=conversation,
                author=clinic_user,
                body=faker.text(),
                internal_encrypted_body=internal_encrypted_body,
            )
            current_session.add(last_member_part)
        current_logger.info(
            f"Created fake conversation {conversation.id} with specialty {medical_specialty_for_conversation} for assigned medical admin {medical_admin.id}"
        )

    current_logger.info(f"Created {count} fake conversations")
    current_session.commit()

erase_all_conversation_data

erase_all_conversation_data()
Source code in components/clinic/public/commands/medical_conversation.py
@clinic_commands.command(requires_authentication=False)
@do_not_run_in_prod
def erase_all_conversation_data() -> None:  # noqa: D103
    from components.clinic.internal.models.medical_conversation import (
        MedicalConversation,
    )
    from components.clinic.internal.models.medical_conversation_ai_suggestions_result import (
        MedicalConversationAiSuggestionsResult,
    )
    from components.clinic.internal.models.medical_conversation_attachment import (
        MedicalConversationAttachment,
    )
    from components.clinic.internal.models.medical_conversation_bookmark import (
        MedicalConversationBookmark,
    )
    from components.clinic.internal.models.medical_conversation_follow_up import (
        MedicalConversationFollowUp,
    )
    from components.clinic.internal.models.medical_conversation_part import (
        MedicalConversationPart,
    )
    from components.clinic.internal.models.medical_conversation_rating_v2 import (
        MedicalConversationRatingV2,
    )
    from components.clinic.internal.models.medical_conversation_redirection import (
        MedicalConversationRedirection,
    )
    from components.clinic.internal.models.medical_conversation_redirection_medical_conversation_part import (
        MedicalConversationRedirectionMedicalConversationPart,
    )
    from components.clinic.internal.models.medical_conversation_review_from_medical_admin import (
        MedicalConversationReviewFromMedicalAdmin,
    )
    from components.clinic.internal.models.medical_conversation_tag_mapping import (
        MedicalConversationTagMapping,
    )

    if not click.confirm("This will erase all conversation data, are you sure?"):
        return

    current_session.query(MedicalConversationAiSuggestionsResult).delete()  # noqa: ALN085
    current_session.query(MedicalConversationReviewFromMedicalAdmin).delete()  # noqa: ALN085
    current_session.query(  # noqa: ALN085
        MedicalConversationRedirectionMedicalConversationPart
    ).delete()
    current_session.query(MedicalConversationRedirection).delete()  # noqa: ALN085
    current_session.query(MedicalConversationBookmark).delete()  # noqa: ALN085
    current_session.query(MedicalConversationFollowUp).delete()  # noqa: ALN085
    current_session.query(MedicalConversationTagMapping).delete()  # noqa: ALN085
    current_session.query(MedicalConversationRatingV2).delete()  # noqa: ALN085
    current_session.query(MedicalConversationAttachment).delete()  # noqa: ALN085
    current_session.query(MedicalConversationPart).delete()  # noqa: ALN085
    current_session.query(MedicalConversation).delete()  # noqa: ALN085

    current_session.commit()

faker module-attribute

faker = Faker('fr_FR')

resolve_closable_conversations

resolve_closable_conversations()
Source code in components/clinic/public/commands/medical_conversation.py
@clinic_commands.command(requires_authentication=False)
def resolve_closable_conversations() -> None:  # noqa: D103
    from components.clinic.internal.business_logic.medical_conversation import (
        resolve_closable_conversations as _resolve_closable_conversations,
    )

    _resolve_closable_conversations()

send_pending_proactive_conversations

send_pending_proactive_conversations(dry_run)
Source code in components/clinic/public/commands/medical_conversation.py
@clinic_commands.command(requires_authentication=False)
@command_with_dry_run
def send_pending_proactive_conversations(dry_run: bool) -> None:  # noqa: D103
    from components.clinic.internal.business_logic.medical_conversation_proactive import (
        send_pending_proactive_conversations,
    )

    send_pending_proactive_conversations(commit=not dry_run)

update_active_after_for_conversations

update_active_after_for_conversations(
    dry_run, recently_modified_only
)

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
@clinic_commands.command(requires_authentication=False)
@command_with_dry_run
@click.option(
    "--recently-modified-only",
    type=bool,
    required=False,
    default=False,
    is_flag=True,
    help="Only look at recently modified conversations",
)
def update_active_after_for_conversations(
    dry_run: bool, recently_modified_only: bool
) -> None:
    """
    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
    """
    from components.clinic.internal.business_logic.medical_conversation import (
        update_active_after_for_conversations as _update_active_after_for_conversations,
    )

    _update_active_after_for_conversations(
        commit=not dry_run, recently_modified_only=recently_modified_only
    )
    if dry_run:
        # Just being safe
        current_session.rollback()

unread_message_reminders

send_medical_conversations_unread_message_reminders

send_medical_conversations_unread_message_reminders(
    dry_run,
)
Source code in components/clinic/public/commands/unread_message_reminders.py
@clinic_commands.command(requires_authentication=False)
@command_with_dry_run
def send_medical_conversations_unread_message_reminders(dry_run: bool) -> None:  # noqa: D103
    from components.clinic.internal.business_logic.medical_conversation import (
        send_medical_conversations_unread_message_reminders,
    )

    send_medical_conversations_unread_message_reminders(dry_run=dry_run)

user

delete_clinic_data_for_user

delete_clinic_data_for_user(clinic_user_id, dry_run)
Source code in components/clinic/public/commands/user.py
@clinic_commands.command(requires_authentication=False)
@click.argument("clinic_user_id", type=str)
@command_with_dry_run
def delete_clinic_data_for_user(clinic_user_id: UUID, dry_run: bool) -> None:  # noqa: D103
    from components.clinic.internal.business_logic.clinic_user import (
        delete_all_clinic_data_for_user,
    )

    current_logger.warn(
        "⚠️ This command will delete all clinic user data (conversations, messages, ratings, etc.)"
    )
    delete_all_clinic_data_for_user(clinic_user_id=clinic_user_id, dry_run=dry_run)

components.clinic.public.dependencies

BaseUserData dataclass

BaseUserData(first_name, last_name)

Base user data used by the clinic

This is a subset of the UserData class, used when only the basic user data is needed.

Attributes:

Name Type Description
first_name str | None

The user's first name

last_name str | None

The user's last name

first_name instance-attribute

first_name

full_name property

full_name

Return the full name of the user

last_name instance-attribute

last_name

BookingSessionPackage dataclass

BookingSessionPackage(
    price_in_cents, included=None, reimbursed=None
)

Package available for a booking session type. This includes session's pricing and number of sessions included or reimbursed.

Attributes:

Name Type Description
price_in_cents int

The price of a session in cents

reimbursed Optional[BookingSessionPackageCount]

The reimbursed sessions information (reimbursed by Alan e.g alternative medicine allowance)

included Optional[BookingSessionPackageCount]

The included sessions information (covered with Alan)

included class-attribute instance-attribute

included = None

price_in_cents instance-attribute

price_in_cents

reimbursed class-attribute instance-attribute

reimbursed = None

BookingSessionPackageCount dataclass

BookingSessionPackageCount(
    count_limit, count_remaining=None
)

Count of sessions depends on the type

Attributes:

Name Type Description
count_limit int

The limit of the session count

count_remaining int | None

The remaining session count

count_limit instance-attribute

count_limit

count_remaining class-attribute instance-attribute

count_remaining = None

ClinicAdapter

Bases: ABC

Adapter for the clinic

clinic_consent_ai_publish_date = None

The release date of the chat x therapy feature - when conversations are created for therapy sessions between member and therapist automatically

get_allowlist_of_dermatology_medical_admins_ids abstractmethod

get_allowlist_of_dermatology_medical_admins_ids()

Return the list of medical admin IDs allowed for dermatology sessions in this country

Source code in components/clinic/public/dependencies.py
@abstractmethod
def get_allowlist_of_dermatology_medical_admins_ids(self) -> list[str]:
    """Return the list of medical admin IDs allowed for dermatology sessions in this country"""
    pass

get_app_base_user_data abstractmethod

get_app_base_user_data(app_user_id)

Get the user data base for the clinic. This is a subset of the user data, used when only the basic user data is needed. It prevents the need to load all the user data when only the basic user data is needed.

Parameters:

Name Type Description Default
app_user_id str

The user ID

required
compute_key_account_info

Whether to compute key account info for the user

required
Source code in components/clinic/public/dependencies.py
@abstractmethod
def get_app_base_user_data(self, app_user_id: str) -> BaseUserData:
    """Get the user data base for the clinic.
    This is a subset of the user data, used when only the basic user data is needed.
    It prevents the need to load all the user data when only the basic user data is needed.

    Args:
        app_user_id: The user ID
        compute_key_account_info: Whether to compute key account info for the user
    """
    pass

get_app_user_available_health_services abstractmethod

get_app_user_available_health_services(app_user_id)

Get the available health services for the user

Parameters:

Name Type Description Default
app_user_id str

The user ID

required
Source code in components/clinic/public/dependencies.py
@abstractmethod
def get_app_user_available_health_services(
    self,
    app_user_id: str,
) -> list[AvailableHealthService]:
    """Get the available health services for the user

    Args:
        app_user_id: The user ID
    """
    pass

get_app_user_data abstractmethod

get_app_user_data(
    app_user_id, compute_key_account_info=False
)

Get the user data for the clinic

Parameters:

Name Type Description Default
app_user_id str

The user ID

required
compute_key_account_info bool

Whether to compute key account info for the user

False
Source code in components/clinic/public/dependencies.py
@abstractmethod
def get_app_user_data(
    self,
    app_user_id: str,
    compute_key_account_info: bool = False,
) -> UserData:
    """Get the user data for the clinic

    Args:
        app_user_id: The user ID
        compute_key_account_info: Whether to compute key account info for the user
    """
    pass

get_booking_session_package abstractmethod

get_booking_session_package(app_user_id, session_type)

Get the pricing for the booking session for the user

Parameters:

Name Type Description Default
app_user_id str

The user ID

required
session_type TherapistBookingSessionType

The session type

required

Returns:

Type Description
BookingSessionPackage | None

The booking session package

BookingSessionPackage | None

If None is returned, the session is free

Source code in components/clinic/public/dependencies.py
@abstractmethod
def get_booking_session_package(
    self,
    app_user_id: str,
    session_type: TherapistBookingSessionType,
) -> BookingSessionPackage | None:
    """Get the pricing for the booking session for the user

    Args:
        app_user_id: The user ID
        session_type: The session type


    Returns:
        The booking session package
        If None is returned, the session is free
    """
    pass

get_coverage_status abstractmethod

get_coverage_status(app_user_id)

Return the start and optionally the end date of the current or upcoming period of eligibility to the clinic restricted services.

Parameters:

Name Type Description Default
app_user_id str

The user ID

required
Source code in components/clinic/public/dependencies.py
@abstractmethod
def get_coverage_status(self, app_user_id: str) -> CoverageStatus | None:
    """Return the start and optionally the end date of the current or upcoming period of eligibility to the clinic restricted services.

    Args:
        app_user_id: The user ID
    """
    pass

get_last_active_id_verification_request_for_user abstractmethod

get_last_active_id_verification_request_for_user(
    app_user_id,
)

Get the last active ID verification request for the user

Parameters:

Name Type Description Default
app_user_id str

The user ID

required
Source code in components/clinic/public/dependencies.py
@abstractmethod
def get_last_active_id_verification_request_for_user(
    self, app_user_id: str
) -> IDVerificationRequest | None:
    """Get the last active ID verification request for the user

    Args:
        app_user_id: The user ID
    """
    pass

has_access_to_orientation_call abstractmethod

has_access_to_orientation_call(app_user_id)

Specifically check if the user has access to the orientation call in countries specifc code

Parameters:

Name Type Description Default
app_user_id str

The user ID

required
Source code in components/clinic/public/dependencies.py
@abstractmethod
def has_access_to_orientation_call(
    self,
    app_user_id: str,
) -> bool:
    """Specifically check if the user has access to the orientation call in countries specifc code

    Args:
        app_user_id: The user ID
    """
    pass

has_app_user_permission abstractmethod

has_app_user_permission(app_user_id, permission)

Check if the user has an employee permission

Parameters:

Name Type Description Default
app_user_id str

The user ID

required
permission EmployeePermission

The permission

required
Source code in components/clinic/public/dependencies.py
@abstractmethod
def has_app_user_permission(
    self,
    app_user_id: str,
    permission: EmployeePermission,
) -> bool:
    """Check if the user has an employee permission

    Args:
        app_user_id: The user ID
        permission: The permission
    """
    pass

is_app_user_admin_of_company abstractmethod

is_app_user_admin_of_company(app_user_id, app_company_id)

Check if the user is an admin of the company

Parameters:

Name Type Description Default
app_user_id str

The user ID

required
app_company_id str

The company ID

required
Source code in components/clinic/public/dependencies.py
@abstractmethod
def is_app_user_admin_of_company(
    self,
    app_user_id: str,
    app_company_id: str,
) -> bool:
    """Check if the user is an admin of the company

    Args:
        app_user_id: The user ID
        app_company_id: The company ID
    """
    pass

release_date_of_conversations_created_for_therapy_sessions class-attribute instance-attribute

release_date_of_conversations_created_for_therapy_sessions = (
    None
)

request_id_verification_request_for_user abstractmethod

request_id_verification_request_for_user(
    app_user_id, user_info, commit=True
)

Get or request ID verification request for the user

Parameters:

Name Type Description Default
app_user_id str

The user ID

required
user_info ClinicUserDataForIdVerification

The user info for the ID verification request

required
commit bool

Whether to commit the changes

True
Source code in components/clinic/public/dependencies.py
@abstractmethod
def request_id_verification_request_for_user(
    self,
    app_user_id: str,
    user_info: ClinicUserDataForIdVerification,
    commit: bool = True,
) -> IDVerificationRequest | None:
    """Get or request ID verification request for the user

    Args:
        app_user_id: The user ID
        user_info: The user info for the ID verification request
        commit: Whether to commit the changes
    """
    pass

should_request_id_verification_for_user abstractmethod

should_request_id_verification_for_user(app_user_id)

Check if the ID verification should be requested for the user

Parameters:

Name Type Description Default
app_user_id str

The user ID

required
Source code in components/clinic/public/dependencies.py
@abstractmethod
def should_request_id_verification_for_user(
    self,
    app_user_id: str,
) -> bool:
    """Check if the ID verification should be requested for the user

    Args:
        app_user_id: The user ID
    """
    pass

update_app_user_phone abstractmethod

update_app_user_phone(app_user_id, phone)

Update the phone number of the user

Parameters:

Name Type Description Default
app_user_id str

The user ID

required
phone str | None

The phone number

required
Source code in components/clinic/public/dependencies.py
@abstractmethod
def update_app_user_phone(
    self,
    app_user_id: str,
    phone: str | None,
) -> None:
    """Update the phone number of the user

    Args:
        app_user_id: The user ID
        phone: The phone number
    """
    pass

update_app_user_ssn abstractmethod

update_app_user_ssn(app_user_id, ssn, commit=False)

Update the SSN of the user

Parameters:

Name Type Description Default
app_user_id str

The user ID

required
ssn str | None

The SSN

required
commit bool

Whether to commit the changes

False
Source code in components/clinic/public/dependencies.py
@abstractmethod
def update_app_user_ssn(
    self,
    app_user_id: str,
    ssn: str | None,
    commit: bool = False,
) -> None:
    """Update the SSN of the user

    Args:
        app_user_id: The user ID
        ssn: The SSN
        commit: Whether to commit the changes
    """
    pass

upload_invoice_as_insurance_document abstractmethod

upload_invoice_as_insurance_document(
    file, app_user_id, upload_invoice_data
)

Upload the invoice as an insurance document if relevant

Parameters:

Name Type Description Default
file IO

The file

required
app_user_id str

The user ID

required
upload_invoice_data UploadInvoiceData

The upload invoice data

required
Source code in components/clinic/public/dependencies.py
@abstractmethod
def upload_invoice_as_insurance_document(
    self,
    file: IO,  # type: ignore[type-arg]
    app_user_id: str,
    upload_invoice_data: UploadInvoiceData,
) -> bool:
    """Upload the invoice as an insurance document if relevant

    Args:
        file: The file
        app_user_id: The user ID
        upload_invoice_data: The upload invoice data
    """
    pass

user_has_24_hour_response_guarantee abstractmethod

user_has_24_hour_response_guarantee(app_user_id)

Check if the user has a 24-hour response guarantee (usually based on the company)

Parameters:

Name Type Description Default
app_user_id str

The user ID

required
Source code in components/clinic/public/dependencies.py
@abstractmethod
def user_has_24_hour_response_guarantee(
    self,
    app_user_id: str,
) -> bool:
    """Check if the user has a 24-hour response guarantee (usually based on the company)

    Args:
        app_user_id: The user ID
    """
    pass

validate_session_duration abstractmethod

validate_session_duration(session_duration)

Validate the session duration, throwing an error if it's not valid

Parameters:

Name Type Description Default
session_duration int

The session duration

required
Source code in components/clinic/public/dependencies.py
@abstractmethod
def validate_session_duration(
    self,
    session_duration: int,
) -> None:
    """Validate the session duration, throwing an error if it's not valid

    Args:
        session_duration: The session duration
    """
    pass

ClinicUserDataForIdVerification dataclass

ClinicUserDataForIdVerification(
    first_name,
    additional_first_names,
    last_name,
    birth_last_name,
    email,
    gender,
    date_of_birth,
    place_of_birth,
    session_id,
)

Data used for ID verification request for the clinic user

Attributes:

Name Type Description
first_name str

The first name of the user

last_name str

The last name of the user

email str

The email of the user

birth_last_name str

The birth last name of the user

place_of_birth str

The place of birth of the user (it's a string representing usually a city or town, to match with the information on ID cards or passports.)

date_of_birth date

The date of birth of the user

gender str

The gender of the user

additional_first_names instance-attribute

additional_first_names

birth_last_name instance-attribute

birth_last_name

date_of_birth instance-attribute

date_of_birth

email instance-attribute

email

first_name instance-attribute

first_name

gender instance-attribute

gender

last_name instance-attribute

last_name

place_of_birth instance-attribute

place_of_birth

session_id instance-attribute

session_id

Dependent dataclass

Dependent(
    app_user_id,
    first_name,
    last_name,
    gender,
    age,
    birth_date,
    dependent_type,
)

Dependent of a user, used to represent a family member

age instance-attribute

age

app_user_id instance-attribute

app_user_id

birth_date instance-attribute

birth_date

dependent_type instance-attribute

dependent_type

first_name instance-attribute

first_name

gender instance-attribute

gender

last_name instance-attribute

last_name

DependentType

Bases: AlanBaseEnum

Type of dependent

CHILD class-attribute instance-attribute

CHILD = 'child'

PARTNER class-attribute instance-attribute

PARTNER = 'partner'

UploadInvoiceData dataclass

UploadInvoiceData(
    session_id,
    starts_at,
    ends_at,
    paid_amount,
    medical_admin_identifier,
    medical_admin_name,
)

Data used to upload an invoice as an insurance document

Attributes:

Name Type Description
session_id UUID

The session ID

starts_at date

The start date

ends_at date

The end date

paid_amount float

The paid amount

medical_admin_identifier Optional[str]

The medical admin identifier

medical_admin_name str

The medical admin name

ends_at instance-attribute

ends_at

medical_admin_identifier instance-attribute

medical_admin_identifier

medical_admin_name instance-attribute

medical_admin_name

paid_amount instance-attribute

paid_amount

session_id instance-attribute

session_id

starts_at instance-attribute

starts_at

UserData dataclass

UserData(
    first_name,
    last_name,
    gender,
    email,
    profile_id,
    birth_date,
    phone,
    country,
    address,
    ssn,
    lang,
    is_alaner,
    dependents,
    is_key_account_or_large_company_and_not_alaner=False,
)

Bases: BaseUserData

Full user data necessary for the clinic

Attributes:

Name Type Description
first_name str | None

First name

last_name str | None

Last name

gender UserGender | None

Gender

email str | None

Email

profile_id UUID

Profile ID

birth_date date | None

Birth date

phone str | None

Phone number

country str | None

Country

address str | None

Address

ssn str | None

Social security number

lang Lang

Language

is_alaner bool

Whether the user is an Alan employee

dependents list[Dependent]

List of dependents

is_key_account_or_large_company_and_not_alaner bool

Whether the user is a key account or large company and not an Alan employee

address instance-attribute

address

birth_date instance-attribute

birth_date

country instance-attribute

country

dependents instance-attribute

dependents

email instance-attribute

email

gender instance-attribute

gender

is_alaner instance-attribute

is_alaner

is_key_account_or_large_company_and_not_alaner class-attribute instance-attribute

is_key_account_or_large_company_and_not_alaner = False

Used only for the alpha (and perhaps beta) releases of Mo-in-Clinic. To be removed afterwards.

lang instance-attribute

lang

phone instance-attribute

phone

profile_id instance-attribute

profile_id

ssn instance-attribute

ssn

get_app_dependency

get_app_dependency(app_name)

Get the ClinicAdapter dependency depends on the app name of the user

The app name needs to be passed because the clinic can call the logic from a country to a different one

Ex: Doctors (FR, ES BE) are connected to the FR instance. This instance needs to call BE and ES logic and data

Parameters:

Name Type Description Default
app_name str

The app name

required
Source code in components/clinic/public/dependencies.py
def get_app_dependency(app_name: str) -> ClinicAdapter:
    """
    Get the ClinicAdapter dependency depends on the app name of the user

    The app name needs to be passed because the clinic can call the logic from a country to a different one

    Ex: Doctors (FR, ES BE) are connected to the FR instance. This instance needs to call BE and ES logic and data

    Args:
        app_name: The app name
    """
    match AppName(app_name):
        case AppName.ALAN_FR:
            from components.fr.public.clinic.adapter import (
                clinic_adapter as fr_clinic_adapter,
            )

            return fr_clinic_adapter

        case AppName.ALAN_BE:
            from components.be.public.clinic.adapter import (
                clinic_adapter as be_clinic_adapter,
            )

            return be_clinic_adapter

        case AppName.ALAN_ES:
            from components.es.public.clinic.adapter import (
                clinic_adapter as es_clinic_adapter,
            )

            return es_clinic_adapter

        case _:
            raise ValueError(f"Unknown app id {app_name}")

components.clinic.public.entities

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

has_access class-attribute instance-attribute
has_access = None
has_upcoming_availability class-attribute instance-attribute
has_upcoming_availability = None
is_recommended = None
name instance-attribute
name

components.clinic.public.enums

available_health_service_name

AvailableHealthServiceName

Bases: AlanBaseEnum

Health services available to members that can be shared to them in the clinic

DATO_CONTENT class-attribute instance-attribute
DATO_CONTENT = 'dato_content'
DERMATOLOGY_CONSULTATION class-attribute instance-attribute
DERMATOLOGY_CONSULTATION = 'dermatology_consultation'
HEALTH_PROGRAM class-attribute instance-attribute
HEALTH_PROGRAM = 'health_program'
ORIENTATION_CALL class-attribute instance-attribute
ORIENTATION_CALL = 'orientation_call'
THERAPIST class-attribute instance-attribute
THERAPIST = 'therapist'
THERAPY_SESSION class-attribute instance-attribute
THERAPY_SESSION = 'therapy_session'
VIDEO_CONSULTATION class-attribute instance-attribute
VIDEO_CONSULTATION = 'video_consultation'
VIDEO_CONSULTATION_LIVI class-attribute instance-attribute
VIDEO_CONSULTATION_LIVI = 'video_consultation_livi'

therapist_booking_session_type

TherapistBookingSessionType

Bases: AlanBaseEnum

Type of booking session supported by the clinic booking experience

consultation class-attribute instance-attribute
consultation = 'consultation'
dermatology class-attribute instance-attribute
dermatology = 'dermatology'
emergency_physiotherapy class-attribute instance-attribute
emergency_physiotherapy = 'emergency_physiotherapy'
orientation class-attribute instance-attribute
orientation = 'orientation'
physiotherapy class-attribute instance-attribute
physiotherapy = 'physiotherapy'
therapy class-attribute instance-attribute
therapy = 'therapy'

components.clinic.public.events

subscription

subscribe_to_events

subscribe_to_events()

All event subscriptions for the Clinic should be done here.

Source code in components/clinic/public/events/subscription.py
def subscribe_to_events() -> None:
    """
    All event subscriptions for the Clinic should be done here.
    """
    from components.clinic.internal.booking.business_logic.events_subscribers import (
        configure_medical_conversation_when_booking_sessions,
        setup_conversation_for_therapy_session,
        trigger_insi_create_or_update_from_id_verification_request,
    )
    from components.clinic.internal.booking.entities.events import (
        SessionCreatedForInternalTeleconsultation,
        SessionCreatedForTherapy,
        TriggerINSiUpdate,
    )
    from components.clinic.internal.business_logic.identity.events_subscribers import (
        update_insi_identity_status_following_id_check,
    )
    from components.id_verification.public.events import (
        IdVerificationValidatedForClinic,
    )

    message_broker = get_message_broker()
    message_broker.subscribe_async(
        IdVerificationValidatedForClinic,
        update_insi_identity_status_following_id_check,
        queue_name=LOW_PRIORITY_QUEUE,
    )
    message_broker.subscribe_async(
        SessionCreatedForInternalTeleconsultation,
        configure_medical_conversation_when_booking_sessions,
        queue_name=LOW_PRIORITY_QUEUE,
    )
    message_broker.subscribe_async(
        SessionCreatedForTherapy,
        setup_conversation_for_therapy_session,
        queue_name=LOW_PRIORITY_QUEUE,
    )
    message_broker.subscribe_async(
        TriggerINSiUpdate,
        trigger_insi_create_or_update_from_id_verification_request,
        queue_name=LOW_PRIORITY_QUEUE,
    )