Skip to content

Commit

Permalink
Dashboard: Adding "You passed this course" message with no certificat…
Browse files Browse the repository at this point in the history
…es (#5310)
  • Loading branch information
annagav authored Mar 8, 2023
1 parent 0e5d58b commit d40fa6d
Show file tree
Hide file tree
Showing 12 changed files with 65 additions and 39 deletions.
7 changes: 4 additions & 3 deletions dashboard/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -247,7 +247,8 @@ def get_info_for_course(course, mmtrack):
"is_elective": ElectiveCourse.objects.filter(course=course).exists(),
"has_exam": course.has_exam,
"certificate_url": get_certificate_url(mmtrack, course),
"overall_grade": mmtrack.get_overall_final_grade_for_course(course)
"overall_grade": mmtrack.get_overall_final_grade_for_course(course),
"is_passed": mmtrack.has_passed_course(course)
}

def _add_run(run, mmtrack_, status):
Expand Down Expand Up @@ -303,7 +304,7 @@ def _add_run(run, mmtrack_, status):
_add_run(run_status.course_run, mmtrack, CourseStatus.CURRENTLY_ENROLLED)
# check if we need to check the certificate
elif run_status.status == CourseRunStatus.CHECK_IF_PASSED:
if not mmtrack.has_passed_course(run_status.course_run.edx_course_key):
if not mmtrack.has_passed_course_run(run_status.course_run.edx_course_key):
_add_run(run_status.course_run, mmtrack, CourseRunStatus.NOT_PASSED)
else:
_add_run(run_status.course_run, mmtrack, CourseStatus.PASSED)
Expand All @@ -329,7 +330,7 @@ def _add_run(run, mmtrack_, status):
# the first one (or two in some cases) has been added with the logic before
for run_status in run_statuses:
if run_status.status == CourseRunStatus.CHECK_IF_PASSED:
if mmtrack.has_passed_course(run_status.course_run.edx_course_key):
if mmtrack.has_passed_course_run(run_status.course_run.edx_course_key):
# in this case the user might have passed the course also in the past
_add_run(run_status.course_run, mmtrack, CourseStatus.PASSED)
else:
Expand Down
16 changes: 9 additions & 7 deletions dashboard/api_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -901,6 +901,7 @@ def setUp(self):
# default behavior for some mmtrack mocked methods
self.mmtrack.get_course_proctorate_exam_results.return_value = []
self.mmtrack.get_overall_final_grade_for_course.return_value = ""
self.mmtrack.has_passed_course.return_value = False

