Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
4 changes: 4 additions & 0 deletions changelog_entry.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
- bump: patch
changes:
fixed:
- Pass country and data versions to APIv2.
52 changes: 47 additions & 5 deletions policyengine_api/jobs/calculate_economy_simulation_job.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,16 @@
compute_difference,
)
from policyengine_core.simulations import Microsimulation
from policyengine_core.tools.hugging_face import download_huggingface_dataset
from policyengine_core.tools.hugging_face import (
download_huggingface_dataset,
)
from policyengine_api.utils.hugging_face import get_latest_commit_tag
import h5py

from policyengine_us import Microsimulation
from policyengine_uk import Microsimulation
import logging
import huggingface_hub

load_dotenv()

Expand All @@ -44,6 +48,34 @@
CPS = "hf://policyengine/policyengine-us-data/cps_2023.h5"
POOLED_CPS = "hf://policyengine/policyengine-us-data/pooled_3_year_cps_2023.h5"
Comment thread
anth-volk marked this conversation as resolved.

datasets = {
"uk": {
"enhanced_frs": ENHANCED_FRS,
"frs": FRS,
},
"us": {
"enhanced_cps": ENHANCED_CPS,
"cps": CPS,
"pooled_cps": POOLED_CPS,
},
}

us_dataset_version = get_latest_commit_tag(
repo_id="policyengine/policyengine-us-data",
repo_type="model",
)
uk_dataset_version = get_latest_commit_tag(
repo_id="policyengine/policyengine-uk-data-private",
repo_type="model",
)

for dataset in datasets["uk"]:
datasets["uk"][dataset] = f"{datasets['uk'][dataset]}@{uk_dataset_version}"

for dataset in datasets["us"]:
datasets["us"][dataset] = f"{datasets['us'][dataset]}@{us_dataset_version}"


check_against_api_v2 = (
os.environ.get("GOOGLE_APPLICATION_CREDENTIALS") is not None
)
Expand Down Expand Up @@ -189,6 +221,12 @@
time_period=time_period,
region=region,
dataset=dataset,
model_version=COUNTRY_PACKAGE_VERSIONS[country_id],
data_version=(
uk_dataset_version
if country_id == "uk"
else us_dataset_version
),
Comment on lines +225 to +229

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue, blocking: This code is HF-dependent and integrated into API v2

I really do not think we should be adding any code that hitches API v2 to Hugging Face. I think all of the following would be acceptable and a means of getting this over the line:

  1. Agree that we'll remove dataset version checks after fully migrating (is this a good idea?)
  2. Agree that we'll write code in a separate PR to check dataset versions within GCP before full migration (probably my preferred way forward)
  3. Switch to using GCP in API v1 now and write code to check dataset versions from GCP

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

resolved: We'll go down pathway 1, removing dataset version checks after fully migrating.

)

try:
Expand Down Expand Up @@ -454,7 +492,7 @@

simulation = CountryMicrosimulation(
reform=reform,
dataset=ENHANCED_FRS,
dataset=datasets["uk"]["enhanced_frs"],
)
simulation.default_calculation_period = time_period
if region != "uk":
Expand Down Expand Up @@ -514,7 +552,7 @@
if dataset in DATASETS:
print(f"Running simulation using {dataset} dataset")

sim_options["dataset"] = ENHANCED_CPS
sim_options["dataset"] = datasets["us"]["enhanced_cps"]

Check warning on line 555 in policyengine_api/jobs/calculate_economy_simulation_job.py

View check run for this annotation

Codecov / codecov/patch

policyengine_api/jobs/calculate_economy_simulation_job.py#L555

Added line #L555 was not covered by tests

# Handle region settings
if region != "us":
Expand All @@ -526,7 +564,7 @@
if "dataset" in sim_options:
filter_dataset = sim_options["dataset"]
else:
filter_dataset = POOLED_CPS
filter_dataset = datasets["us"]["pooled_cps"]

Check warning on line 567 in policyengine_api/jobs/calculate_economy_simulation_job.py

View check run for this annotation

Codecov / codecov/patch

policyengine_api/jobs/calculate_economy_simulation_job.py#L567

Added line #L567 was not covered by tests

