Example #1
0
    def test_upload_with_validation_errors(self):
        request_files = ImmutableMultiDict({'pricingDocumentURL': mock_file('q1.bad', 100)})

        files, errors = upload_service_documents(
            'bucket', self.documents_url, self.service,
            request_files, self.section)

        assert files is None
        assert 'pricingDocumentURL' in errors
Example #2
0
    def test_only_files_in_section_are_uploaded(self):
        request_files = ImmutableMultiDict({'serviceDefinitionDocumentURL': mock_file('q1.pdf', 100)})

        files, errors = upload_service_documents(
            'bucket', self.documents_url, self.service,
            request_files, self.section)

        assert len(files) == 0
        assert len(errors) == 0
    def test_empty_files_are_filtered(self):
        request_files = {'pricingDocumentURL': mock_file('q1.pdf', 0)}

        files, errors = upload_service_documents(
            self.uploader, self.documents_url, self.service,
            request_files, self.section)

        assert len(files) == 0
        assert len(errors) == 0
    def test_upload_with_validation_errors(self):
        request_files = {'pricingDocumentURL': MockFile(b"*" * 100, 'q1.bad')}

        files, errors = upload_service_documents(
            self.uploader, 'documents', self.documents_url, self.service,
            request_files, self.section)

        assert files is None
        assert 'pricingDocumentURL' in errors
    def test_only_files_in_section_are_uploaded(self):
        request_files = {'serviceDefinitionDocumentURL': mock_file('q1.pdf', 100)}

        files, errors = upload_service_documents(
            self.uploader, self.documents_url, self.service,
            request_files, self.section)

        assert len(files) == 0
        assert len(errors) == 0
    def test_empty_files_are_filtered(self):
        request_files = {'pricingDocumentURL': MockFile(b"", 'q1.pdf')}

        files, errors = upload_service_documents(
            self.uploader, 'documents', self.documents_url, self.service,
            request_files, self.section)

        assert len(files) == 0
        assert len(errors) == 0
    def test_only_files_in_section_are_uploaded(self):
        request_files = {'serviceDefinitionDocumentURL': MockFile(b"*" * 100, 'q1.pdf')}

        files, errors = upload_service_documents(
            self.uploader, 'documents', self.documents_url, self.service,
            request_files, self.section)

        assert len(files) == 0
        assert len(errors) == 0
Example #8
0
    def test_empty_files_are_filtered(self):
        request_files = ImmutableMultiDict({'pricingDocumentURL': mock_file('q1.pdf', 0)})

        files, errors = upload_service_documents(
            'bucket', self.documents_url, self.service,
            request_files, self.section)

        assert len(files) == 0
        assert len(errors) == 0
    def test_upload_with_validation_errors(self):
        request_files = {'pricingDocumentURL': mock_file('q1.bad', 100)}

        files, errors = upload_service_documents(
            self.uploader, self.documents_url, self.service,
            request_files, self.section)

        assert files is None
        assert 'pricingDocumentURL' in errors
def update_service(service_id, section_id, question_slug=None):
    service = data_api_client.get_service(service_id)
    if service is None:
        abort(404)
    service = service['services']

    # we don't actually need the framework here; using this to 404 if framework for the service is not live
    # TODO remove `expired` from below. It's a temporary fix to allow access to DOS2 as it's expired.
    get_framework_or_404(data_api_client,
                         service['frameworkSlug'],
                         allowed_statuses=['live', 'expired'])

    content = content_loader.get_manifest(
        service['frameworkSlug'],
        'edit_service_as_admin',
    ).filter(service, inplace_allowed=True)
    section = content.get_section(section_id)
    if question_slug is not None:
        # Overwrite section with single question section for 'question per page' editing.
        section = section.get_question_as_section(question_slug)
    if section is None or not section.editable:
        abort(404)

    errors = None
    posted_data = section.get_data(request.form)

    uploaded_documents, document_errors = upload_service_documents(
        s3.S3(current_app.config['DM_S3_DOCUMENT_BUCKET']), 'documents',
        current_app.config['DM_ASSETS_URL'], service, request.files, section)

    if document_errors:
        errors = section.get_error_messages(document_errors)
    else:
        posted_data.update(uploaded_documents)

    if not errors and section.has_changes_to_save(service, posted_data):
        try:
            data_api_client.update_service(
                service_id,
                posted_data,
                current_user.email_address,
                user_role='admin',
            )
        except HTTPError as e:
            errors = section.get_error_messages(e.message)

    if errors:
        return render_template(
            "edit_section.html",
            section=section,
            service_data=section.unformat_data(dict(service, **posted_data)),
            service_id=service_id,
            errors=govuk_errors(errors),
        ), 400

    return redirect(url_for(".view_service", service_id=service_id))
