def download_brief_responses(framework_slug, lot_slug, brief_id): get_framework_and_lot(framework_slug, lot_slug, data_api_client, status='live', must_allow_brief=True) brief = data_api_client.get_brief(brief_id)["briefs"] if not is_brief_correct( brief, framework_slug, lot_slug, current_user.id, data_api_client ): abort(404) if brief['status'] != "closed": abort(404) responses = get_sorted_responses_for_brief(brief, data_api_client) rows = prepared_response_contents_for_brief(brief, responses) first = rows[0].keys() if responses else [] rows = [first] + [_.values() for _ in rows] transposed = list(six.moves.zip_longest(*rows)) outdata = io.StringIO() with csvx.Writer(outdata) as csv_out: csv_out.write_rows(transposed) csvdata = outdata.getvalue() return Response( csvdata, mimetype='text/csv', headers={ "Content-Disposition": "attachment;filename=responses-to-requirements-{}.csv".format(brief['id']), "Content-Type": "text/csv; header=present" } ), 200
def download_brief_responses(framework_slug, lot_slug, brief_id): get_framework_and_lot(framework_slug, lot_slug, data_api_client, status='live', must_allow_brief=True) brief = data_api_client.get_brief(brief_id)["briefs"] if not is_brief_correct( brief, framework_slug, lot_slug, current_user.id ): abort(404) if brief['status'] != "closed": abort(404) responses = get_sorted_responses_for_brief(brief, data_api_client) rows = prepared_response_contents_for_brief(brief, responses) first = rows[0].keys() if responses else [] rows = [first] + [_.values() for _ in rows] transposed = list(six.moves.zip_longest(*rows)) outdata = io.StringIO() with csvx.Writer(outdata) as csv_out: csv_out.write_rows(transposed) csvdata = outdata.getvalue() return Response( csvdata, mimetype='text/csv', headers={ "Content-Disposition": "attachment;filename=responses-to-requirements-{}.csv".format(brief['id']), "Content-Type": "text/csv; header=present" } ), 200
def edit_brief_question(framework_slug, lot_slug, brief_id, section_slug, question_id): get_framework_and_lot(framework_slug, lot_slug, data_api_client, status='live', must_allow_brief=True) brief = data_api_client.get_brief(brief_id)["briefs"] if not is_brief_correct( brief, framework_slug, lot_slug, current_user.id ) or not brief_can_be_edited(brief): abort(404) content = content_loader.get_manifest(brief['frameworkSlug'], 'edit_brief').filter( {'lot': brief['lotSlug']} ) section = content.get_section(section_slug) if section is None or not section.editable: abort(404) remove_non_cascade_fields(brief, section, question_id) question = section.get_question(question_id) if not question: abort(404) return render_template_with_csrf( "buyers/edit_brief_question.html", brief=brief, section=section, question=question )
def view_brief_section_summary(framework_slug, lot_slug, brief_id, section_slug): get_framework_and_lot(framework_slug, lot_slug, data_api_client, status='live', must_allow_brief=True) brief = data_api_client.get_brief(brief_id)["briefs"] if not is_brief_correct( brief, framework_slug, lot_slug, current_user.id, data_api_client ) or not brief_can_be_edited(brief): abort(404) if not has_permission_to_edit_brief(brief): return redirect('/2/request-access/create_drafts') content = content_loader.get_manifest(brief['frameworkSlug'], 'edit_brief').filter({'lot': brief['lotSlug']}) sections = content.summary(brief) section = sections.get_section(section_slug) remove_non_cascade_fields(brief, section, 'description-of-training') if not section: abort(404) return render_template( "buyers/section_summary.html", brief=brief, section=section ), 200
def edit_brief_question(framework_slug, lot_slug, brief_id, section_slug, question_id): get_framework_and_lot(framework_slug, lot_slug, data_api_client, status='live', must_allow_brief=True) brief = data_api_client.get_brief(brief_id)["briefs"] if not is_brief_correct( brief, framework_slug, lot_slug, current_user.id, data_api_client ) or not brief_can_be_edited(brief): abort(404) if not has_permission_to_edit_brief(brief): return redirect('/2/request-access/create_drafts') content = content_loader.get_manifest(brief['frameworkSlug'], 'edit_brief').filter( {'lot': brief['lotSlug']} ) section = content.get_section(section_slug) if section is None or not section.editable: abort(404) remove_non_cascade_fields(brief, section, question_id) question = section.get_question(question_id) if not question: abort(404) return render_template_with_csrf( "buyers/edit_brief_question.html", brief=brief, section=section, question=question )
def download_brief_responses_xlsx(framework_slug, lot_slug, brief_id): get_framework_and_lot(framework_slug, lot_slug, data_api_client, status='live', must_allow_brief=True) brief = data_api_client.get_brief(brief_id)["briefs"] if not is_brief_correct(brief, framework_slug, lot_slug, current_user.id, data_api_client): abort(404) if brief['status'] != "closed": abort(404) responses = get_sorted_responses_for_brief(brief, data_api_client) rows = prepared_response_contents_for_brief(brief, responses) first = list(rows[0].keys()) if responses else [] rows = [first] + [list(_.values()) for _ in rows] outdata = io.BytesIO() workbook = xlsxwriter.Workbook(outdata) bold = workbook.add_format({'bold': True}) sheet = workbook.add_worksheet('Responses') COLUMN_WIDTH = 25 for column_letter, r in zip(ascii_uppercase, rows): sheet.set_column('{x}:{x}'.format(x=column_letter), COLUMN_WIDTH) for row_number, c in enumerate(r, start=1): cell = '{}{}'.format(column_letter, row_number) if column_letter == 'A': sheet.write(cell, str(c), bold) else: sheet.write(cell, str(c)) workbook.close() return Response( outdata.getvalue(), mimetype= 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', headers={ "Content-Disposition": "attachment;filename=responses-to-requirements-{}.xlsx".format( brief['id']), "Content-Type": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" }), 200
def delete_a_brief(framework_slug, lot_slug, brief_id): get_framework_and_lot(framework_slug, lot_slug, data_api_client, status='live', must_allow_brief=True) brief = data_api_client.get_brief(brief_id)["briefs"] if not is_brief_correct( brief, framework_slug, lot_slug, current_user.id ) or not brief_can_be_edited(brief): abort(404) data_api_client.delete_brief(brief_id, current_user.email_address) flash({"requirements_deleted": brief.get("title")}) return redirect('/2/buyer-dashboard')
def select_seller_for_work_order(framework_slug, lot_slug, brief_id): brief = data_api_client.get_brief(brief_id)["briefs"] if not is_brief_correct( brief, framework_slug, lot_slug, current_user.id ): abort(404) form = WorkOrderSellerForm(data_api_client=data_api_client, brief_id=brief_id) return render_template_with_csrf('workorder/select-seller.html', brief=brief, form=form)
def test_is_brief_correct(self): brief = api_stubs.brief(user_id=123, status='live')['briefs'] assert buyers_helpers.is_brief_correct( brief, 'digital-outcomes-and-specialists', 'digital-specialists', 123) is True assert buyers_helpers.is_brief_correct( brief, 'not-digital-outcomes-and-specialists', 'digital-specialists', 123) is False assert buyers_helpers.is_brief_correct( brief, 'digital-outcomes-and-specialists', 'not-digital-specialists', 123) is False assert buyers_helpers.is_brief_correct( brief, 'digital-outcomes-and-specialists', 'not-digital-specialists', 124) is False assert buyers_helpers.is_brief_correct( api_stubs.brief(user_id=123, status='withdrawn')['briefs'], 'digital-outcomes-and-specialists', 'digital-specialists', 123) is False
def delete_a_brief(framework_slug, lot_slug, brief_id): get_framework_and_lot(framework_slug, lot_slug, data_api_client, status='live', must_allow_brief=True) brief = data_api_client.get_brief(brief_id)["briefs"] if not is_brief_correct( brief, framework_slug, lot_slug, current_user.id, data_api_client ) or not brief_can_be_edited(brief): abort(404) if not has_permission_to_edit_brief(brief): return redirect('/2/request-access/create_drafts') data_api_client.delete_brief(brief_id, current_user.email_address) flash({"requirements_deleted": brief.get("title")}) return redirect('/2/buyer-dashboard')
def download_brief_responses_xlsx(framework_slug, lot_slug, brief_id): get_framework_and_lot(framework_slug, lot_slug, data_api_client, status='live', must_allow_brief=True) brief = data_api_client.get_brief(brief_id)["briefs"] if not is_brief_correct( brief, framework_slug, lot_slug, current_user.id ): abort(404) if brief['status'] != "closed": abort(404) responses = get_sorted_responses_for_brief(brief, data_api_client) rows = prepared_response_contents_for_brief(brief, responses) first = rows[0].keys() if responses else [] rows = [first] + [_.values() for _ in rows] outdata = io.BytesIO() workbook = xlsxwriter.Workbook(outdata) bold = workbook.add_format({'bold': True}) sheet = workbook.add_worksheet('Responses') COLUMN_WIDTH = 25 for column_letter, r in zip(ascii_uppercase, rows): sheet.set_column('{x}:{x}'.format(x=column_letter), COLUMN_WIDTH) for row_number, c in enumerate(r, start=1): cell = '{}{}'.format(column_letter, row_number) if column_letter == 'A': sheet.write(cell, unicode(c), bold) else: sheet.write(cell, unicode(c)) workbook.close() return Response( outdata.getvalue(), mimetype='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', headers={ "Content-Disposition": "attachment;filename=responses-to-requirements-{}.xlsx".format(brief['id']), "Content-Type": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" } ), 200
def view_brief_overview(framework_slug, lot_slug, brief_id): if lot_slug == 'digital-professionals' or lot_slug == 'training': return redirect('/2/brief/{}/overview'.format(brief_id)) if lot_slug in ['rfx', 'atm', 'specialist', 'training2']: return redirect('/2/brief/{}/overview/{}'.format(brief_id, lot_slug)) framework, lot = get_framework_and_lot(framework_slug, lot_slug, data_api_client, status='live', must_allow_brief=True) brief = data_api_client.get_brief(brief_id)["briefs"] if not is_brief_correct(brief, framework_slug, lot_slug, current_user.id, data_api_client) and not allowed_email_domain( current_user.id, brief, data_api_client): abort(404) content = content_loader.get_manifest( brief['frameworkSlug'], 'edit_brief').filter({'lot': brief['lotSlug']}) sections = content.summary(brief) delete_requested = True if request.args.get('delete_requested') else False completed_sections = {} for section in sections: required, optional = count_unanswered_questions([section]) if section_has_at_least_one_required_question(section): completed_sections[section.slug] = True if required == 0 else False else: completed_sections[section.slug] = True if optional == 0 else False brief['clarificationQuestions'] = [ dict(question, number=index + 1) for index, question in enumerate(brief['clarificationQuestions']) ] return render_template_with_csrf( "buyers/brief_overview.html", framework=framework, confirm_remove=request.args.get("confirm_remove", None), brief=brief, sections=sections, completed_sections=completed_sections, step_sections=[ section.step for section in sections if hasattr(section, 'step') ], delete_requested=delete_requested, )
def view_brief_timeline(framework_slug, lot_slug, brief_id): TZ = current_app.config['DM_TIMEZONE'] get_framework_and_lot(framework_slug, lot_slug, data_api_client, status='live', must_allow_brief=True) brief = data_api_client.get_brief(brief_id)["briefs"] if not is_brief_correct( brief, framework_slug, lot_slug, current_user.id, data_api_client ) or brief.get('status') != 'live': abort(404) return render_template( "buyers/brief_publish_confirmation.html", email_address=brief['users'][0]['emailAddress'], published=True, current_date=pendulum.now(TZ), brief=brief )
def view_brief_timeline(framework_slug, lot_slug, brief_id): TZ = current_app.config['DM_TIMEZONE'] get_framework_and_lot(framework_slug, lot_slug, data_api_client, status='live', must_allow_brief=True) brief = data_api_client.get_brief(brief_id)["briefs"] if not is_brief_correct( brief, framework_slug, lot_slug, current_user.id ) or brief.get('status') != 'live': abort(404) return render_template( "buyers/brief_publish_confirmation.html", email_address=brief['users'][0]['emailAddress'], published=True, current_date=pendulum.now(TZ), brief=brief )
def add_supplier_question(framework_slug, lot_slug, brief_id): get_framework_and_lot(framework_slug, lot_slug, data_api_client, status='live', must_allow_brief=True) brief = data_api_client.get_brief(brief_id)["briefs"] if not is_brief_correct( brief, framework_slug, lot_slug, current_user.id ) and not allowed_email_domain(current_user.id, brief, data_api_client): abort(404) if brief["status"] != "live": abort(404) content = content_loader.get_manifest(brief['frameworkSlug'], "clarification_question") section = content.get_section(content.get_next_editable_section_id()) update_data = section.get_data(request.form) errors = {} status_code = 200 if request.method == "POST": try: data_api_client.add_brief_clarification_question(brief_id, update_data['question'], update_data['answer'], current_user.email_address) return redirect( url_for('.supplier_questions', framework_slug=brief['frameworkSlug'], lot_slug=brief['lotSlug'], brief_id=brief['id'])) except HTTPError as e: if e.status_code != 400: raise brief.update(update_data) errors = section.get_error_messages(e.message) status_code = 400 return render_template_with_csrf( "buyers/edit_brief_question.html", status_code=status_code, brief=brief, section=section, question=section.questions[0], button_label="Publish question and answer", errors=errors )
def download_brief_response_attachment(framework_slug, lot_slug, brief_id, response_id, attachment_id): get_framework_and_lot(framework_slug, lot_slug, data_api_client, status='live', must_allow_brief=True) brief = data_api_client.get_brief(brief_id)["briefs"] if not is_brief_correct( brief, framework_slug, lot_slug, current_user.id, data_api_client ): abort(404) if brief['status'] != "closed": abort(404) response = data_api_client.get_brief_response(response_id) if not response or not response.get('briefResponses', {}).get('attachedDocumentURL'): abort(404) try: slug = response['briefResponses']['attachedDocumentURL'][attachment_id] except IndexError: abort(404) if slug is None: abort(404) try: # try newer file storage mimetype = mimetypes.guess_type(slug)[0] or 'binary/octet-stream' return Response( s3_download_file( current_app.config.get('S3_BUCKET_NAME', None), slug, os.path.join( brief['frameworkSlug'], 'documents', 'brief-' + str(brief_id), 'supplier-' + str(response['briefResponses']['supplierCode']) ) ), mimetype=mimetype ) except botocore.exceptions.ClientError: url = get_signed_url(current_app.config['S3_BUCKET_NAME'], slug, None) if not url: abort(404) return redirect(url)
def view_brief_overview(framework_slug, lot_slug, brief_id): if lot_slug == 'digital-professionals' or lot_slug == 'training': return redirect('/2/brief/{}/overview'.format(brief_id)) if lot_slug in ['rfx', 'atm']: return redirect('/2/brief/{}/overview/{}'.format(brief_id, lot_slug)) framework, lot = get_framework_and_lot( framework_slug, lot_slug, data_api_client, status='live', must_allow_brief=True) brief = data_api_client.get_brief(brief_id)["briefs"] if not is_brief_correct( brief, framework_slug, lot_slug, current_user.id ) and not allowed_email_domain(current_user.id, brief, data_api_client): abort(404) content = content_loader.get_manifest(brief['frameworkSlug'], 'edit_brief').filter({'lot': brief['lotSlug']}) sections = content.summary(brief) delete_requested = True if request.args.get('delete_requested') else False completed_sections = {} for section in sections: required, optional = count_unanswered_questions([section]) if section_has_at_least_one_required_question(section): completed_sections[section.slug] = True if required == 0 else False else: completed_sections[section.slug] = True if optional == 0 else False brief['clarificationQuestions'] = [ dict(question, number=index+1) for index, question in enumerate(brief['clarificationQuestions']) ] return render_template_with_csrf( "buyers/brief_overview.html", framework=framework, confirm_remove=request.args.get("confirm_remove", None), brief=brief, sections=sections, completed_sections=completed_sections, step_sections=[section.step for section in sections if hasattr(section, 'step')], delete_requested=delete_requested, )
def create_new_work_order(framework_slug, lot_slug, brief_id): brief = data_api_client.get_brief(brief_id)["briefs"] if not is_brief_correct( brief, framework_slug, lot_slug, current_user.id ): abort(404) form = WorkOrderSellerForm(formdata=request.form, data_api_client=data_api_client, brief_id=brief_id) if not form.validate(): return render_template_with_csrf( 'workorder/select-seller.html', status_code=400, brief=brief, form=form ) try: seller = data_api_client.get_supplier(form.seller.data)['supplier'] work_order = data_api_client.create_work_order( briefId=brief_id, supplierCode=form.seller.data, workOrder=_create_work_order_from_brief(brief, seller) )['workOrder'] except APIError as e: form.errors['seller'] = [e.message] return render_template_with_csrf( 'workorder/select-seller.html', status_code=e.status_code, brief=brief, form=form, ) return redirect( url_for( 'buyers.get_work_order', work_order_id=work_order['id'], ) )
def supplier_questions(framework_slug, lot_slug, brief_id): get_framework_and_lot(framework_slug, lot_slug, data_api_client, status='live', must_allow_brief=True) brief = data_api_client.get_brief(brief_id)["briefs"] if not is_brief_correct( brief, framework_slug, lot_slug, current_user.id ) and not allowed_email_domain(current_user.id, brief, data_api_client): abort(404) if brief["status"] != "live": abort(404) brief['clarificationQuestions'] = [ dict(question, number=index+1) for index, question in enumerate(brief['clarificationQuestions']) ] return render_template( "buyers/supplier_questions.html", brief=brief, )
def view_brief_section_summary(framework_slug, lot_slug, brief_id, section_slug): get_framework_and_lot(framework_slug, lot_slug, data_api_client, status='live', must_allow_brief=True) brief = data_api_client.get_brief(brief_id)["briefs"] if not is_brief_correct( brief, framework_slug, lot_slug, current_user.id ) or not brief_can_be_edited(brief): abort(404) content = content_loader.get_manifest(brief['frameworkSlug'], 'edit_brief').filter({'lot': brief['lotSlug']}) sections = content.summary(brief) section = sections.get_section(section_slug) remove_non_cascade_fields(brief, section, 'description-of-training') if not section: abort(404) return render_template( "buyers/section_summary.html", brief=brief, section=section ), 200
def download_brief_response_attachment(framework_slug, lot_slug, brief_id, response_id, attachment_id): get_framework_and_lot(framework_slug, lot_slug, data_api_client, status='live', must_allow_brief=True) brief = data_api_client.get_brief(brief_id)["briefs"] if not is_brief_correct( brief, framework_slug, lot_slug, current_user.id ): abort(404) if brief['status'] != "closed": abort(404) response = data_api_client.get_brief_response(response_id) if not response or not response.get('briefResponses', {}).get('attachedDocumentURL'): abort(404) try: slug = response['briefResponses']['attachedDocumentURL'][attachment_id] except IndexError: abort(404) if slug is None: abort(404) try: # try newer file storage file = s3_download_file(slug, os.path.join(brief['frameworkSlug'], 'documents', 'brief-' + str(brief_id), 'supplier-' + str(response['briefResponses']['supplierCode']))) mimetype = mimetypes.guess_type(slug)[0] or 'binary/octet-stream' return Response(file, mimetype=mimetype) except botocore.exceptions.ClientError: url = get_signed_url(current_app.config['S3_BUCKET_NAME'], slug, None) if not url: abort(404) return redirect(url)
def update_brief_submission(framework_slug, lot_slug, brief_id, section_id, question_id): get_framework_and_lot(framework_slug, lot_slug, data_api_client, status='live', must_allow_brief=True) brief = data_api_client.get_brief(brief_id)["briefs"] if not is_brief_correct( brief, framework_slug, lot_slug, current_user.id ) or not brief_can_be_edited(brief): abort(404) content = content_loader.get_manifest(brief['frameworkSlug'], 'edit_brief').filter({'lot': brief['lotSlug']}) section = content.get_section(section_id) if section is None or not section.editable: abort(404) question = section.get_question(question_id) if not question: abort(404) update_data = question.get_data(request.form) remove_non_cascade_fields(brief, section, question_id, update_data) question_ids = section.get_section_question_ids() question_id_index = None if question_id in question_ids: question_id_index = question_ids.index(question_id) try: data_api_client.update_brief( brief_id, update_data, updated_by=current_user.email_address, page_questions=question.form_fields ) except HTTPError as e: update_data = section.unformat_data(update_data) mapped = {} for k, v in e.message.iteritems(): if ((k == 'sellerEmailList' or k == 'sellerEmail') and v.startswith('email_not_found~')): mapped[k] = v.split('~')[0] else: mapped[k] = v errors = section.get_error_messages(mapped) for k, v in errors.iteritems(): if ((k == 'sellerEmailList' or k == 'sellerEmail') and e.message[k].startswith('email_not_found~')): v['message'] = '{} {}'.format(e.message[k].split('~')[1], v['message']) # we need the brief_id to build breadcrumbs and the update_data to fill in the form. brief.update(update_data) return render_template_with_csrf( "buyers/edit_brief_question.html", status_code=400, brief=brief, section=section, question=question, errors=errors ) if section.has_summary_page: # If there are more than 1 questions and it is not the last one. if (question_id_index is not None and len(question_ids) > 1 and question_id_index != len(question_ids) - 1): return redirect( url_for( '.edit_brief_question', framework_slug=brief['frameworkSlug'], lot_slug=brief['lotSlug'], brief_id=brief['id'], section_slug=section.slug, question_id=question_ids[question_id_index + 1])) response = __navigate_next(content, brief, lot_slug, section_id) if response: return response if lot_slug != 'training': return redirect( url_for( ".view_brief_section_summary", framework_slug=brief['frameworkSlug'], lot_slug=brief['lotSlug'], brief_id=brief['id'], section_slug=section.slug)) else: response = __navigate_next(content, brief, lot_slug, section_id) if response: return response return redirect( url_for( ".view_brief_overview", framework_slug=brief['frameworkSlug'], lot_slug=brief['lotSlug'], brief_id=brief['id'] ) )
def publish_brief(framework_slug, lot_slug, brief_id): if lot_slug in ['digital-outcome', 'digital-professionals', 'training']: abort(404) TZ = current_app.config['DM_TIMEZONE'] get_framework_and_lot(framework_slug, lot_slug, data_api_client, status='live', must_allow_brief=True) brief = data_api_client.get_brief(brief_id)["briefs"] if not is_brief_correct( brief, framework_slug, lot_slug, current_user.id, data_api_client ) or not brief_can_be_edited(brief): abort(404) if not has_permission_to_edit_brief(brief): return redirect('/2/request-access/publish_opportunities') content = content_loader.get_manifest(brief['frameworkSlug'], 'edit_brief').filter({'lot': brief['lotSlug']}) sections = content.summary(brief) question_and_answers = {} question_and_answers_content = sections.get_question('questionAndAnswerSessionDetails') question_and_answers['id'] = question_and_answers_content['id'] for section in sections: if section.get_question('questionAndAnswerSessionDetails') == question_and_answers_content: question_and_answers['slug'] = section['id'] for question_id in section.get_section_question_ids(): remove_non_cascade_fields(brief, section, question_id) unanswered_required, unanswered_optional = count_unanswered_questions(sections) if request.method == 'POST': if unanswered_required > 0: abort(400, 'There are still unanswered required questions') if not current_user.has_permission('publish_opportunities'): return redirect('/2/request-access/publish_opportunities') data_api_client.publish_brief(brief_id, current_user.name) brief_url = '/2/brief/{}/published'.format(brief_id) brief_url_external = url_for('main.get_brief_by_id', framework_slug=brief['frameworkSlug'], brief_id=brief['id'], _external=True) send_new_opportunity_email_to_sellers(brief, brief_url_external) notification_message = '{}\n{}\nBy: {} ({})'.format( brief['title'], brief['organisation'], current_user.name, current_user.email_address ) notify_team('A buyer has published a new opportunity', notification_message, brief_url_external) return redirect(brief_url) else: email_address = current_user.email_address return render_template_with_csrf( "buyers/brief_publish_confirmation.html", email_address=email_address, question_and_answers=question_and_answers, unanswered_required=unanswered_required, sections=sections, brief=brief, current_date=pendulum.now(TZ) )
def publish_brief(framework_slug, lot_slug, brief_id): if lot_slug == 'digital-outcome': abort(404) TZ = current_app.config['DM_TIMEZONE'] get_framework_and_lot(framework_slug, lot_slug, data_api_client, status='live', must_allow_brief=True) brief = data_api_client.get_brief(brief_id)["briefs"] if not is_brief_correct( brief, framework_slug, lot_slug, current_user.id ) or not brief_can_be_edited(brief): abort(404) content = content_loader.get_manifest(brief['frameworkSlug'], 'edit_brief').filter({'lot': brief['lotSlug']}) brief_users = brief['users'][0] brief_user_name = brief_users['name'] sections = content.summary(brief) question_and_answers = {} question_and_answers_content = sections.get_question('questionAndAnswerSessionDetails') question_and_answers['id'] = question_and_answers_content['id'] for section in sections: if section.get_question('questionAndAnswerSessionDetails') == question_and_answers_content: question_and_answers['slug'] = section['id'] for question_id in section.get_section_question_ids(): remove_non_cascade_fields(brief, section, question_id) unanswered_required, unanswered_optional = count_unanswered_questions(sections) if request.method == 'POST': if unanswered_required > 0: abort(400, 'There are still unanswered required questions') data_api_client.publish_brief(brief_id, brief_user_name) brief_url = '/2/brief/{}/published'.format(brief_id) brief_url_external = url_for('main.get_brief_by_id', framework_slug=brief['frameworkSlug'], brief_id=brief['id'], _external=True) send_new_opportunity_email_to_sellers(brief, brief_url_external) notification_message = '{}\n{}\nBy: {} ({})'.format( brief['title'], brief['organisation'], current_user.name, current_user.email_address ) notify_team('A buyer has published a new opportunity', notification_message, brief_url_external) return redirect(brief_url) else: email_address = brief_users['emailAddress'] return render_template_with_csrf( "buyers/brief_publish_confirmation.html", email_address=email_address, question_and_answers=question_and_answers, unanswered_required=unanswered_required, sections=sections, brief=brief, current_date=pendulum.now(TZ) )
def update_brief_submission(framework_slug, lot_slug, brief_id, section_id, question_id): get_framework_and_lot(framework_slug, lot_slug, data_api_client, status='live', must_allow_brief=True) brief = data_api_client.get_brief(brief_id)["briefs"] if not is_brief_correct( brief, framework_slug, lot_slug, current_user.id, data_api_client ) or not brief_can_be_edited(brief): abort(404) if not has_permission_to_edit_brief(brief): return redirect('/2/request-access/create_drafts') content = content_loader.get_manifest(brief['frameworkSlug'], 'edit_brief').filter({'lot': brief['lotSlug']}) section = content.get_section(section_id) if section is None or not section.editable: abort(404) question = section.get_question(question_id) if not question: abort(404) update_data = question.get_data(request.form) remove_non_cascade_fields(brief, section, question_id, update_data) question_ids = section.get_section_question_ids() question_id_index = None if question_id in question_ids: question_id_index = question_ids.index(question_id) try: data_api_client.update_brief( brief_id, update_data, updated_by=current_user.email_address, page_questions=question.form_fields ) except HTTPError as e: update_data = section.unformat_data(update_data) mapped = {} for k, v in e.message.iteritems(): if ((k == 'sellerEmailList' or k == 'sellerEmail') and v.startswith('email_not_found~')): mapped[k] = v.split('~')[0] else: mapped[k] = v errors = section.get_error_messages(mapped) for k, v in errors.iteritems(): if ((k == 'sellerEmailList' or k == 'sellerEmail') and e.message[k].startswith('email_not_found~')): v['message'] = '{} {}'.format(e.message[k].split('~')[1], v['message']) # we need the brief_id to build breadcrumbs and the update_data to fill in the form. brief.update(update_data) return render_template_with_csrf( "buyers/edit_brief_question.html", status_code=400, brief=brief, section=section, question=question, errors=errors ) if section.has_summary_page: # If there are more than 1 questions and it is not the last one. if (question_id_index is not None and len(question_ids) > 1 and question_id_index != len(question_ids) - 1): return redirect( url_for( '.edit_brief_question', framework_slug=brief['frameworkSlug'], lot_slug=brief['lotSlug'], brief_id=brief['id'], section_slug=section.slug, question_id=question_ids[question_id_index + 1])) response = __navigate_next(content, brief, lot_slug, section_id) if response: return response if lot_slug != 'training': return redirect( url_for( ".view_brief_section_summary", framework_slug=brief['frameworkSlug'], lot_slug=brief['lotSlug'], brief_id=brief['id'], section_slug=section.slug)) else: response = __navigate_next(content, brief, lot_slug, section_id) if response: return response return redirect( url_for( ".view_brief_overview", framework_slug=brief['frameworkSlug'], lot_slug=brief['lotSlug'], brief_id=brief['id'] ) )