Programs - Gary-Community-Ventures/benefits-api GitHub Wiki

Programs

Programs are the main thing that is displayed to the user on the results page. Text like the name, description, and apply link are handled in the admin, while rules for eligibility and value are handled in code. There are 2 methods for calculating eligibility and value.

  • Write our own rules
  • Use Policy Engine

This section will discuss programming the rules for a program, and how add/edit a program in the admin.

Admin

The Admin allows non developers to edit program information without the help of a developer.

Creating a program

  1. Go to the "Translation Admin".
  2. Go to "Programs".
  3. Click "Add New".

image

  1. Enter the "Name abbreviated" that corresponds to the calculator for the program (you may need to ask a developer for this).

image

  1. Add all of the text for the program by clicking the links, and replacing the placeholder text with the desired text.

  2. Click "Edit in Main Admin"

image

  1. Update the fields
    • Documentation for each field can be found in the Main Admin section.

Updating a program

Translation admin

To edit any of the text in the translation admin:

  1. Go to the "Translation Admin".
  2. Go to "Programs".
  3. Search the name of the program.
  4. Select "Go to".
  5. Select the field to update.

The translations can also be accessed through the main admin by clicking on the hamburger menu in the "Translate" column

image

Main Admin

  1. Go to "Programs.
  2. Search the name of the program.
  3. Click on the name.
  4. Update the fields.

image

What things mean

Translation admin

The following translated text can be edited in the translation admin:

  • Name: The name that is displayed to the user
  • Description: The description that is displayed to the user
  • Short description: Deprecated
  • Learn more link: Deprecated
  • Apply button link: The link that will take the user to the application. This is a translation, because there can be different links for different languages
  • Value type: Deprecated
  • Estimated delivery time: Deprecated
  • Estimated application time: The estimated amount of time it would take for the user to apply
  • Website description: The description on the page that lists all of the MFB benefits
  • Estimated value: If this value is not empty, then the program value will be set to this value. Can be text

Main admin

The following things can be edited in the main admin:

  • White label: The white label to show this program in
  • Name abbreviated: The rules to use
  • External name: Syncs programs across environments
  • Legal status required: The citizenship statuses that are eligible for this program
  • Documents: The documents that are needed to apply for this program
  • Active: Only active programs are showed to the user
  • Low confidence: Display a flag to the user that reads "Low Confidence"
  • Fpl: The year that the program should be calculated for. For the custom calculators, this is used for choosing what FPL year to use. For the Policy Engine calculators, this is used to determine which period to use.
  • Category: The category that the program is in

Other program related features

The following can be added to a program.

  • Navigator: An organization that will help the user apply the program.
  • Warning message: An additional message to display to the user under certain conditions.
  • Translation override: Override translated text of the program under certain conditions.

Development

Base classes are provided for both writing custom rules, and using rules from Policy Engine.

Custom rules

Create a new folder with the name of the new program in one of the state (or federal) folders in /programs/programs/. In that folder add the files __init__.py and calculator.py.

To create a program calculator in the calculator.py, create a new class that inherits from ProgramCalculator in /programs/programs/calc.py.

The following methods and attributes can be overridden:

  • household_eligible (method): Eligibility rules that apply to the whole household.
    • An Eligibility object is passed as a parameter. To add a condition, call e.condition(bool). A program will be marked as eligible if all of the conditions are True. There is an additional condition that at least one household member must be eligible.
  • member_eligible (method): Eligibility rules that apply to individual household members.
    • An MemberEligibility object is passed as a parameter. To add a condition, call e.condition(bool). A member will be marked as eligible if all of the conditions are True.
  • household_value (method): The household value of the program if eligible.
  • member_value (method): The value of the program for each eligible household member.
  • amount (attribute): The value returned by household_value if it is not overridden.
  • member_amount (attribute): The value returned by member_value if it is not overridden.
  • dependencies (attribute): A list of fields needed so that the program does not through an error when run. If the screen is missing one of these fields, then the program will be skipped. Syntax:
    • Screen and HouseholdMember fields: {field}
    • IncomeStream fields: income_{field}
    • Expense fields: expense_{field}

Import and add the calculator to the dictionary in the __init__.py file in the corresponding state folders /programs/programs/. The key used is matched to the name_abbreviated of a program.

Policy Engine rules

Policy Engine is the open source rules engine and API that MFB uses for some of it's benefits.

How Policy Engine works

Policy Engine is a fork of OpenFisca, so the best documentation for Policy Engine is the OpenFisca documentation, but here is a way too brief overview of how it works:

  • Variables: Calculations are broken down into nodes (or variables) that depend on 0 or more other variables to calculate it's value. For example, snap_net_income subtracts the snap_deductions variable from the snap_gross_income variable. Variables can be overridden to set leaf variables (variables that don't depend on other variables; e.g. age), override a calculation (this can be useful if there is a bug that needs to be fixed urgently), or to use custom rules.
  • Periods: Benefits change every year, and periods are the way to select which year (or month in some cases) to calculate a variable for.
  • Entities: Entities control who is included in a variable. For example, some benefits (like WIC) are calculated for each member of the household individually, while other benefits (like SNAP) are calculated for the household as a whole.
  • Parameters: Instead of hard coding numbers into variables (like income limit), parameters are used so that changes over time can be tracked.

Issues and pull requests can be filed in the policyengine-us GitHub repo for bugs/benefit updates in Policy Engine

Integrating with Policy Engine

Two base classes are provided to interact with the Policy Engine API.

The PolicyEngineScreenInput, in /programs/programs/policyengine/calculators/dependencies/base.py, base class is used to transform the screen data into a variable in Policy Engine. There are 4 classes to inherit from depending on what entity the variable is in:

  • Household
  • TaxUnit
  • SpmUnit
  • Member

The following methods and attributes can be overridden:

  • value (method): Transforms the screen data into the Policy Engine variable.
    • By default, value returns None. Which means that Policy Engine will calculate the variable and return the result (used for output variables)
    • value can also return a static value.
  • field (attribute): The name of the variable whose value is set.
  • dependencies (attribute): A list of fields needed so that the program does not through an error when run. If the screen is missing one of these fields, then the program will be skipped. Syntax:
    • Screen and HouseholdMember fields: {field}
    • IncomeStream fields: income_{field}
    • Expense fields: expense_{field}

The PolicyEngineCalulator, in /programs/programs/policyengine/calculators/base.py, base class is used to transform the Policy Engine API response into the program eligibility. There are 3 classes to inherit from depending on what entity the the benefit is in:

  • PolicyEngineSpmCalulator
  • PolicyEngineTaxUnitCalulator
  • PolicyEngineMembersCalculator

The following methods and attributes can be overridden:

  • household_value (method): The value of the benefit for the whole household.
    • Used in classes inherited from PolicyEngineSpmCalulator and PolicyEngineTaxUnitCalulator.
    • If the value is greater than 0, then the benefit is eligible.
    • Use self.sim.value to access the variables from the Policy Engine API.
  • member_value (method): The value of the benefit for each member.
    • Used in classes inherited from PolicyEngineMembersCalculator.
    • If the value is greater than 0, then the benefit is eligible.
    • Use self.sim.value to access the variables from the Policy Engine API (pass in the member.id as the second parameter).
  • pe_inputs (attribute): A list of dependency classes inherited from PolicyEngineScreenInput that represents all of the variables needed to calculate the value.
  • pe_outputs (attribute): A list of dependency classes inherited from PolicyEngineScreenInput that represents the variables that Policy Engine needs to calculate.
    • The output dependencies should all have a value of None.
  • pe_name (attribute): The name of the variable that will be used as the value if no household_value or member_value is provided.

The POLICY_ENGINE_CLIENT_ID and POLICY_ENGINE_CLIENT_SECRET environment variables need to be set in order to use the Policy Engine API.

Debugging

Printing variables to the console can be a useful tool for debugging. The following techniques can be used to print the variables:

To print the value of a specific dependency, add print(self.sim.value(self.pe_category, self.pe_sub_category, "[variable_name]", self.pe_period) to the household_value method, or add print(self.sim.value(self.pe_category, str(member.id), "[variable_name]", self.pe_period) to the member_value method. Replace [varibale_name] with the name of the variable to print.

To print all of the variables, add print(self.data) to the end of the __init__ method in PrivateApiSim in /programs/programs/policyengine/engines.py. Additionally, more variables can be logged by adding "[variable_name]": {"[period]": None} to the raw_input in the pe_input function in /programs/programs/policyengine/policy_engine.py. Replace [variable_name] with the name of the variable, and [period] with the period to compute the variable for.

Brief technical overview