Example #1
0
def list_project_searches(project_external_id):
    project = get_project_by_id_or_404(project_external_id)

    page = get_valid_page_or_1()

    searches = DirectAwardSearch.query.filter(DirectAwardSearch.project_id == project.id)

    if 'latest-first' in request.args:
        if convert_to_boolean(request.args.get('latest-first')):
            searches = searches.order_by(desc(DirectAwardSearch.created_at), desc(DirectAwardSearch.id))
        else:
            searches = searches.order_by(asc(DirectAwardSearch.created_at), asc(DirectAwardSearch.id))
    else:
        searches = searches.order_by(asc(DirectAwardSearch.id))

    if convert_to_boolean(request.args.get('only-active', False)):
        searches = searches.filter(DirectAwardSearch.active == True)  # noqa

    pagination_params = request.args.to_dict()
    pagination_params['project_external_id'] = project.external_id

    return paginated_result_response(
        result_name="searches",
        results_query=searches,
        page=page,
        per_page=current_app.config['DM_API_PROJECTS_PAGE_SIZE'],
        endpoint='.list_project_searches',
        request_args=pagination_params,
    ), 200
Example #2
0
def list_projects():
    page = get_valid_page_or_1()

    projects = DirectAwardProject.query.options(
        db.joinedload(
            DirectAwardProject.outcome
        ).joinedload(
            Outcome.direct_award_archived_service
        ).load_only(
            "service_id"
        )
    )

    includes = request.args.get('include', '').split(',')

    with_users = 'users' in includes
    if with_users:
        projects = projects.options(db.joinedload('users').lazyload('supplier'))

    user_id = get_int_or_400(request.args, 'user-id')
    if user_id:
        projects = projects.filter(DirectAwardProject.users.any(id=user_id))

    if "having-outcome" in request.args:
        projects = projects.filter(
            db.cast(
                DirectAwardProject.outcome.has(),
                db.Boolean,
            ) == convert_to_boolean(request.args.get("having-outcome"))
        )

    if "locked" in request.args:
        projects = projects.filter(
            db.cast(
                DirectAwardProject.locked_at.isnot(None),
                db.Boolean,
            ) == convert_to_boolean(request.args.get("locked"))
        )

    if 'latest-first' in request.args:
        if convert_to_boolean(request.args.get('latest-first')):
            projects = projects.order_by(desc(DirectAwardProject.created_at), desc(DirectAwardProject.id))
        else:
            projects = projects.order_by(asc(DirectAwardProject.created_at), asc(DirectAwardProject.id))
    else:
        projects = projects.order_by(asc(DirectAwardProject.id))

    return paginated_result_response(
        result_name="projects",
        results_query=projects,
        page=page,
        per_page=current_app.config['DM_API_PROJECTS_PAGE_SIZE'],
        endpoint='.list_projects',
        request_args=request.args,
        serialize_kwargs={"with_users": with_users}
    ), 200
Example #3
0
def list_audits():
    page = get_valid_page_or_1()
    try:
        per_page = int(
            request.args.get('per_page',
                             current_app.config['DM_API_SERVICES_PAGE_SIZE']))
    except ValueError:
        abort(400, 'invalid page size supplied')

    audits = AuditEvent.query.order_by(asc(AuditEvent.created_at))

    audit_date = request.args.get('audit-date', None)
    if audit_date:
        if is_valid_date(audit_date):
            audits = audits.filter(
                cast(AuditEvent.created_at, Date) == audit_date)
        else:
            abort(400, 'invalid audit date supplied')

    audit_type = request.args.get('audit-type')
    if audit_type:
        if AuditTypes.is_valid_audit_type(audit_type):
            audits = audits.filter(AuditEvent.type == audit_type)
        else:
            abort(400, "Invalid audit type")

    acknowledged = request.args.get('acknowledged', None)
    if acknowledged and acknowledged != 'all':
        if is_valid_acknowledged_state(acknowledged):
            if convert_to_boolean(acknowledged):
                audits = audits.filter(AuditEvent.acknowledged == true())
            elif not convert_to_boolean(acknowledged):
                audits = audits.filter(AuditEvent.acknowledged == false())
        else:
            abort(400, 'invalid acknowledged state supplied')

    object_type = request.args.get('object-type')
    object_id = request.args.get('object-id')
    if object_type:
        if object_type not in AUDIT_OBJECT_TYPES:
            abort(400, 'invalid object-type supplied')
        if not object_id:
            abort(400, 'object-type cannot be provided without object-id')
        model = AUDIT_OBJECT_TYPES[object_type]
        id_field = AUDIT_OBJECT_ID_FIELDS[object_type]

        audits = audits.join(model, model.id == AuditEvent.object_id) \
                       .filter(id_field == object_id)
    elif object_id:
        abort(400, 'object-id cannot be provided without object-type')

    audits = audits.paginate(page=page, per_page=per_page)

    return jsonify(auditEvents=[audit.serialize() for audit in audits.items],
                   links=pagination_links(audits, '.list_audits',
                                          request.args))
