Beacon

Beacon is a tool for City Purchasers to advertise new opportunities and businesses to sign up to receive information about those opportunities. It is divided into two sections: a public-facing section and an administrative section. The public-facing section contains a list and detail view of both currently available and past opportunities, along with overview informational pages, signup pages, and a page to manage email subscriptions.

The administrative section handles the creation and editing of new opportunities along with an approval flow for opportunities.

Additionally, Beacon has two nightly jobs that run: one nightly job sends emails whenever new opportunities become “published” (see the Opportunity model for more information), and a job that runs and sends a bi-weekly update with a list of all non-expired opportunities that have been posted in the past two weeks to all signed-up Vendors. See more in the Nightly Jobs section.

See also

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

Forms

Forms

class MultiCheckboxField(label=None, validators=None, coerce=<type 'unicode'>, choices=None, **kwargs)[source]

Custom multiple select field that displays a list of checkboxes

We have a custom pre_validate to handle cases where a user has choices from multiple categories. the validation to pass.

Variables:
pre_validate(form)[source]

Automatically passes

We override pre-validate to allow the form to use dynamically created CHOICES.

class DynamicSelectField(label=None, validators=None, coerce=<type 'unicode'>, choices=None, **kwargs)[source]

Custom dynamic select field

pre_validate(form)[source]

Ensure we have at least one Category and they all correctly typed

See also

class CategoryForm(formdata=<object object>, **kwargs)[source]

Base form for anything involving Beacon categories

“Categories” and “Subcategories” are originally derived from NIGP codes. NIGP codes can be somewhat hard to parse and aren’t updated incredibly regularly, especially when it comes to things like IT services and software. Therefore, we took time to devise our own descriptions of things that map back to NIGP codes. The category form is the UI representation of that mapping. Each detailed “subcategory” has a parent “category” to which it belongs. Users select the “parent” category they are interested in, and a series of checkboxes are presented to them. This raises some interesting challenges for validation, which are handled in the process method.

See also

Variables:
get_categories()[source]

Getter for the form’s parent categories

get_subcategories()[source]

Getter for the form’s subcategories

pop_categories(categories=True, subcategories=True)[source]

Pop categories and/or subcategories off of the Form’s data attribute

In order to prevent wtforms from throwing ValidationErrors improperly, we need to modify some of the internal form data. This method allows us to pop off the categories or subcategories of the form data as necessary.

Parameters:
  • categories – Pop categories from form’s data if True
  • subcategories – Pop subcategories from form’s data if True
Returns:

Modified form data with categories and/or subcategories removed as necessary

build_categories(all_categories)[source]

Build form’s category and subcategory choices

For our select_multi_checkbox(), we need to give both top-level choices for the select field and individual level subcategories. This method creates those and modifies the form in-place to build the appropriate choices

Parameters:all_categories – A list of Category objects
Returns:A dictionary with top-level parent category names as keys and list of that parent’s subcategories as values.
display_cleanup(all_categories=None)[source]

Clean up form’s data for display purposes:

  1. Constructs and modifies the form’s categories and subcategories
  2. Creates the template-used subcatgories and display categories
  3. Removed the Select All choice from the available categories
Parameters:
  • all_categories – A list of Category objects,
  • None. If None, defaults to all Categories. (or) –
process(formdata=None, obj=None, data=None, **kwargs)[source]

Process the form and append data to the categories

Manually iterates through the flask Request.form, appending valid Categories to the form’s categories data

See also

For more information about parameters, see the Wtforms base form

class VendorSignupForm(formdata=<object object>, **kwargs)[source]

Signup form vendors use to sign up for Beacon updates

The goal here is to lower the barrier to signing up by as much as possible, making it as easy as possible to sign up. This means that very little of this information is required. This form is an implementation of the CategoryForm, which means that it processes categories and subcategories in addition to the below fields.

Variables:
  • business_name – Name of business, required
  • email – Email address of vendor signing up, required, must be unique
  • first_name – First name of vendor, optional
  • last_name – Last name of vendor, optional
  • phone_number – Phone number of vendor, optional
  • fax_number – Fax number of vendor, optional
  • woman_owned – Whether the business is woman owned, optional
  • minority_owned – Whether the business is minority owned, optional
  • veteran_owned – Whether the business is veteran owned, optional
  • disadvantaged_owned – Whether the business is disadvantaged owned, optional
  • subscribed_to_newsletter – Boolean flag for whether a business is signed up to the receive the newsletter
class OpportunitySignupForm(formdata=<object object>, **kwargs)[source]

