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

Migrate household endpoints to new API structure #2038

Merged
merged 27 commits into from
Dec 20, 2024
Merged
Show file tree
Hide file tree
Changes from 23 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
43d6a96
feat: Initial stages of migration of household endpoints
anth-volk Nov 30, 2024
5dfc1b0
feat: Migrate post_household to new structure
anth-volk Nov 30, 2024
506d233
feat: Migrate update household endpoint
anth-volk Nov 30, 2024
ffb42ec
chore: Format
anth-volk Nov 30, 2024
0d3473e
fix: Fix minor bugs
anth-volk Nov 30, 2024
2129cda
fix: Modify validate_country to return 400 on country not found and u…
anth-volk Dec 2, 2024
6bd8420
feat: Refactor household endpoints to match new structure
anth-volk Dec 4, 2024
3e2a8a8
fix: Refactor fixtures
anth-volk Dec 4, 2024
a3a78fc
fix: Adjust test to handle added validation
anth-volk Dec 4, 2024
de96431
fix: Redo blueprint generation
anth-volk Dec 12, 2024
c0fdde9
fix: Remove erroneous print statement
anth-volk Dec 12, 2024
f789559
feat: Basic Werkzeug route validators
anth-volk Dec 12, 2024
f0d2a6e
feat: Error handling module and application to routes
anth-volk Dec 13, 2024
e7737c6
fix: Properly register error routes
anth-volk Dec 13, 2024
082d7cc
fix: Remove unused comment code
anth-volk Dec 13, 2024
6487058
fix: Redefine metadata route against convention
anth-volk Dec 13, 2024
c085d1d
feat: Return household JSON in household updater func and update test
anth-volk Dec 13, 2024
ca4ad9f
test: Fix some failing tests, add tests for error handlers
anth-volk Dec 13, 2024
7c24017
fix: Fix tracer tests
anth-volk Dec 13, 2024
c668a92
fix: Fix failing simulation analysis test
anth-volk Dec 13, 2024
a40e430
fix: Fix manual raising within try-catch blocks
anth-volk Dec 13, 2024
7cc72fe
fix: Fix final household tests
anth-volk Dec 14, 2024
adbfaa9
fix: Fix tests
anth-volk Dec 14, 2024
0d931a3
test: Fix tests
anth-volk Dec 18, 2024
2e70641
feat: Use error response builder
anth-volk Dec 18, 2024
0f39c1b
chore: Refactor against new error implementation
anth-volk Dec 18, 2024
3268c82
fix: Improve error handling
anth-volk Dec 18, 2024
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: minor
changes:
changed:
- Refactored household endpoints to match new API structure
27 changes: 8 additions & 19 deletions policyengine_api/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@
# from werkzeug.middleware.profiler import ProfilerMiddleware

# Endpoints
from policyengine_api.routes.error_routes import ErrorRoutes
from policyengine_api.routes.economy_routes import economy_bp
from policyengine_api.routes.household_routes import household_bp
from policyengine_api.routes.simulation_analysis_routes import (
simulation_analysis_bp,
)
Expand All @@ -22,9 +24,6 @@

from .endpoints import (
get_home,
get_household,
post_household,
update_household,
get_policy,
set_policy,
get_policy_search,
Expand Down Expand Up @@ -56,19 +55,13 @@

CORS(app)

ErrorRoutes.init_app(app)

app.route("/", methods=["GET"])(get_home)

app.register_blueprint(metadata_bp)

app.route("/<country_id>/household/<household_id>", methods=["GET"])(
get_household
)

app.route("/<country_id>/household", methods=["POST"])(post_household)

app.route("/<country_id>/household/<household_id>", methods=["PUT"])(
update_household
)
app.register_blueprint(household_bp)

app.route("/<country_id>/policy/<policy_id>", methods=["GET"])(get_policy)

Expand All @@ -94,12 +87,10 @@
)

# Routes for economy microsimulation
app.register_blueprint(economy_bp, url_prefix="/<country_id>/economy")
app.register_blueprint(economy_bp)

# Routes for AI analysis of economy microsim runs
app.register_blueprint(
simulation_analysis_bp, url_prefix="/<country_id>/simulation-analysis"
)
app.register_blueprint(simulation_analysis_bp)

app.route("/<country_id>/user-policy", methods=["POST"])(set_user_policy)

Expand All @@ -117,9 +108,7 @@

app.route("/simulations", methods=["GET"])(get_simulations)

app.register_blueprint(
tracer_analysis_bp, url_prefix="/<country_id>/tracer-analysis"
)
app.register_blueprint(tracer_analysis_bp)


