diff --git a/biocommons/emails.py b/biocommons/emails.py index 347f3020..27970c87 100644 --- a/biocommons/emails.py +++ b/biocommons/emails.py @@ -333,6 +333,43 @@ def compose_welcome_email( return subject, _wrap_email_html(subject, subject, body_content, portal_url) +def compose_group_membership_rejected_email( + *, + group_name: str, + first_name: str, + settings: Settings, +) -> tuple[str, str]: + """ + Notify a user that their group/bundle access request was rejected. + For the TSI bundle, includes detailed information about open-access alternatives. + """ + portal_url = settings.aai_portal_url or "" + safe_group_name = html.escape(group_name or "") + safe_first_name = html.escape(first_name or "") + subject = f"Your {group_name} Service Bundle request" + + body_content = f""" +

Dear {safe_first_name},

+

Thank you for your interest in the {safe_group_name} Bundle.

+

The TSI Bundle available on the Australian BioCommons Access is only for members of the Threatened Species Initiative consortium.

+

Members are collaborators who have projects supported through our request for partnership rounds. They can access datasets that are under embargo for the first 12 months following their generation.

+

The Threatened Species Initiative is generating genomic resources that are made openly accessible to the community with the standard registration to the Bioplatforms Australia data portal (now Australian BioCommons Access). You do not need to apply to the TSI Bundle to obtain access to these open access genomics datasets. This is also the case for datasets of all other initiatives presented on the Bioplatforms Data Portal.

+

Similarly the access to various tools, including Fgenesh++, can be obtained through Galaxy Australia without the need of the TSI Bundle:

+ +

Please do not hesitate to reach out to us for any non registration related issues:

+ +

Thank you,

+

BioCommons Access team

+ """ + return subject, _wrap_email_html(subject, subject, body_content, portal_url) + + def compose_bundle_request_confirmation_email( *, first_name: str, diff --git a/routers/admin.py b/routers/admin.py index 8d88c6f5..63da8db7 100644 --- a/routers/admin.py +++ b/routers/admin.py @@ -30,6 +30,7 @@ from biocommons.emails import ( compose_email_change_notification, compose_group_membership_approved_email, + compose_group_membership_rejected_email, compose_username_change_notification, format_first_name, ) @@ -1246,8 +1247,11 @@ def approve_group_membership(user_id: Annotated[str, UserIdParam], def reject_group_membership(user_id: Annotated[str, UserIdParam], group_id: Annotated[str, ServiceIdParam], payload: RejectServiceRequest, + client: Annotated[Auth0Client, Depends(get_auth0_client)], admin_record: Annotated[BiocommonsUser, Depends(get_db_user)], - db_session: Annotated[Session, Depends(get_db_session)]): + db_session: Annotated[Session, Depends(get_db_session)], + settings: Annotated[Settings, Depends(get_settings)]): + group_record = BiocommonsGroup.get_by_id_or_404(group_id, db_session) membership = GroupMembership.get_by_user_id_and_group_id_or_404( user_id=user_id, group_id=group_id, @@ -1264,6 +1268,29 @@ def reject_group_membership(user_id: Annotated[str, UserIdParam], session=db_session, commit=False, ) + if group_id == GroupEnum.TSI.value and membership.user and membership.user.email: + first_name = "there" + try: + auth0_user = client.get_user(membership.user.id) + first_name = format_first_name( + full_name=auth0_user.name, + given_name=auth0_user.given_name, + fallback="there", + ) + except Exception: + pass + subject, body_html = compose_group_membership_rejected_email( + group_name=group_record.name, + first_name=first_name, + settings=settings, + ) + enqueue_email( + session=db_session, + to_address=membership.user.email, + subject=subject, + body_html=body_html, + settings=settings, + ) db_session.commit() logger.info( "Rejected group %s for user %s: %s", diff --git a/tests/admin_api/test_admin.py b/tests/admin_api/test_admin.py index 67644797..c44b1b85 100644 --- a/tests/admin_api/test_admin.py +++ b/tests/admin_api/test_admin.py @@ -1335,6 +1335,7 @@ def test_reject_group_membership_records_reason( test_db_session, as_admin_user, tsi_group, + mock_auth0_client, persistent_factories, ): user = BiocommonsUserFactory.create_sync(group_memberships=[]) @@ -1369,6 +1370,35 @@ def test_reject_group_membership_records_reason( assert history[-1].reason == reason +def test_reject_group_membership_sends_email( + test_client, + test_db_session, + as_admin_user, + tsi_group, + mock_auth0_client, + persistent_factories, +): + user = BiocommonsUserFactory.create_sync(group_memberships=[]) + GroupMembershipFactory.create_sync( + group=tsi_group, + user=user, + approval_status=ApprovalStatusEnum.PENDING.value, + ) + test_db_session.commit() + + group_url = quote(tsi_group.group_id, safe='') + resp = test_client.post( + f"/admin/users/{user.id}/groups/{group_url}/reject", + json={"reason": "Not a TSI consortium member"}, + ) + + assert resp.status_code == 200 + queued_emails = test_db_session.exec(select(EmailNotification)).all() + assert len(queued_emails) == 1 + assert queued_emails[0].to_address == user.email + assert queued_emails[0].status == EmailStatusEnum.PENDING + + def test_reject_group_membership_forbidden_without_group_role( test_client, test_db_session,