Skip to content

Commit

Permalink
Merge pull request #2654 from carpentries/feature/2648-new-self-organ…
Browse files Browse the repository at this point in the history
…ised-workshop

[Emails] New self organised workshop
  • Loading branch information
pbanaszkiewicz committed Jun 3, 2024
2 parents cb476a2 + 3b34665 commit 9f2302a
Show file tree
Hide file tree
Showing 30 changed files with 764 additions and 88 deletions.
47 changes: 47 additions & 0 deletions amy/api/v2/serializers.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from rest_framework import serializers

from emails.models import MAX_LENGTH, ScheduledEmail
from extrequests.models import SelfOrganisedSubmission
from recruitment.models import InstructorRecruitmentSignup
from workshops.models import (
Award,
Expand Down Expand Up @@ -308,3 +309,49 @@ class Meta:
"event_required",
"involvement_required",
)


class SelfOrganisedSubmissionSerializer(serializers.ModelSerializer):
event = serializers.SlugRelatedField(read_only=True, slug_field="slug")
additional_contact = serializers.CharField()
country = serializers.CharField()
workshop_types = serializers.SlugRelatedField(
many=True, read_only=True, slug_field="name"
)

class Meta:
model = SelfOrganisedSubmission
fields = (
"pk",
"assigned_to",
"state",
"created_at",
"last_updated_at",
"data_privacy_agreement",
"code_of_conduct_agreement",
"host_responsibilities",
"event",
"personal",
"family",
"email",
"institution",
"institution_other_name",
"institution_other_URL",
"institution_department",
"member_code",
"online_inperson",
"workshop_listed",
"public_event",
"public_event_other",
"additional_contact",
"start",
"end",
"workshop_url",
"workshop_format",
"workshop_format_other",
"workshop_types",
"workshop_types_other",
"workshop_types_other_explain",
"country",
"language",
)
1 change: 1 addition & 0 deletions amy/api/v2/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
router.register("scheduledemail", views.ScheduledEmailViewSet)
router.register("trainingprogress", views.TrainingProgressViewSet)
router.register("trainingrequirement", views.TrainingRequirementViewSet)
router.register("selforganisedsubmission", views.SelfOrganisedSubmissionViewSet)


urlpatterns = [
Expand Down
17 changes: 17 additions & 0 deletions amy/api/v2/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,13 @@
PersonSerializer,
ScheduledEmailLogDetailsSerializer,
ScheduledEmailSerializer,
SelfOrganisedSubmissionSerializer,
TrainingProgressSerializer,
TrainingRequirementSerializer,
)
from emails.controller import EmailController
from emails.models import ScheduledEmail, ScheduledEmailStatus
from extrequests.models import SelfOrganisedSubmission
from recruitment.models import InstructorRecruitmentSignup
from workshops.models import (
Award,
Expand Down Expand Up @@ -251,3 +253,18 @@ class TrainingRequirementViewSet(viewsets.ReadOnlyModelViewSet):
queryset = TrainingRequirement.objects.order_by("pk").all()
serializer_class = TrainingRequirementSerializer
pagination_class = StandardResultsSetPagination


class SelfOrganisedSubmissionViewSet(viewsets.ReadOnlyModelViewSet):
authentication_classes = (
TokenAuthentication,
SessionAuthentication,
)
permission_classes = (
IsAuthenticated,
ApiAccessPermission,
)

queryset = SelfOrganisedSubmission.objects.order_by("pk").all()
serializer_class = SelfOrganisedSubmissionSerializer
pagination_class = StandardResultsSetPagination
3 changes: 3 additions & 0 deletions amy/emails/actions/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@
new_membership_onboarding_receiver,
new_membership_onboarding_update_receiver,
)
from emails.actions.new_self_organised_workshop import (
new_self_organised_workshop_receiver,
)
from emails.actions.persons_merged import persons_merged_receiver
from emails.actions.post_workshop_7days import (
post_workshop_7days_cancel_receiver,
Expand Down
4 changes: 2 additions & 2 deletions amy/emails/actions/admin_signs_instructor_up_for_workshop.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,8 @@ def get_recipients_context_json(
{
"api_uri": api_model_url("person", context["person"].pk),
"property": "email",
},
], # type: ignore
}, # type: ignore
],
)


Expand Down
22 changes: 15 additions & 7 deletions amy/emails/actions/host_instructors_introduction.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,12 @@
HostInstructorsIntroductionKwargs,
StrategyEnum,
)
from emails.utils import api_model_url, immediate_action, scalar_value_none
from emails.utils import (
api_model_url,
immediate_action,
log_condition_elements,
scalar_value_none,
)
from recruitment.models import InstructorRecruitment
from workshops.models import Event, Task

Expand All @@ -41,17 +46,20 @@ def host_instructors_introduction_strategy(event: Event) -> StrategyEnum:
start_date_in_at_least_7days = event.start and event.start >= (
timezone.now().date() + timedelta(days=7)
)
logger.debug(
f"{no_open_recruitment=}, {not_self_organised=}, "
f"{start_date_in_at_least_7days=}"
)

active = not event.tags.filter(name__in=["cancelled", "unresponsive", "stalled"])
host = Task.objects.filter(role__name="host", event=event).first()
at_least_2_instructors = (
Task.objects.filter(role__name="instructor", event=event).count() >= 2
)
logger.debug(f"{active=}, {host=}, {at_least_2_instructors=}")