Example #11
0
    def test_upload_service_documents(self):
        request_files = ImmutableMultiDict({'pricingDocumentURL': mock_file('q1.pdf', 100)})

        with freeze_time('2015-10-04 14:36:05'):
            with patch('boto3.s3.inject.bucket_upload_fileobj'):
                files, errors = upload_service_documents(
                    'bucket', self.documents_url, self.service,
                    request_files, self.section)

        assert 'pricingDocumentURL' in files
        assert len(errors) == 0
    def test_upload_service_documents(self):
        request_files = {'pricingDocumentURL': mock_file('q1.pdf', 100)}

        with freeze_time('2015-10-04 14:36:05'):
            files, errors = upload_service_documents(
                self.uploader, self.documents_url, self.service,
                request_files, self.section)

        self.uploader.save.assert_called_with(
            'g-cloud-7/documents/12345/654321-pricing-document-2015-10-04-1436.pdf', mock.ANY, acl='public-read')

        assert 'pricingDocumentURL' in files
        assert len(errors) == 0
    def test_upload_service_documents(self):
        request_files = {'pricingDocumentURL': self.test_pdf}

        with freeze_time('2015-10-04 14:36:05'):
            files, errors = upload_service_documents(
                self.uploader, 'documents', self.documents_url, self.service,
                request_files, self.section)

        self.uploader.save.assert_called_with(
            'g-cloud-7/documents/12345/654321-pricing-document-2015-10-04-1436.pdf', mock.ANY, acl='public-read')

        assert 'pricingDocumentURL' in files
        assert len(errors) == 0
def update(service_id, section_id):
    service = data_api_client.get_service(service_id)
    if service is None:
        abort(404)
    service = service['services']

    content = content_loader.get_builder('g-cloud-6', 'edit_service_as_admin').filter(service)
    section = content.get_section(section_id)
    if section is None or not section.editable:
        abort(404)

    errors = None
    posted_data = section.get_data(request.form)

    uploaded_documents, document_errors = upload_service_documents(
        S3(current_app.config['DM_S3_DOCUMENT_BUCKET']),
        current_app.config['DM_DOCUMENTS_URL'],
        service, request.files, section)

    if document_errors:
        errors = section.get_error_messages(document_errors, service['lot'])
    else:
        posted_data.update(uploaded_documents)

    if not errors and section.has_changes_to_save(service, posted_data):
        try:
            data_api_client.update_service(
                service_id,
                posted_data,
                current_user.email_address)
        except HTTPError as e:
            errors = section.get_error_messages(e.message, service['lot'])

    if errors:
        if not posted_data.get('serviceName', None):
            posted_data['serviceName'] = service.get('serviceName', '')
        return render_template(
            "edit_section.html",
            **get_template_data(
                section=section,
                service_data=posted_data,
                service_id=service_id,
                errors=errors
            )
        ), 400

    return redirect(url_for(".view", service_id=service_id))