def get_framework_suppliers(framework_slug):
    framework = Framework.query.filter(
        Framework.slug == framework_slug
    ).first_or_404()

    # we're going to need an "alias" for the current_framework_agreement join
    # so that we can refer to it in later clauses
    cfa = orm.aliased(SupplierFramework._CurrentFrameworkAgreement)
    supplier_frameworks = SupplierFramework.query.outerjoin(
        cfa, SupplierFramework.current_framework_agreement
    ).options(
        orm.contains_eager(
            SupplierFramework.current_framework_agreement, alias=cfa
        )
    )
    # now we can use the alias `cfa` to refer to the joinedload that
    # SupplierFramework.current_framework_agreement would usually cause

    supplier_frameworks = supplier_frameworks.filter(
        SupplierFramework.framework_id == framework.id
    ).options(
        db.defaultload(SupplierFramework.framework).lazyload("*"),
        db.defaultload(SupplierFramework.supplier).lazyload("*"),
        db.defaultload(SupplierFramework.prefill_declaration_from_framework).lazyload("*"),
        db.lazyload(SupplierFramework.framework_agreements),
    ).order_by(
        # Listing agreements is something done for Admin only (suppliers only retrieve their individual agreements)
        # and CCS always want to work from the oldest returned date to newest, so order by ascending date
        cfa.signed_agreement_returned_at.asc().nullsfirst(),
        SupplierFramework.supplier_id,
    )

    # endpoint has evolved a bit of an oddly designed interface here in that the two filterable parameters aren't
    # really orthogonal. implementing them as such anyway for clarity.

    agreement_returned = request.args.get('agreement_returned')
    if agreement_returned is not None:
        supplier_frameworks = supplier_frameworks.filter(
            # using the not-nullable FrameworkAgreement.id as a proxy for testing row null-ness
            cfa.id.isnot(None) if convert_to_boolean(agreement_returned) else cfa.id.is_(None)
        )

    status = request.args.get('status')
    if status is not None:
        supplier_frameworks = supplier_frameworks.filter(
            cfa.status.in_(status.split(","))
        )

    with_declarations = convert_to_boolean(request.args.get("with_declarations", "true"))

    return list_result_response(
        "supplierFrameworks",
        supplier_frameworks,
        serialize_kwargs={"with_users": False, "with_declaration": with_declarations}
    ), 200
def list_audits():
    try:
        page = int(request.args.get('page', 1))
    except ValueError:
        abort(400, "Invalid page argument")

    audits = AuditEvent.query.order_by(
        asc(AuditEvent.created_at)
    )

    audit_date = request.args.get('audit-date', None)
    if audit_date:
        if is_valid_date(audit_date):
            audits = audits.filter(
                cast(AuditEvent.created_at, Date) == audit_date
            )
        else:
            abort(400, 'invalid audit date supplied')

    if request.args.get('audit-type'):
        if AuditTypes.is_valid_audit_type(request.args.get('audit-type')):
            audits = audits.filter(
                AuditEvent.type == request.args.get('audit-type')
            )
        else:
            abort(400, "Invalid audit type")

    acknowledged = request.args.get('acknowledged', None)
    if acknowledged and acknowledged != 'all':
        if is_valid_acknowledged_state(acknowledged):
            if convert_to_boolean(acknowledged):
                audits = audits.filter(
                    AuditEvent.acknowledged == true()
                )
            elif not convert_to_boolean(acknowledged):
                audits = audits.filter(
                    AuditEvent.acknowledged == false()
                )
        else:
            abort(400, 'invalid acknowledged state supplied')

    audits = audits.paginate(
        page=page,
        per_page=current_app.config['DM_API_SERVICES_PAGE_SIZE'],
    )

    return jsonify(
        auditEvents=[audit.serialize() for audit in audits.items],
        links=pagination_links(
            audits,
            '.list_audits',
            request.args
        )
    )
