Merge branch 'bugfix/API_refinement' into 'master'

REST API refinement

See merge request caimira/caimira!508
This commit is contained in:
Luis Aleixo 2024-09-20 11:20:11 +02:00
commit 09f0514935
11 changed files with 50 additions and 43 deletions

View file

@ -87,6 +87,7 @@ test-cern-caimira-py39:
- ./app-config/openshift/${CAIMIRA_INSTANCE}/expected
only:
- master
- live/caimira-test
check_openshift_config_test:
extends: .test_openshift_config

9
CHANGELOG.md Normal file
View file

@ -0,0 +1,9 @@
# 4.17.2 (September 19, 2024)
## Features Added
- Initial commit with changelog file.
- New project layout architecture.
- Added CAiMIRA REST API features.
## Bug Fixes
- Virus and CO2 routes and controllers.

View file

@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project]
name = "caimira"
version = "4.17.0a2"
version = "4.17.2"
description = "CAiMIRA - CERN Airborne Model for Indoor Risk Assessment"
license = { text = "Apache-2.0" }
authors = [

View file

@ -1,26 +1,27 @@
import typing
from caimira.calculator.validators.co2.co2_validator import CO2FormData
from caimira.calculator.store.data_registry import DataRegistry
from caimira.calculator.models.models import CO2DataModel
def generate_form_obj(form_data, data_registry):
def generate_form_obj(form_data: typing.Dict, data_registry: DataRegistry) -> CO2FormData:
return CO2FormData.from_dict(form_data=form_data, data_registry=data_registry)
def generate_model(form_obj, data_registry):
sample_size = data_registry.monte_carlo['sample_size']
return form_obj.build_model(sample_size=sample_size)
def generate_model(form_obj: CO2FormData) -> CO2DataModel:
return form_obj.build_model()
def generate_report(model):
def generate_report(model: CO2DataModel) -> typing.Dict:
return dict(model.CO2_fit_params())
def submit_CO2_form(form_data):
data_registry = DataRegistry()
def submit_CO2_form(form_data: typing.Dict) -> typing.Dict:
data_registry: DataRegistry = DataRegistry()
form_obj = generate_form_obj(
form_data=form_data, data_registry=data_registry)
model = generate_model(form_obj=form_obj, data_registry=data_registry)
report_data = generate_report(model=model)
form_obj: CO2FormData = generate_form_obj(form_data=form_data, data_registry=data_registry)
model: CO2DataModel = generate_model(form_obj=form_obj)
report_data: typing.Dict = generate_report(model=model)
return report_data

View file

@ -1,37 +1,37 @@
import concurrent.futures
import functools
import typing
from caimira.calculator.validators.virus.virus_validator import VirusFormData
from caimira.calculator.store.data_registry import DataRegistry
from caimira.calculator.models.models import ExposureModel
import caimira.calculator.report.virus_report_data as rg
def generate_form_obj(form_data, data_registry):
def generate_form_obj(form_data: typing.Dict, data_registry: DataRegistry) -> VirusFormData:
return VirusFormData.from_dict(
form_data=form_data,
data_registry=data_registry,
)
def generate_model(form_obj, data_registry):
sample_size = data_registry.monte_carlo['sample_size']
return form_obj.build_model(sample_size=sample_size)
def generate_report_results(form_obj):
def generate_report(form_obj: VirusFormData, report_generation_parallelism: typing.Optional[int]) -> typing.Dict:
return rg.calculate_report_data(
form=form_obj,
executor_factory=functools.partial(
concurrent.futures.ThreadPoolExecutor, None, # TODO define report_parallelism
concurrent.futures.ThreadPoolExecutor,
report_generation_parallelism,
),
)
def submit_virus_form(form_data):
data_registry = DataRegistry
def submit_virus_form(form_data: typing.Dict, report_generation_parallelism: typing.Optional[int]) -> typing.Dict:
data_registry: DataRegistry = DataRegistry()
form_obj = generate_form_obj(form_data=form_data, data_registry=data_registry)
model = generate_model(form_obj=form_obj, data_registry=data_registry)
report_data = generate_report_results(form_obj=form_obj, model=model)
form_obj: VirusFormData = generate_form_obj(form_data=form_data, data_registry=data_registry)
report_data: typing.Dict = generate_report(form_obj=form_obj, report_generation_parallelism=report_generation_parallelism)
# Handle model representation
if report_data['model']: report_data['model'] = repr(report_data['model'])
return report_data

View file

@ -7,7 +7,7 @@ from caimira.api.controller.virus_report_controller import submit_virus_form
from caimira.api.controller.co2_report_controller import submit_CO2_form
class BaseReportHandler(tornado.web.RedirectHandler):
class BaseReportHandler(tornado.web.RequestHandler):
def set_default_headers(self):
self.set_header("Access-Control-Allow-Origin", "*")
self.set_header("Access-Control-Allow-Headers", "x-requested-with")
@ -22,7 +22,14 @@ class VirusReportHandler(BaseReportHandler):
def post(self):
try:
form_data = json.loads(self.request.body)
report_data = submit_virus_form(form_data)
arguments = self.request.arguments
# Report generation parallelism argument
try:
report_generation_parallelism = int(arguments['report_generation_parallelism'][0])
except (ValueError, IndexError, KeyError):
report_generation_parallelism = None
report_data = submit_virus_form(form_data, report_generation_parallelism)
response_data = {
"status": "success",
@ -36,12 +43,7 @@ class VirusReportHandler(BaseReportHandler):
self.write_error(status_code=400, exc_info=sys.exc_info())
class CO2ReportHandler(tornado.web.RequestHandler):
def set_default_headers(self):
self.set_header("Access-Control-Allow-Origin", "*")
self.set_header("Access-Control-Allow-Headers", "x-requested-with")
self.set_header("Access-Control-Allow-Methods", "POST, GET, OPTIONS")
class CO2ReportHandler(BaseReportHandler):
def post(self):
try:
form_data = json.loads(self.request.body)

View file

@ -807,7 +807,7 @@ class SimplePopulation:
number: typing.Union[int, IntPiecewiseConstant]
#: The times in which the people are in the room.
presence: typing.Union[None, Interval]
presence: typing.Optional[Interval]
#: The physical activity being carried out by the people.
activity: Activity

View file

@ -193,8 +193,7 @@ class CO2FormData(FormData):
vent_states.append(last_time_from_input)
return tuple(vent_states)
def build_model(self, size=None) -> models.CO2DataModel: # type: ignore
size = size or self.data_registry.monte_carlo['sample_size']
def build_model(self, sample_size = None) -> models.CO2DataModel:
# Build a simple infected and exposed population for the case when presence
# intervals and number of people are dynamic. Activity type is not needed.
if self.occupancy_format == 'dynamic':

View file

@ -206,7 +206,7 @@ class FormData:
def validate(self):
raise NotImplementedError("Subclass must implement")
def build_model(self, sample_size=None):
def build_model(self, sample_size: typing.Optional[int] = None):
raise NotImplementedError("Subclass must implement")
def _compute_breaks_in_interval(self, start, finish, n_breaks, duration) -> models.BoundarySequence_t:

View file

@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project]
name = "cern-caimira"
version = "4.17.1a1"
version = "4.17.2"
description = "CAiMIRA - CERN Airborne Model for Indoor Risk Assessment"
license = { text = "Apache-2.0" }
authors = [

View file

@ -392,11 +392,6 @@ function plotCO2Data(url) {
function submitFittingAlgorithm(url) {
if (validateCO2Form()) {
// Disable all the ventilation inputs
$("#fitting_ventilation_states, [name=fitting_ventilation_type]").prop(
"disabled",
true
);
// Disable room capacity input
$("#room_capacity").prop(
"disabled",