Example #15
0
def update(service_id, section_id):
    service = data_api_client.get_service(service_id)
    if service is None:
        abort(404)
    service = service['services']

    content = content_loader.get_builder('g-cloud-6', 'edit_service_as_admin').filter(service)
    section = content.get_section(section_id)
    if section is None or not section.editable:
        abort(404)

    errors = None
    posted_data = section.get_data(request.form)

    uploaded_documents, document_errors = upload_service_documents(
        S3(current_app.config['DM_S3_DOCUMENT_BUCKET']),
        current_app.config['DM_DOCUMENTS_URL'],
        service, request.files, section)

    if document_errors:
        errors = section.get_error_messages(document_errors)
    else:
        posted_data.update(uploaded_documents)

    if not errors and section.has_changes_to_save(service, posted_data):
        try:
            data_api_client.update_service(
                service_id,
                posted_data,
                current_user.email_address)
        except HTTPError as e:
            errors = section.get_error_messages(e.message)

    if errors:
        if not posted_data.get('serviceName', None):
            posted_data['serviceName'] = service.get('serviceName', '')
        return render_template(
            "edit_section.html",
            section=section,
            service_data=posted_data,
            service_id=service_id,
            errors=errors
        ), 400

    return redirect(url_for(".view", service_id=service_id))
    def test_upload_private_service_documents(self):
        request_files = {'pricingDocumentURL': MockFile(b"*" * 100, 'q1.pdf')}

        with freeze_time('2015-10-04 14:36:05'):
            files, errors = upload_service_documents(self.uploader,
                                                     'documents',
                                                     self.documents_url,
                                                     self.service,
                                                     request_files,
                                                     self.section,
                                                     public=False)

        self.uploader.save.assert_called_with(
            'g-cloud-7/documents/12345/654321-pricing-document-2015-10-04-1436.pdf',
            mock.ANY,
            acl='bucket-owner-full-control')

        assert 'pricingDocumentURL' in files
        assert len(errors) == 0
def update_section(framework_slug, service_id, section_id):
    service = data_api_client.get_service(service_id)
    if service is None:
        abort(404)
    service = service['services']

    if not is_service_associated_with_supplier(service):
        abort(404)

    if service["frameworkSlug"] != framework_slug:
        abort(404)

    # we don't actually need the framework here; using this to 404 if framework for the service is not live
    get_framework_or_404(data_api_client, service['frameworkSlug'], allowed_statuses=['live'])

    try:
        content = content_loader.get_manifest(service["frameworkSlug"], 'edit_service').filter(
            service,
            inplace_allowed=True,
        )
    except ContentNotFoundError:
        abort(404)
    section = content.get_section(section_id)
    if section is None or not section.editable:
        abort(404)

    posted_data = section.get_data(request.form)

    errors = None
    # This utils method filters out any empty documents and validates against service document rules
    uploaded_documents, document_errors = upload_service_documents(
        s3.S3(current_app.config['DM_DOCUMENTS_BUCKET']),
        'documents',
        current_app.config['DM_ASSETS_URL'],
        service,
        request.files,
        section,
    )
    if document_errors:
        errors = section.get_error_messages(document_errors)
    else:
        posted_data.update(uploaded_documents)

    if not errors and section.has_changes_to_save(service, posted_data):
        try:
            data_api_client.update_service(
                service_id,
                posted_data,
                current_user.email_address)
        except HTTPError as e:
            errors = section.get_error_messages(e.message)

    if errors:
        session_timeout = displaytimeformat(datetime.utcnow() + timedelta(hours=1))
        return render_template(
            "services/edit_section.html",
            section=section,
            service_data=service,
            service_id=service_id,
            session_timeout=session_timeout,
            errors=errors,
        ), 400
    flash(SERVICE_UPDATED_MESSAGE, "success")
    return redirect(url_for(".edit_service", service_id=service_id, framework_slug=service["frameworkSlug"]))
