Skip to content

Commit

Permalink
Merge pull request #13 from masterismail/issue9
Browse files Browse the repository at this point in the history
Added GH workflow files for auto deployment of docs and to test deployment in PR
  • Loading branch information
nikhilwoodruff authored Jun 27, 2024
2 parents 4c39f10 + f4ff6d4 commit 7dde56e
Show file tree
Hide file tree
Showing 6 changed files with 254 additions and 65 deletions.
33 changes: 33 additions & 0 deletions .github/workflows/pr.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
name: Pull request
on:
pull_request:
branches: [ main ]

jobs:
Test:
runs-on: ubuntu-latest
steps:
- name: Checkout repo
uses: actions/checkout@v2

- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: 3.9

- name: install dependencies
run: |
pip install jupyter-book
pip install furo
- name: Generate documentation
run: make docs

- name: Check documentation build
run: |
if [ -d "docs/_build/html" ]; then
echo "Documentation built successfully"
else
echo "Documentation build failed"
exit 1
fi
34 changes: 34 additions & 0 deletions .github/workflows/push.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
name: Push
on:
push:
branches: [ main ]

jobs:
Deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout repo
uses: actions/checkout@v2

- name: Setup Python
uses: actions/setup-python@v2
with:
python-version: 3.9

- name: install dependencies
run: |
pip install jupyter-book
pip install furo
pip install sphinx-argparse
- name: Generate documentation
run: make docs

- name: Deploy documentation
uses: JamesIves/github-pages-deploy-action@releases/v3
with:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
BRANCH: gh-pages
FOLDER: docs/_build/html
4 changes: 4 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
.PHONY: docs

docs:
jupyter-book build docs/
14 changes: 7 additions & 7 deletions docs/_config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,10 @@ sphinx:
pygments_style: default
html_css_files:
- style.css
extra_extensions:
- sphinx.ext.autodoc
- sphinxarg.ext
- sphinx.ext.viewcode
- sphinx.ext.napoleon
- sphinx_math_dollar
- sphinx.ext.mathjax
# extra_extensions:
# - sphinx.ext.autodoc
# - sphinxarg.ext
# - sphinx.ext.viewcode
# - sphinx.ext.napoleon
# - sphinx_math_dollar
# - sphinx.ext.mathjax
107 changes: 49 additions & 58 deletions policyengine/economic_impact/economic_impact.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,28 @@
from policyengine_core.reforms import Reform
from .inequality_impact.inequality_impact import GiniCalculator, Top10PctShareCalculator, Top1PctShareCalculator
from typing import Dict

class EconomicImpact:
def __init__(self, reform, country):
"""
A class to calculate economic impact metrics based on different reforms and countries.
Attributes:
reform (dict): Dictionary representing the reform parameters.
country (str): Country code in lowercase ('uk' or 'us').
Microsimulation (type): Class representing the microsimulation engine based on country.
baseline (Microsimulation): Instance of Microsimulation for baseline scenario.
reformed (Microsimulation): Instance of Microsimulation for reformed scenario based on given reform.
metric_calculators (Dict[str, BaseMetricCalculator]): Dictionary mapping metric names to metric calculators.
"""

def __init__(self, reform: dict, country: str) -> None:
"""
Initialize EconomicImpact with reform parameters and country code.
Args:
reform (dict): Dictionary representing the reform parameters.
country (str): Country code in lowercase ('uk' or 'us').
"""
self.reform = reform
self.country = country.lower()
self.Microsimulation = self._get_simulation_class()
Expand All @@ -11,73 +32,43 @@ def __init__(self, reform, country):
self.reformed = self.Microsimulation(reform=Reform.from_dict(self.reform, country_id=self.country))

