示例#1
0
def get_question(current_user, brief_id, question_id):
    brief = briefs.find(id=brief_id).one_or_none()
    if not brief:
        raise NotFoundError("Invalid brief id '{}'".format(brief_id))

    if not briefs.has_permission_to_brief(current_user.id, brief.id):
        raise UnauthorisedError('Unauthorised to publish answer')

    question = brief_question_service.find(id=question_id,
                                           brief_id=brief.id).one_or_none()
    question_result = None
    if question:
        question_result = {"id": question.id, "data": question.data}

    return {
        "question": question_result,
        "brief": {
            "title": brief.data.get('title'),
            "id": brief.id,
            "lot": brief.lot.slug,
            "questionsCloseAt": brief.questions_closed_at,
            "closedAt": brief.closed_at,
            "internalReference": brief.data.get('internalReference'),
            "isOpenToAll": brief_business.is_open_to_all(brief),
            "status": brief.status
        }
    }
    def test_can_close_published_opportunity_early_with_single_invited_seller_that_responded(
            self, briefs, brief_responses, lot_slug):
        lot = lots_service.find(slug=lot_slug).one_or_none()
        brief = briefs_service.find(lot=lot, status='live').one_or_none()

        can_close = can_close_opportunity_early(brief)
        assert can_close is True
    def test_can_not_close_published_atm_opportunity_early(
            self, overview_briefs):
        lot = lots_service.find(slug='atm').one_or_none()
        brief = briefs_service.find(lot=lot, status='live').one_or_none()

        can_close = can_close_opportunity_early(brief)
        assert can_close is False
    def test_can_not_close_published_opportunity_early_with_incorrect_seller_selector(
            self, overview_briefs, brief_data):
        lot = lots_service.find(slug=brief_data['lot_slug']).one_or_none()
        brief = briefs_service.find(lot=lot, status='live').one_or_none()
        brief.data['sellerSelector'] = brief_data['seller_selector']

        can_close = can_close_opportunity_early(brief)
        assert can_close is False
    def test_can_not_close_published_opportunity_early_with_multiple_invited_sellers(
            self, overview_briefs, lot_slug):
        lot = lots_service.find(slug=lot_slug).one_or_none()
        brief = briefs_service.find(lot=lot, status='live').one_or_none()
        brief.data['sellers'].update({'456': 'Big Corp'})

        can_close = can_close_opportunity_early(brief)
        assert can_close is False
    def test_can_not_close_published_opportunity_early_with_multiple_seller_responses(
            self, overview_briefs, suppliers, lot_slug):
        lot = lots_service.find(slug=lot_slug).one_or_none()
        brief = briefs_service.find(lot=lot, status='live').one_or_none()

        db.session.add(
            BriefResponse(id=6, brief_id=brief.id, data={}, supplier_code=3))

        db.session.commit()

        can_close = can_close_opportunity_early(brief)
        assert can_close is False
示例#7
0
def get_answers(brief_id):
    brief = briefs.find(id=brief_id).one_or_none()
    if not brief:
        raise NotFoundError("Invalid brief id '{}'".format(brief_id))

    answers = brief_clarification_question_service.get_answers(brief_id)

    return {
        "answers": answers,
        "brief": {
            "title": brief.data.get('title'),
            "id": brief.id,
            "closedAt": brief.closed_at,
            "internalReference": brief.data.get('internalReference')
        },
        "questionCount": get_counts(brief_id, answers=answers)
    }
示例#8
0
def get_questions(current_user, brief_id):
    brief = briefs.find(id=brief_id).one_or_none()
    if not brief:
        raise NotFoundError("Invalid brief id '{}'".format(brief_id))

    if not briefs.has_permission_to_brief(current_user.id, brief.id):
        raise UnauthorisedError('Unauthorised to publish answer')

    questions = brief_question_service.get_questions(brief_id)

    return {
        "questions": questions,
        "brief": {
            "title": brief.data.get('title'),
            "id": brief.id,
            "closedAt": brief.closed_at,
            "internalReference": brief.data.get('internalReference')
        },
        "questionCount": get_counts(brief_id, questions=questions)
    }