Example #18
0
def submit_brief_response(brief_id):
    """Hits up the data API to create a new brief response."""

    brief = get_brief(data_api_client, brief_id)

    if brief['status'] != 'live':
        return render_template(
            "briefs/brief_closed_error.html"
        ), 400

    if not is_supplier_selected_for_brief(data_api_client, current_user, brief):
        return _render_not_selected_for_brief_error_page()

    ineligible = is_supplier_not_eligible_for_brief(data_api_client, current_user.supplier_code, brief)
    if ineligible:
        return _render_error_page(ineligible, brief, clarification_question=True)

    if supplier_has_a_brief_response(data_api_client, current_user.supplier_code, brief_id):
        flash('already_applied', 'error')
        return redirect(url_for(".view_response_result", brief_id=brief_id))

    framework, lot = get_framework_and_lot(
        data_api_client, brief['frameworkSlug'], brief['lotSlug'], allowed_statuses=['live'])

    content = content_loader.get_manifest(framework['slug'], 'edit_brief_response').filter({'lot': lot['slug']})
    section = content.get_section(content.get_next_editable_section_id())
    response_data = section.get_data(request.form)

    service = {'frameworkSlug': brief['frameworkSlug'], 'supplierCode': current_user.supplier_code, 'id': brief_id}
    uploaded_documents, document_errors = upload_service_documents(current_app.config.get('S3_BUCKET_NAME'),
                                                                   "", service, request.files, section)

    if document_errors:
        # replace generic 'Apply for opportunity' title with title including the name of the brief
        section.name = "Apply for ‘{}’".format(brief['title'])
        section.inject_brief_questions_into_boolean_list_question(brief)
        section_summary = section.summary(response_data)
        errors = section_summary.get_error_messages(document_errors)
        if errors.get('attachedDocumentURL', {}).get('question'):
            errors['attachedDocumentURL']['question'] = "Attached document"
        return render_template_with_csrf(
            "briefs/brief_response.html",
            status_code=400,
            brief=brief,
            service_data=response_data,
            section=section,
            errors=errors,
        )
    else:
        response_data.update(uploaded_documents)

    try:
        brief_response = data_api_client.create_brief_response(
            brief_id, current_user.supplier_code, response_data, current_user.email_address
        )['briefResponses']
    except HTTPError as e:
        # replace generic 'Apply for opportunity' title with title including the name of the brief
        section.name = "Apply for ‘{}’".format(brief['title'])
        section.inject_brief_questions_into_boolean_list_question(brief)
        section_summary = section.summary(response_data)

        errors = {}
        if isinstance(e.message, dict):
            errors = section_summary.get_error_messages(e.message)
            rollbar.report_message(json.dumps(errors), 'error', request)
        else:
            flash('already_applied', 'error')

        return render_template_with_csrf(
            "briefs/brief_response.html",
            status_code=400,
            brief=brief,
            service_data=response_data,
            section=section,
            errors=errors,
        )

    response_url = url_for(".view_response_result", brief_id=brief_id, result='success')

    send_thank_you_email_to_responders(brief, brief_response, response_url)

    return redirect(response_url)