log_condition_elements(
not_self_organised=not_self_organised,
no_open_recruitment=no_open_recruitment,
start_date_in_at_least_7days=start_date_in_at_least_7days,
active=active,
host=host,
at_least_2_instructors=at_least_2_instructors,
)

email_should_exist = (
not_self_organised
Expand Down
4 changes: 2 additions & 2 deletions amy/emails/actions/instructor_badge_awarded.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,8 @@ def get_recipients_context_json(
{
"api_uri": api_model_url("person", context["person"].pk),
"property": "email",
},
], # type: ignore
}, # type: ignore
],
)


Expand Down
4 changes: 2 additions & 2 deletions amy/emails/actions/instructor_confirmed_for_workshop.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,8 @@ def get_recipients_context_json(
{
"api_uri": api_model_url("person", context["person"].pk),
"property": "email",
},
], # type: ignore
}, # type: ignore
],
)


Expand Down
4 changes: 2 additions & 2 deletions amy/emails/actions/instructor_declined_from_workshop.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,8 @@ def get_recipients_context_json(
{
"api_uri": api_model_url("person", context["person"].pk),
"property": "email",
},
], # type: ignore
}, # type: ignore
],
)


Expand Down
4 changes: 2 additions & 2 deletions amy/emails/actions/instructor_signs_up_for_workshop.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,8 @@ def get_recipients_context_json(
{
"api_uri": api_model_url("person", context["person"].pk),
"property": "email",
},
], # type: ignore
}, # type: ignore
],
)


Expand Down
9 changes: 7 additions & 2 deletions amy/emails/actions/instructor_training_approaching.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
InstructorTrainingApproachingKwargs,
StrategyEnum,
)
from emails.utils import api_model_url, one_month_before
from emails.utils import api_model_url, log_condition_elements, one_month_before
from workshops.models import Event, Task

logger = logging.getLogger("amy")
Expand All @@ -36,7 +36,12 @@ def instructor_training_approaching_strategy(event: Event) -> StrategyEnum:
Task.objects.filter(event=event, role__name="instructor").count() >= 2
)
start_date_in_future = event.start and event.start >= timezone.now().date()
logger.debug(f"{has_TTT=}, {has_at_least_2_instructors=}, {start_date_in_future=}")

log_condition_elements(
has_TTT=has_TTT,
has_at_least_2_instructors=has_at_least_2_instructors,
start_date_in_future=start_date_in_future,
)

email_should_exist = has_TTT and has_at_least_2_instructors and start_date_in_future
logger.debug(f"{email_should_exist=}")
Expand Down
18 changes: 15 additions & 3 deletions amy/emails/actions/instructor_training_completed_not_badged.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,12 @@
InstructorTrainingCompletedNotBadgedKwargs,
StrategyEnum,
)
from emails.utils import api_model_url, scalar_value_url, two_months_after
from emails.utils import (
api_model_url,
log_condition_elements,
scalar_value_url,
two_months_after,
)
from workshops.models import Person, TrainingProgress, TrainingRequirement

logger = logging.getLogger("amy")
Expand Down Expand Up @@ -82,6 +87,13 @@ def instructor_training_completed_not_badged_strategy(person: Person) -> Strateg
],
).exists()

log_condition_elements(
**{
"person_annotated.passed_training": person_annotated.passed_training,
"all_requirements_passed": all_requirements_passed,
}
)

email_should_exist = (
bool(person_annotated.passed_training) and not all_requirements_passed
)
Expand Down Expand Up @@ -227,8 +239,8 @@ def get_recipients_context_json(
{
"api_uri": api_model_url("person", context["person"].pk),
"property": "email",
}
], # type: ignore
} # type: ignore
],
)


Expand Down
24 changes: 20 additions & 4 deletions amy/emails/actions/new_membership_onboarding.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,12 @@
NewMembershipOnboardingKwargs,
StrategyEnum,
)
from emails.utils import api_model_url, immediate_action, one_month_before
from emails.utils import (
api_model_url,
immediate_action,
log_condition_elements,
one_month_before,
)
from fiscal.models import MembershipTask
from workshops.models import Membership

Expand All @@ -40,15 +45,26 @@ def new_membership_onboarding_strategy(membership: Membership) -> StrategyEnum:
template__signal=NEW_MEMBERSHIP_ONBOARDING_SIGNAL_NAME,
state=ScheduledEmailStatus.SCHEDULED,
).exists()
task_count = MembershipTask.objects.filter(
membership=membership, role__name__in=MEMBERSHIP_TASK_ROLES_EXPECTED
).count()

log_condition_elements(
**{
"membership.pk": membership.pk,
"membership.rolled_from_membership": getattr(
membership, "rolled_from_membership", None
),
"task_count": task_count,
}
)

# Membership can't be removed without removing the tasks first. This is when the
# email would be de-scheduled.
email_should_exist = (
membership.pk
and getattr(membership, "rolled_from_membership", None) is None
and MembershipTask.objects.filter(
membership=membership, role__name__in=MEMBERSHIP_TASK_ROLES_EXPECTED
).count()
and task_count
)

if not email_scheduled and email_should_exist:
Expand Down
Loading

0 comments on commit 9f2302a

Please sign in to comment.