示例#9
0
def create_responses_zip(brief_id):
    brief = briefs.find(id=brief_id).one_or_none()

    if not brief:
        raise CreateResponsesZipException(
            'Failed to load brief for id {}'.format(brief_id))

    responses = brief_responses_service.get_responses_to_zip(
        brief_id, brief.lot.slug)

    if not responses:
        raise CreateResponsesZipException(
            'There were no respones for brief id {}'.format(brief_id))

    if brief.lot.slug not in [
            'digital-professionals', 'training', 'rfx', 'training2', 'atm',
            'specialist'
    ]:
        raise CreateResponsesZipException(
            'Brief id {} is not a compatible lot'.format(brief_id))

    print 'Generating zip for brief id: {}'.format(brief_id)

    BUCKET_NAME = getenv('S3_BUCKET_NAME')
    s3 = boto3.resource('s3',
                        region_name=getenv('AWS_REGION'),
                        aws_access_key_id=getenv('AWS_ACCESS_KEY_ID'),
                        aws_secret_access_key=getenv('AWS_SECRET_ACCESS_KEY'),
                        endpoint_url=getenv('AWS_S3_URL'))
    bucket = s3.Bucket(BUCKET_NAME)

    files = []
    attachments = brief_responses_service.get_all_attachments(brief_id)
    for attachment in attachments:
        if attachment['file_name'].startswith(
                'digital-marketplace') and '/' in attachment['file_name']:
            key = attachment['file_name']
            zip_file_name = attachment['file_name'].split('/')[-1]
        else:
            key = 'digital-marketplace/documents/brief-{}/supplier-{}/{}'.format(
                brief_id, attachment['supplier_code'], attachment['file_name'])
            zip_file_name = attachment['file_name']
        files.append({
            'key':
            key,
            'zip_name':
            'opportunity-{}-documents/{}/{}'.format(
                brief_id, secure_filename(attachment['supplier_name']),
                zip_file_name)
        })

    with tempfile.TemporaryFile() as archive:
        with zipfile.ZipFile(archive,
                             mode='w',
                             compression=zipfile.ZIP_DEFLATED) as zf:
            for file in files:
                s3file = file['key']
                with BytesIO() as s3_stream:
                    try:
                        bucket.download_fileobj(s3file, s3_stream)
                        zf.writestr(file['zip_name'], s3_stream.getvalue())
                    except botocore.exceptions.ClientError as e:
                        raise CreateResponsesZipException(
                            'The file "{}" failed to download'.format(s3file))
                    finally:
                        s3_stream.close()

            csvdata = generate_brief_responses_csv(brief, responses)
            csv_file_name = (
                'opportunity-{}-raw.csv'.format(brief_id)
                if brief.lot.slug == 'digital-professionals' else
                'responses-to-requirements-{}.csv'.format(brief_id))
            zf.writestr(csv_file_name, csvdata.encode('utf-8'))

            if brief.lot.slug == 'digital-professionals':
                compliance_check_template = template_env.get_template(
                    'compliance-check.html')
                compliance_check_html = render_template(
                    compliance_check_template,
                    brief=brief,
                    responses=responses)
                zf.writestr('compliance-check-{}.html'.format(brief_id),
                            compliance_check_html.encode('utf-8'))

                candidates = prepare_specialist_responses(brief, responses)

                response_criteria_template = template_env.get_template(
                    'response-criteria.html')
                response_criteria_html = render_template(
                    response_criteria_template,
                    brief=brief,
                    candidates=candidates)
                zf.writestr('responses-{}.html'.format(brief_id),
                            response_criteria_html.encode('utf-8'))
            elif brief.lot.slug == 'specialist':
                compliance_check_template = template_env.get_template(
                    'compliance-check-specialist.html')
                supplier_labour_hire = {}
                for response in responses:
                    labour_hire = []
                    for state, state_value in response.supplier.data.get(
                            'labourHire', {}).iteritems():
                        if state_value.get(
                                'licenceNumber') and state_value.get('expiry'):
                            state_value['state'] = state.upper()
                            state_value['expiry'] = (pendulum.parse(
                                state_value['expiry']).format(
                                    'DD MMMM YYYY', formatter='alternative'))
                            labour_hire.append(state_value)
                    supplier_labour_hire[response.supplier.code] = labour_hire

                compliance_check_html = render_template(
                    compliance_check_template,
                    brief=brief,
                    supplier_labour_hire=supplier_labour_hire,
                    responses=responses)
                zf.writestr('Compliance check ({}).html'.format(brief_id),
                            compliance_check_html.encode('utf-8'))

                response_criteria_template = template_env.get_template(
                    'response-criteria-specialist.html')

                candidates = []
                for response in responses:
                    data = response.data
                    candidates.append({
                        'essential_requirement_responses':
                        data.get('essentialRequirements', {}),
                        'nice_to_have_requirement_responses':
                        data.get('niceToHaveRequirements', {}),
                        'name':
                        '{} {}'.format(data.get('specialistGivenNames', ''),
                                       data.get('specialistSurname', '')),
                        'seller':
                        response.supplier.name
                    })

                response_criteria_html = render_template(
                    response_criteria_template,
                    brief=brief,
                    candidates=candidates,
                    essential_requirements=brief.data.get(
                        'essentialRequirements', {}),
                    nice_to_have_requirements=brief.data.get(
                        'niceToHaveRequirements', {}))

                zf.writestr('Responses ({}).html'.format(brief_id),
                            response_criteria_html.encode('utf-8'))

        archive.seek(0)

        try:
            brief.responses_zip_filesize = len(archive.read())
            archive.seek(0)
            db.session.add(brief)
            db.session.commit()
        except Exception as e:
            raise CreateResponsesZipException(str(e))

        try:
            bucket.upload_fileobj(
                archive,
                'digital-marketplace/archives/brief-{}/brief-{}-resumes.zip'.
                format(brief_id, brief_id))
        except botocore.exceptions.ClientError as e:
            raise CreateResponsesZipException(
                'The responses archive for brief id "{}" failed to upload'.
                format(brief_id))