def update_section_submission(framework_slug,
                              lot_slug,
                              service_id,
                              section_id,
                              question_slug=None):
    framework, lot = get_framework_and_lot(data_api_client,
                                           framework_slug,
                                           lot_slug,
                                           allowed_statuses=['open'])

    try:
        draft = data_api_client.get_draft_service(service_id)['services']
    except HTTPError as e:
        abort(e.status_code)

    if draft['lotSlug'] != lot_slug or draft['frameworkSlug'] != framework_slug:
        abort(404)

    if not is_service_associated_with_supplier(draft):
        abort(404)

    content = content_loader.get_manifest(framework_slug,
                                          'edit_submission').filter(draft)
    section = content.get_section(section_id)
    if section and (question_slug is not None):
        section = section.get_question_as_section(question_slug)

    if section is None or not section.editable:
        abort(404)

    errors = None
    document_errors = None
    update_data = section.get_data(request.form)

    uploader = s3.S3(current_app.config['DM_SUBMISSIONS_BUCKET'])
    documents_url = url_for('.dashboard', _external=True) + '/assets/'
    uploaded_documents, document_errors = upload_service_documents(
        uploader, documents_url, draft, request.files, section, public=False)

    if document_errors:
        errors = section.get_error_messages(document_errors)
    else:
        update_data.update(uploaded_documents)

    if not errors and section.has_changes_to_save(draft, update_data):
        try:
            data_api_client.update_draft_service(
                service_id,
                update_data,
                current_user.email_address,
                page_questions=section.get_field_names())
        except HTTPError as e:
            update_data = section.unformat_data(update_data)
            errors = section.get_error_messages(e.message)

    if errors:
        keys_required_for_template = ['serviceName', 'lot', 'lotName']
        for k in keys_required_for_template:
            if k in draft and k not in update_data:
                update_data[k] = draft[k]
        return render_template_with_csrf(
            "services/edit_submission_section.html",
            framework=framework,
            section=section,
            next_section_name=get_next_section_name(content, section.id),
            service_data=update_data,
            service_id=service_id,
            one_service_limit=lot['oneServiceLimit'],
            return_to_summary=bool(request.args.get('return_to_summary')),
            errors=errors)

    return_to_summary = bool(request.args.get('return_to_summary'))
    next_section = content.get_next_editable_section_id(section_id)

    if next_section and not return_to_summary and request.form.get(
            'continue_to_next_section'):
        return redirect(
            url_for(".edit_service_submission",
                    framework_slug=framework['slug'],
                    lot_slug=draft['lotSlug'],
                    service_id=service_id,
                    section_id=next_section))
    else:
        return redirect(
            url_for(".view_service_submission",
                    framework_slug=framework['slug'],
                    lot_slug=draft['lotSlug'],
                    service_id=service_id,
                    _anchor=section_id))
def update_section_submission(framework_slug, lot_slug, service_id, section_id, question_slug=None):
    framework, lot = get_framework_and_lot(data_api_client, framework_slug, lot_slug, allowed_statuses=['open'])

    try:
        draft = data_api_client.get_draft_service(service_id)['services']
    except HTTPError as e:
        abort(e.status_code)

    if draft['lotSlug'] != lot_slug or draft['frameworkSlug'] != framework_slug:
        abort(404)

    if not is_service_associated_with_supplier(draft):
        abort(404)

    content = content_loader.get_manifest(framework_slug, 'edit_submission').filter(draft)
    section = content.get_section(section_id)
    if section and (question_slug is not None):
        section = section.get_question_as_section(question_slug)

    if section is None or not section.editable:
        abort(404)

    errors = None
    document_errors = None
    update_data = section.get_data(request.form)

    uploader = s3.S3(current_app.config['DM_SUBMISSIONS_BUCKET'])
    documents_url = url_for('.dashboard', _external=True) + '/assets/'
    uploaded_documents, document_errors = upload_service_documents(
        uploader, documents_url, draft, request.files, section,
        public=False)

    if document_errors:
        errors = section.get_error_messages(document_errors)
    else:
        update_data.update(uploaded_documents)

    if not errors and section.has_changes_to_save(draft, update_data):
        try:
            data_api_client.update_draft_service(
                service_id,
                update_data,
                current_user.email_address,
                page_questions=section.get_field_names()
            )
        except HTTPError as e:
            update_data = section.unformat_data(update_data)
            errors = section.get_error_messages(e.message)

    if errors:
        keys_required_for_template = ['serviceName', 'lot', 'lotName']
        for k in keys_required_for_template:
            if k in draft and k not in update_data:
                update_data[k] = draft[k]
        return render_template_with_csrf(
            "services/edit_submission_section.html",
            framework=framework,
            section=section,
            next_section_name=get_next_section_name(content, section.id),
            service_data=update_data,
            service_id=service_id,
            one_service_limit=lot['oneServiceLimit'],
            return_to_summary=bool(request.args.get('return_to_summary')),
            errors=errors
        )

    return_to_summary = bool(request.args.get('return_to_summary'))
    next_section = content.get_next_editable_section_id(section_id)

    if next_section and not return_to_summary and request.form.get('continue_to_next_section'):
        return redirect(url_for(".edit_service_submission",
                                framework_slug=framework['slug'],
                                lot_slug=draft['lotSlug'],
                                service_id=service_id,
                                section_id=next_section))
    else:
        return redirect(url_for(".view_service_submission",
                                framework_slug=framework['slug'],
                                lot_slug=draft['lotSlug'],
                                service_id=service_id,
                                _anchor=section_id))
