From aaf27ab7bd4b2265f837ca143a8a7f7c0fab93f5 Mon Sep 17 00:00:00 2001 From: Josh Burman Date: Thu, 9 Dec 2021 18:45:43 +0000 Subject: [PATCH 1/4] modified equation to reflect equation psychos are using --- app/lib/irt/item_information_function.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/lib/irt/item_information_function.py b/app/lib/irt/item_information_function.py index ddb3cbb..29d6b90 100644 --- a/app/lib/irt/item_information_function.py +++ b/app/lib/irt/item_information_function.py @@ -11,7 +11,7 @@ class ItemInformationFunction(): if self.model_data.model == '3PL': p = ThreeParameterLogistic(self.model_data, kwargs).result() q = 1 - p - return self.model_data.a_param**2 * ((q / p) * ((p - (self.model_data.c_param**2)) / (1 - (self.model_data.c_param**2)))) + return (self.model_data.a_param * q * (p - self.model_data.c_param)**2) / (p * ((1 - self.model_data.c_param)**2)) else: # potentially error out return None From d107b66e4f35d8fce401a6acfb120968ddd27cc1 Mon Sep 17 00:00:00 2001 From: Josh Burman Date: Thu, 9 Dec 2021 19:43:04 +0000 Subject: [PATCH 2/4] increment semver --- app/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/main.py b/app/main.py index 2c601bc..323dd5a 100644 --- a/app/main.py +++ b/app/main.py @@ -19,7 +19,7 @@ class ServiceListener(SqsListener): logging.info('Process complete for %s', service.file_name) def main(): - logging.info('Starting Solver Service (v1.0.0)...') + logging.info('Starting Solver Service (v1.0.1)...') listener = ServiceListener( 'measure-development-solver-ingest', region_name=os.environ['AWS_REGION'], From e0fca68f4613699096494e66dbd50c7aa69cdd9e Mon Sep 17 00:00:00 2001 From: Josh Burman Date: Fri, 10 Dec 2021 19:45:03 +0000 Subject: [PATCH 3/4] add basic error handling --- app/helpers/service_helper.py | 7 +++++++ app/lib/irt/item_information_function.py | 21 ++++++++++++++------- app/services/loft_service.py | 22 ++++++++++++++++------ 3 files changed, 37 insertions(+), 13 deletions(-) diff --git a/app/helpers/service_helper.py b/app/helpers/service_helper.py index 399882a..06b1fa4 100644 --- a/app/helpers/service_helper.py +++ b/app/helpers/service_helper.py @@ -61,6 +61,13 @@ def solution_to_file(buffer, total_form_items, forms): return buff2 +def error_to_file(buffer, error): + wr = csv.writer(buffer, dialect='excel', delimiter=',') + wr.writerow(['status']) + wr.writerow([error.args[0]]) + + return io.BytesIO(buffer.getvalue().encode()) + def key_to_uuid(key): return re.split("_", key)[0] diff --git a/app/lib/irt/item_information_function.py b/app/lib/irt/item_information_function.py index 29d6b90..9e75197 100644 --- a/app/lib/irt/item_information_function.py +++ b/app/lib/irt/item_information_function.py @@ -1,4 +1,7 @@ +import logging + from lib.irt.models.three_parameter_logistic import ThreeParameterLogistic +from lib.errors.item_generation_error import ItemGenerationError class ItemInformationFunction(): def __init__(self, irt_model): @@ -8,10 +11,14 @@ class ItemInformationFunction(): # further detailed on page 161, equation 4 here: # https://www.ncbi.nlm.nih.gov/pmc/articles/PMC5978482/pdf/10.1177_0146621615613308.pdf def calculate(self, **kwargs): - if self.model_data.model == '3PL': - p = ThreeParameterLogistic(self.model_data, kwargs).result() - q = 1 - p - return (self.model_data.a_param * q * (p - self.model_data.c_param)**2) / (p * ((1 - self.model_data.c_param)**2)) - else: - # potentially error out - return None + try: + if self.model_data.model == '3PL': + p = ThreeParameterLogistic(self.model_data, kwargs).result() + q = 1 - p + return (self.model_data.a_param * q * (p - self.model_data.c_param)**2) / (p * ((1 - self.model_data.c_param)**2)) + else: + # potentially error out + raise ItemGenerationError("irt model not supported or provided") + except ZeroDivisionError as error: + logging.error(error) + raise ItemGenerationError("params not well formatted", error.args[0]) diff --git a/app/services/loft_service.py b/app/services/loft_service.py index 07bfe2a..62410e3 100644 --- a/app/services/loft_service.py +++ b/app/services/loft_service.py @@ -3,6 +3,7 @@ import os, json, random, io, logging from pulp import LpProblem, LpVariable, LpMinimize, LpStatus, lpSum from helpers import aws_helper, tar_helper, csv_helper, service_helper, solver_helper +from lib.errors.item_generation_error import ItemGenerationError from models.solver_run import SolverRun from models.solution import Solution @@ -12,9 +13,12 @@ from services.base import Base class LoftService(Base): def process(self): - self.solver_run = SolverRun.parse_obj(self.retreive_attributes_from_message()) - self.solution = self.generate_solution() - self.result = self.stream_to_s3_bucket() + try: + self.solver_run = SolverRun.parse_obj(self.retreive_attributes_from_message()) + self.solution = self.generate_solution() + self.result = self.stream_to_s3_bucket() + except ItemGenerationError as error: + self.result = self.stream_to_s3_bucket(error) def retreive_attributes_from_message(self): logging.info('Retrieving attributes from message...') @@ -96,12 +100,18 @@ class LoftService(Base): return solution - def stream_to_s3_bucket(self): + def stream_to_s3_bucket(self, error = None): self.file_name = f'{service_helper.key_to_uuid(self.key)}.csv' - logging.info('Streaming %s to s3 bucket - %s', self.file_name, os.environ['S3_PROCESSED_BUCKET']) + solution_file = None # setup writer buffer and write processed forms to file buffer = io.StringIO() - solution_file = service_helper.solution_to_file(buffer, self.solver_run.total_form_items, self.solution.forms) + + if error: + logging.info('Streaming %s error response to s3 bucket - %s', self.file_name, os.environ['S3_PROCESSED_BUCKET']) + solution_file = service_helper.error_to_file(buffer, error) + else: + logging.info('Streaming %s to s3 bucket - %s', self.file_name, os.environ['S3_PROCESSED_BUCKET']) + solution_file = service_helper.solution_to_file(buffer, self.solver_run.total_form_items, self.solution.forms) # upload generated file to s3 and return result return aws_helper.file_stream_upload(solution_file, self.file_name, os.environ['S3_PROCESSED_BUCKET']) From d343e3962ea52269b064f3c24f153f763915a3c7 Mon Sep 17 00:00:00 2001 From: Josh Burman Date: Fri, 10 Dec 2021 20:21:22 +0000 Subject: [PATCH 4/4] forgot to add error folder --- app/lib/errors/item_generation_error.py | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 app/lib/errors/item_generation_error.py diff --git a/app/lib/errors/item_generation_error.py b/app/lib/errors/item_generation_error.py new file mode 100644 index 0000000..f15fd6d --- /dev/null +++ b/app/lib/errors/item_generation_error.py @@ -0,0 +1,2 @@ +class ItemGenerationError(Exception): + pass