Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[FEATURE] [MER-4106] Check certification worker #5438

Draft
wants to merge 13 commits into
base: master
Choose a base branch
from

Conversation

Francisco-Castro
Copy link
Contributor

@Francisco-Castro Francisco-Castro commented Feb 25, 2025

Ticket: MER-4106
Parent ticket (description is here): MER-3796

⚠️ Set up this PR as a draft because I'm going to modify how a student crosses the graded page thresholds.

Things to consider when reviewing this PR:

  • Commit 5579bac indicates the places where a student may trigger the threshold check: graded.ex, discussions_live.ex, and lesson_live.ex.

  • The conditions that may trigger the threshold checks are indicated in certification_eligibility.ex (see commit 6c00c0d)

  • The LV plug set_require_certification_check.ex prevents the CheckCertification worker from running if either of the following conditions are met:

    • The section has certificates disabled (false).

    • The student has already earned a certificate with distinction.

    • In these cases, a flag is added to stop the checks. (see here)

  • The workflow described in the ticket can be followed using the has_qualified function in delivery/granted_certificates.ex. This function returns tuples that indicate the appropriate action: do nothing, notify the instructor, notify the student, or create a certificate.

NOTE:

  • Two atoms are used in the following functions:

    • :send_email_to_instructor and :send_email_to_student
  • Functions:

    • create_granted_cert_maybe_spawn_builder – riggered when a student earns either a completion or a distinction certificate.
    • update_granted_certificate_with_distinction – Triggered when a student earns a distinction certificate.
  • These atoms serve as references for where the email-sending logic should be implemented.

The sending email work is part of MER-4107. @manelli

Copy link
Contributor

@nicocirio nicocirio left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Awesome work Fran! I will continue reviewing it later, but for now I leave some comments (I'm not sure if the worker should trigger the email notification -to inst or student-)

Comment on lines 452 to 454
if certificate.requires_instructor_approval do
{:ok, :send_email_to_instructor}
else
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we also create the granted certificate (with a :pending state)? or do you think we should create the GC later?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch. I’ve changed the logic here. Now, we create the certificate and determine which entity the email should be sent to.

@impl Oban.Worker
def perform(%Oban.Job{args: %{"user_id" => user_id, "section_id" => section_id}}) do
GrantedCertificates.has_qualified(user_id, section_id)

Copy link
Contributor

@nicocirio nicocirio Feb 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we might add a TODO here to explain that:

  • if we get a {:ok, :send_email_to_instructor} we want to trigger that process.
  • if an :earned GC was created/updated (I believe is the {:ok, granted_cert} case) we need to trigger a student email notification (this case corresponds to courses that do not require instructor approval)

If we already have a mer ticket, we can add it's number as part of the "TODO" message.

UPDATE:

Re-reading the ticket's pseudo-code, I understand we are in this worker:

CHECK_CERT (student, section, cert_details)
    case TRANSITION_CERT(student, section, cert_details) do 
      {:ok, :no_change} -> do nothing
      {:ok, :notify_instructor} -> Send email to instructor for approval (I believe all instructors must be notified)
      {:ok, :notify_student} -> Spawn Async BUILD_CERT_FOR_STUDENT job
    end
end

The third clause should be handled by this one:

BUILD_CERT_FOR_STUDENT routine (student_id, cert_id):
  case GENERATE_AND_STORE_CERT(student_id, cert_id) do 
    {:ok, granted_cert_details} -> Send email to student
    {:error, e} -> {:error}
  end
end

And the GENERATE_AND_STORE_CERT is already built (lib/oli/delivery/sections/certificates/workers/generate_pdf.ex)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I made some modifications to the code to address the notification part. I also added references in the description to indicate where this work should be connected to the emailing functionality

@Francisco-Castro Francisco-Castro marked this pull request as ready for review February 25, 2025 21:25
@Francisco-Castro Francisco-Castro marked this pull request as draft February 26, 2025 15:28
Comment on lines +374 to +392
current_percentage =
from(ra in ResourceAccess,
where: ra.section_id == ^section_id,
where: ra.user_id == ^user_id,
where: ra.resource_id in ^required_assignment_ids,
select:
fragment(
"COALESCE(SUM(? / ? * 100) / ?, 0.0)",
ra.score,
ra.out_of,
^total_required_assignments
)
)
|> Oli.Repo.one()

distinction_result =
if current_percentage >= min_percentage_for_distinction,
do: :passed_min_percentage_for_distinction,
else: :failed_min_percentage_for_distinction
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This logic checks if the average score of the required assignments exceeds the required threshold. However, according to the Figma design we should check that all the pages (individually) exceed that required value.

Comment on lines +404 to +427
current_percentage =
from(ra in ResourceAccess,
where: ra.section_id == ^section_id,
where: ra.user_id == ^user_id,
where: ra.resource_id in ^required_assignment_ids,
select:
fragment(
"COALESCE(SUM(? / ? * 100) / ?, 0.0)",
ra.score,
ra.out_of,
^total_required_assignments
)
)
|> Oli.Repo.one()

completion_result =
if current_percentage >= min_percentage_for_completion,
do: :passed_min_percentage_for_completion,
else: :failed_min_percentage_for_completion

distinction_result =
if current_percentage >= min_percentage_for_distinction,
do: :passed_min_percentage_for_distinction,
else: :failed_min_percentage_for_distinction
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants