Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add documentation and integrate OpenFisca Tools #12

Merged
merged 25 commits into from
Oct 9, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Add documentation starter
  • Loading branch information
nikhilwoodruff committed Oct 8, 2022
commit 516414fd74a9a7243f1f98e193435168a48ffa0e
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,5 @@ coverage.xml
.coverage
build/
dist/
docs/_build/
**/*.ipynb_checkpoints/
9 changes: 9 additions & 0 deletions docs/_config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
title: PolicyEngine Core documentation
author: PolicyEngine

execute:
execute_notebooks: force

sphinx:
config:
html_theme: furo
9 changes: 9 additions & 0 deletions docs/_toc.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
format: jb-book
root: intro
parts:
- caption: Python API
chapters:
- file: python_api/country_template
- file: python_api/data_storage
- file: python_api/entities

18 changes: 18 additions & 0 deletions docs/intro.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Introduction

This is the documentation for PolicyEngine Core, the open-source Python package powering PolicyEngine's tax-benefit microsimulation models. It is a fork of [OpenFisca-Core](https://github.com/openfisca/openfisca-core), developed and maintained by [OpenFisca](https://www.openfisca.org/).

PolicyEngine Core does not simulate any specific tax-benefit policy: instead, it is a general framework for building tax-benefit microsimulation models. It is currently used by PolicyEngine UK and PolicyEngine US, which each define the custom logic, parameters and data required to simulate the tax-benefit systems of the UK and the US respectively.

The country models each provide:

* A set of *entity types* (e.g. `Person`).
* A set of *parameters* (e.g. `Flat tax rate`). Parameters are global data points that have different values for different time periods.
* A set of *variables* (e.g. `Tax liability`). Variables are properties of entities that can be dependent on entities (including other variable values), parameters and the time period.

PolicyEngine Core then enables users to:

* Calculate the value of a variable in a specific time period.
* Trace the computation tree of a calculation.

PolicyEngine Core also includes many helper functions designed to simplify the process of modelling a country's policy.
142 changes: 142 additions & 0 deletions docs/python_api/country_template.ipynb
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Country template\n",
"\n",
"The `country_template` folder is a working example of a country model. To create a new country model, create a new repository with the folder's contents:\n",
"\n",
"* `parameters/` contains the parameter tree, with examples.\n",
"* `reforms/` contains several example reforms written using the Python reforms API.\n",
"* `situation_examples/` contains several example situations, in JSON format and accessible as Python objects.\n",
"* `tests/` contains a set of YAML tests to check policy implementations function.\n",
"* `variables/` contain definitions of variables with formulas.\n",
"* `entities.py` contains definitions of entities.\n",
"\n",
"## Running a simulation\n",
"\n",
"The below example runs a simulation using the example country model template."
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[600.]\n"
]
}
],
"source": [
"from policyengine_core import SimulationBuilder\n",
"from policyengine_core.country_template import CountryTaxBenefitSystem\n",
"from policyengine_core.country_template.situation_examples import single\n",
"\n",
"system = CountryTaxBenefitSystem()\n",
"\n",
"simulation = SimulationBuilder().build_from_dict(\n",
" system,\n",
" single,\n",
")\n",
"\n",
"print(simulation.calculate(\"disposable_income\", \"2017-01\"))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Testing a country model\n",
"\n",
"The example below runs all tests defined for the example country model. This can also be done by running:\n",
"\n",
"`policyengine-core test -c policyengine_core.country_template policyengine_core/country_template/tests`"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"============================= test session starts ==============================\n",
"platform darwin -- Python 3.9.12, pytest-5.4.3, py-1.11.0, pluggy-0.13.1\n",
"rootdir: /Users/nikhil/policyengine/policyengine-core\n",
"plugins: anyio-3.5.0\n",
"collected 0 items\n",
"\n",
"============================ no tests ran in 0.00s =============================\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"ERROR: file not found: /Users/nikhil/policyengine/policyengine-core/docs/python_api/policyengine_core/country_template/tests\n",
"\n"
]
},
{
"data": {
"text/plain": [
"CompletedProcess(args=['policyengine-core', 'test', '-c', 'policyengine_core.country_template', 'policyengine_core/country_template/tests'], returncode=4)"
]
},
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"import subprocess\n",
"from policyengine_core.country_template import COUNTRY_DIR\n",
"\n",
"subprocess.run(\n",
" [\n",
" \"policyengine-core\",\n",
" \"test\",\n",
" \"-c\",\n",
" \"policyengine_core.country_template\",\n",
" COUNTRY_DIR / \"tests\",\n",
" ]\n",
")\n"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3.9.12 ('base')",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.9.12"
},
"orig_nbformat": 4,
"vscode": {
"interpreter": {
"hash": "40d3a090f54c6569ab1632332b64b2c03c39dcf918b08424e98f38b5ae0af88f"
}
}
},
"nbformat": 4,
"nbformat_minor": 2
}
30 changes: 30 additions & 0 deletions docs/python_api/data_storage.ipynb
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Data storage"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3.9.12 ('base')",
"language": "python",
"name": "python3"
},
"language_info": {
"name": "python",
"version": "3.9.12"
},
"orig_nbformat": 4,
"vscode": {
"interpreter": {
"hash": "40d3a090f54c6569ab1632332b64b2c03c39dcf918b08424e98f38b5ae0af88f"
}
}
},
"nbformat": 4,
"nbformat_minor": 2
}
30 changes: 30 additions & 0 deletions docs/python_api/entities.ipynb
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Entities"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3.9.12 ('base')",
"language": "python",
"name": "python3"
},
"language_info": {
"name": "python",
"version": "3.9.12"
},
"orig_nbformat": 4,
"vscode": {
"interpreter": {
"hash": "40d3a090f54c6569ab1632332b64b2c03c39dcf918b08424e98f38b5ae0af88f"
}
}
},
"nbformat": 4,
"nbformat_minor": 2
}
3 changes: 3 additions & 0 deletions docs/python_api/intro.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Using PolicyEngine Core

This chapter contains documentation for individual modules in the PolicyEngine Core source code.
2 changes: 2 additions & 0 deletions policyengine_core/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
from policyengine_core.simulations import SimulationBuilder
from policyengine_core.taxbenefitsystems import TaxBenefitSystem
3 changes: 2 additions & 1 deletion policyengine_core/country_template/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,10 @@

from policyengine_core.country_template import entities
from policyengine_core.country_template.situation_examples import couple
from pathlib import Path


COUNTRY_DIR = os.path.dirname(os.path.abspath(__file__))
COUNTRY_DIR = Path(__file__).parent


# Our country tax and benefit class inherits from the general TaxBenefitSystem class.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,57 +19,6 @@ def get_parser():
True # Can be added as an argument of add_subparsers in Python 3
)

def build_serve_parser(parser):
# Define OpenFisca modules configuration
parser = add_tax_benefit_system_arguments(parser)

# Define server configuration
parser.add_argument(
"-p",
"--port",
action="store",
help="port to serve on (use --bind to specify host and port)",
type=int,
)
parser.add_argument(
"--tracker-url",
action="store",
help="tracking service url",
type=str,
)
parser.add_argument(
"--tracker-idsite",
action="store",
help="tracking service id site",
type=int,
)
parser.add_argument(
"--tracker-token",
action="store",
help="tracking service authentication token",
type=str,
)
parser.add_argument(
"--welcome-message",
action="store",
help="welcome message users will get when visiting the API root",
type=str,
)
parser.add_argument(
"-f",
"--configuration-file",
action="store",
help="configuration file",
type=str,
)

return parser

parser_serve = subparsers.add_parser(
"serve", help="Run the OpenFisca Web API"
)
parser_serve = build_serve_parser(parser_serve)

def build_test_parser(parser):
parser.add_argument(
"path",
Expand Down Expand Up @@ -150,19 +99,11 @@ def build_test_parser(parser):


def main():
if sys.argv[0].endswith("openfisca-run-test"):
sys.argv[0:1] = ["openfisca", "test"]
message = "The 'openfisca-run-test' command has been deprecated in favor of 'openfisca test' since version 25.0, and will be removed in the future."
warnings.warn(message, Warning)

parser = get_parser()

args, _ = parser.parse_known_args()

if args.command == "serve":
from openfisca_web_api.scripts.serve import main

return sys.exit(main(parser))
if args.command == "test":
from policyengine_core.scripts.run_test import main

Expand Down
6 changes: 5 additions & 1 deletion policyengine_core/taxbenefitsystems/tax_benefit_system.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from __future__ import annotations

from typing import Any, Dict, Optional, Sequence
from typing import Any, Dict, List, Optional, Sequence, Union

import copy
import glob
Expand All @@ -23,6 +23,7 @@
from policyengine_core.periods import Instant, Period
from policyengine_core.populations import Population, GroupPopulation
from policyengine_core.simulations import SimulationBuilder
from policyengine_core.tools.test_runner import run_tests
from policyengine_core.variables import Variable

log = logging.getLogger(__name__)
Expand Down Expand Up @@ -540,3 +541,6 @@ def entities_plural(self):

def entities_by_singular(self):
return {entity.key: entity for entity in self.entities}

def test(self, paths: str, verbose: bool = False) -> None:
run_tests(self, paths, options=dict(verbose=verbose))
7 changes: 5 additions & 2 deletions policyengine_core/tools/test_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
import pytest

from policyengine_core.tools import assert_near
from policyengine_core.simulation_builder import SimulationBuilder
from policyengine_core.simulations import SimulationBuilder
from policyengine_core.errors import SituationParsingError, VariableNotFound
from policyengine_core.warnings import LibYAMLWarning

Expand Down Expand Up @@ -87,8 +87,11 @@ def run_tests(tax_benefit_system, paths, options=None):
if options.get("verbose"):
argv.append("--verbose")

if isinstance(paths, str):
if not isinstance(paths, list):
paths = [paths]

if not isinstance(paths[0], str):
paths = [str(path) for path in paths]

return pytest.main(
[*argv, *paths] if True else paths,
Expand Down
Loading