Example #6
0
def list_users():
    user_query = User.query.order_by(User.id)
    page = get_valid_page_or_1()

    # email_address is a primary key
    email_address = request.args.get('email_address')
    if email_address:
        if not is_valid_email_address(email_address):
            abort(400, "email_address must be a valid email address")
        single_user = user_query.filter(
            User.email_address == email_address.lower()).first_or_404()
        return jsonify(users=[single_user.serialize()])

    role = request.args.get('role')
    if role:
        if role in User.ROLES:
            user_query = user_query.filter(User.role == role)
        else:
            abort(400, 'Invalid user role: {}'.format(role))

    supplier_id = request.args.get('supplier_id')
    if supplier_id is not None:
        try:
            supplier_id = int(supplier_id)
        except ValueError:
            abort(400, "Invalid supplier_id: {}".format(supplier_id))

        supplier = Supplier.query.filter(
            Supplier.supplier_id == supplier_id).all()
        if not supplier:
            abort(404, "supplier_id '{}' not found".format(supplier_id))

        user_query = user_query.filter(User.supplier_id == supplier_id)

    personal_data_removed = request.args.get('personal_data_removed')
    if personal_data_removed is not None:
        user_query = user_query.filter(
            User.personal_data_removed == convert_to_boolean(
                personal_data_removed))

    user_research_opted_in = request.args.get('user_research_opted_in')
    if user_research_opted_in is not None:
        user_query = user_query.filter(
            User.user_research_opted_in == convert_to_boolean(
                user_research_opted_in))

    return paginated_result_response(
        result_name=RESOURCE_NAME,
        results_query=user_query,
        page=page,
        per_page=current_app.config['DM_API_SERVICES_PAGE_SIZE'],
        endpoint='.list_users',
        request_args=request.args), 200
def list_countersigned_agreement_file(supplier_id, framework_slug):
    supplier = data_api_client.get_supplier(supplier_id)['suppliers']
    framework = data_api_client.get_framework(framework_slug)['frameworks']
    supplier_framework = data_api_client.get_supplier_framework_info(supplier_id, framework_slug)['frameworkInterest']
    if not supplier_framework['onFramework'] or supplier_framework['agreementStatus'] in (None, 'draft'):
        abort(404)
    agreements_bucket = s3.S3(
        current_app.config['DM_AGREEMENTS_BUCKET'], endpoint_url=current_app.config.get("DM_S3_ENDPOINT_URL")
    )
    countersigned_agreement_document = agreements_bucket.get_key(supplier_framework.get('countersignedPath'))

    remove_countersigned_agreement_confirm = convert_to_boolean(request.args.get('remove_countersigned_agreement'))

    countersigned_agreement = []
    if countersigned_agreement_document:
        last_modified = datetimeformat(parse_date(countersigned_agreement_document['last_modified']))
        document_name = degenerate_document_path_and_return_doc_name(supplier_framework.get('countersignedPath'))
        countersigned_agreement = [{"last_modified": last_modified, "document_name": document_name}]

    return render_template(
        "suppliers/upload_countersigned_agreement.html",
        supplier=supplier,
        framework=framework,
        countersigned_agreement=countersigned_agreement,
        remove_countersigned_agreement_confirm=remove_countersigned_agreement_confirm
    )
def list_projects():
    page = get_valid_page_or_1()

    projects = DirectAwardProject.query

    includes = request.args.get('include', '').split(',')

    with_users = 'users' in includes
    if with_users:
        projects = projects.options(
            db.joinedload('users').lazyload('supplier'))

    user_id = get_int_or_400(request.args, 'user-id')
    if user_id:
        projects = projects.filter(DirectAwardProject.users.any(id=user_id))

    if 'latest-first' in request.args:
        if convert_to_boolean(request.args.get('latest-first')):
            projects = projects.order_by(desc(DirectAwardProject.created_at),
                                         desc(DirectAwardProject.id))
        else:
            projects = projects.order_by(asc(DirectAwardProject.created_at),
                                         asc(DirectAwardProject.id))
    else:
        projects = projects.order_by(asc(DirectAwardProject.id))

    return paginated_result_response(
        result_name="projects",
        results_query=projects,
        page=page,
        per_page=current_app.config['DM_API_PROJECTS_PAGE_SIZE'],
        endpoint='.list_projects',
        request_args=request.args,
        serialize_kwargs={"with_users": with_users}), 200