def edit_service_submission(framework_slug, lot_slug, service_id, section_id, question_slug=None):
    """
        Also accepts URL parameter `force_continue_button` which will allow rendering of a 'Save and continue' button,
        used for when copying services.
    """
    framework, lot = get_framework_and_lot_or_404(data_api_client, framework_slug, lot_slug, allowed_statuses=['open'])

    # Suppliers must have registered interest in a framework before they can edit draft services
    if not get_supplier_framework_info(data_api_client, framework_slug):
        abort(404)

    force_return_to_summary = framework['framework'] == "digital-outcomes-and-specialists"
    force_continue_button = request.args.get('force_continue_button')
    next_question = None

    try:
        draft = data_api_client.get_draft_service(service_id)['services']
    except HTTPError as e:
        abort(e.status_code)

    if draft['lotSlug'] != lot_slug or draft['frameworkSlug'] != framework_slug:
        abort(404)

    if not is_service_associated_with_supplier(draft):
        abort(404)

    content = content_loader.get_manifest(framework_slug, 'edit_submission').filter(draft, inplace_allowed=True)
    section = content.get_section(section_id)
    if section and (question_slug is not None):
        next_question = section.get_question_by_slug(section.get_next_question_slug(question_slug))
        section = section.get_question_as_section(question_slug)

    if section is None or not section.editable:
        abort(404)

    errors = None
    if request.method == "POST":
        update_data = section.get_data(request.form)

        if request.files:
            uploader = s3.S3(current_app.config['DM_SUBMISSIONS_BUCKET'])
            documents_url = url_for('.dashboard', _external=True) + '/assets/'
            # This utils method filters out any empty documents and validates against service document rules
            uploaded_documents, document_errors = upload_service_documents(
                uploader, 'submissions', documents_url, draft, request.files, section,
                public=False)

            if document_errors:
                errors = section.get_error_messages(document_errors, question_descriptor_from="question")
            else:
                update_data.update(uploaded_documents)

        if not errors and section.has_changes_to_save(draft, update_data):
            try:
                data_api_client.update_draft_service(
                    service_id,
                    update_data,
                    current_user.email_address,
                    page_questions=section.get_field_names()
                )
            except HTTPError as e:
                update_data = section.unformat_data(update_data)
                errors = section.get_error_messages(e.message, question_descriptor_from="question")

        if not errors:
            if next_question and not force_return_to_summary:
                return redirect(url_for(".edit_service_submission",
                                        framework_slug=framework['slug'],
                                        lot_slug=draft['lotSlug'],
                                        service_id=service_id,
                                        section_id=section_id,
                                        question_slug=next_question.slug))
            else:
                return redirect(url_for(".view_service_submission",
                                        framework_slug=framework['slug'],
                                        lot_slug=draft['lotSlug'],
                                        service_id=service_id,
                                        _anchor=section_id))

        update_data.update(
            (k, draft[k]) for k in ('serviceName', 'lot', 'lotName',) if k in draft and k not in update_data
        )
        service_data = update_data
        # fall through to regular GET path to display errors
    else:
        service_data = section.unformat_data(draft)

    session_timeout = displaytimeformat(datetime.utcnow() + timedelta(hours=1))

    return render_template(
        "services/edit_submission_section.html",
        section=section,
        framework=framework,
        lot=lot,
        next_question=next_question,
        service_data=service_data,
        service_id=service_id,
        force_return_to_summary=force_return_to_summary,
        force_continue_button=force_continue_button,
        session_timeout=session_timeout,
        errors=errors,
    )