Esempio n. 1
0
def publish_answer(current_user, brief_id, data):
    brief = briefs.get(brief_id)
    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')

    publish_question = data.get('question')
    if not publish_question:
        raise ValidationError('Question is required')

    answer = data.get('answer')
    if not answer:
        raise ValidationError('Answer is required')

    brief_clarification_question = brief_clarification_question_service.save(
        BriefClarificationQuestion(_brief_id=brief.id,
                                   question=publish_question,
                                   answer=answer,
                                   user_id=current_user.id))

    question_id = data.get('questionId')
    if question_id:
        question = brief_question_service.get(question_id)
        if question.brief_id == brief.id:
            question.answered = True
            brief_question_service.save(question)

    audit_service.log_audit_event(
        audit_type=audit_types.create_brief_clarification_question,
        user=current_user.email_address,
        data={'briefId': brief.id},
        db_object=brief_clarification_question)
Esempio n. 2
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 close_opportunity_early(user_id, brief_id):
    brief = brief_service.get(brief_id)
    if not brief:
        raise NotFoundError('Opportunity {} does not exist'.format(brief_id))

    if not brief_service.has_permission_to_brief(user_id, brief_id):
        raise UnauthorisedError(
            'Not authorised to close opportunity {}'.format(brief_id))

    if not can_close_opportunity_early(brief):
        raise BriefError('Unable to close opportunity {}'.format(brief_id))

    user = users.get(user_id)
    if not user:
        raise NotFoundError('User {} does not exist'.format(user_id))

    brief = brief_service.close_opportunity_early(brief)
    create_responses_zip(brief.id)
    send_opportunity_closed_early_email(brief, user)

    try:
        audit_service.log_audit_event(
            audit_type=audit_types.close_opportunity_early,
            data={'briefId': brief.id},
            db_object=brief,
            user=user.email_address)

        publish_tasks.brief.delay(publish_tasks.compress_brief(brief),
                                  'closed_early',
                                  email_address=user.email_address,
                                  name=user.name)
    except Exception as e:
        rollbar.report_exc_info()

    return brief
def withdraw_opportunity(user_id, brief_id, withdrawal_reason):
    brief = brief_service.get(brief_id)
    if not brief:
        raise NotFoundError('Opportunity {} does not exist'.format(brief_id))

    if not brief_service.has_permission_to_brief(user_id, brief_id):
        raise UnauthorisedError(
            'Not authorised to withdraw opportunity {}'.format(brief_id))

    if brief.status != 'live':
        raise BriefError('Unable to withdraw opportunity {}'.format(brief_id))

    if not withdrawal_reason:
        raise ValidationError(
            'Withdrawal reason is required for opportunity {}'.format(
                brief_id))

    user = users.get(user_id)
    if not user:
        raise NotFoundError('User {} does not exist'.format(user_id))

    brief = brief_service.withdraw_opportunity(brief, withdrawal_reason)
    organisation = agency_service.get_agency_name(user.agency_id)
    sellers_to_contact = brief_service.get_sellers_to_notify(
        brief, brief_business.is_open_to_all(brief))

    for email_address in sellers_to_contact:
        send_opportunity_withdrawn_email_to_seller(brief, email_address,
                                                   organisation)

    send_opportunity_withdrawn_email_to_buyers(brief, user)

    try:
        audit_service.log_audit_event(
            audit_type=audit_types.withdraw_opportunity,
            data={'briefId': brief.id},
            db_object=brief,
            user=user.email_address)

        publish_tasks.brief.delay(publish_tasks.compress_brief(brief),
                                  'withdrawn',
                                  email_address=user.email_address,
                                  name=user.name)
    except Exception as e:
        rollbar.report_exc_info()

    return brief
