Scout

Scout is a tool for City Purchasers to search for contract information, including line item information, and subscribe to receive updates when a contract is about to expire. It’s functionality revolves around the ever-present Search Form, which is backed by a Postgres Materialized View with full-text search. For more information about how this search is created, please see Full-text Search with Postgres and Sqlalchemy, parts one and two.

Scout also features Contract and Company detail pages, with pricing, detail, and metadata information for contracts and contact information for companies.

See also

How to use Scout, an internal product guide for more information about the user interface and user experience.

Forms

class SearchForm(*args, **kwargs)[source]

Form to handle Scout search and filter

Variables:
  • q – Search term – required
  • company_name – Flag to include company name in search
  • contract_description – Flag to include contract description in search
  • line_item – Flag to include line item name in search
  • financial_id – Flag to include financial ID in search archived: Flag to include archived ContractBase objects in search
  • contract_type – Filter to include only ContractBase objects that match a certain ContractType
class FeedbackForm(*args, **kwargs)[source]

Form to collect sender and body for Scout contract feedback

Variables:
  • sender – Email address of sender
  • body – Body of message, required
class NoteForm(*args, **kwargs)[source]

Form to allow users to write notes about contracts

Variables:
  • note – Body of note
  • user – ID of the User who wrote the note

Helpers

build_filter(req_args, fields, search_for, filter_form, _all)[source]

Build the non-exclusive filter conditions for scout search

Along with building the filter for the search, build_filter also modifies the passed in filter_form, setting the checked property on the appropriate form fields.

Parameters:
  • req_args – request.args from Flask.request
  • fields

    list of three-tuples. Each three-tuple should contain the following:

    • database column name
    • desired output display name
    • Model property that maps to the specific column name in question.

    For build_filter, only the column name and Model property are used. For build_cases(), all are used.

  • search_for – string search term
  • filter_form
  • _all – Boolean – true if we are searching across all fields, false otherwise
Returns:

List of clauses that can be used in Sqlalchemy query filters

build_cases(req_args, fields, search_for, _all)[source]

Build case statements for categorizing search matches in scout search

Parameters:
  • req_args – request.args from Flask.request
  • fields

    list of three-tuples. Each three-tuple should contain the following:

    • database column name
    • desired output display name
    • Model property that maps to the specific column name in question.

    For build_cases, all three parts of the tuple are used

  • search_for – string search term
  • _all – Boolean – true if we are searching across all fields, false otherwise
Returns:

List of clauses that can be used in a Sqlalchemy case expressions

feedback_handler(contract, search_for=None)[source]

Allow user to send feedback on the data present in a specific contract

Parameters:
  • contractContractBase object
  • search_for – search term or None.
Returns:

Redirects to or renders the appropriate feedback handling template

add_archived_filter(query, archived)[source]

Adds exclusionary filters and archived contracts to contract searches.

All searches exclude invalid contract objects, such as ones that have no financial id or no expiration date. Occasionally, the user will also want to search expired contracts. If the flag is passed, “archived” contracts, (which are either expired or manually flagged as no longer usuable) are shown as well.

Parameters:
  • query – Sqlalchemy contract search query
  • archived – Boolean to determine if archived contracts should be included in search results
Returns:

Original query with additional exclusionary filters and optionally archived contracts

find_contract_metadata(search_for, case_statements, filter_or, filter_and, archived=False)[source]

Takes a search term, case statements, and filter clauses and returns out a list of search results objects to be rendered into the template.

Parameters:
Returns:

A Sqlalchemy resultset that contains the fields to render the search results view.

return_all_contracts(filter_and, archived=False)[source]

Return all contracts in the event of an empty search

Parameters:
  • filter_and – An iterable of Sqlalchemy query filters, used for exclusionary filtering
  • archived – Boolean of whether or not to add the is_archived filter
Returns:

A Sqlalchemy resultset that contains the fields to render the search results view.

Views

GET /scout/

The landing page for scout. Renders the “big search” template.

Status Codes:
  • 200 OK – Renders the appropriate landing page.
POST /scout/search

The search results page for scout

In order to create permalinks to each search/filter result combination, POST methods have their form arguments popped off and then are immediately redirected to GET methods.

See also