def list_brief_responses():
    page = get_valid_page_or_1()
    brief_id = get_int_or_400(request.args, 'brief_id')
    supplier_id = get_int_or_400(request.args, 'supplier_id')
    awarded_at = request.args.get('awarded_at')
    with_data = convert_to_boolean(request.args.get("with-data", "true"))

    if request.args.get('status'):
        statuses = request.args['status'].split(',')
    else:
        statuses = COMPLETED_BRIEF_RESPONSE_STATUSES
    brief_responses = BriefResponse.query.filter(BriefResponse.status.in_(statuses))

    if supplier_id is not None:
        brief_responses = brief_responses.filter(BriefResponse.supplier_id == supplier_id)

    if brief_id is not None:
        brief_responses = brief_responses.filter(BriefResponse.brief_id == brief_id)

    if awarded_at is not None:
        day_start = datetime.strptime(awarded_at, DATE_FORMAT)
        day_end = datetime(day_start.year, day_start.month, day_start.day, 23, 59, 59, 999999)
        # Inclusive date range filtering
        brief_responses = brief_responses.filter(BriefResponse.awarded_at.between(day_start, day_end))

    brief_responses = brief_responses.options(
        db.defaultload(BriefResponse.brief).defaultload(Brief.framework).lazyload("*"),
        db.defaultload(BriefResponse.brief).defaultload(Brief.lot).lazyload("*"),
        db.defaultload(BriefResponse.brief).defaultload(Brief.awarded_brief_response).lazyload("*"),
        db.defaultload(BriefResponse.supplier).lazyload("*"),
    )

    if request.args.get('framework'):
        brief_responses = brief_responses.join(BriefResponse.brief).join(Brief.framework).filter(
            Brief.framework.has(Framework.slug.in_(
                framework_slug.strip() for framework_slug in request.args["framework"].split(",")
            ))
        )

    serialize_kwargs = {"with_data": with_data}

    if brief_id or supplier_id:
        return list_result_response(RESOURCE_NAME, brief_responses, serialize_kwargs=serialize_kwargs), 200

    return paginated_result_response(
        result_name=RESOURCE_NAME,
        results_query=brief_responses,
        serialize_kwargs=serialize_kwargs,
        page=page,
        per_page=current_app.config['DM_API_BRIEF_RESPONSES_PAGE_SIZE'],
        endpoint='.list_brief_responses',
        request_args=request.args
    ), 200
Example #10
0
def update_service_status(service_id, status):
    """
    Updates the status parameter of a service, and archives the old one.
    :param service_id:
    :param status:
    :return: the newly updated service in the response
    """

    # Statuses are defined in the Supplier model
    valid_statuses = [
        "published",
        "enabled",
        "disabled",
        "deleted",
    ]

    is_valid_service_id_or_400(service_id)

    service = Service.query.filter(
        Service.service_id == service_id
    ).first_or_404()

    if status not in valid_statuses:
        valid_statuses_single_quotes = display_list(
            ["\'{}\'".format(vstatus) for vstatus in valid_statuses]
        )
        abort(400, "'{}' is not a valid status. Valid statuses are {}".format(
            status, valid_statuses_single_quotes
        ))

    update_json = validate_and_return_updater_request()

    prior_status, service.status = service.status, status

    commit_and_archive_service(service, update_json,
                               AuditTypes.update_service_status,
                               audit_data={'old_status': prior_status,
                                           'new_status': status})

    if prior_status != status:
        wait_for_response = convert_to_boolean(request.args.get("wait-for-index", "true"))
        if prior_status == 'published':
            # If it's being unpublished, delete it from the search api.
            delete_service_from_index(service, wait_for_response=wait_for_response)
        else:
            # If it's being published, index in the search api.
            index_service(service, wait_for_response=wait_for_response)

    return single_result_response(RESOURCE_NAME, service), 200
Example #11
0
def list_users():
    user_query = User.query.order_by(User.id)
    page = get_valid_page_or_1()

    # email_address is a primary key
    email_address = request.args.get('email_address')
    if email_address:
        single_user = user_query.filter(
            User.email_address == email_address.lower()
        ).first_or_404()
        return jsonify(
            users=[single_user.serialize()]
        )

    role = request.args.get('role')
    if role:
        if role in User.ROLES:
            user_query = user_query.filter(
                User.role == role
            )
        else:
            abort(400, 'Invalid user role: {}'.format(role))

    supplier_id = request.args.get('supplier_id')
    if supplier_id is not None:
        try:
            supplier_id = int(supplier_id)
        except ValueError:
            abort(400, "Invalid supplier_id: {}".format(supplier_id))

        supplier = Supplier.query.filter(Supplier.supplier_id == supplier_id).all()
        if not supplier:
            abort(404, "supplier_id '{}' not found".format(supplier_id))

        user_query = user_query.filter(User.supplier_id == supplier_id)

    personal_data_removed = request.args.get('personal_data_removed')
    if personal_data_removed is not None:
        user_query = user_query.filter(User.personal_data_removed == convert_to_boolean(personal_data_removed))

    return paginated_result_response(
        result_name=RESOURCE_NAME,
        results_query=user_query,
        page=page,
        per_page=current_app.config['DM_API_SERVICES_PAGE_SIZE'],
        endpoint='.list_users',
        request_args=request.args
    ), 200
