def download_brief_responses(brief_id): brief = Brief.query.filter( Brief.id == brief_id ).first_or_404() brief_user_ids = [user.id for user in brief.users] if current_user.id not in brief_user_ids: return forbidden("Unauthorised to view brief or brief does not exist") if brief.status != 'closed': return forbidden("You can only download documents for closed briefs") response = ('', 404) if brief.lot.slug in ['digital-professionals', 'training', 'rfx', 'atm']: try: file = s3_download_file( 'brief-{}-resumes.zip'.format(brief_id), os.path.join(brief.framework.slug, 'archives', 'brief-{}'.format(brief_id)) ) except botocore.exceptions.ClientError as e: rollbar.report_exc_info() not_found("Brief documents not found for brief id '{}'".format(brief_id)) response = Response(file, mimetype='application/zip') response.headers['Content-Disposition'] = 'attachment; filename="brief-{}-responses.zip"'.format(brief_id) elif brief.lot.slug == 'digital-outcome': responses = BriefResponse.query.filter( BriefResponse.brief_id == brief_id, BriefResponse.withdrawn_at.is_(None) ).all() csvdata = generate_brief_responses_csv(brief, responses) response = Response(csvdata, mimetype='text/csv') response.headers['Content-Disposition'] = ( 'attachment; filename="responses-to-requirements-{}.csv"'.format(brief_id)) return response
def decline_join_request(team_id, token): data = get_json_from_request() if 'reason' not in data or not data['reason']: abort('Must provide reason for decline') try: team = team_business.get_team(team_id) except NotFoundError as e: return not_found(e.message) except UnauthorisedError as e: return forbidden(e.message) join_request = team_business.get_join_request(token) if not join_request or int(join_request.data['team_id']) != team_id: return not_found( 'The token is invalid, or this request has already been declined or accepted' ) if ('user_id' in join_request.data and (team_member_service.get_team_members_by_user_id( team_id, [int(join_request.data['user_id'])]) or team_member_service.get_team_leads_by_user_id( team_id, [int(join_request.data['user_id'])]))): abort('This user is already a member of the team') team_business.decline_join_request(join_request, data['reason'], team_id) return jsonify(success=True)
def supplier_edit(supplier_code): """Seller edit (role=supplier) --- tags: - seller edit definitions: SellerEdit: type: object properties: supplier: type: object properties: abn: type: string code: type: string name: type: string data: type: object agreementStatus: type: object properties: canSign: type: boolean canUserSign: type: boolean show: type: boolean signed: type: boolean startDate: type: string parameters: - name: supplier_code in: path type: number required: true responses: 200: description: Supplier edit info schema: $ref: '#/definitions/SellerEdit' 403: description: Unauthorised to get info. """ if current_user.supplier_code != supplier_code: return forbidden('Unauthorised to get info') info = seller_edit_business.get_supplier_edit_info({ 'supplier_code': current_user.supplier_code, 'email_address': current_user.email_address }) return jsonify(info), 200
def update_supplier(supplier_code): """Update supplier (role=supplier) --- tags: - seller edit parameters: - name: supplier_code in: path type: number required: true - name: body in: body required: true schema: $ref: '#/definitions/SellerEdit' responses: 200: description: Supplier updated successfully. schema: $ref: '#/definitions/SellerEdit' 400: description: Bad request. 403: description: Unauthorised to update supplier. 404: description: Supplier not found. 500: description: Unexpected error. """ if current_user.supplier_code != supplier_code: return forbidden('Unauthorised to update supplier') try: data = get_json_from_request() seller_edit_business.update_supplier( data, { 'supplier_code': current_user.supplier_code, 'email_address': current_user.email_address, 'name': current_user.name, 'role': current_user.role }) except NotFoundError as nfe: not_found(nfe.message) except DeletedError as de: abort(de.message) except ValidationError as ve: abort(ve.message) info = seller_edit_business.get_supplier_edit_info({ 'supplier_code': current_user.supplier_code, 'email_address': current_user.email_address }) return jsonify(info), 200
def get_questions(brief_id): result = None try: result = questions_business.get_questions(current_user, brief_id) except NotFoundError as nfe: not_found(nfe.message) except UnauthorisedError as ue: return forbidden(ue.message) return jsonify(result), 200
def get_team(team_id): team = None try: team = team_business.get_team(team_id) except NotFoundError as e: return not_found(e.message) except UnauthorisedError as e: return forbidden(e.message) return jsonify(team)
def create_team(): try: team = team_business.create_team() except TeamError as e: abort(e.message) except NotFoundError as e: return not_found(e.message) except UnauthorisedError as e: return forbidden(e.message) return jsonify(team)
def request_to_join(team_id): try: team_business.request_to_join(current_user.email_address, team_id, current_user.agency_id) except NotFoundError as e: return not_found(e.message) except UnauthorisedError as e: return forbidden(e.message) except ValidationError as e: abort(e.message) return jsonify(success=True)
def publish_answer(brief_id): data = get_json_from_request() try: questions_business.publish_answer(current_user, brief_id, data) except NotFoundError as nfe: return not_found(nfe.message) except ValidationError as ve: return abort(ve.message) except UnauthorisedError as ue: return forbidden(ue.message) return jsonify(success=True), 200
def update_team(team_id): data = get_json_from_request() try: team = team_business.update_team(team_id, data) except ValidationError as e: return abort(e.message) except NotFoundError as e: return not_found(e.message) except UnauthorisedError as e: return forbidden(e.message) return jsonify(team)
def accept_agreement(supplier_code): """Accept agreement (role=supplier) --- tags: - seller edit parameters: - name: supplier_code in: path type: number required: true responses: 200: description: Supplier edit info schema: $ref: '#/definitions/SellerEdit' 400: description: Bad request. 403: description: Unauthorised to accept agreement. 404: description: Supplier not found. 500: description: Unexpected error. """ if current_user.supplier_code != supplier_code: return forbidden('Unauthorised to accept agreement') try: seller_edit_business.accept_agreement({ 'supplier_code': current_user.supplier_code, 'email_address': current_user.email_address, 'user_id': current_user.id }) except NotFoundError as nfe: not_found(nfe.message) except DeletedError as de: abort(de.message) except ValidationError as ve: abort(ve.message) except UnauthorisedError as ue: abort(ue.message) info = seller_edit_business.get_supplier_edit_info({ 'supplier_code': current_user.supplier_code, 'email_address': current_user.email_address }) return jsonify(info), 200
def get_join_request(team_id, token): try: team = team_business.get_team(team_id) except NotFoundError as e: return not_found(e.message) except UnauthorisedError as e: return forbidden(e.message) join_request = team_business.get_join_request(token) if not join_request or int(join_request.data['team_id']) != team_id: return not_found( 'The token is invalid, or this request has already been declined or accepted' ) return jsonify(join_request=join_request)
def download_brief_response_file(brief_id, supplier_code, slug): brief = Brief.query.filter( Brief.id == brief_id ).first_or_404() brief_user_ids = [user.id for user in brief.users] if hasattr(current_user, 'role') and (current_user.role == 'buyer' and current_user.id in brief_user_ids) \ or (current_user.role == 'supplier' and current_user.supplier_code == supplier_code): file = s3_download_file(slug, os.path.join(brief.framework.slug, 'documents', 'brief-' + str(brief_id), 'supplier-' + str(supplier_code))) mimetype = mimetypes.guess_type(slug)[0] or 'binary/octet-stream' return Response(file, mimetype=mimetype) else: return forbidden("Unauthorised to view brief or brief does not exist")
def notify_auth_rep(supplier_code): """Notify auth rep (role=supplier) --- tags: - seller edit parameters: - name: supplier_code in: path type: number required: true responses: 200: description: Supplier edit info schema: $ref: '#/definitions/SellerEdit' 400: description: Bad request. 403: description: Unauthorised to notify authorised representative. 404: description: Supplier not found. 500: description: Unexpected error. """ if current_user.supplier_code != supplier_code: return forbidden('Unauthorised to notify authorised representative') try: seller_edit_business.notify_auth_rep({ 'supplier_code': current_user.supplier_code, 'email_address': current_user.email_address }) except NotFoundError as nfe: not_found(nfe.message) except DeletedError as de: abort(de.message) info = seller_edit_business.get_supplier_edit_info({ 'supplier_code': current_user.supplier_code, 'email_address': current_user.email_address }) return jsonify(info), 200
def decline_agreement(supplier_code): """Decline agreement (role=supplier) --- tags: - seller edit parameters: - name: supplier_code in: path type: number required: true responses: 200: description: Agreement declined. 400: description: Bad request. 403: description: Unauthorised to decline agreement. 404: description: Supplier not found. 500: description: Unexpected error. """ if current_user.supplier_code != supplier_code: return forbidden('Unauthorised to decline agreement') try: seller_edit_business.decline_agreement({ 'supplier_code': current_user.supplier_code, 'email_address': current_user.email_address }) except NotFoundError as nfe: not_found(nfe.message) except DeletedError as de: abort(de.message) except UnauthorisedError as ue: abort(ue.message) return Response(status=200)
def upload_brief_rfx_attachment_file(brief_id, slug): """Add brief attachments (role=buyer) --- tags: - brief parameters: - name: brief_id in: path type: number required: true - name: slug in: path type: string required: true - name: file in: body required: true responses: 200: description: Attachment uploaded successfully. 403: description: Unauthorised to update brief. 404: description: Brief not found. 500: description: Unexpected error. """ brief = briefs.get(brief_id) if not brief: not_found("Invalid brief id '{}'".format(brief_id)) brief_user_ids = [user.id for user in brief.users] if current_user.id not in brief_user_ids: return forbidden('Unauthorised to update brief') return jsonify({"filename": s3_upload_file_from_request(request, slug, os.path.join(brief.framework.slug, 'attachments', 'brief-' + str(brief_id))) })
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)
def update_brief(brief_id): """Update RFX brief (role=buyer) --- tags: - brief definitions: RFXBrief: type: object properties: title: type: string organisation: type: string location: type: array items: type: string summary: type: string industryBriefing: type: string sellerCategory: type: string sellers: type: object attachments: type: array items: type: string requirementsDocument: type: array items: type: string responseTemplate: type: array items: type: string evaluationType: type: array items: type: string proposalType: type: array items: type: string evaluationCriteria: type: array items: type: object includeWeightings: type: boolean closedAt: type: string contactNumber: type: string startDate: type: string contractLength: type: string contractExtensions: type: string budgetRange: type: string workingArrangements: type: string securityClearance: type: string parameters: - name: brief_id in: path type: number required: true - name: body in: body required: true schema: $ref: '#/definitions/RFXBrief' responses: 200: description: Brief updated successfully. schema: $ref: '#/definitions/RFXBrief' 400: description: Bad request. 403: description: Unauthorised to update RFX brief. 404: description: Brief not found. 500: description: Unexpected error. """ brief = briefs.get(brief_id) if not brief: not_found("Invalid brief id '{}'".format(brief_id)) if brief.status != 'draft': abort('Cannot edit a {} brief'.format(brief.status)) if brief.lot.slug not in ['rfx', 'atm']: abort('Brief lot not supported for editing') if current_user.role == 'buyer': brief_user_ids = [user.id for user in brief.users] if current_user.id not in brief_user_ids: return forbidden('Unauthorised to update brief') data = get_json_from_request() publish = False if 'publish' in data and data['publish']: del data['publish'] publish = True if brief.lot.slug == 'rfx': # validate the RFX JSON request data errors = RFXDataValidator(data).validate(publish=publish) if len(errors) > 0: abort(', '.join(errors)) if brief.lot.slug == 'atm': # validate the ATM JSON request data errors = ATMDataValidator(data).validate(publish=publish) if len(errors) > 0: abort(', '.join(errors)) if brief.lot.slug == 'rfx' and 'evaluationType' in data: if 'Written proposal' not in data['evaluationType']: data['proposalType'] = [] if 'Response template' not in data['evaluationType']: data['responseTemplate'] = [] if brief.lot.slug == 'rfx' and 'sellers' in data and len(data['sellers']) > 0: data['sellerSelector'] = 'someSellers' if len(data['sellers']) > 1 else 'oneSeller' data['areaOfExpertise'] = '' if brief.lot.slug == 'atm' and 'openTo' in data: if data['openTo'] == 'all': data['sellerSelector'] = 'allSellers' data['sellerCategory'] = '' elif data['openTo'] == 'category': data['sellerSelector'] = 'someSellers' brief_domain = ( domain_service.get_by_name_or_id(int(data['sellerCategory'])) if data['sellerCategory'] else None ) if brief_domain: data['areaOfExpertise'] = brief_domain.name previous_status = brief.status if publish: brief.publish(closed_at=data['closedAt']) if 'sellers' in brief.data and data['sellerSelector'] != 'allSellers': for seller_code, seller in brief.data['sellers'].iteritems(): supplier = suppliers.get_supplier_by_code(seller_code) if brief.lot.slug == 'rfx': send_seller_invited_to_rfx_email(brief, supplier) try: brief_url_external = '{}/2/digital-marketplace/opportunities/{}'.format( current_app.config['FRONTEND_ADDRESS'], brief_id ) _notify_team_brief_published( brief.data['title'], brief.data['organisation'], current_user.name, current_user.email_address, brief_url_external ) except Exception as e: pass brief.data = data briefs.save_brief(brief) if publish: brief_url_external = '{}/2/digital-marketplace/opportunities/{}'.format( current_app.config['FRONTEND_ADDRESS'], brief_id ) publish_tasks.brief.delay( publish_tasks.compress_brief(brief), 'published', previous_status=previous_status, name=current_user.name, email_address=current_user.email_address, url=brief_url_external ) try: audit_service.log_audit_event( audit_type=AuditTypes.update_brief, user=current_user.email_address, data={ 'briefId': brief.id, 'briefData': brief.data }, db_object=brief) except Exception as e: rollbar.report_exc_info() return jsonify(brief.serialize(with_users=False))
def delete_brief(brief_id): """Delete brief (role=buyer) --- tags: - brief definitions: DeleteBrief: type: object properties: message: type: string parameters: - name: brief_id in: path type: number required: true responses: 200: description: Brief deleted successfully. schema: $ref: '#/definitions/DeleteBrief' 400: description: Bad request. Brief status must be 'draft'. 403: description: Unauthorised to delete brief. 404: description: brief_id not found. 500: description: Unexpected error. """ brief = briefs.get(brief_id) if not brief: not_found("Invalid brief id '{}'".format(brief_id)) if current_user.role == 'buyer': brief_user_ids = [user.id for user in brief.users] if current_user.id not in brief_user_ids: return forbidden('Unauthorised to delete brief') if brief.status != 'draft': abort('Cannot delete a {} brief'.format(brief.status)) audit = AuditEvent( audit_type=AuditTypes.delete_brief, user=current_user.email_address, data={ 'briefId': brief_id }, db_object=None ) try: deleted_brief = publish_tasks.compress_brief(brief) audit_service.save(audit) briefs.delete(brief) publish_tasks.brief.delay( deleted_brief, 'deleted', user=current_user.email_address ) except Exception as e: extra_data = {'audit_type': AuditTypes.delete_brief, 'briefId': brief.id, 'exception': e.message} rollbar.report_exc_info(extra_data=extra_data) return jsonify(message='Brief {} deleted'.format(brief_id)), 200
def get_brief_overview(brief_id): """Overview (role=buyer) --- tags: - brief definitions: BriefOverview: type: object properties: sections: type: array items: $ref: '#/definitions/BriefOverviewSections' title: type: string BriefOverviewSections: type: array items: $ref: '#/definitions/BriefOverviewSection' BriefOverviewSection: type: object properties: links: type: array items: $ref: '#/definitions/BriefOverviewSectionLinks' title: type: string BriefOverviewSectionLinks: type: array items: $ref: '#/definitions/BriefOverviewSectionLink' BriefOverviewSectionLink: type: object properties: complete: type: boolean path: type: string nullable: true text: type: string responses: 200: description: Data for the Overview page schema: $ref: '#/definitions/BriefOverview' 400: description: Lot not supported. 403: description: Unauthorised to view brief. 404: description: brief_id not found """ brief = briefs.get(brief_id) if not brief: not_found("Invalid brief id '{}'".format(brief_id)) if current_user.role == 'buyer': brief_user_ids = [user.id for user in brief.users] if current_user.id not in brief_user_ids: return forbidden('Unauthorised to view brief') if not (brief.lot.slug == 'digital-professionals' or brief.lot.slug == 'training'): abort('Lot {} is not supported'.format(brief.lot.slug)) sections = brief_overview_service.get_sections(brief) return jsonify(sections=sections, status=brief.status, title=brief.data['title']), 200
def get_brief_responses(brief_id): """All brief responses (role=supplier,buyer) --- tags: - brief security: - basicAuth: [] parameters: - name: brief_id in: path type: number required: true definitions: BriefResponses: properties: briefResponses: type: array items: id: BriefResponse responses: 200: description: A list of brief responses schema: id: BriefResponses 404: description: brief_id not found """ brief = briefs.get(brief_id) if not brief: not_found("Invalid brief id '{}'".format(brief_id)) if current_user.role == 'buyer': brief_user_ids = [user.id for user in brief.users] if current_user.id not in brief_user_ids: return forbidden("Unauthorised to view brief or brief does not exist") supplier_code = getattr(current_user, 'supplier_code', None) if current_user.role == 'supplier': validation_result = supplier_business.get_supplier_messages(supplier_code, True) if len(validation_result.errors) > 0: abort(validation_result.errors) # strip data from seller view if 'sellers' in brief.data: brief.data['sellers'] = {} if brief.responses_zip_filesize: brief.responses_zip_filesize = None if 'industryBriefing' in brief.data: brief.data['industryBriefing'] = '' if 'attachments' in brief.data: brief.data['attachments'] = [] if 'backgroundInformation' in brief.data: brief.data['backgroundInformation'] = '' if 'outcome' in brief.data: brief.data['outcome'] = '' if 'endUsers' in brief.data: brief.data['endUsers'] = '' if 'workAlreadyDone' in brief.data: brief.data['workAlreadyDone'] = '' if 'timeframeConstraints' in brief.data: brief.data['timeframeConstraints'] = '' if 'contactNumber' in brief.data: brief.data['contactNumber'] = '' if current_user.role == 'buyer' and brief.status != 'closed': brief_responses = [] else: brief_responses = brief_responses_service.get_brief_responses(brief_id, supplier_code) return jsonify(brief=brief.serialize(with_users=False, with_author=False), briefResponses=brief_responses)
def _can_do_brief_response(brief_id): try: brief = Brief.query.get(brief_id) except DataError: brief = None if brief is None: abort("Invalid brief ID '{}'".format(brief_id)) if brief.status != 'live': abort("Brief must be live") if brief.framework.status != 'live': abort("Brief framework must be live") if not hasattr(current_user, 'role') or current_user.role != 'supplier': forbidden("Only supplier role users can respond to briefs") try: supplier = Supplier.query.filter( Supplier.code == current_user.supplier_code ).first() except DataError: supplier = None if not supplier: forbidden("Invalid supplier Code '{}'".format(current_user.supplier_code)) validation_result = SupplierValidator(supplier).validate_all() if len(validation_result.errors) > 0: abort(validation_result.errors) def domain(email): return email.split('@')[-1] current_user_domain = domain(current_user.email_address) \ if domain(current_user.email_address) not in current_app.config.get('GENERIC_EMAIL_DOMAINS') \ else None rfx_lot = lots_service.find(slug='rfx').one_or_none() rfx_lot_id = rfx_lot.id if rfx_lot else None atm_lot = lots_service.find(slug='atm').one_or_none() atm_lot_id = atm_lot.id if atm_lot else None is_selected = False seller_selector = brief.data.get('sellerSelector', '') open_to = brief.data.get('openTo', '') brief_category = brief.data.get('sellerCategory', '') brief_domain = domain_service.get_by_name_or_id(int(brief_category)) if brief_category else None if brief.lot_id == rfx_lot_id: if str(current_user.supplier_code) in brief.data['sellers'].keys(): is_selected = True elif brief.lot_id == atm_lot_id: if seller_selector == 'allSellers' and len(supplier.assessed_domains) > 0: is_selected = True elif seller_selector == 'someSellers' and open_to == 'category' and brief_domain and\ brief_domain.name in supplier.assessed_domains: is_selected = True else: if not seller_selector or seller_selector == 'allSellers': is_selected = True elif seller_selector == 'someSellers': seller_domain_list = [domain(x).lower() for x in brief.data['sellerEmailList']] if current_user.email_address in brief.data['sellerEmailList'] \ or (current_user_domain and current_user_domain.lower() in seller_domain_list): is_selected = True elif seller_selector == 'oneSeller': if current_user.email_address.lower() == brief.data['sellerEmail'].lower() \ or (current_user_domain and current_user_domain.lower() == domain(brief.data['sellerEmail'].lower())): is_selected = True if not is_selected: forbidden("Supplier not selected for this brief") if (len(supplier.frameworks) == 0 or 'digital-marketplace' != supplier.frameworks[0].framework.slug): abort("Supplier does not have Digital Marketplace framework") if len(supplier.assessed_domains) == 0: abort("Supplier does not have at least one assessed domain") else: training_lot = lots_service.find(slug='training').one_or_none() if brief.lot_id == training_lot.id: if 'Training, Learning and Development' not in supplier.assessed_domains: abort("Supplier needs to be assessed in 'Training, Learning and Development'") lot = lots_service.first(slug='digital-professionals') if brief.lot_id == lot.id: # Check the supplier can respond to the category brief_category = brief.data.get('areaOfExpertise', None) if brief_category and brief_category not in supplier.assessed_domains: abort("Supplier needs to be assessed in '{}'".format(brief_category)) # Check if there are more than 3 brief response already from this supplier when professional aka specialists brief_response_count = brief_responses_service.find(supplier_code=supplier.code, brief_id=brief.id, withdrawn_at=None).count() if (brief_response_count > 2): # TODO magic number abort("There are already 3 brief responses for supplier '{}'".format(supplier.code)) else: # Check if brief response already exists from this supplier when outcome for all other types if brief_responses_service.find(supplier_code=supplier.code, brief_id=brief.id, withdrawn_at=None).one_or_none(): abort("Brief response already exists for supplier '{}'".format(supplier.code)) return supplier, brief