Signup form vendors can use for individual opportunities

Variables:
  • business_name – Name of business, required
  • email – Email address of vendor signing up, required, must be unique
  • also_categories – Flag for whether or not a business should be signed up to receive updates about opportunities with the same categories as this one
class UnsubscribeForm(formdata=<object object>, **kwargs)[source]

Subscription management form, where Vendors can unsubscribe from all different emails

Variables:
  • email – Email address of vendor signing up, required
  • categories – A multicheckbox of all categories the Vendor is signed up to receive emails about
  • opportunities – A multicheckbox of all opportunities the Vendor is signed up to receive emails about
  • subscribed_to_newsletter – A flag for whether or not the Vendor should receive the biweekly update newsletter
class OpportunityDocumentForm(formdata=None, obj=None, prefix='', data=None, meta=None, **kwargs)[source]

Document subform for the main OpportunityForm

Variables:
  • title – Name of document to be uploaded
  • document – Actual document file that should be uploaded
upload_document(_id)[source]

Take the document and filename and either upload it to S3 or the local uploads folder

Parameters:_id – The id of the Opportunity the document will be attached to
Returns:A two-tuple of (the document name, the document filepath/url)
class OpportunityForm(formdata=<object object>, **kwargs)[source]

Form to create and edit individual opportunities

This form is an implementation of the CategoryForm, which means that it processes categories and subcategories in addition to the below fields.

Variables:
  • department – link to Department that is primarily responsible for administering the RFP, required
  • opportunity_type – link to ContractType objects that have the allow_opportunities field set to True
  • contact_email – Email address of the opportunity’s point of contact for questions
  • title – Title of the opportunity, required
  • description – 500 or less word description of the opportunity, required
  • planned_publish – Date when the opportunity should be made public on Beacon
  • planned_submission_start – Date when the opportunity opens to accept responses
  • planned_submission_end – Date when the opportunity closes and no longer accepts submissions
  • vendor_documents_needed – A multicheckbox for all documents that a vendor might need to respond to this opportunity.
  • documents – A list of OpportunityDocumentForm fields.

See also

  • ContractType
    The ContractType model informs the construction of the “How to Bid” section in the template
  • Opportunity
    The base model that powers the form.
display_cleanup(opportunity=None)[source]

Cleans up data for display in the form

  1. Builds the choices for the vendor_documents_needed
  2. Formats the contact email for the form
  3. Localizes the planned_submission_end time
Parameters:opportunity – A purchasing.opportunities.model.Opportunity object or None.
data_cleanup()[source]

Cleans up form data for processing and storage

  1. Pops off categories
  2. Pops off documents (they are handled separately)
  3. Sets the foreign keys Opportunity model relationships
  4. Removes csrf token
Returns:An opportunity_data dictionary, which can be used to instantiate or modify an Opportunity instance
process(formdata=None, obj=None, data=None, **kwargs)[source]

Processes category data and localizes planned_submission_end times

Validators

email_present(form, field)[source]

Checks that we have a vendor with that email address

city_domain_email(form, field)[source]

Checks that the email is a current user or a city domain

max_words(_max=500)[source]

Checks that a text-area field has fewer than the allowed number of words.

Parameters:_max – Maximum words allowed, defaults to 500
after_now(form, field)[source]

Checks that a date occurs before now

validate_phone_number(form, field)[source]

Strips out non-integer characters, checks that it is 10-digits

Helper functions

parse_contact(contact_email, department)[source]

Finds or creates a user as the contact

Parameters:
  • contact_email – The email address of the User. If the user cannot be found in the database, the domain of their email must match the configured CITY_DOMAIN
  • department – The Department of the user
Returns:

The ID of the new/existing contact

build_label_tooltip(name, description)[source]

Builds bootstrap-style tooltips for contract documents

Parameters:
  • name – The name of the document
  • description – The description of the document – lives in the tooltip and is shown on hover.
Returns:

A formatted label with tooltip

select_multi_checkbox(field, ul_class='', **kwargs)[source]

Custom multi-select widget for vendor documents needed

Returns:SelectMulti checkbox widget with tooltip labels
class SignupData(email, business_name)[source]

Small python object to hold default data coming from the Flask session

Parameters:
  • email – Email address taken from session
  • business_name – Business name taken from session
init_form(form, model=None)[source]

Initialize a passed form given a model or object

Parameters:
Returns:

The passed form, initialized with either the passed model or a new instance of purchasing.opportunities.util.SignupData

signup_for_opp(form, opportunity, multi=False)[source]

Sign a vendor up for an opportunity