Example #12
0
def update_service(service_id):
    """
        Update a service. Looks service up in DB, and updates the JSON listing.
    """

    is_valid_service_id_or_400(service_id)

    service = Service.query.filter(
        Service.service_id == service_id).first_or_404()

    update_details = validate_and_return_updater_request()
    update = validate_and_return_service_request(service_id)

    # Check for an update to the copied_to_following_framework flag on the service object
    if 'copiedToFollowingFramework' in update:
        if not isinstance(update['copiedToFollowingFramework'], bool):
            abort(400,
                  "Invalid value for 'copiedToFollowingFramework' supplied")
        service.copied_to_following_framework = update[
            'copiedToFollowingFramework']

    if 'supplierId' in update and int(
            update['supplierId']) != service.supplier_id:
        audit_type = AuditTypes.update_service_supplier
        if len(update.keys()) > 1:
            abort(
                400,
                "Cannot update supplierID and other fields at the same time")
        # Discard any other updates
        update = {'supplierId': int(update['supplierId'])}
    else:
        audit_type = (AuditTypes.update_service_admin
                      if request.args.get('user-role') == 'admin' else
                      AuditTypes.update_service)
    updated_service = update_and_validate_service(service, update)

    commit_and_archive_service(updated_service, update_details, audit_type)
    index_service(updated_service,
                  wait_for_response=convert_to_boolean(
                      request.args.get("wait-for-index", "true")))

    return jsonify(message="done"), 200
def get_framework_suppliers(framework_slug):
    framework = Framework.query.filter(
        Framework.slug == framework_slug).first_or_404()

    agreement_returned = request.args.get('agreement_returned')

    supplier_frameworks = SupplierFramework.query.filter(
        SupplierFramework.framework_id == framework.id)

    if agreement_returned is not None:
        if convert_to_boolean(agreement_returned):
            supplier_frameworks = supplier_frameworks.filter(
                SupplierFramework.agreement_returned_at.isnot(None)).order_by(
                    SupplierFramework.agreement_returned_at.desc())
        else:
            supplier_frameworks = supplier_frameworks.filter(
                SupplierFramework.agreement_returned_at.is_(None))

    return jsonify(supplierFrameworks=[
        supplier_framework.serialize()
        for supplier_framework in supplier_frameworks
    ])
def get_framework_suppliers(framework_slug):
    framework = Framework.query.filter(
        Framework.slug == framework_slug
    ).first_or_404()

    agreement_returned = request.args.get('agreement_returned')

    supplier_frameworks = SupplierFramework.query.filter(
        SupplierFramework.framework_id == framework.id
    )

    if agreement_returned is not None:
        if convert_to_boolean(agreement_returned):
            supplier_frameworks = supplier_frameworks.filter(
                SupplierFramework.agreement_returned_at.isnot(None)
            ).order_by(SupplierFramework.agreement_returned_at.desc())
        else:
            supplier_frameworks = supplier_frameworks.filter(
                SupplierFramework.agreement_returned_at.is_(None)
            )

    return jsonify(supplierFrameworks=[
        supplier_framework.serialize() for supplier_framework in supplier_frameworks
    ])
def list_audits():
    page = get_valid_page_or_1()
    try:
        per_page = int(request.args.get('per_page', current_app.config['DM_API_SERVICES_PAGE_SIZE']))
    except ValueError:
        abort(400, 'invalid page size supplied')

    audits = AuditEvent.query.order_by(
        asc(AuditEvent.created_at)
    )

    audit_date = request.args.get('audit-date', None)
    if audit_date:
        if is_valid_date(audit_date):
            audits = audits.filter(
                cast(AuditEvent.created_at, Date) == audit_date
            )
        else:
            abort(400, 'invalid audit date supplied')

    audit_type = request.args.get('audit-type')
    if audit_type:
        if AuditTypes.is_valid_audit_type(audit_type):
            audits = audits.filter(
                AuditEvent.type == audit_type
            )
        else:
            abort(400, "Invalid audit type")

    acknowledged = request.args.get('acknowledged', None)
    if acknowledged and acknowledged != 'all':
        if is_valid_acknowledged_state(acknowledged):
            if convert_to_boolean(acknowledged):
                audits = audits.filter(
                    AuditEvent.acknowledged == true()
                )
            elif not convert_to_boolean(acknowledged):
                audits = audits.filter(
                    AuditEvent.acknowledged == false()
                )
        else:
            abort(400, 'invalid acknowledged state supplied')

    object_type = request.args.get('object-type')
    object_id = request.args.get('object-id')
    if object_type:
        if object_type not in AUDIT_OBJECT_TYPES:
            abort(400, 'invalid object-type supplied')
        if not object_id:
            abort(400, 'object-type cannot be provided without object-id')
        model = AUDIT_OBJECT_TYPES[object_type]
        id_field = AUDIT_OBJECT_ID_FIELDS[object_type]

        audits = audits.join(model, model.id == AuditEvent.object_id) \
                       .filter(id_field == object_id)
    elif object_id:
        abort(400, 'object-id cannot be provided without object-type')

    audits = audits.paginate(
        page=page,
        per_page=per_page
    )

    return jsonify(
        auditEvents=[audit.serialize() for audit in audits.items],
        links=pagination_links(
            audits,
            '.list_audits',
            request.args
        )
    )
