def delete(self): args = self.del_req_parser.parse_args() current_user_id = g.current_user['id'] response = response_repository.get_by_id(args['id']) if not response: return errors.RESPONSE_NOT_FOUND if response.user_id != current_user_id: return errors.UNAUTHORIZED response.withdraw() response_repository.save(response) try: user = user_repository.get_by_id(current_user_id) event = response.application_form.event organisation = event.organisation emailer.email_user( 'withdrawal', template_parameters=dict( organisation_name=organisation.name ), event=event, user=user ) except: LOGGER.error('Failed to send withdrawal confirmation email for response with ID : {id}, but the response was withdrawn succesfully'.format(id=args['id'])) return {}, 204
def get(self): args = self.req_parser.parse_args() event_id = args['event_id'] user_id = g.current_user['id'] try: offer = db.session.query(Offer).filter(Offer.event_id == event_id).filter(Offer.user_id == user_id).first() response = response_repository.get_submitted_by_user_id_for_event(user_id, event_id) if not response: return errors.RESPONSE_NOT_FOUND request_travel = response_repository.get_answer_by_question_key_and_response_id('travel_grant', response.id) if not offer: return errors.OFFER_NOT_FOUND elif offer.is_expired(): return errors.OFFER_EXPIRED else: return offer_info(offer, request_travel), 200 except SQLAlchemyError as e: LOGGER.error("Database error encountered: {}".format(e)) return errors.DB_NOT_AVAILABLE except: LOGGER.error("Encountered unknown error: {}".format( traceback.format_exc())) return errors.DB_NOT_AVAILABLE
def _get_candidate_nominator(response): nominating_capacity = response_repository.get_answer_by_question_key_and_response_id( 'nominating_capacity', response.id) if not nominating_capacity: raise ValueError('Missing nominating capacity answer') is_nomination = nominating_capacity.value == 'other' if is_nomination: question_answers = response_repository.get_question_answers_by_section_key_and_response_id( 'nominee_section', response.id) nomination_info = { qa.Question.key: qa.Answer.value for qa in question_answers } candidate = '{nomination_title} {nomination_firstname} {nomination_lastname}'.format( **nomination_info) candidate_firstname = nomination_info['nomination_firstname'] nominator = '{} {} {}'.format(response.user.user_title, response.user.firstname, response.user.lastname) else: candidate = '{} {} {}'.format(response.user.user_title, response.user.firstname, response.user.lastname) candidate_firstname = response.user.firstname nominator = None return candidate, candidate_firstname, nominator
def delete(self): args = self.req_parser.parse_args() event_id = args['event_id'] tag_id = args['tag_id'] response_id = args['response_id'] if not _validate_user_admin_or_reviewer(g.current_user['id'], event_id, response_id): return errors.FORBIDDEN response_repository.remove_tag_from_response(response_id, tag_id) return {}
def get(self): args = self.req_parser.parse_args() response = response_repository.get_by_id_and_user_id( args['response_id'], g.current_user['id']) if not response: return RESPONSE_NOT_FOUND return reference_request_repository.get_all_by_response_id( response.id), 200
def get(self, event_id): req_parser = reqparse.RequestParser() req_parser.add_argument('include_unsubmitted', type=inputs.boolean, required=True) # Note: Including [] in the question_ids parameter because that gets added by Axios on the front-end req_parser.add_argument('question_ids[]', type=int, required=False, action='append') req_parser.add_argument('language', type=str, required=True) args = req_parser.parse_args() include_unsubmitted = args['include_unsubmitted'] question_ids = args['question_ids[]'] language = args['language'] print('Include unsubmitted:', include_unsubmitted) responses = response_repository.get_all_for_event(event_id, not include_unsubmitted) review_config = review_configuration_repository.get_configuration_for_event(event_id) required_reviewers = 1 if review_config is None else review_config.num_reviews_required + review_config.num_optional_reviews response_reviewers = review_repository.get_response_reviewers_for_event(event_id) response_to_reviewers = { k: list(g) for k, g in itertools.groupby(response_reviewers, lambda r: r.response_id) } review_responses = review_repository.get_review_responses_for_event(event_id) reviewer_to_review_response = { r.reviewer_user_id: r for r in review_responses } serialized_responses = [] for response in responses: reviewers = [_serialize_reviewer(r, reviewer_to_review_response.get(r.reviewer_user_id, None)) for r in response_to_reviewers.get(response.id, [])] reviewers = _pad_list(reviewers, required_reviewers) if question_ids: answers = [_serialize_answer(answer, language) for answer in response.answers if answer.question_id in question_ids] else: answers = [] serialized = { 'response_id': response.id, 'user_title': response.user.user_title, 'firstname': response.user.firstname, 'lastname': response.user.lastname, 'start_date': response.started_timestamp.isoformat(), 'is_submitted': response.is_submitted, 'is_withdrawn': response.is_withdrawn, 'submitted_date': None if response.submitted_timestamp is None else response.submitted_timestamp.isoformat(), 'language': response.language, 'answers': answers, 'reviewers': reviewers, 'tags': [_serialize_tag(rt.tag, language) for rt in response.response_tags] } serialized_responses.append(serialized) return serialized_responses
def test_build_response_html_app_info(self): self._seed_static_data() response = response_repository.get_by_id(self.response_id) html_info_string = build_response_html_app_info(response, 'en') self.assertIsNotNone(html_info_string)
def post(self): args = self.post_req_parser.parse_args() response_id = args['response_id'] title = args['title'] firstname = args['firstname'] lastname = args['lastname'] relation = args['relation'] email = args['email'] user = user_repository.get_by_id(g.current_user['id']) if not user: return USER_NOT_FOUND event = event_repository.get_event_by_response_id(response_id) if not event: return EVENT_NOT_FOUND response = response_repository.get_by_id(response_id) if not response: return RESPONSE_NOT_FOUND reference_request = ReferenceRequest(response_id=response_id, title=title, firstname=firstname, lastname=lastname, relation=relation, email=email) reference_request_repository.create(reference_request) link = "{host}/reference/{token}".format(host=misc.get_baobab_host(), token=reference_request.token) candidate, candidate_firstname, nominator = _get_candidate_nominator( response) if nominator is None: nomination_text = "has nominated themself" else: nomination_text = "has been nominated by {}".format(nominator) subject = 'REFERENCE REQUEST - {}'.format(event.name) body = REFERENCE_REQUEST_EMAIL_BODY.format( title=title, firstname=firstname, lastname=lastname, candidate=candidate, candidate_firstname=candidate_firstname, nomination_text=nomination_text, event_name=event.name, event_url=event.url, application_close_date=event.application_close, link=link) send_mail(recipient=email, subject=subject, body_text=body) reference_request.set_email_sent(datetime.now()) reference_request_repository.add(reference_request) return reference_request, 201
def test_repo_get_response(self): """Test for when retrieving a response from the repository and not via the api directly""" self._seed_data() response = response_repository.get_by_id_and_user_id( self.test_response.id, self.other_user_data['id']) self.assertEqual(response.application_form_id, self.test_form.id) self.assertEqual(response.user_id, self.other_user_data['id']) self.assertIsNone(response.submitted_timestamp) self.assertFalse(response.is_withdrawn) self.assertTrue(response.answers) same_response = response_repository.get_by_id(self.test_response.id) self.assertEqual(response, same_response) all_user_responses = response_repository.get_by_user_id( self.other_user_data['id']) self.assertEqual(len(all_user_responses), 2) self.assertEqual(response, all_user_responses[0])
def test_repo_get_answers(self): self._seed_data() # retrieve using response_id # first answer should have correct response_id and question_id answers_by_response = response_repository.get_answers_by_response_id( self.test_response.id) self.assertEqual(answers_by_response[0].response_id, self.test_response.id) self.assertEqual(answers_by_response[0].question_id, self.test_question.id) # retrieve using question_id # test the first answer here is the same as first answer above # test_question completed by dummy users and other_user answers_by_question = response_repository.get_answers_by_question_id( self.test_question.id) self.assertEqual(answers_by_question[0], answers_by_response[0]) self.assertEqual( len(self.test_users) + 1, len(answers_by_question)) # including other_user response # retrieve using response_id and question_id # use same question_id and response_id as previous test --> can test object is the same answer_by_qid_rid = response_repository.get_answer_by_question_id_and_response_id( self.test_question.id, self.test_response.id) self.assertEqual(answer_by_qid_rid, answers_by_response[0]) # use different ids. Check expected ids are in the returned answer answer_by_qid_rid_q2 = response_repository.get_answer_by_question_id_and_response_id( self.test_question2.id, self.responses[0].id) self.assertEqual(answer_by_qid_rid_q2.response_id, self.responses[0].id) self.assertEqual(answer_by_qid_rid_q2.question_id, self.test_question2.id) # retrieve all (Question, Answer) tuples using section_id and response_id qa_by_sid_rid = response_repository.get_question_answers_by_section_key_and_response_id( self.test_section.key, self.test_response.id) self.assertTupleEqual(qa_by_sid_rid[0], (self.test_question, self.test_answer1)) other_response = self.responses[0] qa_by_sid_rid_dummy = response_repository.get_question_answers_by_section_key_and_response_id( self.test_section.key, other_response.id) answer1 = response_repository.get_answer_by_question_id_and_response_id( self.test_question.id, other_response.id) answer2 = response_repository.get_answer_by_question_id_and_response_id( self.test_question2.id, other_response.id) self.assertEqual(len(qa_by_sid_rid_dummy), 2) # answered 2 questions self.assertTupleEqual(qa_by_sid_rid_dummy[0], (self.test_question, answer1)) self.assertTupleEqual(qa_by_sid_rid_dummy[1], (self.test_question2, answer2))
def test_build_response_html_answers(self): self._seed_static_data() application_form = application_form_repository.get_by_id( self.application_form_id) answers = response_repository.get_by_id(self.response_id).answers html_answer_string = build_response_html_answers( answers, 'en', application_form) self.assertIsNotNone(html_answer_string)
def post(self): args = self.post_req_parser.parse_args() response_id = args['response_id'] title = args['title'] firstname = args['firstname'] lastname = args['lastname'] relation = args['relation'] email = args['email'] user = user_repository.get_by_id(g.current_user['id']) if not user: return USER_NOT_FOUND event = event_repository.get_event_by_response_id(response_id) if not event: return EVENT_NOT_FOUND response = response_repository.get_by_id(response_id) if not response: return RESPONSE_NOT_FOUND reference_request = ReferenceRequest(response_id=response_id, title=title, firstname=firstname, lastname=lastname, relation=relation, email=email) reference_request_repository.create(reference_request) link = "{host}/reference/{token}".format(host=misc.get_baobab_host(), token=reference_request.token) try: candidate, candidate_firstname, nominator = _get_candidate_nominator( response) except ValueError as e: LOGGER.error(e) return BAD_CONFIGURATION email_user('reference-request-self-nomination' if nominator is None else 'reference-request', template_parameters=dict( candidate=candidate, candidate_firstname=candidate_firstname, nominator=nominator, event_url=event.url, application_close_date=event.application_close, link=link), event=event, user=user) reference_request.set_email_sent(datetime.now()) reference_request_repository.add(reference_request) return reference_request, 201
def get(self, event_id): parser = reqparse.RequestParser() parser.add_argument('language', type=str, required=True) args = parser.parse_args() responses = response_repository.get_all_for_event(event_id) review_form = review_repository.get_review_form(event_id) return [ ReviewResponseSummaryListAPI._serialise_response( response, review_form, args['language']) for response in responses ], 200
def get(self): args = self.post_req_parser.parse_args() token = args['token'] reference_request = reference_request_repository.get_by_token( token) # type: ReferenceRequest if not reference_request: return REFRERENCE_REQUEST_WITH_TOKEN_NOT_FOUND response_id = reference_request.response_id response = response_repository.get_by_id(response_id) # type: Response if not response: return RESPONSE_NOT_FOUND event = event_repository.get_event_by_response_id( response_id) # type: Event if not event: return EVENT_NOT_FOUND reference = reference_repository.get_by_reference_request_id( reference_request.id) app_form = event.get_application_form() # type: ApplicationForm # Determine whether the response is a nomination try: candidate, _, nominator = _get_candidate_nominator(response) except ValueError as e: LOGGER.error(e) return BAD_CONFIGURATION return_object = { 'candidate': candidate, 'nominator': nominator, 'relation': reference_request.relation, 'name': event.get_name('en'), 'description': event.get_description('en'), 'is_application_open': event.is_application_open, 'email_from': event.email_from, 'reference_submitted_timestamp': reference.timestamp if reference is not None else None } return return_object, 200
def get(self): LOGGER.debug('Received get request for reference-request') user = user_repository.get_by_id(g.current_user['id']) if not user: return USER_NOT_FOUND args = self.req_parser.parse_args() response = response_repository.get_by_id_and_user_id( args['response_id'], user.id) if not response: return RESPONSE_NOT_FOUND return reference_request_repository.get_all_by_response_id( response.id), 200
def get(self): args = self.get_req_parser.parse_args() event_id = args['event_id'] current_user_id = g.current_user['id'] event = event_repository.get_by_id(event_id) if not event: return errors.EVENT_NOT_FOUND if not event.has_application_form(): return errors.FORM_NOT_FOUND form = event.get_application_form() responses = response_repository.get_all_for_user_application(current_user_id, form.id) return responses
def test_build_response_html_answers_with_answers_missing(self): """ Tests that the builder doesn't break when a question is submitted without an answer """ self._seed_static_data() application_form = application_form_repository.get_by_id( self.application_form_id) answers = response_repository.get_by_id(self.response_id2).answers html_string = build_response_html_answers(answers, 'en', application_form) self.assertIsNotNone(html_string)
def post(self, event_id): parser = reqparse.RequestParser() parser.add_argument('response_ids', type=int, required=True, action='append') parser.add_argument('reviewer_email', type=str, required=True) args = parser.parse_args() response_ids = args['response_ids'] reviewer_email = args['reviewer_email'] filtered_response_ids = response_repository.filter_ids_to_event( response_ids, event_id) print('response_ids:', response_ids) print('filtered_response_ids:', filtered_response_ids) if set(filtered_response_ids) != set(response_ids): return FORBIDDEN event = event_repository.get_by_id(event_id) reviewer_user = user_repository.get_by_email(reviewer_email, g.organisation.id) if reviewer_user is None: return USER_NOT_FOUND if not reviewer_user.is_reviewer(event_id): _add_reviewer_role(reviewer_user.id, event_id) response_reviewers = [ ResponseReviewer(response_id, reviewer_user.id) for response_id in response_ids ] db.session.add_all(response_reviewers) db.session.commit() if len(response_ids) > 0: email_user('reviews-assigned', template_parameters=dict( num_reviews=len(response_ids), baobab_host=misc.get_baobab_host(), system_name=g.organisation.system_name, event_key=event.key), event=event, user=reviewer_user) return {}, 201
def get_event_status(event_id, user_id): invited_guest = invited_guest_repository.get_for_event_and_user( event_id, user_id) if invited_guest: registration = invited_guest_repository.get_registration_for_event_and_user( event_id, user_id) # If they're an invited guest, we don't bother with whether they applied or not return EventStatus( invited_guest=invited_guest.role, registration_status=_get_registration_status(registration)) response = response_repository.get_by_user_id_for_event(user_id, event_id) if response is None: application_status = None elif response.is_submitted: application_status = 'Submitted' elif response.is_withdrawn: application_status = 'Withdrawn' else: application_status = 'Not Submitted' outcome = outcome_repository.get_latest_by_user_for_event( user_id, event_id) if outcome is None: outcome_status = None else: outcome_status = outcome.status.name offer = offer_repository.get_by_user_id_for_event(user_id, event_id) if offer is None: offer_status = None elif offer.candidate_response: offer_status = 'Accepted' elif offer.candidate_response == False: offer_status = 'Rejected' elif offer.is_expired(): offer_status = 'Expired' else: offer_status = 'Pending' registration = registration_repository.get_by_user_id(user_id, event_id) registration_status = _get_registration_status(registration) return EventStatus(application_status=application_status, outcome_status=outcome_status, offer_status=offer_status, registration_status=registration_status)
def get(self, event_id): req_parser = reqparse.RequestParser() req_parser.add_argument('response_id', type=int, required=True) req_parser.add_argument('language', type=str, required=True) args = req_parser.parse_args() response_id = args['response_id'] language = args['language'] response = response_repository.get_by_id(response_id) review_form = review_repository.get_review_form(event_id) review_form_id = None if review_form is None else review_form.id review_config = review_configuration_repository.get_configuration_for_event(event_id) num_reviewers = review_config.num_reviews_required + review_config.num_optional_reviews if review_config is not None else 1 return ResponseDetailAPI._serialize_response(response, language, review_form_id, num_reviewers)
def get(self): args = self.get_req_parser.parse_args() user = user_repository.get_by_id(g.current_user['id']) response = response_repository.get_by_id(args['response_id']) if not response: return RESPONSE_NOT_FOUND event = event_repository.get_event_by_response_id(response.id) if not user.is_event_admin(event.id): return FORBIDDEN reference_responses = reference_request_repository.get_references_by_response_id( response.id) return [ reference_response.Reference for reference_response in reference_responses ], 200
def put(self): args = self.put_req_parser.parse_args() user_id = g.current_user['id'] is_submitted = args['is_submitted'] language = args['language'] response = response_repository.get_by_id(args['id']) if not response: return errors.RESPONSE_NOT_FOUND if response.user_id != user_id: return errors.UNAUTHORIZED if response.application_form_id != args['application_form_id']: return errors.UPDATE_CONFLICT response.is_submitted = is_submitted response.language = language if is_submitted: response.submit() response_repository.save(response) answers = [] for answer_args in args['answers']: answer = response_repository.get_answer_by_question_id_and_response_id( answer_args['question_id'], response.id) if answer: answer.update(answer_args['value']) else: answer = Answer(response.id, answer_args['question_id'], answer_args['value']) answers.append(answer) response_repository.save_answers(answers) try: if response.is_submitted: LOGGER.info( 'Sending confirmation email for response with ID : {id}'. format(id=response.id)) user = user_repository.get_by_id(user_id) response = response_repository.get_by_id_and_user_id( response.id, user_id) self.send_confirmation(user, response) except: LOGGER.warn( 'Failed to send confirmation email for response with ID : {id}, but the response was submitted succesfully' .format(id=response.id)) finally: return response, 200
def post(self): args = self.post_req_parser.parse_args() user_id = g.current_user['id'] is_submitted = args['is_submitted'] application_form_id = args['application_form_id'] language = args['language'] if len(language) != 2: language = 'en' # Fallback to English if language doesn't look like an ISO 639-1 code application_form = application_form_repository.get_by_id( application_form_id) if application_form is None: return errors.FORM_NOT_FOUND_BY_ID user = user_repository.get_by_id(user_id) responses = response_repository.get_all_for_user_application( user_id, application_form_id) if not application_form.nominations and len(responses) > 0: return errors.RESPONSE_ALREADY_SUBMITTED response = Response(application_form_id, user_id, language) if is_submitted: response.submit() response_repository.save(response) answers = [] for answer_args in args['answers']: answer = Answer(response.id, answer_args['question_id'], answer_args['value']) answers.append(answer) response_repository.save_answers(answers) try: if response.is_submitted: LOGGER.info( 'Sending confirmation email for response with ID : {id}'. format(id=response.id)) user = user_repository.get_by_id(user_id) response = response_repository.get_by_id_and_user_id( response.id, user_id) self.send_confirmation(user, response) except: LOGGER.warn( 'Failed to send confirmation email for response with ID : {id}, but the response was submitted succesfully' .format(id=response.id)) finally: return response, 201
def get(self, event_id): event = event_repository.get_by_id(event_id) if not event: return EVENT_NOT_FOUND num_responses = response_repository.get_total_count_by_event(event_id) num_submitted_respones = response_repository.get_submitted_count_by_event( event_id) num_withdrawn_responses = response_repository.get_withdrawn_count_by_event( event_id) submitted_response_timeseries = _process_timeseries( response_repository.get_submitted_timeseries_by_event(event_id)) review_config = review_config_repository.get_configuration_for_event( event_id) required_reviews = 1 if review_config is None else review_config.num_reviews_required reviews_completed = review_repository.get_count_reviews_completed_for_event( event_id) reviews_incomplete = review_repository.get_count_reviews_incomplete_for_event( event_id) reviews_unallocated = review_repository.count_unassigned_reviews( event_id, required_reviews) reviews_timeseries = _process_timeseries( review_repository.get_review_complete_timeseries_by_event( event_id)) offers_allocated = offer_repository.count_offers_allocated(event_id) offers_accepted = offer_repository.count_offers_accepted(event_id) offers_rejected = offer_repository.count_offers_rejected(event_id) offers_timeseries = _process_timeseries( offer_repository.timeseries_offers_accepted(event_id)) num_registrations = registration_repository.count_registrations( event_id) num_guests = guest_registration_repository.count_guests(event_id) num_registered_guests = guest_registration_repository.count_registered_guests( event_id) registration_timeseries = _process_timeseries( registration_repository.timeseries_registrations(event_id)) guest_registration_timeseries = _process_timeseries( guest_registration_repository.timeseries_guest_registrations( event_id)) registration_timeseries = _combine_timeseries( registration_timeseries, guest_registration_timeseries) return { 'num_responses': num_responses, 'num_submitted_responses': num_submitted_respones, 'num_withdrawn_responses': num_withdrawn_responses, 'submitted_timeseries': submitted_response_timeseries, 'reviews_completed': reviews_completed, 'review_incomplete': reviews_incomplete, 'reviews_unallocated': reviews_unallocated, 'reviews_complete_timeseries': reviews_timeseries, 'offers_allocated': offers_allocated, 'offers_accepted': offers_accepted, 'offers_rejected': offers_rejected, 'offers_accepted_timeseries': offers_timeseries, 'num_registrations': num_registrations, 'num_guests': num_guests, 'num_registered_guests': num_registered_guests, 'registration_timeseries': registration_timeseries }, 200
def get(self): def _get_answer(question_id, answers): # Get the answer for a question for a in answers: if a.question_id == question_id: return a return None def _get_files(application_form, answers): # Get the response files that should be exported in the ZIP file file_names = [] for section in application_form.sections: for question in section.questions: answer = _get_answer(question.id, answers) if answer is not None: # We are only interested in the files, # the text answers will be in the main PDF file if question.type == 'multi-file': file_names.extend(json.loads(answer.value)) if question.type == 'file': file_names.append(json.loads(answer.value)) return file_names req_parser = reqparse.RequestParser() req_parser.add_argument('response_id', type=int, required=True) req_parser.add_argument('language', type=str, required=True) args = req_parser.parse_args() response_id = args['response_id'] language = args['language'] response = response_repository.get_by_id(response_id) application_form = application_form_repository.get_by_id(response_id) # Build the HTML string response_string = strings.build_response_html_app_info(response, language) + \ strings.build_response_html_answers(response.answers, language, application_form) # Convert to PDF files_to_compress = [('response.pdf', pdfconvertor.html_to_pdf(response.id, response_string))] # The files that were uploaded as part of the response files_to_get = _get_files(application_form, response.answers) bucket = storage.get_storage_bucket() # Download and name the files files_to_compress.extend([ (f['rename'] or f['filename'], io.BytesIO(bucket.blob(f['filename']).download_as_bytes())) for f in files_to_get ]) # Zip files zipped_files = zip_in_memory(files_to_compress) with tempfile.NamedTemporaryFile() as temp: temp.write(zipped_files.getvalue()) return send_file(temp.name, as_attachment=True, attachment_filename=f"response_{response.id}.zip")