Status Codes:
  • 200 OK – Render the search template with the given results
  • 302 Found – Pop the passed form arguments to the request URL and redirect to the search view

The search results page for scout

In order to create permalinks to each search/filter result combination, POST methods have their form arguments popped off and then are immediately redirected to GET methods.

See also

Status Codes:
  • 200 OK – Render the search template with the given results
  • 302 Found – Pop the passed form arguments to the request URL and redirect to the search view
POST /scout/contracts/feedback/(search_for)

Provide feedback about an empty search

This page is only viewable in the event of a search that returns 0 results.

Parameters:
  • search_for – Search term.

See also

feedback_handler() for information on how the feedback is processed and handled

GET /scout/contracts/feedback/(search_for)

Provide feedback about an empty search

This page is only viewable in the event of a search that returns 0 results.

Parameters:
  • search_for – Search term.

See also

feedback_handler() for information on how the feedback is processed and handled

GET /scout/filter

The landing page for filtering by departments

Status Codes:
  • 200 OK – Renders the appropriate landing page.
GET /scout/filter/(int: department_id)

Filter contracts by which ones have departmental subscribers

Parameters:
  • department_id – Department’s unique ID
Status Codes:
  • 200 OK – Renders template with all contracts followed by that department
  • 404 Not Found – When department is not found with the specified ID
POST /scout/contracts/(int: contract_id)

Contract profile page

For GET requests, render the profile page. For POSTs, try to submit a new note.

Parameters:
Status Codes:
  • 200 OK – Renders the contract profile template
  • 302 Found – Try to post note, then redirect to the same page’s contract view
  • 404 Not Found – Unique contract ID not found
GET /scout/contracts/(int: contract_id)

Contract profile page

For GET requests, render the profile page. For POSTs, try to submit a new note.

Parameters:
Status Codes:
  • 200 OK – Renders the contract profile template
  • 302 Found – Try to post note, then redirect to the same page’s contract view
  • 404 Not Found – Unique contract ID not found
GET /scout/companies/(int: company_id)

Company profile page

Parameters:
  • contract_id – Unique ID for a Company object
Status Codes:
  • 200 OK – Renders the company profile template
  • 404 Not Found – Unique company ID not found
POST /scout/contracts/(int: contract_id)/feedback

Provide feedback about a contract

Parameters:

See also

feedback_handler() for information on how the feedback is processed and handled

GET /scout/contracts/(int: contract_id)/feedback

Provide feedback about a contract

Parameters:

See also

feedback_handler() for information on how the feedback is processed and handled

GET /scout/contracts/(int: contract_id)/subscribe

Subscribes a user to receive updates about a particular contract

Parameters:
Status Codes:
  • 302 Found – Subscribe the current user and redirect to the page they came from
  • 404 Not Found – Contract not found
GET /scout/contracts/(int: contract_id)/unsubscribe

Unsubscribes a user from receiving updates about a particular contract

Parameters:
Status Codes:
  • 302 Found – Unsubscribe the current user and redirect to the page they came from
  • 404 Not Found – Contract not found

Nightly Jobs

class CountyScrapeJob(time_override=False)[source]

Nightly task to scrape the County for new line item information

See also

  • purchasing.data.importer.scrape_county.main()
  • purchasing.tasks.scrape_county_task()
start_time

Override default start time, kick scrape task off immediately

run_job(job)[source]

Boot up a scrape county job on a Celery worker (it can be long-running)

class ScoutJobBase(time_override=False)[source]

Base class for Scout email notifications

See also

Notification

notification_props

Placeholder for properties to be assigned to the Notification class.

Based on the implementation, this dictionary should include at least a ‘subjct’ and ‘html_template’ key.

Raises:NotImplementedError
get_expiring_contracts()[source]

Get expiring contracts. Must be implemented in subclasses

Raises:NotImplementedError
build_notifications()[source]
class ScoutContractsExpireTodayJob(time_override=False)[source]

Get all contracts that expire today and send notification reminders

get_expiring_contracts()[source]

Get all contracts expiring today

Returns:List of ContractBase objects that expire today
class ScoutContractsExpireSoonJob(time_override=False)[source]

Get all contracts that are expiring in 30 days and send reminders

get_expiring_contracts()[source]

Get all contracts expiring today

Returns:List of ContractBase objects that expire in 30 days