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
Exemplo n.º 2
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)
Exemplo n.º 3
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 get_team(team_id):
    team = team_service.get_team(team_id)
    if not team:
        raise NotFoundError('Team {} does not exist'.format(team_id))

    team_member = team_member_service.find(
        is_team_lead=True,
        team_id=team_id,
        user_id=current_user.id
    ).one_or_none()

    if not team_member:
        raise UnauthorisedError('Only team leads can edit a team')

    domain = get_email_domain(current_user.email_address)

    if team['teamMembers'] is not None:
        for user_id, team_member in team['teamMembers'].iteritems():
            missing_permissions = [permission for permission in permission_types
                                   if permission not in team_member['permissions']]

            for permission in missing_permissions:
                team_member['permissions'][permission] = False

    team.update(domain=domain)

    return team
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
def update_team(team_id, data):
    team = team_service.get_team_for_update(team_id)
    if not team:
        raise NotFoundError('Team {} does not exist'.format(team_id))

    if len([tm for tm in team.team_members if tm.user_id == current_user.id and tm.is_team_lead is True]) == 0:
        raise UnauthorisedError('Only team leads can edit a team')

    update_team_information(team, data)
    result = update_team_leads_and_members(team, data)
    update_permissions(team, data)

    create_team = data.get('createTeam', False)
    saved = False
    if create_team:
        validation_result = TeamValidator(team, current_user).validate_all()
        if len([e for e in validation_result.errors]) > 0:
            raise ValidationError([e for e in validation_result.errors])

        team.status = 'completed'
        team_service.save(team)
        saved = True

        send_team_lead_notification_emails(team_id)
        send_team_member_notification_emails(team_id)
    elif team.status == 'completed':
        validation_result = TeamValidator(team, current_user).validate_all()
        if len([e for e in validation_result.errors]) > 0:
            raise ValidationError([e for e in validation_result.errors])

        team_service.save(team)
        saved = True
        new_team_leads = result.get('new_team_leads', [])
        new_team_members = result.get('new_team_members', [])
        removed_team_members = result.get('removed_team_members', [])

        if len(new_team_leads) > 0:
            send_team_lead_notification_emails(team_id, new_team_leads)
        if len(new_team_members) > 0:
            send_team_member_notification_emails(team_id, new_team_members)
        if len(removed_team_members) > 0:
            send_removed_team_member_notification_emails(team_id, removed_team_members)
    elif team.status == 'created':
        validation_result = TeamValidator(team, current_user).validate_all()
        team_member_errors = [e for e in validation_result.errors if str(e.get('id')).startswith('TM')]
        if team_member_errors:
            raise ValidationError(team_member_errors)

        team_service.save(team)

    if saved:
        publish_tasks.team.delay(
            publish_tasks.compress_team(team),
            'updated'
        )

    return get_team(team_id)
def get_insight(current_user, now):
    role = current_user.role if hasattr(current_user, 'role') else None
    if role == 'admin':
        insight = insight_service.get_insight(now, False)
    else:
        insight = insight_service.get_insight(now)

    if not insight:
        raise NotFoundError("Invalid date '{}'".format(now))

    return insight.get('data')
Exemplo n.º 8
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)
    }
Exemplo n.º 9
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 accept_agreement(user_info):
    supplier_code = user_info.get('supplier_code')
    email_address = user_info.get('email_address')
    user_id = user_info.get('user_id')

    supplier = suppliers.get_supplier_by_code(supplier_code)
    mandatory_supplier_checks(supplier)

    if email_address != supplier.data.get('email'):
        raise UnauthorisedError('Unauthorised to accept agreement')

    agreement = get_current_agreement()
    if agreement is None:
        raise NotFoundError('Current master agreement not found')

    already_signed = signed_agreement_service.first(
        agreement_id=agreement.id, supplier_code=supplier_code)
    if already_signed:
        raise ValidationError('Already signed agreement')

    signed_agreement = SignedAgreement(
        agreement_id=agreement.id,
        user_id=user_id,
        signed_at=pendulum.now('Australia/Canberra'),
        supplier_code=supplier_code)
    signed_agreement_service.save(signed_agreement)

    publish_tasks.supplier.delay(publish_tasks.compress_supplier(supplier),
                                 'accepted_agreement',
                                 updated_by=email_address)

    audit_service.log_audit_event(
        audit_type=audit_types.accepted_master_agreement,
        user=email_address,
        data={
            'supplierCode': supplier.code,
            'supplierData': supplier.data
        },
        db_object=supplier)