Esempio n. 5
0
def get_suppliers_responded(brief_id):
    """Get suppliers responded (role=buyer)
    ---
    tags:
      - "Brief Response"
    security:
      - basicAuth: []
    parameters:
      - name: brief_response_id
        in: path
        type: number
        required: true
    definitions:
      BriefResponse:
        type: object
        properties:
          id:
            type: number
          data:
            type: object
          brief_id:
            type: number
          supplier_code:
            type: number
    responses:
      200:
        description: Suppliers that have responded to brief_id
        schema:
          id: BriefResponse
      404:
        description: brief_response_id not found
    """
    brief = briefs.get(brief_id)
    if not brief:
        return not_found('brief {} not found'.format(brief_id))

    if not briefs.has_permission_to_brief(current_user.id, brief.id):
        return forbidden('Unauthorised to get suppliers responded')

    suppliers = brief_responses_service.get_suppliers_responded(brief_id)
    work_order = work_order_service.find(brief_id=brief_id).one_or_none()

    return jsonify(brief=brief.serialize(with_users=False),
                   suppliers=suppliers,
                   workOrderCreated=True if work_order else False)
Esempio n. 6
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)
    }
def get_opportunity_to_edit(user_id, brief_id):
    brief = brief_service.get(brief_id)
    if not brief:
        raise NotFoundError('Opportunity {} does not exist'.format(brief_id))

    if not brief_service.has_permission_to_brief(user_id, brief_id):
        raise UnauthorisedError(
            'Not authorised to edit opportunity {}'.format(brief_id))

    if brief.status != 'live':
        raise BriefError('Unable to edit opportunity {}'.format(brief_id))

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

    return {
        'brief': brief.serialize(with_users=False),
        'domains': domains,
        'isOpenToAll': brief_business.is_open_to_all(brief)
    }
