Exemple #1
0
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
Exemple #2
0
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 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
    )
Exemple #5
0
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
    )
Exemple #6
0
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')
Exemple #8
0
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
Exemple #10
0
def start_new_brief(framework_slug, lot_slug):
    if lot_slug in ['digital-outcome', 'digital-professionals', 'training']:
        abort(404)

    if not has_permission_to_edit_brief():
        return redirect('/2/request-access/create_drafts')

    framework, lot = get_framework_and_lot(framework_slug,
                                           lot_slug,
                                           data_api_client,
                                           status='live',
                                           must_allow_brief=True)

    content = content_loader.get_manifest(framework_slug, 'edit_brief').filter(
        {'lot': lot['slug']})

    section = content.get_section(content.get_next_editable_section_id())

    return render_template_with_csrf(
        "buyers/create_brief_question.html",
        brief={},
        framework=framework,
        lot=lot,
        section=section,
        question=section.questions[0],
    )
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
    )
Exemple #12
0
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 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
    )
Exemple #14
0
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)
Exemple #15
0
def info_page_for_starting_a_brief(framework_slug, lot_slug):
    if lot_slug in ['digital-outcome', 'digital-professionals']:
        abort(404)
    framework, lot = get_framework_and_lot(framework_slug, lot_slug, data_api_client,
                                           status='live', must_allow_brief=True)
    return render_template(
        "buyers/start_brief_info.html",
        framework=framework,
        lot=lot
    ), 200
def info_page_for_starting_a_brief(framework_slug, lot_slug):
    if lot_slug == 'digital-outcome':
        abort(404)
    framework, lot = get_framework_and_lot(framework_slug, lot_slug, data_api_client,
                                           status='live', must_allow_brief=True)
    return render_template(
        "buyers/start_brief_info.html",
        framework=framework,
        lot=lot
    ), 200
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,
    )
Exemple #18
0
def create_new_brief(framework_slug, lot_slug):
    if lot_slug in ['digital-outcome', 'digital-professionals', 'training']:
        abort(404)

    if not has_permission_to_edit_brief():
        return redirect('/2/request-access/create_drafts')

    framework, lot = get_framework_and_lot(framework_slug, lot_slug, data_api_client,
                                           status='live', must_allow_brief=True)

    content = content_loader.get_manifest(framework_slug, 'edit_brief').filter(
        {'lot': lot['slug']}
    )

    section = content.get_section(content.get_next_editable_section_id())

    update_data = section.get_data(request.form)

    try:
        brief = data_api_client.create_brief(
            framework_slug,
            lot_slug,
            current_user.id,
            update_data,
            updated_by=current_user.email_address,
            page_questions=section.get_field_names()
        )["briefs"]
    except HTTPError as e:
        update_data = section.unformat_data(update_data)
        errors = section.get_error_messages(e.message)

        return render_template_with_csrf(
            "buyers/create_brief_question.html",
            status_code=400,
            data=update_data,
            brief={},
            framework=framework,
            lot=lot,
            section=section,
            question=section.questions[0],
            errors=errors
        )

    response = __navigate_next(content, brief, lot_slug, section.id)
    if response:
        return response

    return redirect(
        url_for(".view_brief_overview",
                framework_slug=framework_slug,
                lot_slug=lot_slug,
                brief_id=brief['id']))
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)
Exemple #21
0
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 create_new_brief(framework_slug, lot_slug):
    if lot_slug == 'digital-outcome':
        abort(404)
    framework, lot = get_framework_and_lot(framework_slug, lot_slug, data_api_client,
                                           status='live', must_allow_brief=True)

    content = content_loader.get_manifest(framework_slug, 'edit_brief').filter(
        {'lot': lot['slug']}
    )

    section = content.get_section(content.get_next_editable_section_id())

    update_data = section.get_data(request.form)

    try:
        brief = data_api_client.create_brief(
            framework_slug,
            lot_slug,
            current_user.id,
            update_data,
            updated_by=current_user.email_address,
            page_questions=section.get_field_names()
        )["briefs"]
    except HTTPError as e:
        update_data = section.unformat_data(update_data)
        errors = section.get_error_messages(e.message)

        return render_template_with_csrf(
            "buyers/create_brief_question.html",
            status_code=400,
            data=update_data,
            brief={},
            framework=framework,
            lot=lot,
            section=section,
            question=section.questions[0],
            errors=errors
        )

    response = __navigate_next(content, brief, lot_slug, section.id)
    if response:
        return response

    return redirect(
        url_for(".view_brief_overview",
                framework_slug=framework_slug,
                lot_slug=lot_slug,
                brief_id=brief['id']))
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 start_new_brief(framework_slug, lot_slug):
    if lot_slug == 'digital-outcome':
        abort(404)
    framework, lot = get_framework_and_lot(framework_slug, lot_slug, data_api_client,
                                           status='live', must_allow_brief=True)

    content = content_loader.get_manifest(framework_slug, 'edit_brief').filter(
        {'lot': lot['slug']}
    )

    section = content.get_section(content.get_next_editable_section_id())

    return render_template_with_csrf(
        "buyers/create_brief_question.html",
        brief={},
        framework=framework,
        lot=lot,
        section=section,
        question=section.questions[0],
    )
Exemple #25
0
    def test_get_framework_and_lot(self):
        data_api_client = mock.Mock()
        data_api_client.get_framework.return_value = api_stubs.framework(
            slug='digital-outcomes-and-specialists',
            status='live',
            lots=[
                api_stubs.lot(slug='digital-specialists', allows_brief=True)
            ])

        framework, lot = buyers_helpers.get_framework_and_lot(
            'digital-outcomes-and-specialists', 'digital-specialists',
            data_api_client)

        assert framework['status'] == "live"
        assert framework['name'] == 'Digital Outcomes and Specialists'
        assert framework['slug'] == 'digital-outcomes-and-specialists'
        assert framework['clarificationQuestionsOpen'] is True
        assert lot == {
            'slug': 'digital-specialists',
            'oneServiceLimit': False,
            'allowsBrief': True,
            'id': 1,
            'name': 'Digital Specialists',
        }
Exemple #26
0
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)
        )
Exemple #27
0
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']
        )
    )
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 == '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)
        )