Example #16
0
def list_audits():
    page = get_valid_page_or_1()
    try:
        per_page = int(request.args.get('per_page', current_app.config['DM_API_SERVICES_PAGE_SIZE']))
    except ValueError:
        abort(400, 'invalid page size supplied')

    earliest_for_each_object = convert_to_boolean(request.args.get('earliest_for_each_object'))

    if earliest_for_each_object:
        # the rest of the filters we add will be added against a subquery which we will join back onto the main table
        # to retrieve the rest of the row. this allows the potentially expensive DISTINCT ON pass to be performed
        # against an absolutely minimal subset of rows which can probably be pulled straight from an index
        audits = db.session.query(AuditEvent.id)
    else:
        audits = AuditEvent.query

    audit_date = request.args.get('audit-date', None)
    if audit_date:
        if is_valid_date(audit_date):
            audit_datetime = datetime.strptime(audit_date, DATE_FORMAT)
            audits = audits.filter(
                AuditEvent.created_at.between(audit_datetime, audit_datetime + timedelta(days=1))
            )
        else:
            abort(400, 'invalid audit date supplied')

    audit_type = request.args.get('audit-type')
    if audit_type:
        if AuditTypes.is_valid_audit_type(audit_type):
            audits = audits.filter(
                AuditEvent.type == audit_type
            )
        else:
            abort(400, "Invalid audit type")

    user = request.args.get('user')
    if user:
        audits = audits.filter(
            AuditEvent.user == user
        )

    acknowledged = request.args.get('acknowledged', None)
    if acknowledged and acknowledged != 'all':
        if is_valid_acknowledged_state(acknowledged):
            if convert_to_boolean(acknowledged):
                audits = audits.filter(
                    AuditEvent.acknowledged == true()
                )
            elif not convert_to_boolean(acknowledged):
                audits = audits.filter(
                    AuditEvent.acknowledged == false()
                )
        else:
            abort(400, 'invalid acknowledged state supplied')

    object_type = request.args.get('object-type')
    object_id = request.args.get('object-id')
    if object_type:
        if object_type not in AUDIT_OBJECT_TYPES:
            abort(400, 'invalid object-type supplied')

        ref_model = AUDIT_OBJECT_TYPES[object_type]
        ext_id_field = AUDIT_OBJECT_ID_FIELDS[object_type]

        audits = audits.filter(AuditEvent.object.is_type(ref_model))

        # "object_id" here is the *external* object_id
        if object_id:
            ref_object = ref_model.query.filter(
                ext_id_field == object_id
            ).first()

            if ref_object is None:
                abort(404, "Object with given object-type and object-id doesn't exist")

            # this `.identity_key_from_instance(...)[1][0]` is exactly the method used by sqlalchemy_utils' generic
            # relationship code to extract an object's pk value, so *should* be relatively stable, API-wise.
            # the `[1]` is to select the pk's *value* rather than the `Column` object and the `[0]` simply fetches
            # the first of any pk values - generic relationships are already assuming that compound pks aren't in
            # use by the target.
            ref_object_pk = class_mapper(ref_model).identity_key_from_instance(ref_object)[1][0]
            audits = audits.filter(
                AuditEvent.object_id == ref_object_pk
            )
    elif object_id:
        abort(400, 'object-id cannot be provided without object-type')

    if earliest_for_each_object:
        if not (
            acknowledged and
            convert_to_boolean(acknowledged) is False and
            audit_type == "update_service" and
            object_type == "services"
        ):
            current_app.logger.warning(
                "earliest_for_each_object option currently intended for use on acknowledged update_service events. "
                "If use with any other events is to be regular, the scope of the corresponding partial index "
                "should be expanded to cover it."
            )
        # we need to join the built-up subquery back onto the AuditEvent table to retrieve the rest of the row
        audits_subquery = audits.order_by(
            AuditEvent.object_type,
            AuditEvent.object_id,
            AuditEvent.created_at,
            AuditEvent.id,
        ).distinct(
            AuditEvent.object_type,
            AuditEvent.object_id,
        ).subquery()

        audits = AuditEvent.query.join(audits_subquery, audits_subquery.c.id == AuditEvent.id)

    sort_order = db.desc if convert_to_boolean(request.args.get('latest_first')) else db.asc
    audits = audits.order_by(sort_order(AuditEvent.created_at), sort_order(AuditEvent.id))

    return paginated_result_response(
        result_name=RESOURCE_NAME,
        results_query=audits,
        page=page,
        per_page=per_page,
        endpoint='.list_audits',
        request_args=request.args
    ), 200