@app.route("/liveness-check", methods=["GET"])
Expand Down
3 changes: 0 additions & 3 deletions policyengine_api/endpoints/__init__.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
from .home import get_home
from .household import (
get_household,
post_household,
get_household_under_policy,
get_calculate,
update_household,
)
from .policy import (
get_policy,
Expand Down
168 changes: 0 additions & 168 deletions policyengine_api/endpoints/household.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,174 +74,6 @@ def get_household_year(household):
return household_year


@validate_country
def get_household(country_id: str, household_id: str) -> dict:
"""Get a household's input data with a given ID.

Args:
country_id (str): The country ID.
household_id (str): The household ID.
"""

# Retrieve from the household table
row = database.query(
f"SELECT * FROM household WHERE id = ? AND country_id = ?",
(household_id, country_id),
).fetchone()

if row is not None:
household = dict(row)
household["household_json"] = json.loads(household["household_json"])
return dict(
status="ok",
message=None,
result=household,
)
else:
response_body = dict(
status="error",
message=f"Household #{household_id} not found.",
)
return Response(
json.dumps(response_body),
status=404,
mimetype="application/json",
)


@validate_country
def post_household(country_id: str) -> dict:
"""Set a household's input data.

Args:
country_id (str): The country ID.
"""

payload = request.json
label = payload.get("label")
household_json = payload.get("data")
household_hash = hash_object(household_json)
api_version = COUNTRY_PACKAGE_VERSIONS.get(country_id)

try:
database.query(
f"INSERT INTO household (country_id, household_json, household_hash, label, api_version) VALUES (?, ?, ?, ?, ?)",
(
country_id,
json.dumps(household_json),
household_hash,
label,
api_version,
),
)
except sqlalchemy.exc.IntegrityError:
pass

household_id = database.query(
f"SELECT id FROM household WHERE country_id = ? AND household_hash = ?",
(country_id, household_hash),
).fetchone()["id"]

response_body = dict(
status="ok",
message=None,
result=dict(
household_id=household_id,
),
)
return Response(
json.dumps(response_body),
status=201,
mimetype="application/json",
)


@validate_country
def update_household(country_id: str, household_id: str) -> Response:
"""
Update a household via UPDATE request

Args: country_id (str): The country ID
"""

# Fetch existing household first
try:
row = database.query(
f"SELECT * FROM household WHERE id = ? AND country_id = ?",
(household_id, country_id),
).fetchone()

if row is not None:
household = dict(row)
household["household_json"] = json.loads(
household["household_json"]
)
household["label"]
else:
response_body = dict(
status="error",
message=f"Household #{household_id} not found.",
)
return Response(
json.dumps(response_body),
status=404,
mimetype="application/json",
)
except Exception as e:
logging.exception(e)
response_body = dict(
status="error",
message=f"Error fetching household #{household_id} while updating: {e}",
)
return Response(
json.dumps(response_body),
status=500,
mimetype="application/json",
)

payload = request.json
label = payload.get("label") or household["label"]
household_json = payload.get("data") or household["household_json"]
household_hash = hash_object(household_json)
api_version = COUNTRY_PACKAGE_VERSIONS.get(country_id)

try:
database.query(
f"UPDATE household SET household_json = ?, household_hash = ?, label = ?, api_version = ? WHERE id = ?",
(
json.dumps(household_json),
household_hash,
label,
api_version,
household_id,
),
)
except Exception as e:
logging.exception(e)
response_body = dict(
status="error",
message=f"Error fetching household #{household_id} while updating: {e}",
)
return Response(
json.dumps(response_body),
status=500,
mimetype="application/json",
)

response_body = dict(
status="ok",
message=None,
result=dict(
household_id=household_id,
),
)
return Response(
json.dumps(response_body),
status=200,
mimetype="application/json",
)


@validate_country
def get_household_under_policy(
country_id: str, household_id: str, policy_id: str
Expand Down
16 changes: 6 additions & 10 deletions policyengine_api/routes/economy_routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,17 @@
from policyengine_api.constants import COUNTRY_PACKAGE_VERSIONS
from flask import request, Response
import json
from werkzeug.exceptions import InternalServerError
anth-volk marked this conversation as resolved.
Show resolved Hide resolved

economy_bp = Blueprint("economy", __name__)
economy_service = EconomyService()


@validate_country
@economy_bp.route("/<policy_id>/over/<baseline_policy_id>", methods=["GET"])
@economy_bp.route(
"/<country_id>/economy/<int:policy_id>/over/<int:baseline_policy_id>",
anth-volk marked this conversation as resolved.
Show resolved Hide resolved
methods=["GET"],
)
def get_economic_impact(country_id, policy_id, baseline_policy_id):

policy_id = int(policy_id or get_current_law_policy_id(country_id))
Expand Down Expand Up @@ -43,12 +47,4 @@ def get_economic_impact(country_id, policy_id, baseline_policy_id):
)
return result
except Exception as e:
return Response(
{
"status": "error",
"message": "An error occurred while calculating the economic impact. Details: "
+ str(e),
"result": None,
},
500,
)
raise InternalServerError(str(e))
Loading
Loading