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
Пример #2
0
def send_removed_team_member_notification_emails(team_id, user_ids):
    team = team_service.find(id=team_id).first()

    to_addresses = []
    for user_id in user_ids:
        user = users.get(user_id)
        to_addresses.append(user.email_address)

    if len(to_addresses) == 0:
        return

    email_body = render_email_template(
        'team_member_removed.md',
        frontend_url=current_app.config['FRONTEND_ADDRESS'],
        team_lead=escape_markdown(current_user.name),
        team_name=escape_markdown(team.name))

    subject = 'You have been removed from the {} team'.format(
        team.name.encode('utf-8'))

    send_or_handle_error(
        to_addresses,
        email_body,
        subject,
        current_app.config['DM_GENERIC_NOREPLY_EMAIL'],
        current_app.config['DM_GENERIC_SUPPORT_NAME'],
        event_description_for_errors='team member removed email')

    audit_service.log_audit_event(audit_type=audit_types.team_member_removed,
                                  data={
                                      'to_address': to_addresses,
                                      'subject': subject
                                  },
                                  db_object=team,
                                  user='')
Пример #3
0
def update_brief_remove_user(brief_id, user):
    updater_json = validate_and_return_updater_request()
    brief = briefs.get(brief_id)

    if user.isdigit():
        u = [users.get(int(user))]
    else:
        u = [users.get_by_email(user)]
    if len(u) < 1:
        raise ValidationError("No user found: " + user)
    else:
        brief.users.remove(u[0])
        audit = AuditEvent(
            audit_type=AuditTypes.update_brief,
            user=updater_json['updated_by'],
            data={
                'briefId': brief.id,
                'briefJson': {
                    'remove_user': user
                },
            },
            db_object=brief,
        )

    db.session.add(brief)
    db.session.add(audit)
    db.session.commit()

    return jsonify(briefs=brief.serialize(with_users=True)), 200
def create_team():
    user = users.get(current_user.id)
    created_teams = team_service.get_teams_for_user(user.id, 'created')
    completed_teams = team_service.get_teams_for_user(user.id)

    if len(completed_teams) == 0:
        if len(created_teams) == 0:
            team = team_service.save(
                Team(name='',
                     status='created',
                     team_members=[
                         TeamMember(user_id=user.id, is_team_lead=True)
                     ]))

            audit_service.log_audit_event(audit_type=audit_types.create_team,
                                          data={},
                                          db_object=team,
                                          user=current_user.email_address)

            publish_tasks.team.delay(publish_tasks.compress_team(team),
                                     'created')

            return get_team(team.id)

        created_team = created_teams.pop()
        return get_team(created_team.id)
    else:
        team = completed_teams[0]
        raise TeamError(
            'You can only be in one team. You\'re already a member of {}.'.
            format(team.name))
    def test_edit_documents_only_apply_edits_with_edited_flag_set(self, agency_service, briefs, users):
        atm_brief = brief_service.get(1)
        user = user_service.get(2)
        agency_service.get_agency_name.return_value = 'DTA'
        original_attachments = atm_brief.data['attachments']

        edits = {
            'closingDate': '',
            'title': 'test',
            'summary': 'test',
            'sellers': {},
            'attachments': [],
            'documentsEdited': False
        }

        brief = brief_edit_business.edit_opportunity(user.id, atm_brief.id, edits)
        assert len(original_attachments) > 0
        assert brief.data['attachments'] == original_attachments

        edits = {
            'closingDate': '',
            'title': 'test',
            'summary': 'test',
            'sellers': {},
            'attachments': ['ABC.PDF'],
            'documentsEdited': True
        }

        brief = brief_edit_business.edit_opportunity(user.id, atm_brief.id, edits)
        assert brief.data['attachments'] == ['ABC.PDF']
def create(current_user):
    lot = lots_service.find(slug='training2').one_or_none()
    framework = frameworks_service.find(
        slug='digital-marketplace').one_or_none()
    user = users.get(current_user.id)
    agency_name = ''

    email_domain = user.email_address.split('@')[1]
    agency = agency_service.find(domain=email_domain).one_or_none()
    if agency:
        agency_name = agency.name

    domain = domain_service.find(
        name='Training, Learning and Development').one_or_none()
    seller_category = None
    if domain:
        seller_category = str(domain.id)
    else:
        raise Exception('Training, Learning and Development domain not found')

    brief = briefs.create_brief(user,
                                current_user.get_team(),
                                framework,
                                lot,
                                data={
                                    'organisation': agency_name,
                                    'sellerCategory': seller_category
                                })

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

    return brief
