def create_test_certificate(course, course_key, university): """ Generate the pdf certicate, save it on disk """ if university.certificate_logo: logo_path = os.path.join(university.certificate_logo.url, university.certificate_logo.path) else: logo_path = None teachers = get_teachers_list_from_course(course_key) certificate_language = Course.get_course_language(unicode(course_key)) key = make_hashkey(random.random()) filename = "TEST_attestation_suivi_%s_%s.pdf" % ( course.id.to_deprecated_string().replace('/', '_'), key ) certificate = CertificateInfo(settings.STUDENT_NAME_FOR_TEST_CERTIFICATE, course.display_name, university.name, logo_path, filename, teachers, language=certificate_language ) certificate.generate() return certificate
def send_to_grader(self, submission, system): """ Send a given submission to the grader, via the xqueue @param submission: The student submission to send to the grader @param system: Modulesystem @return: Boolean true (not useful right now) """ # Prepare xqueue request #------------------------------------------------------------ xqueue = system.get('xqueue') if xqueue is None: return False qinterface = xqueue['interface'] qtime = datetime.strftime(datetime.now(), xqueue_interface.dateformat) anonymous_student_id = system.anonymous_student_id # Generate header queuekey = xqueue_interface.make_hashkey(str(system.seed) + qtime + anonymous_student_id + str(len(self.child_history))) xheader = xqueue_interface.make_xheader( lms_callback_url=xqueue['construct_callback'](), lms_key=queuekey, queue_name=self.queue_name ) contents = self.payload.copy() # Metadata related to the student submission revealed to the external # grader student_info = { 'anonymous_student_id': anonymous_student_id, 'submission_time': qtime, } # Update contents with student response and student info contents.update({ 'student_info': json.dumps(student_info), 'student_response': submission, 'max_score': self.max_score(), }) # Submit request. When successful, 'msg' is the prior length of the # queue qinterface.send_to_queue( header=xheader, body=json.dumps(contents) ) # State associated with the queueing request queuestate = { 'key': queuekey, 'time': qtime, } return True
def send_to_grader(self, submission, system): """ Send a given submission to the grader, via the xqueue @param submission: The student submission to send to the grader @param system: Modulesystem @return: Boolean true (not useful right now) """ # Prepare xqueue request #------------------------------------------------------------ xqueue = system.get('xqueue') if xqueue is None: return False qinterface = xqueue['interface'] qtime = datetime.strftime(datetime.now(UTC), xqueue_interface.dateformat) anonymous_student_id = system.anonymous_student_id # Generate header queuekey = xqueue_interface.make_hashkey(str(system.seed) + qtime + anonymous_student_id + str(len(self.child_history))) xheader = xqueue_interface.make_xheader( lms_callback_url=xqueue['construct_callback'](), lms_key=queuekey, queue_name=self.queue_name ) contents = self.payload.copy() # Metadata related to the student submission revealed to the external grader student_info = { 'anonymous_student_id': anonymous_student_id, 'submission_time': qtime, } # Update contents with student response and student info contents.update({ 'student_info': json.dumps(student_info), 'student_response': submission, 'max_score': self.max_score(), }) # Submit request. When successful, 'msg' is the prior length of the queue qinterface.send_to_queue( header=xheader, body=json.dumps(contents) ) # State associated with the queueing request queuestate = { 'key': queuekey, 'time': qtime, } return True
def _generate_cert(self, cert, course, student, grade_contents, template_pdf, generate_pdf): """ Generate a certificate for the student. If `generate_pdf` is True, sends a request to XQueue. """ course_id = unicode(course.id) key = make_hashkey(random.random()) cert.key = key contents = { 'action': 'create', 'username': student.username, 'course_id': course_id, 'course_name': course.display_name or course_id, 'name': cert.name, 'grade': grade_contents, 'template_pdf': template_pdf, } if generate_pdf: cert.status = status.generating else: cert.status = status.downloadable cert.verify_uuid = uuid4().hex cert.save() logging.info(u'certificate generated for user: %s with generate_pdf status: %s', student.username, generate_pdf) if generate_pdf: try: self._send_to_xqueue(contents, key) except XQueueAddToQueueError as exc: cert.status = ExampleCertificate.STATUS_ERROR cert.error_reason = unicode(exc) cert.save() LOGGER.critical( ( u"Could not add certificate task to XQueue. " u"The course was '%s' and the student was '%s'." u"The certificate task status has been marked as 'error' " u"and can be re-submitted with a management command." ), course_id, student.id ) else: LOGGER.info( ( u"The certificate status has been set to '%s'. " u"Sent a certificate grading task to the XQueue " u"with the key '%s'. " ), cert.status, key ) return cert
def generate_fun_certificate(student, course_id, course_display_name, course, teachers, organization_display_name, organization_logo, certificate_base_filename, ignore_grades, new_grade, fail_flag): """Generates a certificate for one student and one course.""" profile = UserProfile.objects.get(user=student) student_name = unicode(profile.name).encode('utf-8') # grade the student cert, _created = GeneratedCertificate.objects.get_or_create( user=student, course_id=course_id ) request.user = student grade = grades.grade(student, request, course) cert.grade = grade['percent'] cert.user = student cert.course_id = course_id cert.name = profile.name fail = False if ignore_grades: cert.grade = 1 elif new_grade: fail = fail_flag cert.grade = new_grade elif grade['grade'] is None: ## edx grading fail = True if fail: cert.status = status.notpassing else: key = make_hashkey(random.random()) cert.key = key certificate_filename = certificate_base_filename + key + ".pdf" certificate_language = Course.get_course_language(unicode(course_id)) info = CertificateInfo( student_name, course_display_name, organization_display_name, organization_logo, certificate_filename, teachers, language=certificate_language ) info.generate() cert.status = status.downloadable cert.download_url = settings.CERTIFICATE_BASE_URL + certificate_filename cert.save() return cert.status
def generate_fun_certificate(student, course_id, course_display_name, course, teachers, organization_display_name, organization_logo, certificate_base_filename,ignore_grades, new_grade): """Generates a certificate for one student and one course.""" profile = UserProfile.objects.get(user=student) student_name = unicode(profile.name).encode('utf-8') # grade the student cert, created = GeneratedCertificate.objects.get_or_create( user=student, course_id=course_id) request.user = student grade = grades.grade(student, request, course) cert.grade = grade['percent'] cert.user = student cert.course_id = course_id cert.name = profile.name if ignore_grades: grade['grade'] = 'A' grade['percent'] = 100.0 if new_grade: grade['grade'] = 'A' cert.grade = new_grade cert.save() if grade['grade'] is None: cert.status = status.notpassing else: key = make_hashkey(random.random()) cert.key = key info = CertificateInfo() info.full_name = student_name info.course_name = course_display_name info.teachers = teachers info.organization = organization_display_name info.organization_logo = organization_logo certificate_filename = certificate_base_filename + key + ".pdf"; info.pdf_file_name = os.path.join( settings.CERTIFICATES_DIRECTORY, certificate_filename) if info.generate(): cert.status = status.downloadable cert.download_url = settings.CERTIFICATE_BASE_URL + certificate_filename else: cert.status = status.error cert.error_reason = "Error while generating PDF file" cert.save() return cert.status
def _generate_certificate(user, course_id): """ Generate a certificate for this user, in this course run. """ profile = UserProfile.objects.get(user=user) profile_name = profile.name course = modulestore().get_course(course_id, depth=0) course_grade = CourseGradeFactory().read(user, course) enrollment_mode, __ = CourseEnrollment.enrollment_mode_for_user(user, course_id) key = make_hashkey(random.random()) uuid = uuid4().hex cert, created = GeneratedCertificate.objects.update_or_create( user=user, course_id=course_id, defaults={ 'user': user, 'course_id': course_id, 'mode': enrollment_mode, 'name': profile_name, 'status': CertificateStatuses.downloadable, 'grade': course_grade.percent, 'download_url': '', 'key': key, 'verify_uuid': uuid } ) if created: created_msg = 'Certificate was created.' else: created_msg = 'Certificate already existed and was updated.' log.info( 'Generated certificate with status {status} for {user} : {course}. {created_msg}'.format( status=cert.status, user=cert.user.id, course=cert.course_id, created_msg=created_msg )) return cert
def _generate_cert(cert, course, student, grade_contents, template_pdf): """ Generate a certificate for the student. If `generate_pdf` is True, sends a request to XQueue. """ course_id = unicode(course.id) key = make_hashkey(random.random()) cert.key = key contents = { 'action': 'create', 'username': student.username, 'course_id': course_id, 'course_name': course.display_name or course_id, 'name': cert.name, 'grade': grade_contents, 'template_pdf': template_pdf, } cert.status = CertificateStatuses.downloadable cert.verify_uuid = uuid4().hex cert.save() return cert
def add_cert(self, student, course_id, course=None): """ Arguments: student - User.object course_id - courseenrollment.course_id (string) Request a new certificate for a student. Will change the certificate status to 'generating'. Certificate must be in the 'unavailable', 'error', 'deleted' or 'generating' state. If a student has a passing grade or is in the whitelist table for the course a request will made for a new cert. If a student has allow_certificate set to False in the userprofile table the status will change to 'restricted' If a student does not have a passing grade the status will change to status.notpassing Returns the student's status """ VALID_STATUSES = [status.generating, status.unavailable, status.deleted, status.error, status.notpassing] cert_status = certificate_status_for_student( student, course_id)['status'] if cert_status in VALID_STATUSES: # grade the student # re-use the course passed in optionally so we don't have to re-fetch everything # for every student if course is None: course = courses.get_course_by_id(course_id) profile = UserProfile.objects.get(user=student) cert, created = GeneratedCertificate.objects.get_or_create( user=student, course_id=course_id) grade = grades.grade(student, self.request, course) is_whitelisted = self.whitelist.filter( user=student, course_id=course_id, whitelist=True).exists() if is_whitelisted or grade['grade'] is not None: key = make_hashkey(random.random()) cert.grade = grade['percent'] cert.user = student cert.course_id = course_id cert.key = key cert.name = profile.name # check to see whether the student is on the # the embargoed country restricted list # otherwise, put a new certificate request # on the queue if self.restricted.filter(user=student).exists(): cert.status = status.restricted else: contents = { 'action': 'create', 'username': student.username, 'course_id': course_id, 'name': profile.name, } cert.status = status.generating self._send_to_xqueue(contents, key) cert.save() else: cert_status = status.notpassing cert.grade = grade['percent'] cert.status = cert_status cert.user = student cert.course_id = course_id cert.name = profile.name cert.save() return cert_status
def add_cert(self, student, course_id, course=None, forced_grade=None, template_file=None, title="None"): """ Request a new certificate for a student. Arguments: student - User.object course_id - courseenrollment.course_id (CourseKey) forced_grade - a string indicating a grade parameter to pass with the certificate request. If this is given, grading will be skipped. Will change the certificate status to 'generating'. Certificate must be in the 'unavailable', 'error', 'deleted' or 'generating' state. If a student has a passing grade or is in the whitelist table for the course a request will be made for a new cert. If a student has allow_certificate set to False in the userprofile table the status will change to 'restricted' If a student does not have a passing grade the status will change to status.notpassing Returns the student's status """ VALID_STATUSES = [status.generating, status.unavailable, status.deleted, status.error, status.notpassing] cert_status = certificate_status_for_student(student, course_id)["status"] new_status = cert_status if cert_status in VALID_STATUSES: # grade the student # re-use the course passed in optionally so we don't have to re-fetch everything # for every student if course is None: course = courses.get_course_by_id(course_id) profile = UserProfile.objects.get(user=student) profile_name = profile.name # Needed self.request.user = student self.request.session = {} course_name = course.display_name or course_id.to_deprecated_string() is_whitelisted = self.whitelist.filter(user=student, course_id=course_id, whitelist=True).exists() grade = grades.grade(student, self.request, course) enrollment_mode, __ = CourseEnrollment.enrollment_mode_for_user(student, course_id) mode_is_verified = enrollment_mode == GeneratedCertificate.MODES.verified user_is_verified = SoftwareSecurePhotoVerification.user_is_verified(student) user_is_reverified = SoftwareSecurePhotoVerification.user_is_reverified_for_all(course_id, student) cert_mode = enrollment_mode if mode_is_verified and user_is_verified and user_is_reverified: template_pdf = "certificate-template-{id.org}-{id.course}-verified.pdf".format(id=course_id) elif mode_is_verified and not (user_is_verified and user_is_reverified): template_pdf = "certificate-template-{id.org}-{id.course}.pdf".format(id=course_id) cert_mode = GeneratedCertificate.MODES.honor else: # honor code and audit students template_pdf = "certificate-template-{id.org}-{id.course}.pdf".format(id=course_id) if forced_grade: grade["grade"] = forced_grade cert, __ = GeneratedCertificate.objects.get_or_create(user=student, course_id=course_id) cert.mode = cert_mode cert.user = student cert.grade = grade["percent"] cert.course_id = course_id cert.name = profile_name # Strip HTML from grade range label grade_contents = grade.get("grade", None) try: grade_contents = lxml.html.fromstring(grade_contents).text_content() except (TypeError, XMLSyntaxError, ParserError) as e: # Despite blowing up the xml parser, bad values here are fine grade_contents = None if is_whitelisted or grade_contents is not None: # check to see whether the student is on the # the embargoed country restricted list # otherwise, put a new certificate request # on the queue if self.restricted.filter(user=student).exists(): new_status = status.restricted cert.status = new_status cert.save() else: key = make_hashkey(random.random()) cert.key = key contents = { "action": "create", "username": student.username, "course_id": course_id.to_deprecated_string(), "course_name": course_name, "name": profile_name, "grade": grade_contents, "template_pdf": template_pdf, } if template_file: contents["template_pdf"] = template_file new_status = status.generating cert.status = new_status cert.save() self._send_to_xqueue(contents, key) else: cert_status = status.notpassing cert.status = cert_status cert.save() return new_status
def send_to_grader(self, submission, system): """ Send a given submission to the grader, via the xqueue @param submission: The student submission to send to the grader @param system: Modulesystem @return: Boolean true (not useful right now) """ # Prepare xqueue request #------------------------------------------------------------ xqueue = system.get('xqueue') if xqueue is None: return False qinterface = xqueue['interface'] qtime = datetime.strftime(datetime.now(UTC), xqueue_interface.dateformat) anonymous_student_id = system.anonymous_student_id # Generate header queuekey = xqueue_interface.make_hashkey(str(system.seed) + qtime + anonymous_student_id + str(len(self.child_history))) xheader = xqueue_interface.make_xheader( lms_callback_url=xqueue['construct_callback'](), lms_key=queuekey, queue_name=self.queue_name ) contents = self.payload.copy() # Metadata related to the student submission revealed to the external grader student_info = { 'anonymous_student_id': anonymous_student_id, 'submission_time': qtime, } # Update contents with student response and student info contents.update({ 'student_info': json.dumps(student_info), 'student_response': submission, 'max_score': self.max_score(), }) # Submit request. When successful, 'msg' is the prior length of the queue error, error_message = qinterface.send_to_queue( header=xheader, body=json.dumps(contents) ) # State associated with the queueing request queuestate = { 'key': queuekey, 'time': qtime, } _ = self.system.service(self, "i18n").ugettext success = True message = _("Successfully saved your submission.") if error: success = False # Translators: the `grader` refers to the grading service open response problems # are sent to, either to be machine-graded, peer-graded, or instructor-graded. message = _('Unable to submit your submission to the grader. Please try again later.') log.error("Unable to submit to grader. location: {0}, error_message: {1}".format( self.location_string, error_message )) return (success, message)
def message_post(self, data, system): """ Handles a student message post (a reaction to the grade they received from an open ended grader type) Returns a boolean success/fail and an error message """ event_info = dict() event_info['problem_id'] = self.location_string event_info['student_id'] = system.anonymous_student_id event_info['survey_responses'] = data _ = self.system.service(self, "i18n").ugettext survey_responses = event_info['survey_responses'] for tag in ['feedback', 'submission_id', 'grader_id', 'score']: if tag not in survey_responses: # This is a student_facing_error return { 'success': False, # Translators: 'tag' is one of 'feedback', 'submission_id', # 'grader_id', or 'score'. They are categories that a student # responds to when filling out a post-assessment survey # of his or her grade from an openended problem. 'msg': _("Could not find needed tag {tag_name} in the " "survey responses. Please try submitting " "again.").format(tag_name=tag) } try: submission_id = int(survey_responses['submission_id']) grader_id = int(survey_responses['grader_id']) feedback = str(survey_responses['feedback'].encode('ascii', 'ignore')) score = int(survey_responses['score']) except: # This is a dev_facing_error error_message = ( "Could not parse submission id, grader id, " "or feedback from message_post ajax call. " "Here is the message data: {0}".format(survey_responses) ) log.exception(error_message) # This is a student_facing_error return { 'success': False, 'msg': _( "There was an error saving your feedback. Please " "contact course staff." ) } xqueue = system.get('xqueue') if xqueue is None: return {'success': False, 'msg': _("Couldn't submit feedback.")} qinterface = xqueue['interface'] qtime = datetime.strftime(datetime.now(UTC), xqueue_interface.dateformat) anonymous_student_id = system.anonymous_student_id queuekey = xqueue_interface.make_hashkey(str(system.seed) + qtime + anonymous_student_id + str(len(self.child_history))) xheader = xqueue_interface.make_xheader( lms_callback_url=xqueue['construct_callback'](), lms_key=queuekey, queue_name=self.message_queue_name ) student_info = { 'anonymous_student_id': anonymous_student_id, 'submission_time': qtime, } contents = { 'feedback': feedback, 'submission_id': submission_id, 'grader_id': grader_id, 'score': score, 'student_info': json.dumps(student_info), } error, error_message = qinterface.send_to_queue( header=xheader, body=json.dumps(contents) ) # Convert error to a success value success = True message = _("Successfully saved your feedback.") if error: success = False message = _("Unable to save your feedback. Please try again later.") log.error("Unable to send feedback to grader. location: {0}, error_message: {1}".format( self.location_string, error_message )) else: self.child_state = self.DONE # This is a student_facing_message return {'success': success, 'msg': message}
def add_cert(self, student, course_id, course=None): """ Arguments: student - User.object course_id - courseenrollment.course_id (string) Request a new certificate for a student. Will change the certificate status to 'generating'. Certificate must be in the 'unavailable', 'error', 'deleted' or 'generating' state. If a student has a passing grade or is in the whitelist table for the course a request will made for a new cert. If a student has allow_certificate set to False in the userprofile table the status will change to 'restricted' If a student does not have a passing grade the status will change to status.notpassing Returns the student's status """ VALID_STATUSES = [status.generating, status.unavailable, status.deleted, status.error, status.notpassing] cert_status = certificate_status_for_student(student, course_id)['status'] new_status = cert_status if cert_status in VALID_STATUSES: # grade the student # re-use the course passed in optionally so we don't have to re-fetch everything # for every student if course is None: course = courses.get_course_by_id(course_id) profile = UserProfile.objects.get(user=student) # Needed self.request.user = student self.request.session = {} grade = grades.grade(student, self.request, course) is_whitelisted = self.whitelist.filter( user=student, course_id=course_id, whitelist=True).exists() enrollment_mode = CourseEnrollment.enrollment_mode_for_user(student, course_id) mode_is_verified = (enrollment_mode == GeneratedCertificate.MODES.verified) user_is_verified = SoftwareSecurePhotoVerification.user_is_verified(student) user_is_reverified = SoftwareSecurePhotoVerification.user_is_reverified_for_all(course_id, student) org = course_id.split('/')[0] course_num = course_id.split('/')[1] cert_mode = enrollment_mode if (mode_is_verified and user_is_verified and user_is_reverified): template_pdf = "certificate-template-{0}-{1}-verified.pdf".format( org, course_num) elif (mode_is_verified and not (user_is_verified and user_is_reverified)): template_pdf = "certificate-template-{0}-{1}.pdf".format( org, course_num) cert_mode = GeneratedCertificate.MODES.honor else: # honor code and audit students template_pdf = "certificate-template-{0}-{1}.pdf".format( org, course_num) cert, created = GeneratedCertificate.objects.get_or_create( user=student, course_id=course_id) cert.mode = cert_mode cert.user = student cert.grade = grade['percent'] cert.course_id = course_id cert.name = profile.name if is_whitelisted or grade['grade'] is not None: # check to see whether the student is on the # the embargoed country restricted list # otherwise, put a new certificate request # on the queue if self.restricted.filter(user=student).exists(): new_status = status.restricted cert.status = new_status cert.save() else: key = make_hashkey(random.random()) cert.key = key contents = { 'action': 'create', 'username': student.username, 'course_id': course_id, 'name': profile.name, 'grade': grade['grade'], 'template_pdf': template_pdf, } new_status = status.generating cert.status = new_status cert.save() self._send_to_xqueue(contents, key) else: new_status = status.notpassing cert.status = new_status cert.save() return new_status
def add_cert(self, student, course_id, course=None, forced_grade=None, template_file=None, title='None', generate_pdf=True): """ Request a new certificate for a student. Arguments: student - User.object course_id - courseenrollment.course_id (CourseKey) forced_grade - a string indicating a grade parameter to pass with the certificate request. If this is given, grading will be skipped. generate_pdf - Boolean should a message be sent in queue to generate certificate PDF Will change the certificate status to 'generating' or `downloadable` in case of web view certificates. Certificate must be in the 'unavailable', 'error', 'deleted' or 'generating' state. If a student has a passing grade or is in the whitelist table for the course a request will be made for a new cert. If a student has allow_certificate set to False in the userprofile table the status will change to 'restricted' If a student does not have a passing grade the status will change to status.notpassing Returns the student's status and newly created certificate instance """ valid_statuses = [ status.generating, status.unavailable, status.deleted, status.error, status.notpassing, status.downloadable ] cert_status = certificate_status_for_student(student, course_id)['status'] new_status = cert_status cert = None if cert_status not in valid_statuses: LOGGER.warning( ( u"Cannot create certificate generation task for user %s " u"in the course '%s'; " u"the certificate status '%s' is not one of %s." ), student.id, unicode(course_id), cert_status, unicode(valid_statuses) ) else: # grade the student # re-use the course passed in optionally so we don't have to re-fetch everything # for every student if course is None: course = modulestore().get_course(course_id, depth=0) profile = UserProfile.objects.get(user=student) profile_name = profile.name # Needed self.request.user = student self.request.session = {} course_name = course.display_name or unicode(course_id) is_whitelisted = self.whitelist.filter(user=student, course_id=course_id, whitelist=True).exists() grade = grades.grade(student, self.request, course) enrollment_mode, __ = CourseEnrollment.enrollment_mode_for_user(student, course_id) mode_is_verified = enrollment_mode in GeneratedCertificate.VERIFIED_CERTS_MODES user_is_verified = SoftwareSecurePhotoVerification.user_is_verified(student) cert_mode = enrollment_mode # For credit mode generate verified certificate if cert_mode == CourseMode.CREDIT_MODE: cert_mode = CourseMode.VERIFIED if mode_is_verified and user_is_verified: template_pdf = "certificate-template-{id.org}-{id.course}-verified.pdf".format(id=course_id) elif mode_is_verified and not user_is_verified: template_pdf = "certificate-template-{id.org}-{id.course}.pdf".format(id=course_id) cert_mode = GeneratedCertificate.MODES.honor else: # honor code and audit students template_pdf = "certificate-template-{id.org}-{id.course}.pdf".format(id=course_id) if forced_grade: grade['grade'] = forced_grade cert, __ = GeneratedCertificate.objects.get_or_create(user=student, course_id=course_id) cert.mode = cert_mode cert.user = student cert.grade = grade['percent'] cert.course_id = course_id cert.name = profile_name cert.download_url = '' # Strip HTML from grade range label grade_contents = grade.get('grade', None) try: grade_contents = lxml.html.fromstring(grade_contents).text_content() except (TypeError, XMLSyntaxError, ParserError) as exc: LOGGER.info( ( u"Could not retrieve grade for student %s " u"in the course '%s' " u"because an exception occurred while parsing the " u"grade contents '%s' as HTML. " u"The exception was: '%s'" ), student.id, unicode(course_id), grade_contents, unicode(exc) ) # Despite blowing up the xml parser, bad values here are fine grade_contents = None if is_whitelisted or grade_contents is not None: if is_whitelisted: LOGGER.info( u"Student %s is whitelisted in '%s'", student.id, unicode(course_id) ) # check to see whether the student is on the # the embargoed country restricted list # otherwise, put a new certificate request # on the queue if self.restricted.filter(user=student).exists(): new_status = status.restricted cert.status = new_status cert.save() LOGGER.info( ( u"Student %s is in the embargoed country restricted " u"list, so their certificate status has been set to '%s' " u"for the course '%s'. " u"No certificate generation task was sent to the XQueue." ), student.id, new_status, unicode(course_id) ) else: key = make_hashkey(random.random()) cert.key = key contents = { 'action': 'create', 'username': student.username, 'course_id': unicode(course_id), 'course_name': course_name, 'name': profile_name, 'grade': grade_contents, 'template_pdf': template_pdf, } if template_file: contents['template_pdf'] = template_file if generate_pdf: new_status = status.generating else: new_status = status.downloadable cert.verify_uuid = uuid4().hex cert.status = new_status cert.save() if generate_pdf: try: self._send_to_xqueue(contents, key) except XQueueAddToQueueError as exc: new_status = ExampleCertificate.STATUS_ERROR cert.status = new_status cert.error_reason = unicode(exc) cert.save() LOGGER.critical( ( u"Could not add certificate task to XQueue. " u"The course was '%s' and the student was '%s'." u"The certificate task status has been marked as 'error' " u"and can be re-submitted with a management command." ), course_id, student.id ) else: LOGGER.info( ( u"The certificate status has been set to '%s'. " u"Sent a certificate grading task to the XQueue " u"with the key '%s'. " ), new_status, key ) else: new_status = status.notpassing cert.status = new_status cert.save() LOGGER.info( ( u"Student %s does not have a grade for '%s', " u"so their certificate status has been set to '%s'. " u"No certificate generation task was sent to the XQueue." ), student.id, unicode(course_id), new_status ) return new_status, cert
def make_certificate_hash_key(): return make_hashkey(random.random())
def message_post(self, data, system): """ Handles a student message post (a reaction to the grade they received from an open ended grader type) Returns a boolean success/fail and an error message """ event_info = dict() event_info['problem_id'] = self.location_string event_info['student_id'] = system.anonymous_student_id event_info['survey_responses'] = data survey_responses = event_info['survey_responses'] for tag in ['feedback', 'submission_id', 'grader_id', 'score']: if tag not in survey_responses: # This is a student_facing_error return {'success': False, 'msg': "Could not find needed tag {0} in the survey responses. Please try submitting again.".format( tag)} try: submission_id = int(survey_responses['submission_id']) grader_id = int(survey_responses['grader_id']) feedback = str(survey_responses['feedback'].encode('ascii', 'ignore')) score = int(survey_responses['score']) except: # This is a dev_facing_error error_message = ( "Could not parse submission id, grader id, " "or feedback from message_post ajax call. " "Here is the message data: {0}".format(survey_responses) ) log.exception(error_message) # This is a student_facing_error return {'success': False, 'msg': "There was an error saving your feedback. Please contact course staff."} xqueue = system.get('xqueue') if xqueue is None: return {'success': False, 'msg': "Couldn't submit feedback."} qinterface = xqueue['interface'] qtime = datetime.strftime(datetime.now(UTC), xqueue_interface.dateformat) anonymous_student_id = system.anonymous_student_id queuekey = xqueue_interface.make_hashkey(str(system.seed) + qtime + anonymous_student_id + str(len(self.child_history))) xheader = xqueue_interface.make_xheader( lms_callback_url=xqueue['construct_callback'](), lms_key=queuekey, queue_name=self.message_queue_name ) student_info = { 'anonymous_student_id': anonymous_student_id, 'submission_time': qtime, } contents = { 'feedback': feedback, 'submission_id': submission_id, 'grader_id': grader_id, 'score': score, 'student_info': json.dumps(student_info), } (error, msg) = qinterface.send_to_queue( header=xheader, body=json.dumps(contents) ) # Convert error to a success value success = True if error: success = False self.child_state = self.DONE # This is a student_facing_message return {'success': success, 'msg': "Successfully submitted your feedback."}
def add_cert(self, student, course_id, course=None, forced_grade=None, template_file=None, title='None', generate_pdf=True): """ Request a new certificate for a student. Arguments: student - User.object course_id - courseenrollment.course_id (CourseKey) forced_grade - a string indicating a grade parameter to pass with the certificate request. If this is given, grading will be skipped. generate_pdf - Boolean should a message be sent in queue to generate certificate PDF Will change the certificate status to 'generating' or `downloadable` in case of web view certificates. Certificate must be in the 'unavailable', 'error', 'deleted' or 'generating' state. If a student has a passing grade or is in the whitelist table for the course a request will be made for a new cert. If a student has allow_certificate set to False in the userprofile table the status will change to 'restricted' If a student does not have a passing grade the status will change to status.notpassing Returns the student's status and newly created certificate instance """ valid_statuses = [ status.generating, status.unavailable, status.deleted, status.error, status.notpassing, status.downloadable ] cert_status = certificate_status_for_student(student, course_id)['status'] new_status = cert_status cert = None if cert_status not in valid_statuses: LOGGER.warning( (u"Cannot create certificate generation task for user %s " u"in the course '%s'; " u"the certificate status '%s' is not one of %s."), student.id, unicode(course_id), cert_status, unicode(valid_statuses)) else: # grade the student # re-use the course passed in optionally so we don't have to re-fetch everything # for every student if course is None: course = modulestore().get_course(course_id, depth=0) profile = UserProfile.objects.get(user=student) profile_name = profile.name # Needed self.request.user = student self.request.session = {} course_name = course.display_name or unicode(course_id) is_whitelisted = self.whitelist.filter(user=student, course_id=course_id, whitelist=True).exists() grade = grades.grade(student, self.request, course) enrollment_mode, __ = CourseEnrollment.enrollment_mode_for_user( student, course_id) mode_is_verified = ( enrollment_mode == GeneratedCertificate.MODES.verified) user_is_verified = SoftwareSecurePhotoVerification.user_is_verified( student) cert_mode = enrollment_mode if mode_is_verified and user_is_verified: template_pdf = "certificate-template-{id.org}-{id.course}-verified.pdf".format( id=course_id) elif mode_is_verified and not user_is_verified: template_pdf = "certificate-template-{id.org}-{id.course}.pdf".format( id=course_id) cert_mode = GeneratedCertificate.MODES.honor else: # honor code and audit students template_pdf = "certificate-template-{id.org}-{id.course}.pdf".format( id=course_id) if forced_grade: grade['grade'] = forced_grade cert, __ = GeneratedCertificate.objects.get_or_create( user=student, course_id=course_id) cert.mode = cert_mode cert.user = student cert.grade = grade['percent'] cert.course_id = course_id cert.name = profile_name cert.download_url = '' # Strip HTML from grade range label grade_contents = grade.get('grade', None) try: grade_contents = lxml.html.fromstring( grade_contents).text_content() except (TypeError, XMLSyntaxError, ParserError) as exc: LOGGER.info( (u"Could not retrieve grade for student %s " u"in the course '%s' " u"because an exception occurred while parsing the " u"grade contents '%s' as HTML. " u"The exception was: '%s'"), student.id, unicode(course_id), grade_contents, unicode(exc)) # Despite blowing up the xml parser, bad values here are fine grade_contents = None if is_whitelisted or grade_contents is not None: if is_whitelisted: LOGGER.info(u"Student %s is whitelisted in '%s'", student.id, unicode(course_id)) # check to see whether the student is on the # the embargoed country restricted list # otherwise, put a new certificate request # on the queue if self.restricted.filter(user=student).exists(): new_status = status.restricted cert.status = new_status cert.save() LOGGER.info(( u"Student %s is in the embargoed country restricted " u"list, so their certificate status has been set to '%s' " u"for the course '%s'. " u"No certificate generation task was sent to the XQueue." ), student.id, new_status, unicode(course_id)) else: key = make_hashkey(random.random()) cert.key = key contents = { 'action': 'create', 'username': student.username, 'course_id': unicode(course_id), 'course_name': course_name, 'name': profile_name, 'grade': grade_contents, 'template_pdf': template_pdf, } if template_file: contents['template_pdf'] = template_file if generate_pdf: new_status = status.generating else: new_status = status.downloadable cert.verify_uuid = uuid4().hex cert.status = new_status cert.save() if generate_pdf: try: self._send_to_xqueue(contents, key) except XQueueAddToQueueError as exc: new_status = ExampleCertificate.STATUS_ERROR cert.status = new_status cert.error_reason = unicode(exc) cert.save() LOGGER.critical(( u"Could not add certificate task to XQueue. " u"The course was '%s' and the student was '%s'." u"The certificate task status has been marked as 'error' " u"and can be re-submitted with a management command." ), course_id, student.id) else: LOGGER.info(( u"The certificate status has been set to '%s'. " u"Sent a certificate grading task to the XQueue " u"with the key '%s'. "), new_status, key) else: new_status = status.notpassing cert.status = new_status cert.save() LOGGER.info(( u"Student %s does not have a grade for '%s', " u"so their certificate status has been set to '%s'. " u"No certificate generation task was sent to the XQueue."), student.id, unicode(course_id), new_status) return new_status, cert
def message_post(self, data, system): """ Handles a student message post (a reaction to the grade they received from an open ended grader type) Returns a boolean success/fail and an error message """ event_info = dict() event_info['problem_id'] = self.location_string event_info['student_id'] = system.anonymous_student_id event_info['survey_responses'] = data _ = self.system.service(self, "i18n").ugettext survey_responses = event_info['survey_responses'] for tag in ['feedback', 'submission_id', 'grader_id', 'score']: if tag not in survey_responses: # This is a student_facing_error return { 'success': False, # Translators: 'tag' is one of 'feedback', 'submission_id', # 'grader_id', or 'score'. They are categories that a student # responds to when filling out a post-assessment survey # of his or her grade from an openended problem. 'msg': _("Could not find needed tag {tag_name} in the " "survey responses. Please try submitting " "again.").format(tag_name=tag) } try: submission_id = int(survey_responses['submission_id']) grader_id = int(survey_responses['grader_id']) feedback = str(survey_responses['feedback'].encode( 'ascii', 'ignore')) score = int(survey_responses['score']) except: # This is a dev_facing_error error_message = ( "Could not parse submission id, grader id, " "or feedback from message_post ajax call. " "Here is the message data: {0}".format(survey_responses)) log.exception(error_message) # This is a student_facing_error return { 'success': False, 'msg': _("There was an error saving your feedback. Please " "contact course staff.") } xqueue = system.get('xqueue') if xqueue is None: return {'success': False, 'msg': _("Couldn't submit feedback.")} qinterface = xqueue['interface'] qtime = datetime.strftime(datetime.now(UTC), xqueue_interface.dateformat) anonymous_student_id = system.anonymous_student_id queuekey = xqueue_interface.make_hashkey( str(system.seed) + qtime + anonymous_student_id + str(len(self.child_history))) xheader = xqueue_interface.make_xheader( lms_callback_url=xqueue['construct_callback'](), lms_key=queuekey, queue_name=self.message_queue_name) student_info = { 'anonymous_student_id': anonymous_student_id, 'submission_time': qtime, } contents = { 'feedback': feedback, 'submission_id': submission_id, 'grader_id': grader_id, 'score': score, 'student_info': json.dumps(student_info), } error, error_message = qinterface.send_to_queue( header=xheader, body=json.dumps(contents)) # Convert error to a success value success = True message = _("Successfully saved your feedback.") if error: success = False message = _( "Unable to save your feedback. Please try again later.") log.error( "Unable to send feedback to grader. location: {0}, error_message: {1}" .format(self.location_string, error_message)) else: self.child_state = self.DONE # This is a student_facing_message return {'success': success, 'msg': message}
def add_cert(self, student, course_id, course=None, forced_grade=None, template_file=None, title='None'): """ Request a new certificate for a student. Arguments: student - User.object course_id - courseenrollment.course_id (CourseKey) forced_grade - a string indicating a grade parameter to pass with the certificate request. If this is given, grading will be skipped. Will change the certificate status to 'generating'. Certificate must be in the 'unavailable', 'error', 'deleted' or 'generating' state. If a student has a passing grade or is in the whitelist table for the course a request will be made for a new cert. If a student has allow_certificate set to False in the userprofile table the status will change to 'restricted' If a student does not have a passing grade the status will change to status.notpassing Returns the student's status """ valid_statuses = [ status.generating, status.unavailable, status.deleted, status.error, status.notpassing ] cert_status = certificate_status_for_student(student, course_id)['status'] new_status = cert_status if cert_status not in valid_statuses: LOGGER.warning( ( u"Cannot create certificate generation task for user %s " u"in the course '%s'; " u"the certificate status '%s' is not one of %s." ), student.id, unicode(course_id), cert_status, unicode(valid_statuses) ) else: # grade the student # re-use the course passed in optionally so we don't have to re-fetch everything # for every student if course is None: course = courses.get_course_by_id(course_id) profile = UserProfile.objects.get(user=student) profile_name = profile.name # Needed self.request.user = student self.request.session = {} course_name = course.display_name or course_id.to_deprecated_string() is_whitelisted = self.whitelist.filter(user=student, course_id=course_id, whitelist=True).exists() grade = grades.grade(student, self.request, course) enrollment_mode, __ = CourseEnrollment.enrollment_mode_for_user(student, course_id) mode_is_verified = (enrollment_mode == GeneratedCertificate.MODES.verified) user_is_verified = SoftwareSecurePhotoVerification.user_is_verified(student) user_is_reverified = SoftwareSecurePhotoVerification.user_is_reverified_for_all(course_id, student) cert_mode = enrollment_mode if (mode_is_verified and user_is_verified and user_is_reverified): template_pdf = "certificate-template-{id.org}-{id.course}-verified.pdf".format(id=course_id) elif (mode_is_verified and not (user_is_verified and user_is_reverified)): template_pdf = "certificate-template-{id.org}-{id.course}.pdf".format(id=course_id) cert_mode = GeneratedCertificate.MODES.honor else: # honor code and audit students template_pdf = "certificate-template-{id.org}-{id.course}.pdf".format(id=course_id) if forced_grade: grade['grade'] = forced_grade cert, __ = GeneratedCertificate.objects.get_or_create(user=student, course_id=course_id) cert.mode = cert_mode cert.user = student cert.grade = grade['percent'] cert.course_id = course_id cert.name = profile_name # Strip HTML from grade range label grade_contents = grade.get('grade', None) try: grade_contents = lxml.html.fromstring(grade_contents).text_content() except (TypeError, XMLSyntaxError, ParserError) as exc: LOGGER.info( ( u"Could not retrieve grade for student %s " u"in the course '%s' " u"because an exception occurred while parsing the " u"grade contents '%s' as HTML. " u"The exception was: '%s'" ), student.id, unicode(course_id), grade_contents, unicode(exc) ) # Despite blowing up the xml parser, bad values here are fine grade_contents = None if is_whitelisted or grade_contents is not None: if is_whitelisted: LOGGER.info( u"Student %s is whitelisted in '%s'", student.id, unicode(course_id) ) # check to see whether the student is on the # the embargoed country restricted list # otherwise, put a new certificate request # on the queue if self.restricted.filter(user=student).exists(): new_status = status.restricted cert.status = new_status cert.save() LOGGER.info( ( u"Student %s is in the embargoed country restricted " u"list, so their certificate status has been set to '%s' " u"for the course '%s'. " u"No certificate generation task was sent to the XQueue." ), student.id, new_status, unicode(course_id) ) else: key = make_hashkey(random.random()) cert.key = key contents = { 'action': 'create', 'username': student.username, 'course_id': course_id.to_deprecated_string(), 'course_name': course_name, 'name': profile_name, 'grade': grade_contents, 'template_pdf': template_pdf, } if template_file: contents['template_pdf'] = template_file new_status = status.generating cert.status = new_status cert.save() self._send_to_xqueue(contents, key) LOGGER.info( ( u"The certificate status has been set to '%s'. " u"Sent a certificate grading task to the XQueue " u"with the key '%s'. " ), key, new_status ) else: cert_status = status.notpassing cert.status = cert_status cert.save() LOGGER.info( ( u"Student %s does not have a grade for '%s', " u"so their certificate status has been set to '%s'. " u"No certificate generation task was sent to the XQueue." ), student.id, unicode(course_id), cert_status ) return new_status
def add_cert(self, student, course_id, course=None, forced_grade=None, template_file=None, title='None'): """ Request a new certificate for a student. Arguments: student - User.object course_id - courseenrollment.course_id (CourseKey) forced_grade - a string indicating a grade parameter to pass with the certificate request. If this is given, grading will be skipped. Will change the certificate status to 'generating'. Certificate must be in the 'unavailable', 'error', 'deleted' or 'generating' state. If a student has a passing grade or is in the whitelist table for the course a request will be made for a new cert. If a student has allow_certificate set to False in the userprofile table the status will change to 'restricted' If a student does not have a passing grade the status will change to status.notpassing Returns the student's status """ VALID_STATUSES = [ status.generating, status.unavailable, status.deleted, status.error, status.notpassing ] cert_status = certificate_status_for_student(student, course_id)['status'] new_status = cert_status if cert_status in VALID_STATUSES: # grade the student # re-use the course passed in optionally so we don't have to re-fetch everything # for every student if course is None: course = courses.get_course_by_id(course_id) profile = UserProfile.objects.get(user=student) profile_name = profile.name # Needed self.request.user = student self.request.session = {} course_name = course.display_name or course_id.to_deprecated_string( ) is_whitelisted = self.whitelist.filter(user=student, course_id=course_id, whitelist=True).exists() grade = grades.grade(student, self.request, course) enrollment_mode, __ = CourseEnrollment.enrollment_mode_for_user( student, course_id) mode_is_verified = ( enrollment_mode == GeneratedCertificate.MODES.verified) user_is_verified = SoftwareSecurePhotoVerification.user_is_verified( student) user_is_reverified = SoftwareSecurePhotoVerification.user_is_reverified_for_all( course_id, student) cert_mode = enrollment_mode if (mode_is_verified and user_is_verified and user_is_reverified): template_pdf = "certificate-template-{id.org}-{id.course}-verified.pdf".format( id=course_id) elif (mode_is_verified and not (user_is_verified and user_is_reverified)): template_pdf = "certificate-template-{id.org}-{id.course}.pdf".format( id=course_id) cert_mode = GeneratedCertificate.MODES.honor else: # honor code and audit students template_pdf = "certificate-template-{id.org}-{id.course}.pdf".format( id=course_id) if forced_grade: grade['grade'] = forced_grade cert, __ = GeneratedCertificate.objects.get_or_create( user=student, course_id=course_id) cert.mode = cert_mode cert.user = student cert.grade = grade['percent'] cert.course_id = course_id cert.name = profile_name # Strip HTML from grade range label grade_contents = grade.get('grade', None) try: grade_contents = lxml.html.fromstring( grade_contents).text_content() except (TypeError, XMLSyntaxError, ParserError) as e: # Despite blowing up the xml parser, bad values here are fine grade_contents = None if is_whitelisted or grade_contents is not None: # check to see whether the student is on the # the embargoed country restricted list # otherwise, put a new certificate request # on the queue if self.restricted.filter(user=student).exists(): new_status = status.restricted cert.status = new_status cert.save() else: key = make_hashkey(random.random()) cert.key = key contents = { 'action': 'create', 'username': student.username, 'course_id': course_id.to_deprecated_string(), 'course_name': course_name, 'name': profile_name, 'grade': grade_contents, 'template_pdf': template_pdf, } if template_file: contents['template_pdf'] = template_file new_status = status.generating cert.status = new_status cert.save() self._send_to_xqueue(contents, key) else: cert_status = status.notpassing cert.status = cert_status cert.save() return new_status
def send_to_grader(self, submission, system): """ Send a given submission to the grader, via the xqueue @param submission: The student submission to send to the grader @param system: Modulesystem @return: Boolean true (not useful right now) """ # Prepare xqueue request #------------------------------------------------------------ xqueue = system.get('xqueue') if xqueue is None: return False qinterface = xqueue['interface'] qtime = datetime.strftime(datetime.now(UTC), xqueue_interface.dateformat) anonymous_student_id = system.anonymous_student_id # Generate header queuekey = xqueue_interface.make_hashkey( str(system.seed) + qtime + anonymous_student_id + str(len(self.child_history))) xheader = xqueue_interface.make_xheader( lms_callback_url=xqueue['construct_callback'](), lms_key=queuekey, queue_name=self.queue_name) contents = self.payload.copy() # Metadata related to the student submission revealed to the external grader student_info = { 'anonymous_student_id': anonymous_student_id, 'submission_time': qtime, } # Update contents with student response and student info contents.update({ 'student_info': json.dumps(student_info), 'student_response': submission, 'max_score': self.max_score(), }) # Submit request. When successful, 'msg' is the prior length of the queue error, error_message = qinterface.send_to_queue( header=xheader, body=json.dumps(contents)) # State associated with the queueing request queuestate = { 'key': queuekey, 'time': qtime, } _ = self.system.service(self, "i18n").ugettext success = True message = _("Successfully saved your submission.") if error: success = False # Translators: the `grader` refers to the grading service open response problems # are sent to, either to be machine-graded, peer-graded, or instructor-graded. message = _( 'Unable to submit your submission to the grader. Please try again later.' ) log.error( "Unable to submit to grader. location: {0}, error_message: {1}" .format(self.location_string, error_message)) return (success, message)