def list_audits():
    page = get_valid_page_or_1()
    try:
        per_page = int(
            request.args.get('per_page',
                             current_app.config['DM_API_SERVICES_PAGE_SIZE']))
    except ValueError:
        abort(400, 'invalid page size supplied')

    audits = AuditEvent.query.order_by(
        desc(AuditEvent.created_at) if convert_to_boolean(
            request.args.get('latest_first')) else asc(AuditEvent.created_at))

    audit_date = request.args.get('audit-date', None)
    if audit_date:
        if is_valid_date(audit_date):
            audit_datetime = datetime.strptime(audit_date, DATE_FORMAT)
            audits = audits.filter(
                AuditEvent.created_at.between(
                    audit_datetime, audit_datetime + timedelta(days=1)))
        else:
            abort(400, 'invalid audit date supplied')

    audit_type = request.args.get('audit-type')
    if audit_type:
        if AuditTypes.is_valid_audit_type(audit_type):
            audits = audits.filter(AuditEvent.type == audit_type)
        else:
            abort(400, "Invalid audit type")

    acknowledged = request.args.get('acknowledged', None)
    if acknowledged and acknowledged != 'all':
        if is_valid_acknowledged_state(acknowledged):
            if convert_to_boolean(acknowledged):
                audits = audits.filter(AuditEvent.acknowledged == true())
            elif not convert_to_boolean(acknowledged):
                audits = audits.filter(AuditEvent.acknowledged == false())
        else:
            abort(400, 'invalid acknowledged state supplied')

    object_type = request.args.get('object-type')
    object_id = request.args.get('object-id')
    if object_type:
        if object_type not in AUDIT_OBJECT_TYPES:
            abort(400, 'invalid object-type supplied')
        if not object_id:
            abort(400, 'object-type cannot be provided without object-id')
        model = AUDIT_OBJECT_TYPES[object_type]
        id_field = AUDIT_OBJECT_ID_FIELDS[object_type]

        ref_object = model.query.filter(id_field == object_id).first()

        if ref_object is None:
            abort(404,
                  "Object with given object-type and object-id doesn't exist")

        audits = audits.filter(AuditEvent.object == ref_object)

    elif object_id:
        abort(400, 'object-id cannot be provided without object-type')

    audits = audits.paginate(page=page, per_page=per_page)

    return jsonify(auditEvents=[audit.serialize() for audit in audits.items],
                   links=pagination_links(audits, '.list_audits',
                                          request.args))
Example #18
0
def list_audits():
    page = get_valid_page_or_1()
    try:
        per_page = int(request.args.get('per_page', current_app.config['DM_API_SERVICES_PAGE_SIZE']))
    except ValueError:
        abort(400, 'invalid page size supplied')

    earliest_for_each_object = convert_to_boolean(request.args.get('earliest_for_each_object'))

    if earliest_for_each_object:
        # the rest of the filters we add will be added against a subquery which we will join back onto the main table
        # to retrieve the rest of the row. this allows the potentially expensive DISTINCT ON pass to be performed
        # against an absolutely minimal subset of rows which can probably be pulled straight from an index
        audits = db.session.query(AuditEvent.id)
    else:
        audits = AuditEvent.query

    audit_date = request.args.get('audit-date', None)
    if audit_date:
        if is_valid_date(audit_date):
            audit_datetime = datetime.strptime(audit_date, DATE_FORMAT)
            audits = audits.filter(
                AuditEvent.created_at.between(audit_datetime, audit_datetime + timedelta(days=1))
            )
        else:
            abort(400, 'invalid audit date supplied')

    audit_type = request.args.get('audit-type')
    if audit_type:
        if AuditTypes.is_valid_audit_type(audit_type):
            audits = audits.filter(
                AuditEvent.type == audit_type
            )
        else:
            abort(400, "Invalid audit type")

    user = request.args.get('user')
    if user:
        audits = audits.filter(
            AuditEvent.user == user
        )

    data_supplier_id = request.args.get('data-supplier-id')
    if data_supplier_id:
        #  This filter relies on index `idx_audit_events_data_supplier_id`. See `app..models.main` for its definition.
        audits = audits.filter(
            func.coalesce(
                AuditEvent.__table__.c.data['supplierId'].astext,
                AuditEvent.__table__.c.data['supplier_id'].astext,
            ) == data_supplier_id
        )

    acknowledged = request.args.get('acknowledged', None)
    if acknowledged and acknowledged != 'all':
        if is_valid_acknowledged_state(acknowledged):
            if convert_to_boolean(acknowledged):
                audits = audits.filter(
                    AuditEvent.acknowledged == true()
                )
            elif not convert_to_boolean(acknowledged):
                audits = audits.filter(
                    AuditEvent.acknowledged == false()
                )
        else:
            abort(400, 'invalid acknowledged state supplied')

    object_type = request.args.get('object-type')
    object_id = request.args.get('object-id')
    if object_type:
        if object_type not in AUDIT_OBJECT_TYPES:
            abort(400, 'invalid object-type supplied')

        ref_model = AUDIT_OBJECT_TYPES[object_type]
        ext_id_field = AUDIT_OBJECT_ID_FIELDS[object_type]

        audits = audits.filter(AuditEvent.object.is_type(ref_model))

        # "object_id" here is the *external* object_id
        if object_id:
            ref_object = ref_model.query.filter(
                ext_id_field == object_id
            ).first()

            if ref_object is None:
                abort(404, "Object with given object-type and object-id doesn't exist")

            # this `.identity_key_from_instance(...)[1][0]` is exactly the method used by sqlalchemy_utils' generic
            # relationship code to extract an object's pk value, so *should* be relatively stable, API-wise.
            # the `[1]` is to select the pk's *value* rather than the `Column` object and the `[0]` simply fetches
            # the first of any pk values - generic relationships are already assuming that compound pks aren't in
            # use by the target.
            ref_object_pk = class_mapper(ref_model).identity_key_from_instance(ref_object)[1][0]
            audits = audits.filter(
                AuditEvent.object_id == ref_object_pk
            )
    elif object_id:
        abort(400, 'object-id cannot be provided without object-type')

    if earliest_for_each_object:
        if not (
            acknowledged and
            convert_to_boolean(acknowledged) is False and
            audit_type == "update_service" and
            object_type == "services"
        ):
            current_app.logger.warning(
                "earliest_for_each_object option currently intended for use on acknowledged update_service events. "
                "If use with any other events is to be regular, the scope of the corresponding partial index "
                "should be expanded to cover it."
            )
        # we need to join the built-up subquery back onto the AuditEvent table to retrieve the rest of the row
        audits_subquery = audits.order_by(
            AuditEvent.object_type,
            AuditEvent.object_id,
            AuditEvent.created_at,
            AuditEvent.id,
        ).distinct(
            AuditEvent.object_type,
            AuditEvent.object_id,
        ).subquery()

        audits = AuditEvent.query.join(audits_subquery, audits_subquery.c.id == AuditEvent.id)

    sort_order = db.desc if convert_to_boolean(request.args.get('latest_first')) else db.asc
    audits = audits.order_by(sort_order(AuditEvent.created_at), sort_order(AuditEvent.id))

    return paginated_result_response(
        result_name=RESOURCE_NAME,
        results_query=audits,
        page=page,
        per_page=per_page,
        endpoint='.list_audits',
        request_args=request.args
    ), 200