def edit_opportunity(user_id, brief_id, edits):
    brief = brief_service.get(brief_id)
    if not brief:
        raise NotFoundError('Opportunity {} does not exist'.format(brief_id))

    if not brief_service.has_permission_to_brief(user_id, brief_id):
        raise UnauthorisedError(
            'Not authorised to edit opportunity {}'.format(brief_id))

    if brief.status != 'live':
        raise BriefError('Unable to edit opportunity {}'.format(brief_id))

    user = user_service.get(user_id)
    if not user:
        raise NotFoundError('User {} does not exist'.format(user_id))

    previous_data = copy.deepcopy(brief.data)
    previous_data['closed_at'] = brief.closed_at.to_iso8601_string(
        extended=True)

    edit_title(brief, edits['title'])
    edit_summary(brief, edits['summary'])
    edit_closing_date(brief, edits['closingDate'])

    if 'documentsEdited' in edits and edits['documentsEdited']:
        if 'attachments' in edits:
            edit_attachments(brief, edits['attachments'])
        if 'requirementsDocument' in edits and 'requirementsDocument' in brief.data:
            edit_requirements_document(brief, edits['requirementsDocument'])
        if 'responseTemplate' in edits and 'responseTemplate' in brief.data:
            edit_response_template(brief, edits['responseTemplate'])

    organisation = None
    sellers_to_contact = []
    if (title_was_edited(brief.data['title'], previous_data['title']) or
            summary_was_edited(brief.data['summary'], previous_data['summary'])
            or closing_date_was_edited(
                brief.closed_at.to_iso8601_string(extended=True),
                previous_data['closed_at'])
            or documents_were_edited(brief.data.get('attachments', []),
                                     previous_data.get('attachments', []))
            or documents_were_edited(
                brief.data.get('requirementsDocument', []),
                previous_data.get('requirementsDocument', [])) or
            documents_were_edited(brief.data.get('responseTemplate', []),
                                  previous_data.get('responseTemplate', []))):
        organisation = agency_service.get_agency_name(user.agency_id)
        # We need to find sellers to contact about the current incoming edits before sellers are edited as we're
        # not sending additional sellers emails about the current edits that have been made.
        sellers_to_contact = brief_service.get_sellers_to_notify(
            brief, brief_business.is_open_to_all(brief))

    sellers_to_invite = {}
    if 'sellers' in edits and sellers_were_edited(
            edits['sellers'], brief.data.get('sellers', {})):
        sellers_to_invite = get_sellers_to_invite(brief, edits['sellers'])
        edit_sellers(brief, sellers_to_invite)
        edit_seller_selector(brief, sellers_to_invite)

    # strip out any data keys not whitelisted
    brief = brief_business.remove_keys_not_whitelisted(brief)

    data_to_validate = copy.deepcopy(brief.data)
    # only validate the sellers being added in the edit
    if 'sellers' in edits and len(edits['sellers'].keys()) > 0:
        data_to_validate['sellers'] = copy.deepcopy(edits.get('sellers', {}))

    validator = None
    if brief.lot.slug == 'rfx':
        validator = RFXDataValidator(data_to_validate)
    elif brief.lot.slug == 'training2':
        validator = TrainingDataValidator(data_to_validate)
    elif brief.lot.slug == 'atm':
        validator = ATMDataValidator(data_to_validate)
    elif brief.lot.slug == 'specialist':
        validator = SpecialistDataValidator(data_to_validate)

    if validator is None:
        raise ValidationError('Validator not found for {}'.format(
            brief.lot.slug))

    errors = []
    if (title_was_edited(brief.data['title'], previous_data['title'])
            and not validator.validate_title()):
        errors.append('You must add a title')

    if (summary_was_edited(brief.data['summary'], previous_data['summary'])
            and not validator.validate_summary()):
        message = ('You must add what the specialist will do'
                   if brief.lot.slug == 'specialist' else
                   'You must add a summary of work to be done')

        errors.append(message)

    if (brief.lot.slug != 'atm' and 'sellers' in edits and sellers_were_edited(
            edits['sellers'], brief.data.get('sellers', {}))
            and not validator.validate_sellers()):
        message = (
            'You must select some sellers'
            if brief.lot.slug == 'specialist' else
            'You must select at least one seller and each seller must be assessed for the chosen category'
        )

        errors.append(message)

    if (closing_date_was_edited(
            brief.closed_at.to_iso8601_string(extended=True),
            previous_data['closed_at'])
            and not validator.validate_closed_at(minimum_days=1)):
        message = (
            'The closing date must be at least 1 day into the future or not more than one year long'
            if brief.lot.slug == 'specialist' else
            'The closing date must be at least 1 day into the future')

        errors.append(message)

    if len(errors) > 0:
        raise ValidationError(', '.join(errors))

    brief_service.save(brief, do_commit=False)

    edit = BriefHistory(brief_id=brief.id, user_id=user_id, data=previous_data)

    brief_history_service.save(edit, do_commit=False)
    brief_service.commit_changes()

    if len(sellers_to_contact) > 0 and organisation:
        for email_address in sellers_to_contact:
            send_opportunity_edited_email_to_seller(brief, email_address,
                                                    organisation)

    for code, data in sellers_to_invite.items():
        supplier = supplier_service.get_supplier_by_code(code)
        if supplier:
            if brief.lot.slug == 'rfx':
                send_seller_invited_to_rfx_email(brief, supplier)
            elif brief.lot.slug == 'specialist':
                send_specialist_brief_seller_invited_email(brief, supplier)
            elif brief.lot.slug == 'training':
                send_seller_invited_to_training_email(brief, supplier)

    send_opportunity_edited_email_to_buyers(brief, user, edit)

    try:
        audit_service.log_audit_event(
            audit_type=audit_types.opportunity_edited,
            data={'briefId': brief.id},
            db_object=brief,
            user=user.email_address)

        publish_tasks.brief.delay(publish_tasks.compress_brief(brief),
                                  'edited',
                                  email_address=user.email_address,
                                  name=user.name)
    except Exception as e:
        rollbar.report_exc_info()

    return brief