# Run sim to filter by region
region_sim = Microsimulation(
Expand All @@ -547,7 +585,7 @@
sim_options["dataset"] = df[state_code == region.upper()]

if dataset == "default" and region == "us":
sim_options["dataset"] = CPS
sim_options["dataset"] = datasets["us"]["cps"]

Check warning on line 588 in policyengine_api/jobs/calculate_economy_simulation_job.py

View check run for this annotation

Codecov / codecov/patch

policyengine_api/jobs/calculate_economy_simulation_job.py#L588

Added line #L588 was not covered by tests

# Return completed simulation
return Microsimulation(**sim_options)
Expand Down Expand Up @@ -723,6 +761,8 @@
dataset: str,
time_period: str,
scope: Literal["macro", "household"] = "macro",
model_version: str | None = None,
data_version: str | None = None,
) -> dict[str, Any]:
"""
Set up the simulation options for the APIv2 job.
Expand All @@ -738,6 +778,8 @@
"data": self._setup_data(
dataset=dataset, country_id=country_id, region=region
),
"model_version": model_version,
"data_version": data_version,
}

def _setup_region(self, country_id: str, region: str) -> str:
Expand Down
122 changes: 122 additions & 0 deletions policyengine_api/utils/hugging_face.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
from huggingface_hub import (
hf_hub_download,
model_info,
ModelInfo,
HfApi,
)
from huggingface_hub.errors import RepositoryNotFoundError
from getpass import getpass
import os
import warnings
import traceback

with warnings.catch_warnings():
warnings.simplefilter("ignore")


def get_latest_commit_tag(repo_id, repo_type="model"):
"""
Get the tag associated with the latest commit in a HF repo.
Returns the tag name or None if no tag is associated.
"""
api = HfApi()

is_repo_private = check_is_repo_private(repo_id)

authentication_token: str = None
if is_repo_private:
authentication_token: str = get_or_prompt_hf_token()

# Get list of commits
commits = api.list_repo_commits(
repo_id=repo_id, repo_type=repo_type, token=authentication_token
)

if not commits:
return None

Check warning on line 36 in policyengine_api/utils/hugging_face.py

View check run for this annotation

Codecov / codecov/patch

policyengine_api/utils/hugging_face.py#L36

Added line #L36 was not covered by tests

latest_commit = commits[0] # Most recent commit is first

# Get all tags in the repository
tags = api.list_repo_refs(
repo_id=repo_id, repo_type=repo_type, token=authentication_token
).tags

# Find tag that points to the latest commit
for tag in tags:
if tag.target_commit == latest_commit.commit_id:
return tag.ref.replace("refs/tags/", "")

return None

Check warning on line 50 in policyengine_api/utils/hugging_face.py

View check run for this annotation

Codecov / codecov/patch

policyengine_api/utils/hugging_face.py#L50

Added line #L50 was not covered by tests


def check_is_repo_private(repo: str) -> bool:
"""
Check if a Hugging Face repository is private.

Args:
repo (str): The Hugging Face repo name, in format "{org}/{repo}".

Returns:
bool: True if the repo is private, False otherwise.
"""
try:
fetched_model_info: ModelInfo = model_info(repo)
return fetched_model_info.private
except RepositoryNotFoundError:
return True # If repo not found, assume it's private
except Exception as e:
raise Exception(

Check warning on line 69 in policyengine_api/utils/hugging_face.py

View check run for this annotation

Codecov / codecov/patch

policyengine_api/utils/hugging_face.py#L68-L69

Added lines #L68 - L69 were not covered by tests
f"Unable to check if repo {repo} is private. The full error is {traceback.format_exc()}"
)


def download_huggingface_dataset(
repo: str,
repo_filename: str,
version: str = None,
local_dir: str | None = None,
):
"""
Download a dataset from the Hugging Face Hub.

Args:
repo (str): The Hugging Face repo name, in format "{org}/{repo}".
repo_filename (str): The filename of the dataset.
version (str, optional): The version of the dataset. Defaults to None.
local_dir (str, optional): The local directory to save the dataset to. Defaults to None.
"""
is_repo_private = check_is_repo_private(repo)

Check warning on line 89 in policyengine_api/utils/hugging_face.py

View check run for this annotation

Codecov / codecov/patch

policyengine_api/utils/hugging_face.py#L89

Added line #L89 was not covered by tests

authentication_token: str = None

Check warning on line 91 in policyengine_api/utils/hugging_face.py

View check run for this annotation

Codecov / codecov/patch

policyengine_api/utils/hugging_face.py#L91

Added line #L91 was not covered by tests
if is_repo_private:
authentication_token: str = get_or_prompt_hf_token()

Check warning on line 93 in policyengine_api/utils/hugging_face.py

View check run for this annotation

Codecov / codecov/patch

policyengine_api/utils/hugging_face.py#L93

Added line #L93 was not covered by tests

return hf_hub_download(

Check warning on line 95 in policyengine_api/utils/hugging_face.py

View check run for this annotation

Codecov / codecov/patch

policyengine_api/utils/hugging_face.py#L95

Added line #L95 was not covered by tests
repo_id=repo,
repo_type="model",
filename=repo_filename,
revision=version,
token=authentication_token,
local_dir=local_dir,
)


def get_or_prompt_hf_token() -> str:
"""
Either get the Hugging Face token from the environment,
or prompt the user for it and store it in the environment.

Returns:
str: The Hugging Face token.
"""

token = os.environ.get("HUGGING_FACE_TOKEN")
if token is None:
token = getpass(

Check warning on line 116 in policyengine_api/utils/hugging_face.py

View check run for this annotation

Codecov / codecov/patch

policyengine_api/utils/hugging_face.py#L116

Added line #L116 was not covered by tests
"Enter your Hugging Face token (or set HUGGING_FACE_TOKEN environment variable): "
)
# Optionally store in env for subsequent calls in same session
os.environ["HUGGING_FACE_TOKEN"] = token

Check warning on line 120 in policyengine_api/utils/hugging_face.py

View check run for this annotation

Codecov / codecov/patch

policyengine_api/utils/hugging_face.py#L120

Added line #L120 was not covered by tests

return token
Loading