示例#10
0
def create_evidence(domain_id, brief_id=None):
    """Create evidence (role=supplier)
    ---
    tags:
        - evidence
    definitions:
        EvidenceCreated:
            type: object
            properties:
                id:
                    type: number
                domain_id:
                    type: number
                supplier_code:
                    type: number
    responses:
        200:
            description: Evidence created successfully.
            schema:
                $ref: '#/definitions/EvidenceCreated'
        400:
            description: Bad request.
        403:
            description: Unauthorised to create evidence.
        500:
            description: Unexpected error.
    """
    domain = domain_service.get_by_name_or_id(domain_id, show_legacy=False)
    if not domain:
        abort('Unknown domain id')

    supplier = suppliers.get_supplier_by_code(current_user.supplier_code)
    if supplier.data.get('recruiter', '') == 'yes':
        abort('Assessment can\'t be started against a recruiter only supplier')

    existing_evidence = evidence_service.get_latest_evidence_for_supplier_and_domain(
        domain_id, current_user.supplier_code)
    if domain.name in supplier.assessed_domains:
        abort('This supplier is already assessed for this domain')

    open_assessment = assessments.get_open_assessments(
        domain_id=domain_id, supplier_code=current_user.supplier_code)
    if open_assessment or (existing_evidence and existing_evidence.status
                           in ['draft', 'submitted']):
        abort(
            'This supplier already has a draft assessment or is awaiting assessment for this domain'
        )

    if brief_id:
        brief = briefs.find(id=brief_id).one_or_none()
        if not brief or brief.status != 'live':
            abort('Brief id does not exist or is not open for responses')

    try:
        data = {}

        if existing_evidence and existing_evidence.status == 'rejected':
            data = existing_evidence.data.copy()
        else:
            # does this supplier already have a max price for this domain set? if so, pre-populate
            current_max_price = suppliers.get_supplier_max_price_for_domain(
                current_user.supplier_code, domain.name)
            if current_max_price:
                data['maxDailyRate'] = int(current_max_price)

        evidence = evidence_service.create_evidence(domain_id,
                                                    current_user.supplier_code,
                                                    current_user.id,
                                                    brief_id=brief_id,
                                                    data=data)

    except Exception as e:
        rollbar.report_exc_info()
        abort(e.message)

    publish_tasks.evidence.delay(publish_tasks.compress_evidence(evidence),
                                 'created',
                                 name=current_user.name,
                                 domain=domain.name,
                                 supplier_code=current_user.supplier_code)

    return jsonify(evidence.serialize())
 def test_can_not_close_opportunity_early_with_incorrect_status(
         self, overview_briefs, status):
     brief = briefs_service.find(status=status).first()
     can_close = can_close_opportunity_early(brief)
     assert can_close is False