Пример #7
0
def create_atm_brief():
    """Create ATM brief (role=buyer)
    ---
    tags:
        - brief
    definitions:
        ATMBriefCreated:
            type: object
            properties:
                id:
                    type: number
                lot:
                    type: string
                status:
                    type: string
                author:
                    type: string
    responses:
        200:
            description: Brief created successfully.
            schema:
                $ref: '#/definitions/ATMBriefCreated'
        400:
            description: Bad request.
        403:
            description: Unauthorised to create ATM brief.
        500:
            description: Unexpected error.
    """
    try:
        lot = lots_service.find(slug='atm').one_or_none()
        framework = frameworks_service.find(slug='digital-marketplace').one_or_none()
        user = users.get(current_user.id)
        brief = briefs.create_brief(user, framework, lot)
    except Exception as e:
        rollbar.report_exc_info()
        return jsonify(message=e.message), 400

    try:
        audit_service.log_audit_event(
            audit_type=AuditTypes.create_brief,
            user=current_user.email_address,
            data={
                'briefId': brief.id
            },
            db_object=brief)
    except Exception as e:
        rollbar.report_exc_info()

    return jsonify(brief.serialize(with_users=False))
def get_people_overview():
    people_overview = {}
    user = users.get(current_user.id)
    completed_teams = team_service.get_teams_for_user(user.id)

    people = users.get_buyer_team_members(user.agency_id)
    people_overview.update(users=people)

    organisation = agency_service.get_agency_name(user.agency_id)
    people_overview.update(organisation=organisation)

    completed_teams_count = len(completed_teams)
    people_overview.update(completedTeamsCount=completed_teams_count)

    return people_overview
def get_teams_overview():
    teams_overview = {}
    user = users.get(current_user.id)
    completed_teams = team_service.get_teams_for_user(user.id)

    overview = team_service.get_teams_overview(user.id, user.agency_id)
    teams_overview.update(overview=overview)

    organisation = agency_service.get_agency_name(user.agency_id)
    teams_overview.update(organisation=organisation)

    completed_teams_count = len(completed_teams)
    teams_overview.update(completedTeamsCount=completed_teams_count)

    return teams_overview
def get_teams_overview():
    teams_overview = {}
    user = users.get(current_user.id)
    completed_teams = team_service.get_teams_for_user(user.id)
    email_domain = get_email_domain(user.email_address)

    overview = team_service.get_teams_overview(user.id, email_domain)
    teams_overview.update(overview=overview)

    organisation = users.get_user_organisation(email_domain)
    teams_overview.update(organisation=organisation)

    completed_teams_count = len(completed_teams)
    teams_overview.update(completedTeamsCount=completed_teams_count)

    return teams_overview
def get_people_overview():
    people_overview = {}
    user = users.get(current_user.id)
    completed_teams = team_service.get_teams_for_user(user.id)
    domain = get_email_domain(user.email_address)

    people = users.get_buyer_team_members(domain)
    people_overview.update(users=people)

    organisation = users.get_user_organisation(domain)
    people_overview.update(organisation=organisation)

    completed_teams_count = len(completed_teams)
    people_overview.update(completedTeamsCount=completed_teams_count)

    return people_overview
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 test_edit_opportunity_creates_history_record(self, agency_service, briefs, users):
        specialist_brief = brief_service.get(2)
        user = user_service.get(2)
        agency_service.get_agency_name.return_value = 'DTA'

        edits = {
            'closingDate': '',
            'title': 'test',
            'sellers': {},
            'summary': ''
        }

        brief = brief_edit_business.edit_opportunity(user.id, specialist_brief.id, edits)
        history = brief_history_service.all()

        assert len(history) == 1
        assert history[0].brief_id == brief.id
        assert history[0].user_id == user.id
Пример #14
0
def send_team_member_notification_emails(team_id, user_ids=None):
    team = team_service.find(id=team_id).first()

    if user_ids is None or len(user_ids) == 0:
        # Team members added through the create flow
        members = team_member_service.find(team_id=team_id,
                                           is_team_lead=False).all()
    else:
        # Team members added through the edit flow
        members = team_member_service.get_team_members_by_user_id(
            team_id, user_ids)

    to_addresses = []
    for member in members:
        user = users.get(member.user_id)
        to_addresses.append(user.email_address)

    if len(to_addresses) == 0:
        return

    email_body = render_email_template(
        'team_member_added.md',
        frontend_url=current_app.config['FRONTEND_ADDRESS'],
        team_lead=escape_markdown(current_user.name),
        team_name=escape_markdown(team.name))

    subject = '{} added you as a member of {}'.format(
        current_user.name, team.name.encode('utf-8'))

    send_or_handle_error(
        to_addresses,
        email_body,
        subject,
        current_app.config['DM_GENERIC_NOREPLY_EMAIL'],
        current_app.config['DM_GENERIC_SUPPORT_NAME'],
        event_description_for_errors='team member added email')

    audit_service.log_audit_event(audit_type=audit_types.team_member_added,
                                  data={
                                      'to_address': to_addresses,
                                      'subject': subject
                                  },
                                  db_object=team,
                                  user='')
