From 1b7c56ac3e6adea28009ad6c8f277604657abda3 Mon Sep 17 00:00:00 2001 From: Josh Burman Date: Sat, 4 Sep 2021 23:21:44 -0400 Subject: [PATCH 01/11] added solve endpoint, as well as types for request object usaged as well as validation --- app/main.py | 66 ++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 65 insertions(+), 1 deletion(-) diff --git a/app/main.py b/app/main.py index 18fcd45..83c3a4e 100644 --- a/app/main.py +++ b/app/main.py @@ -1,7 +1,67 @@ from fastapi import FastAPI, __version__ +from pydantic import BaseModel +from typing import Set, List, Optional, Dict app = FastAPI() +class MetaData(BaseModel): + key: str + value: str + field_id: Optional[int] + option_id: Optional[int] + +class Item(BaseModel): + id: int + metadata: List[MetaData] + +class Constraint(BaseModel): + key: str + value: str + field_id: Optional[int] + option_id: Optional[int] + minimum: int + maximum: int + +class IRTModel(BaseModel): + a_param: float + b_param: float + c_param: float + model: str + +class Targets(BaseModel): + neg_2_5: int + neg_1_5: int + neg_0_5: int + _0_5: int + _1: int + +class ObjectiveFunction(BaseModel): + # minimizing tif/tcc target value is only option currently + # as we add more we can build this out to be more dynamic + # likely with models representing each objective function type + tif_targets: Targets + tcc_targets: Targets + weight: Dict = {'tif': 1, 'tcc': 1} + +class AdvancedOptions(BaseModel): + # will supported currently + linearity_check: bool + show_progress: bool + max_solution_time: Optional[int] + brand_bound_tolerance: Optional[float] + max_forms: Optional[int] + precision: Optional[float] + extra_param_range: Optional[List[Dict]] + +class SolverContent(BaseModel): + items: List[Item] + constraints: List[Constraint] + irt_model: IRTModel + objective_fuction: ObjectiveFunction + total_form_items: int + advanced_options: Optional[List[AdvancedOptions]] + engine: str + @app.get("/") async def root(): return {"message": "Welcome to Measures LOFT solver service. v0.1"} @@ -13,10 +73,14 @@ async def health(): "git_repo": "https://github.com/yardstick/measure-solver", "server": "OK", "fastapi version": __version__, - "app version": "0.1.0" + "app version": "0.1.1" } return content @app.get('/readycheck') async def ready(): return 'OK' # just means we're on air + +@app.post('/solve/') +async def solve(solver_content: SolverContent): + return solver_content From c4fdd68af24d824f31d15cdd40ac1da20ecb6dbc Mon Sep 17 00:00:00 2001 From: Josh Burman Date: Sat, 4 Sep 2021 23:50:22 -0400 Subject: [PATCH 02/11] moved models to separate folder --- app/main.py | 60 ++------------------------------ app/models/advanced_options.py | 12 +++++++ app/models/constraint.py | 10 ++++++ app/models/irt_model.py | 7 ++++ app/models/item.py | 8 +++++ app/models/metadata.py | 8 +++++ app/models/objective_function.py | 12 +++++++ app/models/solver_content.py | 17 +++++++++ app/models/targets.py | 8 +++++ 9 files changed, 84 insertions(+), 58 deletions(-) create mode 100644 app/models/advanced_options.py create mode 100644 app/models/constraint.py create mode 100644 app/models/irt_model.py create mode 100644 app/models/item.py create mode 100644 app/models/metadata.py create mode 100644 app/models/objective_function.py create mode 100644 app/models/solver_content.py create mode 100644 app/models/targets.py diff --git a/app/main.py b/app/main.py index 83c3a4e..e39822c 100644 --- a/app/main.py +++ b/app/main.py @@ -2,66 +2,10 @@ from fastapi import FastAPI, __version__ from pydantic import BaseModel from typing import Set, List, Optional, Dict +from models.solver_content import SolverContent + app = FastAPI() -class MetaData(BaseModel): - key: str - value: str - field_id: Optional[int] - option_id: Optional[int] - -class Item(BaseModel): - id: int - metadata: List[MetaData] - -class Constraint(BaseModel): - key: str - value: str - field_id: Optional[int] - option_id: Optional[int] - minimum: int - maximum: int - -class IRTModel(BaseModel): - a_param: float - b_param: float - c_param: float - model: str - -class Targets(BaseModel): - neg_2_5: int - neg_1_5: int - neg_0_5: int - _0_5: int - _1: int - -class ObjectiveFunction(BaseModel): - # minimizing tif/tcc target value is only option currently - # as we add more we can build this out to be more dynamic - # likely with models representing each objective function type - tif_targets: Targets - tcc_targets: Targets - weight: Dict = {'tif': 1, 'tcc': 1} - -class AdvancedOptions(BaseModel): - # will supported currently - linearity_check: bool - show_progress: bool - max_solution_time: Optional[int] - brand_bound_tolerance: Optional[float] - max_forms: Optional[int] - precision: Optional[float] - extra_param_range: Optional[List[Dict]] - -class SolverContent(BaseModel): - items: List[Item] - constraints: List[Constraint] - irt_model: IRTModel - objective_fuction: ObjectiveFunction - total_form_items: int - advanced_options: Optional[List[AdvancedOptions]] - engine: str - @app.get("/") async def root(): return {"message": "Welcome to Measures LOFT solver service. v0.1"} diff --git a/app/models/advanced_options.py b/app/models/advanced_options.py new file mode 100644 index 0000000..9df8cac --- /dev/null +++ b/app/models/advanced_options.py @@ -0,0 +1,12 @@ +from pydantic import BaseModel +from typing import List, Optional, Dict + +class AdvancedOptions(BaseModel): + # will supported currently + linearity_check: bool + show_progress: bool + max_solution_time: Optional[int] + brand_bound_tolerance: Optional[float] + max_forms: Optional[int] + precision: Optional[float] + extra_param_range: Optional[List[Dict]] diff --git a/app/models/constraint.py b/app/models/constraint.py new file mode 100644 index 0000000..df98e20 --- /dev/null +++ b/app/models/constraint.py @@ -0,0 +1,10 @@ +from pydantic import BaseModel +from typing import Optional + +class Constraint(BaseModel): + key: str + value: str + field_id: Optional[int] + option_id: Optional[int] + minimum: int + maximum: int diff --git a/app/models/irt_model.py b/app/models/irt_model.py new file mode 100644 index 0000000..4751ba9 --- /dev/null +++ b/app/models/irt_model.py @@ -0,0 +1,7 @@ +from pydantic import BaseModel + +class IRTModel(BaseModel): + a_param: float + b_param: float + c_param: float + model: str diff --git a/app/models/item.py b/app/models/item.py new file mode 100644 index 0000000..ede564b --- /dev/null +++ b/app/models/item.py @@ -0,0 +1,8 @@ +from pydantic import BaseModel +from typing import List + +from models.metadata import MetaData + +class Item(BaseModel): + id: int + metadata: List[MetaData] diff --git a/app/models/metadata.py b/app/models/metadata.py new file mode 100644 index 0000000..34866ca --- /dev/null +++ b/app/models/metadata.py @@ -0,0 +1,8 @@ +from pydantic import BaseModel +from typing import Optional + +class MetaData(BaseModel): + key: str + value: str + field_id: Optional[int] + option_id: Optional[int] diff --git a/app/models/objective_function.py b/app/models/objective_function.py new file mode 100644 index 0000000..75b1d88 --- /dev/null +++ b/app/models/objective_function.py @@ -0,0 +1,12 @@ +from pydantic import BaseModel +from typing import Dict + +from models.targets import Targets + +class ObjectiveFunction(BaseModel): + # minimizing tif/tcc target value is only option currently + # as we add more we can build this out to be more dynamic + # likely with models representing each objective function type + tif_targets: Targets + tcc_targets: Targets + weight: Dict = {'tif': 1, 'tcc': 1} diff --git a/app/models/solver_content.py b/app/models/solver_content.py new file mode 100644 index 0000000..89586d1 --- /dev/null +++ b/app/models/solver_content.py @@ -0,0 +1,17 @@ +from pydantic import BaseModel +from typing import List, Optional + +from models.item import Item +from models.constraint import Constraint +from models.irt_model import IRTModel +from models.objective_function import ObjectiveFunction +from models.advanced_options import AdvancedOptions + +class SolverContent(BaseModel): + items: List[Item] + constraints: List[Constraint] + irt_model: IRTModel + objective_fuction: ObjectiveFunction + total_form_items: int + advanced_options: Optional[List[AdvancedOptions]] + engine: str diff --git a/app/models/targets.py b/app/models/targets.py new file mode 100644 index 0000000..c50f39f --- /dev/null +++ b/app/models/targets.py @@ -0,0 +1,8 @@ +from pydantic import BaseModel + +class Targets(BaseModel): + neg_2_5: int + neg_1_5: int + neg_0_5: int + _0_5: int + _1: int From 8ac5d5efdbd55c61a83d733aaadf0de541263ab8 Mon Sep 17 00:00:00 2001 From: Josh Burman Date: Tue, 7 Sep 2021 15:10:31 -0400 Subject: [PATCH 03/11] added token to request object, as well as created response object --- app/main.py | 10 +++++++--- app/models/form.py | 7 +++++++ app/models/{solver_content.py => solve_request.py} | 3 ++- app/models/solve_response.py | 8 ++++++++ app/models/targets.py | 10 +++++----- 5 files changed, 29 insertions(+), 9 deletions(-) create mode 100644 app/models/form.py rename app/models/{solver_content.py => solve_request.py} (91%) create mode 100644 app/models/solve_response.py diff --git a/app/main.py b/app/main.py index e39822c..082beb3 100644 --- a/app/main.py +++ b/app/main.py @@ -1,8 +1,11 @@ from fastapi import FastAPI, __version__ from pydantic import BaseModel from typing import Set, List, Optional, Dict +from random import randint -from models.solver_content import SolverContent +from models.solve_request import SolveRequest +from models.solve_response import SolveResponse +from models.form import Form app = FastAPI() @@ -26,5 +29,6 @@ async def ready(): return 'OK' # just means we're on air @app.post('/solve/') -async def solve(solver_content: SolverContent): - return solver_content +async def solve(solve_request: SolveRequest): + response = SolveResponse(response_id=randint(100,5000), forms=[Form(items=solve_request.items)]) + return response diff --git a/app/models/form.py b/app/models/form.py new file mode 100644 index 0000000..4963daa --- /dev/null +++ b/app/models/form.py @@ -0,0 +1,7 @@ +from pydantic import BaseModel +from typing import List + +from models.item import Item + +class Form(BaseModel): + items: List[Item] diff --git a/app/models/solver_content.py b/app/models/solve_request.py similarity index 91% rename from app/models/solver_content.py rename to app/models/solve_request.py index 89586d1..df213bb 100644 --- a/app/models/solver_content.py +++ b/app/models/solve_request.py @@ -7,7 +7,8 @@ from models.irt_model import IRTModel from models.objective_function import ObjectiveFunction from models.advanced_options import AdvancedOptions -class SolverContent(BaseModel): +class SolveRequest(BaseModel): + token: str items: List[Item] constraints: List[Constraint] irt_model: IRTModel diff --git a/app/models/solve_response.py b/app/models/solve_response.py new file mode 100644 index 0000000..dcdc3d7 --- /dev/null +++ b/app/models/solve_response.py @@ -0,0 +1,8 @@ +from pydantic import BaseModel +from typing import List, Optional + +from models.form import Form + +class SolveResponse(BaseModel): + response_id: int + forms: List[Form] diff --git a/app/models/targets.py b/app/models/targets.py index c50f39f..7b56132 100644 --- a/app/models/targets.py +++ b/app/models/targets.py @@ -1,8 +1,8 @@ from pydantic import BaseModel class Targets(BaseModel): - neg_2_5: int - neg_1_5: int - neg_0_5: int - _0_5: int - _1: int + n_2_5: int + n_1_5: int + n_0_5: int + p_0_5: int + p_1: int From 4a8952fae3d42c0e5b2438ad8e9def30e374f04a Mon Sep 17 00:00:00 2001 From: Josh Burman Date: Wed, 8 Sep 2021 11:25:55 -0400 Subject: [PATCH 04/11] change tif/tcc targets to be more variable --- app/main.py | 7 ++++++- app/models/form.py | 2 +- app/models/objective_function.py | 8 ++++---- app/models/targets.py | 8 -------- 4 files changed, 11 insertions(+), 14 deletions(-) delete mode 100644 app/models/targets.py diff --git a/app/main.py b/app/main.py index 082beb3..436bb93 100644 --- a/app/main.py +++ b/app/main.py @@ -30,5 +30,10 @@ async def ready(): @app.post('/solve/') async def solve(solve_request: SolveRequest): - response = SolveResponse(response_id=randint(100,5000), forms=[Form(items=solve_request.items)]) + response = SolveResponse( + response_id=randint(100,5000), + forms=[Form( + items=[item.id for item in solve_request.items] + )] + ) return response diff --git a/app/models/form.py b/app/models/form.py index 4963daa..5ff5414 100644 --- a/app/models/form.py +++ b/app/models/form.py @@ -4,4 +4,4 @@ from typing import List from models.item import Item class Form(BaseModel): - items: List[Item] + items: List[int] diff --git a/app/models/objective_function.py b/app/models/objective_function.py index 75b1d88..05fa461 100644 --- a/app/models/objective_function.py +++ b/app/models/objective_function.py @@ -1,12 +1,12 @@ from pydantic import BaseModel -from typing import Dict +from typing import Dict, List -from models.targets import Targets +from models.target import Target class ObjectiveFunction(BaseModel): # minimizing tif/tcc target value is only option currently # as we add more we can build this out to be more dynamic # likely with models representing each objective function type - tif_targets: Targets - tcc_targets: Targets + tif_targets: List[Target] + tcc_targets: List[Target] weight: Dict = {'tif': 1, 'tcc': 1} diff --git a/app/models/targets.py b/app/models/targets.py deleted file mode 100644 index 7b56132..0000000 --- a/app/models/targets.py +++ /dev/null @@ -1,8 +0,0 @@ -from pydantic import BaseModel - -class Targets(BaseModel): - n_2_5: int - n_1_5: int - n_0_5: int - p_0_5: int - p_1: int From 2906141a8f2f622251e7cb44991349df5f4426c6 Mon Sep 17 00:00:00 2001 From: Josh Burman Date: Wed, 8 Sep 2021 11:26:16 -0400 Subject: [PATCH 05/11] renamed targets to target --- app/models/target.py | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 app/models/target.py diff --git a/app/models/target.py b/app/models/target.py new file mode 100644 index 0000000..1383208 --- /dev/null +++ b/app/models/target.py @@ -0,0 +1,5 @@ +from pydantic import BaseModel + +class Target(BaseModel): + theta: float + value: float From 43d2088f135d1dc3ec05de49bb8d94044708ca8f Mon Sep 17 00:00:00 2001 From: Josh Burman Date: Fri, 10 Sep 2021 14:04:21 -0400 Subject: [PATCH 06/11] changed some model names to more accurately reflect the concerns were working with --- app/main.py | 10 +++++----- app/models/{solve_response.py => solution.py} | 2 +- app/models/{solve_request.py => solver_run.py} | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) rename app/models/{solve_response.py => solution.py} (80%) rename app/models/{solve_request.py => solver_run.py} (94%) diff --git a/app/main.py b/app/main.py index 436bb93..8ec950c 100644 --- a/app/main.py +++ b/app/main.py @@ -3,8 +3,8 @@ from pydantic import BaseModel from typing import Set, List, Optional, Dict from random import randint -from models.solve_request import SolveRequest -from models.solve_response import SolveResponse +from models.solver_run import SolverRun +from models.solution import Solution from models.form import Form app = FastAPI() @@ -29,11 +29,11 @@ async def ready(): return 'OK' # just means we're on air @app.post('/solve/') -async def solve(solve_request: SolveRequest): - response = SolveResponse( +async def solve(solver_run: SolverRun): + response = Solution( response_id=randint(100,5000), forms=[Form( - items=[item.id for item in solve_request.items] + items=[item.id for item in solver_run.items] )] ) return response diff --git a/app/models/solve_response.py b/app/models/solution.py similarity index 80% rename from app/models/solve_response.py rename to app/models/solution.py index dcdc3d7..1681439 100644 --- a/app/models/solve_response.py +++ b/app/models/solution.py @@ -3,6 +3,6 @@ from typing import List, Optional from models.form import Form -class SolveResponse(BaseModel): +class Solution(BaseModel): response_id: int forms: List[Form] diff --git a/app/models/solve_request.py b/app/models/solver_run.py similarity index 94% rename from app/models/solve_request.py rename to app/models/solver_run.py index df213bb..43b223c 100644 --- a/app/models/solve_request.py +++ b/app/models/solver_run.py @@ -7,7 +7,7 @@ from models.irt_model import IRTModel from models.objective_function import ObjectiveFunction from models.advanced_options import AdvancedOptions -class SolveRequest(BaseModel): +class SolverRun(BaseModel): token: str items: List[Item] constraints: List[Constraint] From b472af60205e6fce10eaa224c83a324cd162d690 Mon Sep 17 00:00:00 2001 From: Josh Burman Date: Tue, 14 Sep 2021 11:46:51 -0400 Subject: [PATCH 07/11] some model name changing, and constains model improvement --- app/main.py | 2 +- app/models/constraint.py | 7 +++---- app/models/item.py | 4 ++-- app/models/metadata.py | 8 -------- app/models/objective_function.py | 1 + app/models/solver_run.py | 3 +-- 6 files changed, 8 insertions(+), 17 deletions(-) delete mode 100644 app/models/metadata.py diff --git a/app/main.py b/app/main.py index 8ec950c..1fad27b 100644 --- a/app/main.py +++ b/app/main.py @@ -36,4 +36,4 @@ async def solve(solver_run: SolverRun): items=[item.id for item in solver_run.items] )] ) - return response + return solver_run diff --git a/app/models/constraint.py b/app/models/constraint.py index df98e20..84eb1b2 100644 --- a/app/models/constraint.py +++ b/app/models/constraint.py @@ -1,10 +1,9 @@ from pydantic import BaseModel from typing import Optional +from models.attribute import Attribute + class Constraint(BaseModel): - key: str - value: str - field_id: Optional[int] - option_id: Optional[int] + reference_attribute: Attribute minimum: int maximum: int diff --git a/app/models/item.py b/app/models/item.py index ede564b..9a6f9d7 100644 --- a/app/models/item.py +++ b/app/models/item.py @@ -1,8 +1,8 @@ from pydantic import BaseModel from typing import List -from models.metadata import MetaData +from models.attribute import Attribute class Item(BaseModel): id: int - metadata: List[MetaData] + attribute: List[Attribute] diff --git a/app/models/metadata.py b/app/models/metadata.py deleted file mode 100644 index 34866ca..0000000 --- a/app/models/metadata.py +++ /dev/null @@ -1,8 +0,0 @@ -from pydantic import BaseModel -from typing import Optional - -class MetaData(BaseModel): - key: str - value: str - field_id: Optional[int] - option_id: Optional[int] diff --git a/app/models/objective_function.py b/app/models/objective_function.py index 05fa461..0c7835d 100644 --- a/app/models/objective_function.py +++ b/app/models/objective_function.py @@ -9,4 +9,5 @@ class ObjectiveFunction(BaseModel): # likely with models representing each objective function type tif_targets: List[Target] tcc_targets: List[Target] + objective: "minimize" weight: Dict = {'tif': 1, 'tcc': 1} diff --git a/app/models/solver_run.py b/app/models/solver_run.py index 43b223c..5dea7f0 100644 --- a/app/models/solver_run.py +++ b/app/models/solver_run.py @@ -8,11 +8,10 @@ from models.objective_function import ObjectiveFunction from models.advanced_options import AdvancedOptions class SolverRun(BaseModel): - token: str items: List[Item] constraints: List[Constraint] irt_model: IRTModel objective_fuction: ObjectiveFunction total_form_items: int - advanced_options: Optional[List[AdvancedOptions]] + advanced_options: Optional[AdvancedOptions] engine: str From 03e042e91ff30516efb4268ce4f01a3c0ad4bb81 Mon Sep 17 00:00:00 2001 From: Josh Burman Date: Tue, 14 Sep 2021 11:48:25 -0400 Subject: [PATCH 08/11] attribute instead of metadata --- app/models/attribute.py | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 app/models/attribute.py diff --git a/app/models/attribute.py b/app/models/attribute.py new file mode 100644 index 0000000..0219ea6 --- /dev/null +++ b/app/models/attribute.py @@ -0,0 +1,7 @@ +from pydantic import BaseModel +from typing import Optional + +class Attribute(BaseModel): + value: str + type: Optional[str] + id: str From ba62bc4dac5bae845e16aa41a20279a2e248c106 Mon Sep 17 00:00:00 2001 From: Josh Burman Date: Tue, 14 Sep 2021 14:10:38 -0400 Subject: [PATCH 09/11] weird comment not needed and removed on advanced options --- app/models/advanced_options.py | 1 - 1 file changed, 1 deletion(-) diff --git a/app/models/advanced_options.py b/app/models/advanced_options.py index 9df8cac..1a9fb34 100644 --- a/app/models/advanced_options.py +++ b/app/models/advanced_options.py @@ -2,7 +2,6 @@ from pydantic import BaseModel from typing import List, Optional, Dict class AdvancedOptions(BaseModel): - # will supported currently linearity_check: bool show_progress: bool max_solution_time: Optional[int] From b1eab48f6f5cd8da03a7d53831b311ed4fb53db7 Mon Sep 17 00:00:00 2001 From: Josh Burman Date: Tue, 14 Sep 2021 14:11:19 -0400 Subject: [PATCH 10/11] should return response --- app/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/main.py b/app/main.py index 1fad27b..8ec950c 100644 --- a/app/main.py +++ b/app/main.py @@ -36,4 +36,4 @@ async def solve(solver_run: SolverRun): items=[item.id for item in solver_run.items] )] ) - return solver_run + return response From 157107c73f6d90e47978cd5ea98fea160ed06539 Mon Sep 17 00:00:00 2001 From: Josh Burman Date: Tue, 14 Sep 2021 14:28:09 -0400 Subject: [PATCH 11/11] attributes not attribute, remove optional from solution model --- app/models/item.py | 2 +- app/models/solution.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/models/item.py b/app/models/item.py index 9a6f9d7..975593e 100644 --- a/app/models/item.py +++ b/app/models/item.py @@ -5,4 +5,4 @@ from models.attribute import Attribute class Item(BaseModel): id: int - attribute: List[Attribute] + attributes: List[Attribute] diff --git a/app/models/solution.py b/app/models/solution.py index 1681439..d895574 100644 --- a/app/models/solution.py +++ b/app/models/solution.py @@ -1,5 +1,5 @@ from pydantic import BaseModel -from typing import List, Optional +from typing import List from models.form import Form