Generic helper method to handle subscriptions from both the list view (signing up form multiple opportunities) and the detail view (signing up for a single opportunity). Responsible for creation of new Vendor objects if necessary, and sending emails based on the opportunities selected to receive updates about.

Parameters:
  • form – The relevant subscription form
  • opportunity – Either an opportunity model or a list of opportunity ids
  • multi – A boolean to flag if there are multiple opportunities that should to subscribe to or a single opportunity
Returns:

True if email sent successfully, false otherwise

Views

Public-facing

GET /beacon/opportunities/expired

View expired contracts

Status Codes:
  • 200 OK – render the expired opportunities templates
POST /beacon/opportunities

Browse available opportunities

Status Codes:
GET /beacon/opportunities

Browse available opportunities

Status Codes:
POST /beacon/manage

Manage a vendor’s signups

Status Codes:
GET /beacon/manage

Manage a vendor’s signups

Status Codes:
GET /beacon/

Landing page for opportunities site

Status Codes:
  • 200 OK – Successfully render the splash page
POST /beacon/opportunities/(int: opportunity_id)

View one opportunity in detail

Status Codes:
GET /beacon/opportunities/(int: opportunity_id)

View one opportunity in detail

Status Codes:

Administration

GET /beacon/admin/opportunities/pending

View which contracts are currently pending approval

Status Codes:
  • 200 OK – Render the pending template
POST /beacon/admin/opportunities/new

Create a new opportunity

Status Codes:
  • 200 OK – Render the opportunity create/edit template
  • 302 Found – Post data for a new opportunity via the OpportunityForm and redirect to the edit view of the created opportunity
GET /beacon/admin/opportunities/new

Create a new opportunity

Status Codes:
  • 200 OK – Render the opportunity create/edit template
  • 302 Found – Post data for a new opportunity via the OpportunityForm and redirect to the edit view of the created opportunity
GET /beacon/admin/signups

Basic dashboard view for category-level signups

Status Codes:
  • 200 OK – Download a tab-separated file of all vendor signups
GET /beacon/admin/opportunities/(int: opportunity_id)/document/(int: document_id)/remove

Remove a particular opportunity document

See also

OpportunityForm

Status Codes:
  • 302 Found – Delete the relevant opportunity document and redirect to the edit view for the opportunity whose document was deleted
GET /beacon/admin/opportunities/(int: opportunity_id)/publish

Publish an opportunity

If an Opportunity has been created by a non-admin, it will be stuck in a “pending” state until it has been approved by an admin. This view function handles the publication event for a specific Opportunity

Status Codes:
GET /beacon/admin/opportunities/(int: opportunity_id)/archive

Archives opportunities in pending view

Status Codes:
POST /beacon/admin/opportunities/(int: opportunity_id)

Edit an opportunity

Status Codes:
  • 200 OK – Render the opportunity create/edit template
  • 302 Found – Post data for the relevant opportunity to edit via the OpportunityForm and redirect to the edit view of the opportunity
GET /beacon/admin/opportunities/(int: opportunity_id)

Edit an opportunity

Status Codes:
  • 200 OK – Render the opportunity create/edit template
  • 302 Found – Post data for the relevant opportunity to edit via the OpportunityForm and redirect to the edit view of the opportunity

Nightly Jobs

class BeaconNewOppotunityOpenJob(time_override=False)[source]

Send a nightly email update when new opportunities are posted

build_notifications()[source]

Implements EmailJobBase build_notifications method

Returns:list of Notification objects, one for each new Opportunity. For each Opportunity, the to_email field is the union of all followers of the opportunity and any followers of any categories that the Opportunity has
get_opportunities()[source]

Get new opportunities to send to businesses

Returns:list of Opportunity objects that are approved (is_public == True), have not had notification emails sent yet (publish_notification_sent == False), and are set to publish today (db.func.DATE(Opportunity.planned_publish) == datetime.date.today())
class BeaconBiweeklyDigestJob(time_override=False)[source]

Send a biweekly update of all non-expired Opportunities posted to Beacon

run_job(job)[source]

Runs the biweekly update job and updates the app status as necessary

should_run()[source]

Returns true only if we are on the first or fifteenth of the month or time_override is True

build_notifications()[source]

Implements EmailJobBase build_notifications method

Returns:list of Notification objects, one for each non-expired opportunity that has been published since the last Beacon newsletter was sent out
get_opportunities()[source]

Get bulk opportunities to send to businesses

Returns:list of Opportunity objects that have a published_at date after the last newsletter was sent out and expire on or after today