def request_to_join(user_email_address, team_id, agency_id):
    team = team_service.get_team(team_id)
    if not team:
        raise NotFoundError('Team {} does not exist'.format(team_id))
    if not get_teams_by_agency(agency_id, team_id):
        raise UnauthorisedError('Can not request to join this team')

    claim = user_claims_service.make_claim(type='join_team',
                                           email_address=user_email_address,
                                           data={
                                               "team_id": team_id,
                                               "team_name": team['name'],
                                               "agency_id": agency_id,
                                               "user_id": current_user.id,
                                               "user_name": current_user.name,
                                               "approved": False
                                           })
    if not claim:
        raise ValidationError('Could not create join team request')

    send_request_to_join_team_leaders_email(team_id, claim.token)
    send_request_to_join_requester_email(user_email_address, team_id)
def get_opportunity_history(brief_id,
                            show_documents=False,
                            include_sellers=True):
    brief = brief_service.get(brief_id)
    if not brief:
        raise NotFoundError('Opportunity {} does not exist'.format(brief_id))

    response = {
        'brief': {
            'framework': brief.framework.slug,
            'id': brief.id,
            'title': brief.data['title'] if 'title' in brief.data else ''
        }
    }

    edits = []
    changes = brief_history_service.get_edits(brief.id)

    for i, change in enumerate(changes):
        source = brief if i == 0 else changes[i - 1]
        edit_data = get_changes_made_to_opportunity(source, change)

        if edit_data:
            edit_data['editedAt'] = change.edited_at
            if not include_sellers and 'sellers' in edit_data:
                del edit_data['sellers']
            if not brief_business.is_open_to_all(brief) and not show_documents:
                if 'attachments' in edit_data:
                    del edit_data['attachments']
                if 'requirementsDocument' in edit_data:
                    del edit_data['requirementsDocument']
                if 'responseTemplate' in edit_data:
                    del edit_data['responseTemplate']
            edits.append(edit_data)

    response['edits'] = edits

    return response
def get_team(team_id, allow_anyone=None):
    team = team_service.get_team(team_id)
    if not team:
        raise NotFoundError('Team {} does not exist'.format(team_id))

    if allow_anyone is None:
        team_member = team_member_service.find(
            is_team_lead=True, team_id=team_id,
            user_id=current_user.id).one_or_none()

        if not team_member:
            raise UnauthorisedError('Only team leads can edit a team')

    if team['teamMembers'] is not None:
        for user_id, team_member in team['teamMembers'].iteritems():
            missing_permissions = [
                permission for permission in permission_types
                if permission not in team_member['permissions']
            ]

            for permission in missing_permissions:
                team_member['permissions'][permission] = False

    return team
def mandatory_supplier_checks(supplier):
    if not supplier:
        raise NotFoundError("Invalid supplier code '{}'".format(supplier_code))

    if supplier.status == 'deleted':
        raise DeletedError('Cannot edit a {} supplier'.format(supplier.status))
Exemplo n.º 16
0
def get_suppliers():
    """Suppliers search names by keyword
    ---
    tags:
        - suppliers
    definitions:
        SupplierMinimal:
            type: object
            properties:
                name:
                    type: string
                code:
                    type: integer
                panel:
                    type: boolean
                sme:
                    type: boolean
        Suppliers:
            type: object
            properties:
                sellers:
                    type: array
                    items:
                        $ref: '#/definitions/SupplierMinimal'
    parameters:
        - name: keyword
          in: query
          type: string
          required: true
          description: the keyword to search on
        - name: category
          in: query
          type: string
          required: false
          description: the seller category to filter on
    responses:
        200:
            description: a list of matching suppliers
            schema:
                $ref: '#/definitions/Suppliers'
        400:
            description: invalid request data, such as a missing keyword param
    """
    keyword = request.args.get('keyword') or ''
    category = request.args.get('category') or ''
    all_suppliers = request.args.get('all') or ''
    brief_id = request.args.get('briefId', None)

    brief = None
    if brief_id:
        brief = briefs.get(brief_id)

    if not brief:
        raise NotFoundError("Invalid brief id '{}'".format(brief_id))

    exclude = json.loads(
        request.args.get('exclude')) if request.args.get('exclude') else []
    exclude_recruiters = True if brief and brief.lot.slug != 'specialist' else False
    supplier_codes_to_exclude = [int(code) for code in exclude]

    if keyword:
        results = suppliers.get_suppliers_by_name_keyword(
            keyword,
            framework_slug='digital-marketplace',
            category=category,
            exclude=supplier_codes_to_exclude,
            exclude_recruiters=exclude_recruiters)

        supplier_results = []
        for result in results:
            if all_suppliers:
                supplier = {}
                supplier['name'] = result.name
                supplier['code'] = result.code
                supplier_results.append(supplier)
            elif len(result.assessed_domains) > 0:
                if category:
                    domain = domain_service.get_by_name_or_id(int(category))
                    if not domain or domain.name not in result.assessed_domains:
                        continue
                supplier = {}
                supplier['name'] = result.name
                supplier['code'] = result.code
                supplier_results.append(supplier)

        return jsonify(sellers=supplier_results), 200
    else:
        return jsonify(message='You must provide a keyword param.'), 400
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