diff --git a/cara/apps/calculator/__init__.py b/cara/apps/calculator/__init__.py index d84eeaeb..f7a4834d 100644 --- a/cara/apps/calculator/__init__.py +++ b/cara/apps/calculator/__init__.py @@ -1,6 +1,8 @@ # This module is part of CARA. Please see the repository at # https://gitlab.cern.ch/cara/cara for details of the license and terms of use. +import asyncio +import concurrent.futures import datetime import base64 import html @@ -87,7 +89,7 @@ class Missing404Handler(BaseRequestHandler): class ConcentrationModel(BaseRequestHandler): - def post(self): + async def post(self): requested_model_config = { name: self.get_argument(name) for name in self.request.arguments } @@ -107,20 +109,27 @@ class ConcentrationModel(BaseRequestHandler): return base_url = self.request.protocol + "://" + self.request.host - report_generator = self.settings['report_generator'] - report = report_generator.build_report(base_url, form) + report_generator: ReportGenerator = self.settings['report_generator'] + report_task = self.settings["worker_pool"].submit( + report_generator.build_report, base_url, form, + ) + report: str = await asyncio.wrap_future(report_task) self.finish(report) class StaticModel(BaseRequestHandler): - def get(self): + async def get(self): form = model_generator.FormData.from_dict(model_generator.baseline_raw_form_data()) base_url = self.request.protocol + "://" + self.request.host - report_generator = self.settings['report_generator'] - report = report_generator.build_report(base_url, form) + report_generator: ReportGenerator = self.settings['report_generator'] + report_task = self.settings["worker_pool"].submit( + report_generator.build_report, base_url, form, + ) + report: str = await asyncio.wrap_future(report_task) self.finish(report) + class LandingPage(BaseRequestHandler): def get(self): template = self.settings["template_environment"].get_template( @@ -226,4 +235,5 @@ def make_app( # COOKIE_SECRET being undefined will result in no login information being # presented to the user. cookie_secret=os.environ.get('COOKIE_SECRET', ''), + worker_pool=concurrent.futures.ProcessPoolExecutor(), ) diff --git a/cara/tests/apps/calculator/test_webapp.py b/cara/tests/apps/calculator/test_webapp.py index 49f3b065..40b75595 100644 --- a/cara/tests/apps/calculator/test_webapp.py +++ b/cara/tests/apps/calculator/test_webapp.py @@ -48,7 +48,23 @@ class TestBasicApp(tornado.testing.AsyncHTTPTestCase): @tornado.testing.gen_test(timeout=_TIMEOUT) def test_report(self): - response = yield self.http_client.fetch(self.get_url('/calculator/baseline-model/result')) + requests = [ + self.http_client.fetch(self.get_url('/calculator/baseline-model/result')), + # At the same time, request a non-report page (to check whether the report is blocking). + self.http_client.fetch(self.get_url('/calculator/')), + ] + response = yield requests[0] + other_response = yield requests[1] + + def end_time(resp): + return resp.start_time + resp.request_time + + # The start time is before the other request, + # but the end time is after the other request (because it takes longer + # to process a report than a simple page). + assert response.start_time < other_response.start_time + assert end_time(response) > end_time(other_response) + self.assertEqual(response.code, 200) assert 'CERN HSE' not in response.body.decode() assert 'expected number of new cases is' in response.body.decode() @@ -66,6 +82,7 @@ class TestCernApp(tornado.testing.AsyncHTTPTestCase): assert 'CERN HSE' in response.body.decode() assert 'expected number of new cases is' in response.body.decode() + class TestOpenApp(tornado.testing.AsyncHTTPTestCase): def get_app(self): return cara.apps.calculator.make_app(calculator_prefix="/mycalc")