def assert_course_equal(
self,
Expand All @@ -915,7 +916,7 @@ def assert_course_equal(
has_to_pay=False,
has_exam=False,
is_elective=False,
proct_exams=None
proct_exams=None,
):
"""Helper to format the course info"""
proct_exams = proct_exams or []
Expand All @@ -939,6 +940,7 @@ def assert_course_equal(
"has_exam": has_exam,
"certificate_url": "",
"overall_grade": "",
"is_passed": False
}
# remove the runs part: assumed checked with the mock assertion
del course_data_from_call['runs']
Expand Down Expand Up @@ -1142,7 +1144,7 @@ def test_info_not_enrolled_not_passed_not_offered(
self, mock_schedulable, mock_format, mock_get_cert, mock_future_exams, mock_has_to_pay, mock_exam_course_key):
"""test for get_info_for_course for course with run not passed and nothing offered"""
self.mmtrack.configure_mock(**{
'has_passed_course.return_value': False,
'has_passed_course_run.return_value': False,
'is_enrolled_mmtrack.return_value': True
})
with patch(
Expand All @@ -1169,7 +1171,7 @@ def test_info_grade(
self, mock_schedulable, mock_format, mock_get_cert, mock_future_exams, mock_has_to_pay, mock_exam_course_key):
"""test for get_info_for_course for course with a course current and another not passed"""
self.mmtrack.configure_mock(**{
'has_passed_course.return_value': False,
'has_passed_course_run.return_value': False,
'is_enrolled_mmtrack.return_value': True
})
with patch(
Expand Down Expand Up @@ -1198,7 +1200,7 @@ def test_info_check_but_not_passed(
test for get_info_for_course in case a check if the course has been passed is required
"""
self.mmtrack.configure_mock(**{
'has_passed_course.return_value': False,
'has_passed_course_run.return_value': False,
'is_enrolled_mmtrack.return_value': True
})
with patch(
Expand Down Expand Up @@ -1255,7 +1257,7 @@ def test_info_check_but_not_passed_no_next(
test for get_info_for_course in case a check if the course has been passed
is required for the course, the course has not been passed and there is no next run
"""
self.mmtrack.configure_mock(**{'has_passed_course.return_value': False})
self.mmtrack.configure_mock(**{'has_passed_course_run.return_value': False})
with patch(
'dashboard.api.get_status_for_courserun',
autospec=True,
Expand Down Expand Up @@ -1283,7 +1285,7 @@ def test_info_check_passed(
is required for the course and the course has been passed
"""
self.mmtrack.configure_mock(**{
'has_passed_course.return_value': True,
'has_passed_course_run.return_value': True,
'is_enrolled_mmtrack.return_value': True
})
with patch(
Expand Down Expand Up @@ -1433,7 +1435,7 @@ def test_info_read_cert_for_all_no_next(
test for get_info_for_course in case the less recent course is flagged to be checked if passed
"""
self.mmtrack.configure_mock(**{
'has_passed_course.return_value': True,
'has_passed_course_run.return_value': True,
'is_enrolled_mmtrack.return_value': True
})
with patch(
Expand Down
28 changes: 26 additions & 2 deletions dashboard/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -358,18 +358,42 @@ def paid_but_missed_deadline(self, course_run):
return True
return False

def has_passed_course(self, edx_course_key):
def has_passed_course_run(self, edx_course_key):
"""
Returns whether the user has passed a course run.
Args:
edx_course_key (str): an edX course run key
Returns:
bool: whether the user has passed the course
bool: whether the user has passed the course_run
"""
final_grade = self.get_final_grade(edx_course_key)
return final_grade.passed if final_grade else False

def has_passed_course(self, course):
"""
Returns true if the user has passed this course overall
Args:
course (Course): course instance
Returns:
bool: whether the user has passed the course
"""
if self.has_exams:
course_cert = MicromastersCourseCertificate.objects.filter(
user=self.user,
course_id=course.id).exists()
if course_cert:
return True
else:
return FinalGrade.objects.filter(
user=self.user,
course_run__course_id=course.id,
course_run__start_date__gt=datetime.datetime(2022, 9, 1, tzinfo=pytz.UTC),
).passed().exists()
else:
return self.final_grade_qset.filter(course_run__course_id=course.id).passed().exists()

def get_final_grade_percent(self, edx_course_key):
"""
Returns the course final grade number for the user if she passed.
Expand Down
12 changes: 6 additions & 6 deletions dashboard/utils_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -259,9 +259,9 @@ def test_is_enrolled_mmtrack_fa(self):
assert mmtrack.is_enrolled_mmtrack(course_id) is True

@ddt.data(True, False)
def test_has_passed_course(self, final_grade_passed):
def test_has_passed_course_run(self, final_grade_passed):
"""
Test that has_passed_course returns True when a passed FinalGrade exists
Test that has_passed_course_run returns True when a passed FinalGrade exists
"""
final_grade = FinalGradeFactory.create(
user=self.user,
Expand All @@ -273,18 +273,18 @@ def test_has_passed_course(self, final_grade_passed):
program=self.program,
edx_user_data=self.cached_edx_user_data
)
assert mmtrack.has_passed_course(final_grade.course_run.edx_course_key) is final_grade_passed
assert mmtrack.has_passed_course_run(final_grade.course_run.edx_course_key) is final_grade_passed

def test_has_passed_course_no_grade(self):
def test_has_passed_course_run_no_grade(self):
"""
Test that has_passed_course returns False when no FinalGrade exists
Test that has_passed_course_run returns False when no FinalGrade exists
"""
mmtrack = MMTrack(
user=self.user,
program=self.program,
edx_user_data=self.cached_edx_user_data
)
assert mmtrack.has_passed_course('random-course-id') is False
assert mmtrack.has_passed_course_run('random-course-id') is False

def test_get_final_grade_percent(self):
"""
Expand Down
2 changes: 1 addition & 1 deletion exams/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ def authorize_for_exam_run(user, course_run, exam_run):
ExamProfile.objects.get_or_create(profile=mmtrack.user.profile)

# if they didn't pass, they don't get authorized
if not mmtrack.has_passed_course(course_run.edx_course_key):
if not mmtrack.has_passed_course_run(course_run.edx_course_key):
errors_message = MESSAGE_NOT_PASSED_OR_EXIST_TEMPLATE.format(
user=mmtrack.user.username,
course_id=course_run.edx_course_key
Expand Down
10 changes: 5 additions & 5 deletions exams/api_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ def test_exam_authorization_for_inactive_user(self):
create_order(user, self.course_run)
mmtrack = get_mmtrack(user, self.program)
self.assertTrue(mmtrack.has_paid(self.course_run.edx_course_key))
self.assertTrue(mmtrack.has_passed_course(self.course_run.edx_course_key))
self.assertTrue(mmtrack.has_passed_course_run(self.course_run.edx_course_key))

# Neither user has exam profile nor authorization.
assert ExamProfile.objects.filter(profile=mmtrack.user.profile).exists() is False
Expand All @@ -123,7 +123,7 @@ def test_exam_authorization(self):
create_order(self.user, self.course_run)
mmtrack = get_mmtrack(self.user, self.program)
self.assertTrue(mmtrack.has_paid(self.course_run.edx_course_key))
self.assertTrue(mmtrack.has_passed_course(self.course_run.edx_course_key))
self.assertTrue(mmtrack.has_passed_course_run(self.course_run.edx_course_key))

# Neither user has exam profile nor authorization.
assert ExamProfile.objects.filter(profile=mmtrack.user.profile).exists() is False
Expand All @@ -148,7 +148,7 @@ def test_exam_authorization_attempts_consumed(self):
create_order(self.user, self.course_run)
mmtrack = get_mmtrack(self.user, self.program)
self.assertTrue(mmtrack.has_paid(self.course_run.edx_course_key))
self.assertTrue(mmtrack.has_passed_course(self.course_run.edx_course_key))
self.assertTrue(mmtrack.has_passed_course_run(self.course_run.edx_course_key))
old_run = ExamRunFactory.create(course=self.course_run.course)
ExamAuthorizationFactory.create_batch(
ATTEMPTS_PER_PAID_RUN_OLD,
Expand Down Expand Up @@ -273,14 +273,14 @@ def test_exam_authorization_when_not_passed_course(self):
test exam_authorization when user has not passed course but paid.
"""
create_order(self.user, self.course_run)
with patch('dashboard.utils.MMTrack.has_passed_course', autospec=True, return_value=False):
with patch('dashboard.utils.MMTrack.has_passed_course_run', autospec=True, return_value=False):
mmtrack = get_mmtrack(self.user, self.program)
expected_errors_message = MESSAGE_NOT_PASSED_OR_EXIST_TEMPLATE.format(
user=mmtrack.user.username,
course_id=self.course_run.edx_course_key
)
assert mmtrack.has_paid(self.course_run.edx_course_key) is True
assert mmtrack.has_passed_course(self.course_run.edx_course_key) is False
assert mmtrack.has_passed_course_run(self.course_run.edx_course_key) is False

# Neither user has exam profile nor authorization.
assert ExamProfile.objects.filter(profile=mmtrack.user.profile).exists() is False
Expand Down
4 changes: 2 additions & 2 deletions seed_data/management/commands/alter_data_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ def test_set_to_passed(self, course_run_program_type):
grade = Decimal('0.75')
set_to_passed(user=self.user, course_run=course_run, grade=grade)
mmtrack = get_mmtrack(self.user, course_run.course.program)
assert mmtrack.has_passed_course(course_run.edx_course_key)
assert mmtrack.has_passed_course_run(course_run.edx_course_key)
assert int(mmtrack.get_final_grade_percent(course_run.edx_course_key)) == (grade * 100)

@ddt.data('fa', 'non_fa')
Expand All @@ -45,7 +45,7 @@ def test_set_to_failed(self, course_run_program_type):
grade = Decimal('0.55')
set_to_failed(user=self.user, course_run=course_run, grade=grade)
mmtrack = get_mmtrack(self.user, course_run.course.program)
assert not mmtrack.has_passed_course(course_run.edx_course_key)
assert not mmtrack.has_passed_course_run(course_run.edx_course_key)
assert int(mmtrack.get_final_grade_percent(course_run.edx_course_key)) == (grade * 100)

@ddt.data('fa', 'non_fa')
Expand Down
3 changes: 2 additions & 1 deletion static/js/components/CouponNotificationDialog_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,8 @@ const COURSE: Course = {
proctorate_exams_grades: [],
is_elective: false,
certificate_url: "",
overall_grade: ""
overall_grade: "",
is_passed: false
}

const PROGRAM: AvailableProgram = {
Expand Down
13 changes: 5 additions & 8 deletions static/js/components/dashboard/courses/StatusMessages.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ import {
hasCanUpgradeCourseRun,
hasMissedDeadlineCourseRun
} from "./util"
import { hasPassingExamGrade, hasPassedCourseRun } from "../../../lib/grades"
import { hasPassedCourseRun } from "../../../lib/grades"
import { formatPrettyDateTimeAmPmTz, parseDateString } from "../../../util/date"

type Message = {
Expand Down Expand Up @@ -113,7 +113,6 @@ export const calculateMessages = (props: CalculateMessagesProps) => {

const exams = course.has_exam
const paid = firstRun.has_paid
const passedExam = hasPassingExamGrade(course)
const paymentDueDate = moment(
R.defaultTo("", firstRun.course_upgrade_deadline)
)
Expand Down Expand Up @@ -200,12 +199,10 @@ export const calculateMessages = (props: CalculateMessagesProps) => {
})
}
// course is passed and paid but no certificate yet
if (firstRun["status"] === "passed" && paid && !course.certificate_url) {
if (!exams || (exams && passedExam)) {
messages.push({
message: "You passed this course."
})
}
if (!course.certificate_url && course.is_passed) {
messages.push({
message: "You passed this course."
})
}
// Course is running, user has already paid,
if (
Expand Down
5 changes: 2 additions & 3 deletions static/js/components/dashboard/courses/StatusMessages_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -307,9 +307,7 @@ describe("Course Status Messages", () => {

it("should prompt when passed the course", () => {
course.has_exam = true
course.can_schedule_exam = true
course.proctorate_exams_grades = [makeProctoredExamResult()]
course.proctorate_exams_grades[0].passed = true
course.is_passed = true

const messages = calculateMessages(calculateMessagesProps).value
assert.equal(messages[0]["message"], "You passed this course.")
Expand All @@ -320,6 +318,7 @@ describe("Course Status Messages", () => {
makeRunPast(course.runs[0])
makeRunPassed(course.runs[0])
makeRunPaid(course.runs[0])
course.is_passed = true
assertIsJust(calculateMessages(calculateMessagesProps), [
{
message: "You passed this course."
Expand Down
3 changes: 2 additions & 1 deletion static/js/factories/dashboard.js
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,8 @@ export const makeCourse = (positionInProgram: number): Course => {
is_elective: false,
proctorate_exams_grades: [],
certificate_url: "",
overall_grade: ""
overall_grade: "",
is_passed: false
}
}

Expand Down
1 change: 1 addition & 0 deletions static/js/flow/programTypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ export type Course = {
has_exam: boolean,
certificate_url: string,
overall_grade: string,
is_passed: boolean,
}

export type ProgramPageCourse = {
Expand Down

0 comments on commit d40fa6d

Please sign in to comment.