示例#12
0
def get_brief(brief_id):
    """Get brief
    ---
    tags:
        - brief
    parameters:
      - name: brief_id
        in: path
        type: number
        required: true
    responses:
        200:
            description: Brief retrieved successfully.
            schema:
                type: object
                properties:
                    brief:
                        type: object
                    brief_response_count:
                        type: number
                    invited_seller_count:
                        type: number
                    can_respond:
                        type: boolean
                    open_to_all:
                        type: boolean
                    is_brief_owner:
                        type: boolean
                    is_buyer:
                        type: boolean
                    has_responded:
                        type: boolean
                    has_chosen_brief_category:
                        type: boolean
                    is_assessed_for_category:
                        type: boolean
                    is_assessed_in_any_category:
                        type: boolean
                    is_approved_seller:
                        type: boolean
                    is_awaiting_application_assessment:
                        type: boolean
                    is_awaiting_domain_assessment:
                        type: boolean
                    has_been_assessed_for_brief:
                        type: boolean
                    open_to_category:
                        type: boolean
                    is_applicant:
                        type: boolean
                    is_recruiter:
                        type: boolean
                    domains:
                        type: array
                        items:
                            type: object
        403:
            description: Unauthorised to view brief.
        404:
            description: Brief not found.
        500:
            description: Unexpected error.
    """
    brief = briefs.find(id=brief_id).one_or_none()
    if not brief:
        not_found("No brief for id '%s' found" % (brief_id))

    user_role = current_user.role if hasattr(current_user, 'role') else None
    invited_sellers = brief.data['sellers'] if 'sellers' in brief.data else {}

    is_buyer = False
    is_brief_owner = False
    brief_user_ids = [user.id for user in brief.users]
    if user_role == 'buyer':
        is_buyer = True
        if current_user.id in brief_user_ids:
            is_brief_owner = True

    if brief.status == 'draft' and not is_brief_owner:
        return forbidden("Unauthorised to view brief")

    brief_response_count = len(brief_responses_service.get_brief_responses(brief_id, None))
    invited_seller_count = len(invited_sellers)
    open_to_all = brief.lot.slug == 'atm' and brief.data.get('openTo', '') == 'all'
    open_to_category = brief.lot.slug == 'atm' and brief.data.get('openTo', '') == 'category'
    is_applicant = user_role == 'applicant'

    # gather facts about the user's status against this brief
    user_status = BriefUserStatus(brief, current_user)
    has_chosen_brief_category = user_status.has_chosen_brief_category()
    is_assessed_for_category = user_status.is_assessed_for_category()
    is_assessed_in_any_category = user_status.is_assessed_in_any_category()
    is_awaiting_application_assessment = user_status.is_awaiting_application_assessment()
    is_awaiting_domain_assessment = user_status.is_awaiting_domain_assessment()
    has_been_assessed_for_brief = user_status.has_been_assessed_for_brief()
    is_recruiter_only = user_status.is_recruiter_only()
    is_approved_seller = user_status.is_approved_seller()
    can_respond = user_status.can_respond()
    has_responded = user_status.has_responded()

    # remove private data for non brief owners
    brief.data['contactEmail'] = ''
    brief.data['users'] = None
    if not is_buyer:
        if 'sellers' in brief.data:
            brief.data['sellers'] = {}
        brief.responses_zip_filesize = None
        brief.data['contactNumber'] = ''
        if not can_respond:
            brief.data['proposalType'] = []
            brief.data['evaluationType'] = []
            brief.data['responseTemplate'] = []
            brief.data['requirementsDocument'] = []
            brief.data['industryBriefing'] = ''
            brief.data['backgroundInformation'] = ''
            brief.data['outcome'] = ''
            brief.data['endUsers'] = ''
            brief.data['workAlreadyDone'] = ''
            brief.data['timeframeConstraints'] = ''
            brief.data['attachments'] = []
    else:
        brief.data['contactEmail'] = [user.email_address for user in brief.users][0]
        if not is_brief_owner:
            if 'sellers' in brief.data:
                brief.data['sellers'] = {}
            brief.data['industryBriefing'] = ''
            brief.data['contactNumber'] = ''

    domains = []
    for domain in domain_service.all():
        domains.append({
            'id': str(domain.id),
            'name': domain.name
        })

    return jsonify(brief=brief.serialize(with_users=False, with_author=is_brief_owner),
                   brief_response_count=brief_response_count,
                   invited_seller_count=invited_seller_count,
                   can_respond=can_respond,
                   has_chosen_brief_category=has_chosen_brief_category,
                   is_assessed_for_category=is_assessed_for_category,
                   is_assessed_in_any_category=is_assessed_in_any_category,
                   is_approved_seller=is_approved_seller,
                   is_awaiting_application_assessment=is_awaiting_application_assessment,
                   is_awaiting_domain_assessment=is_awaiting_domain_assessment,
                   has_been_assessed_for_brief=has_been_assessed_for_brief,
                   open_to_all=open_to_all,
                   open_to_category=open_to_category,
                   is_brief_owner=is_brief_owner,
                   is_buyer=is_buyer,
                   is_applicant=is_applicant,
                   is_recruiter_only=is_recruiter_only,
                   has_responded=has_responded,
                   domains=domains)