# Set up metric calculators
self.metric_calculators = {
"inequality/gini": self.calculate_gini,
"inequality/top_1_pct_share": self.calculate_top_1_pct_share,
"inequality/top_10_pct_share": self.calculate_top_10_pct_share,
# We willadd more metrics here as needed
self.metric_calculators: Dict[str, object] = {
"inequality/gini": GiniCalculator(self.baseline, self.reformed),
"inequality/top_1_pct_share": Top1PctShareCalculator(self.baseline, self.reformed),
"inequality/top_10_pct_share": Top10PctShareCalculator(self.baseline, self.reformed),
}
# to get micrsosim based on country
def _get_simulation_class(self):

def _get_simulation_class(self) -> type:
"""
Get the appropriate Microsimulation class based on the country code.
Returns:
type: Microsimulation class based on the country.
Raises:
ValueError: If the country is not supported ('uk' or 'us').
"""
if self.country == "uk":
from policyengine_uk import Microsimulation
elif self.country == "us":
from policyengine_us import Microsimulation
else:
raise ValueError(f"Unsupported country: {self.country}")
return Microsimulation

def calculate_baseline(self, variable, period=2024, map_to="person"):
self.baseline_person = self.baseline.calculate(variable, period=period, map_to=map_to)

def calculate_reformed(self, variable, period=2024, map_to="person"):
self.reformed_person = self.reformed.calculate(variable, period=period, map_to=map_to)

def calculate_gini(self):
self.calculate_baseline("household_net_income")
self.calculate_reformed("household_net_income")

baseline_value = self.baseline_person.gini()
reformed_value = self.reformed_person.gini()
change_value = reformed_value - baseline_value

return {
"baseline": baseline_value,
"reform": reformed_value,
"change": change_value
}

def calculate_top_1_pct_share(self):
self.calculate_baseline("household_net_income")
self.calculate_reformed("household_net_income")

baseline_value = self.baseline_person.top_1_pct_share()
reformed_value = self.reformed_person.top_1_pct_share()
change_value = reformed_value - baseline_value

def calculate(self, metric: str) -> dict:
"""
Calculate the specified economic impact metric.
return {
"baseline": baseline_value,
"reform": reformed_value,
"change": change_value
}

def calculate_top_10_pct_share(self):
self.calculate_baseline("household_net_income")
self.calculate_reformed("household_net_income")
Args:
metric (str): Name of the metric to calculate ("inequality/gini", "inequality/top_1_pct_share", "inequality/top_10_pct_share").
baseline_value = self.baseline_person.top_10_pct_share()
reformed_value = self.reformed_person.top_10_pct_share()
change_value = reformed_value - baseline_value
Returns:
dict: Dictionary containing metric values ("baseline", "reform", "change").
return {
"baseline": baseline_value,
"reform": reformed_value,
"change": change_value
}

# We can add more methods for other metrics as needed

def calculate(self, metric):
Raises:
ValueError: If the metric is unknown.
"""
if metric not in self.metric_calculators:
raise ValueError(f"Unknown metric: {metric}")
return self.metric_calculators[metric]()
return self.metric_calculators[metric].calculate()
127 changes: 127 additions & 0 deletions policyengine/economic_impact/inequality_impact/inequality_impact.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
from typing import Union

class BaseMetricCalculator:
"""
Base class for calculating metrics based on baseline and reformed data.
Attributes:
baseline (object): Object representing baseline data.
reformed (object): Object representing reformed data.
"""

def __init__(self, baseline: object, reformed: object) -> None:
"""
Initialize with baseline and reformed data objects.
Args:
baseline (object): Object representing baseline data.
reformed (object): Object representing reformed data.
"""
self.baseline = baseline
self.reformed = reformed

def calculate_baseline(self, variable: str, period: int = 2024, map_to: str = "person") -> Union[float, int]:
"""
Calculate baseline metric value.
Args:
variable (str): Variable to calculate.
period (int): Year or period for calculation (default: 2024).
map_to (str): Mapping type (default: "person").
Returns:
Union[float, int]: Calculated metric value.
"""
return self.baseline.calculate(variable, period=period, map_to=map_to)

def calculate_reformed(self, variable: str, period: int = 2024, map_to: str = "person") -> Union[float, int]:
"""
Calculate reformed metric value.
Args:
variable (str): Variable to calculate.
period (int): Year or period for calculation (default: 2024).
map_to (str): Mapping type (default: "person").
Returns:
Union[float, int]: Calculated metric value.
"""
return self.reformed.calculate(variable, period=period, map_to=map_to)

class GiniCalculator(BaseMetricCalculator):
"""
Calculate Gini coefficient metrics based on baseline and reformed data.
Inherits from BaseMetricCalculator.
"""

def calculate(self) -> dict:
"""
Calculate Gini coefficient metrics.
Returns:
dict: Dictionary containing "baseline", "reform", and "change" values.
"""
baseline_person = self.calculate_baseline("household_net_income")
reformed_person = self.calculate_reformed("household_net_income")

baseline_value = baseline_person.gini()
reformed_value = reformed_person.gini()
change_value = reformed_value - baseline_value

return {
"baseline": baseline_value,
"reform": reformed_value,
"change": change_value
}

class Top1PctShareCalculator(BaseMetricCalculator):
"""
Calculate top 1% income share metrics based on baseline and reformed data.
Inherits from BaseMetricCalculator.
"""

def calculate(self) -> dict:
"""
Calculate top 1% income share metrics.
Returns:
dict: Dictionary containing "baseline", "reform", and "change" values.
"""
baseline_person = self.calculate_baseline("household_net_income")
reformed_person = self.calculate_reformed("household_net_income")

baseline_value = baseline_person.top_1_pct_share()
reformed_value = reformed_person.top_1_pct_share()
change_value = reformed_value - baseline_value

return {
"baseline": baseline_value,
"reform": reformed_value,
"change": change_value
}

class Top10PctShareCalculator(BaseMetricCalculator):
"""
Calculate top 10% income share metrics based on baseline and reformed data.
Inherits from BaseMetricCalculator.
"""

def calculate(self) -> dict:
"""
Calculate top 10% income share metrics.
Returns:
dict: Dictionary containing "baseline", "reform", and "change" values.
"""
baseline_person = self.calculate_baseline("household_net_income")
reformed_person = self.calculate_reformed("household_net_income")

baseline_value = baseline_person.top_10_pct_share()
reformed_value = reformed_person.top_10_pct_share()
change_value = reformed_value - baseline_value

return {
"baseline": baseline_value,
"reform": reformed_value,
"change": change_value
}

0 comments on commit 7dde56e

Please sign in to comment.