def list_audits():
    page = get_valid_page_or_1()
    try:
        per_page = int(request.args.get('per_page', current_app.config['DM_API_SERVICES_PAGE_SIZE']))
    except ValueError:
        abort(400, 'invalid page size supplied')

    audits = AuditEvent.query.order_by(
        desc(AuditEvent.created_at)
        if convert_to_boolean(request.args.get('latest_first'))
        else asc(AuditEvent.created_at)
    )

    audit_date = request.args.get('audit-date', None)
    if audit_date:
        if is_valid_date(audit_date):
            audit_datetime = datetime.strptime(audit_date, DATE_FORMAT)
            audits = audits.filter(
                AuditEvent.created_at.between(audit_datetime, audit_datetime + timedelta(days=1))
            )
        else:
            abort(400, 'invalid audit date supplied')

    audit_type = request.args.get('audit-type')
    if audit_type:
        audits = audits.filter(
            AuditEvent.type == audit_type
        )

    acknowledged = request.args.get('acknowledged', None)
    if acknowledged and acknowledged != 'all':
        if is_valid_acknowledged_state(acknowledged):
            if convert_to_boolean(acknowledged):
                audits = audits.filter(
                    AuditEvent.acknowledged == true()
                )
            elif not convert_to_boolean(acknowledged):
                audits = audits.filter(
                    AuditEvent.acknowledged == false()
                )
        else:
            abort(400, 'invalid acknowledged state supplied')

    object_type = request.args.get('object-type')
    object_id = request.args.get('object-id')
    if object_type:
        if object_type not in AUDIT_OBJECT_TYPES:
            abort(400, 'invalid object-type supplied')
        if not object_id:
            abort(400, 'object-type cannot be provided without object-id')
        model = AUDIT_OBJECT_TYPES[object_type]
        id_field = AUDIT_OBJECT_ID_FIELDS[object_type]

        ref_object = model.query.filter(
            id_field == object_id
        ).first()

        if ref_object is None:
            abort(404, "Object with given object-type and object-id doesn't exist")

        audits = audits.filter(AuditEvent.object == ref_object)

    elif object_id:
        abort(400, 'object-id cannot be provided without object-type')

    audits = audits.paginate(
        page=page,
        per_page=per_page
    )

    return jsonify(
        auditEvents=[audit.serialize() for audit in audits.items],
        links=pagination_links(
            audits,
            '.list_audits',
            request.args
        )
    )