Пример #15
0
def send_team_lead_notification_emails(team_id, user_ids=None):
    team = team_service.find(id=team_id).first()

    if user_ids is None or len(user_ids) == 0:
        # Team leads added through the create flow
        team_leads = team_member_service.find(team_id=team_id,
                                              is_team_lead=True).all()
        team_leads = [
            team_lead for team_lead in team_leads
            if team_lead.user_id != current_user.id
        ]
    else:
        # Team leads added through the edit flow
        team_leads = team_member_service.get_team_leads_by_user_id(
            team_id, user_ids)

    to_addresses = []
    for team_lead in team_leads:
        user = users.get(team_lead.user_id)
        to_addresses.append(user.email_address)

    if len(to_addresses) == 0:
        return

    email_body = render_email_template(
        'team_lead_added.md',
        frontend_url=current_app.config['FRONTEND_ADDRESS'],
        team_name=escape_markdown(team.name))

    subject = 'You have been upgraded to a team lead'

    send_or_handle_error(to_addresses,
                         email_body,
                         subject,
                         current_app.config['DM_GENERIC_NOREPLY_EMAIL'],
                         current_app.config['DM_GENERIC_SUPPORT_NAME'],
                         event_description_for_errors='team lead added email')

    audit_service.log_audit_event(audit_type=audit_types.team_lead_added,
                                  data={'to_address': to_addresses},
                                  db_object=team,
                                  user='')
    def test_edit_opportunity_creates_audit_event(self, agency_service, briefs, users):
        specialist_brief = brief_service.get(2)
        user = user_service.get(2)
        agency_service.get_agency_name.return_value = 'DTA'

        edits = {
            'closingDate': '',
            'title': 'test',
            'sellers': {},
            'summary': ''
        }

        brief = brief_edit_business.edit_opportunity(user.id, specialist_brief.id, edits)
        audit_event = audit_service.find(
            object_id=specialist_brief.id,
            object_type='Brief',
            type=audit_types.opportunity_edited.value
        ).one_or_none()

        assert audit_event is not None
Пример #17
0
def get(user_id):
    """Get User
    ---
    tags:
      - users
    parameters:
      - name: user_id
        in: path
        type: integer
        required: true
    responses:
      200:
        description: A User
        type: object
        schema:
          $ref: '#/definitions/UserDetail'
    """
    user = users.get(user_id)
    user_detail = UserView(user)

    return jsonify(user_detail.__dict__)
    def test_edit_opportunity_creates_history_record_with_original_data(self, agency_service, briefs, users):
        specialist_brief = brief_service.get(2)
        original_closed_at = copy.deepcopy(specialist_brief.closed_at)
        original_specialist_data = copy.deepcopy(specialist_brief.data)
        user = user_service.get(2)
        agency_service.get_agency_name.return_value = 'DTA'

        edits = {
            'closingDate': '',
            'title': 'test',
            'sellers': {},
            'summary': ''
        }

        brief = brief_edit_business.edit_opportunity(user.id, specialist_brief.id, edits)
        history = brief_history_service.all()

        assert pendulum.parse(history[0].data['closed_at'], tz='utc') == original_closed_at
        assert history[0].data['sellers'] == original_specialist_data['sellers']
        assert history[0].data['sellerSelector'] == original_specialist_data['sellerSelector']
        assert history[0].data['title'] == original_specialist_data['title']
    def test_only_sellers_were_edited_is_false_when_title_edited(self, agency_service, briefs, users):
        specialist_brief = brief_service.get(2)
        user = user_service.get(2)
        agency_service.get_agency_name.return_value = 'DTA'

        edits = {
            'closingDate': '',
            'title': 'test',
            'sellers': {
                '1': {
                    'name': 'Seller 1'
                },
                '2': {
                    'name': 'Seller 2'
                }
            },
            'summary': ''
        }

        brief = brief_edit_business.edit_opportunity(user.id, specialist_brief.id, edits)
        only_sellers_edited = brief_edit_business.only_sellers_were_edited(specialist_brief.id)
        assert only_sellers_edited is False
    def test_only_sellers_were_edited_is_false_when_summary_edited(self, agency_service, briefs, users):
        specialist_brief = brief_service.get(2)
        user = user_service.get(2)
        agency_service.get_agency_name.return_value = 'DTA'

        edits = {
            'closingDate': pendulum.now('Australia/Canberra').add(days=7).to_date_string(),
            'title': '',
            'sellers': {
                '1': {
                    'name': 'Seller 1'
                },
                '2': {
                    'name': 'Seller 2'
                }
            },
            'summary': 'New summary'
        }

        brief = brief_edit_business.edit_opportunity(user.id, specialist_brief.id, edits)
        only_sellers_edited = brief_edit_business.only_sellers_were_edited(specialist_brief.id)
        assert only_sellers_edited is False
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