diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 5df28185..be50695c 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -36,6 +36,7 @@ deploy_to_test:
script:
- curl -X POST -k https://openshift-dev.cern.ch:443/apis/build.openshift.io/v1/namespaces/test-cara/buildconfigs/cara-router/webhooks/${OPENSHIFT_TEST_BUILD_WEBHOOK_SECRET}/generic
- curl -X POST -k https://openshift-dev.cern.ch:443/apis/build.openshift.io/v1/namespaces/test-cara/buildconfigs/cara-webservice/webhooks/${OPENSHIFT_TEST_BUILD_WEBHOOK_SECRET}/generic
+ - curl -X POST -k https://openshift-dev.cern.ch:443/apis/build.openshift.io/v1/namespaces/test-cara/buildconfigs/cara-calculator-open/webhooks/${OPENSHIFT_TEST_BUILD_WEBHOOK_SECRET}/generic
- curl -X POST -k https://openshift-dev.cern.ch:443/apis/build.openshift.io/v1/namespaces/test-cara/buildconfigs/auth-service/webhooks/${OPENSHIFT_TEST_BUILD_WEBHOOK_SECRET}/generic
diff --git a/README.md b/README.md
index f0653b89..c0c18d46 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
# CARA - COVID Airborne Risk Assessment
-CARA is a risk assessment tool developed to model the concentration of viruses in enclosed spaces, in order to inform space-management decisions.
+CARA is a risk assessment tool developed to model the concentration of viruses in enclosed spaces, in order to inform space-management decisions.
CARA models the concentration profile of potential infectious viruses in enclosed spaces with clear and intuitive graphs.
The user can set a number of parameters, including room volume, exposure time, activity type, mask-wearing and ventilation.
@@ -23,7 +23,7 @@ The objective is to facilitate targeted decision-making and investment through c
While the SARS-CoV-2 virus is in circulation among the population, the notion of 'zero risk' or 'completely safe scenario' does not exist.
Each event modelled is unique, and the results generated therein are only as accurate as the inputs and assumptions.
-## Authors
+## Authors
CARA was developed by following members of CERN - European Council for Nuclear Research (visit https://home.cern/):
Andre Henriques1, Marco Andreini1, Gabriella Azzopardi2, James Devine3, Philip Elson4, Nicolas Mounet2, Markus Kongstein Rognlien2,6, Nicola Tarocco5
@@ -70,7 +70,7 @@ Once you have used the scripts, the hourly temperature data for your location sh
'Feb': [0.9, 0.3, 0.0, -0.5, -0.7, -1.1, -1.2, -1.1, -0.7, 0.8, 2.5,
4.2, 5.4, 6.2, 6.3, 6.2, 6.1, 5.5, 4.5, 4.1, 3.5, 2.8, 2.5, 2.0],...`
-CARA currently supports **only one geographic location for weather data per instance**.
+CARA currently supports **only one geographic location for weather data per instance**.
## Running CARA locally
@@ -80,7 +80,7 @@ In order to run cara locally with docker, run the following:
$ docker run -it -p 8080:8080 gitlab-registry.cern.ch/cara/cara/calculator
-This will start a local version of CARA, which can be visited at http://localhost:8080/.
+This will start a local version of CARA, which can be visited at http://localhost:8080/.
## Development guide
@@ -98,6 +98,12 @@ To run with the CERN theme:
python -m cara.apps.calculator --theme=cara/apps/calculator/themes/cern
```
+To run the calculator on a different URL path:
+
+```
+python -m cara.apps.calculator --prefix=/mycalc
+```
+
### Running the CARA Expert-App app in development mode
```
diff --git a/app-config/nginx/nginx.conf b/app-config/nginx/nginx.conf
index 8104caa3..e2e45836 100644
--- a/app-config/nginx/nginx.conf
+++ b/app-config/nginx/nginx.conf
@@ -99,13 +99,22 @@ http {
}
location /calculator {
- # Anything under calculator is authenticated.
+ return 302 /calculator-cern;
+ }
+
+ location /calculator-cern {
+ # CERN calculator is authenticated.
auth_request /auth/probe;
error_page 401 = @error401;
# cara-webservice is the name of the tornado server (for the calculator)
# in each of docker-compose, test-cara.web.cern.ch and cara.web.cern.ch.
- proxy_pass http://cara-webservice:8080/calculator;
+ proxy_pass http://cara-webservice:8080/calculator-cern;
+ }
+
+ location /calculator-open {
+ # Public open calculator
+ proxy_pass http://cara-calculator-open:8080/calculator-open;
}
}
}
diff --git a/app-config/openshift/application.yaml b/app-config/openshift/application.yaml
index 03c3c103..6d6f2c32 100644
--- a/app-config/openshift/application.yaml
+++ b/app-config/openshift/application.yaml
@@ -198,7 +198,7 @@
env:
- name: APP_NAME
value: cara-voila
- image: '${PROJECT_NAME}/cara-webservice'
+ image: '${PROJECT_NAME}/cara-app'
ports:
- containerPort: 8080
protocol: TCP
@@ -211,7 +211,7 @@
- cara-app
from:
kind: ImageStreamTag
- name: 'cara-webservice:latest'
+ name: 'cara-app:latest'
namespace: ${PROJECT_NAME}
-
apiVersion: v1
@@ -267,8 +267,10 @@
name: auth-service-secrets
- name: APP_NAME
value: cara-webservice
+ - name: CARA_CALCULATOR_PREFIX
+ value: /calculator-cern
- name: CARA_THEME
- value: cara/apps/calculator/themes/cern
+ value: cara/apps/calculator/themes/cern
image: '${PROJECT_NAME}/cara-webservice'
ports:
- containerPort: 8080
@@ -285,6 +287,41 @@
name: 'cara-webservice:latest'
namespace: ${PROJECT_NAME}
- type: ConfigChange
+ -
+ apiVersion: v1
+ kind: DeploymentConfig
+ metadata:
+ name: cara-calculator-open
+ spec:
+ replicas;: 1
+ template:
+ metadata:
+ labels:
+ app: cara-calculator-open
+ spec:
+ containers:
+ - name: cara-calculator-open
+ env:
+ - name: APP_NAME
+ value: cara-webservice
+ - name: CARA_CALCULATOR_PREFIX
+ value: /calculator-open
+ image: '${PROJECT_NAME}/cara-webservice'
+ ports:
+ - containerPort: 8080
+ protocol: TCP
+ triggers:
+ - type: ConfigChange
+ - type: ImageChange
+ imageChangeParams:
+ automatic: true
+ containerNames:
+ - cara-calculator-open
+ from:
+ kind: ImageStreamTag
+ name: 'cara-webservice:latest'
+ namespace: ${PROJECT_NAME}
+ - type: ConfigChange
parameters:
- name: PROJECT_NAME
diff --git a/app-config/openshift/services.yaml b/app-config/openshift/services.yaml
index 6e39cb6c..a8c0c4fe 100644
--- a/app-config/openshift/services.yaml
+++ b/app-config/openshift/services.yaml
@@ -10,6 +10,21 @@
labels:
template: "cara-services"
objects:
+ -
+ apiVersion: v1
+ kind: Service
+ metadata:
+ labels:
+ app: auth-service
+ name: auth-service
+ spec:
+ ports:
+ - name: 8080-tcp
+ port: 8080
+ protocol: TCP
+ targetPort: 8080
+ selector:
+ deploymentconfig: auth-service
-
apiVersion: v1
kind: Service
@@ -24,7 +39,7 @@
protocol: TCP
targetPort: 8080
selector:
- app: cara-app
+ deploymentconfig: cara-app
-
apiVersion: v1
kind: Service
@@ -43,7 +58,7 @@
protocol: TCP
targetPort: 8443
selector:
- app: cara-router
+ deploymentconfig: cara-router
-
apiVersion: v1
kind: Service
@@ -58,4 +73,19 @@
protocol: TCP
targetPort: 8080
selector:
- app: cara-webservice
+ deploymentconfig: cara-webservice
+ -
+ apiVersion: v1
+ kind: Service
+ metadata:
+ labels:
+ app: cara-calculator-open
+ name: cara-calculator-open
+ spec:
+ ports:
+ - name: 8080-tcp
+ port: 8080
+ protocol: TCP
+ targetPort: 8080
+ selector:
+ deploymentconfig: cara-calculator-open
diff --git a/app.sh b/app.sh
index 1aa80c4a..023c0def 100755
--- a/app.sh
+++ b/app.sh
@@ -10,6 +10,10 @@ if [[ "$APP_NAME" == "cara-webservice" ]]; then
args+=("--theme=${CARA_THEME}")
fi
+ if [ ! -z "$CARA_CALCULATOR_PREFIX" ]; then
+ args+=("--prefix=${CARA_CALCULATOR_PREFIX}")
+ fi
+
echo "Starting the cara webservice with: python -m cara.apps.calculator ${args[@]}"
python -m cara.apps.calculator "${args[@]}"
elif [[ "$APP_NAME" == "cara-voila" ]]; then
diff --git a/cara/apps/calculator/__init__.py b/cara/apps/calculator/__init__.py
index 57376206..d84eeaeb 100644
--- a/cara/apps/calculator/__init__.py
+++ b/cara/apps/calculator/__init__.py
@@ -66,6 +66,7 @@ class BaseRequestHandler(RequestHandler):
print(traceback.format_exc())
self.finish(template.render(
user=self.current_user,
+ calculator_prefix=self.settings["calculator_prefix"],
active_page='Error',
contents=contents
))
@@ -79,6 +80,7 @@ class Missing404Handler(BaseRequestHandler):
"page.html.j2")
self.finish(template.render(
user=self.current_user,
+ calculator_prefix=self.settings["calculator_prefix"],
active_page='Error',
contents='Unfortunately the page you were looking for does not exist.
'
))
@@ -123,7 +125,10 @@ class LandingPage(BaseRequestHandler):
def get(self):
template = self.settings["template_environment"].get_template(
"index.html.j2")
- report = template.render(user=self.current_user)
+ report = template.render(
+ user=self.current_user,
+ calculator_prefix=self.settings["calculator_prefix"],
+ )
self.finish(report)
@@ -133,6 +138,7 @@ class AboutPage(BaseRequestHandler):
template = template_environment.get_template("about.html.j2")
report = template.render(
user=self.current_user,
+ calculator_prefix=self.settings["calculator_prefix"],
active_page="about",
text_blocks=template_environment.globals['common_text']
)
@@ -146,6 +152,7 @@ class CalculatorForm(BaseRequestHandler):
report = template.render(
user=self.current_user,
xsrf_form_html=self.xsrf_form_html(),
+ calculator_prefix=self.settings["calculator_prefix"],
calculator_version=__version__,
)
self.finish(report)
@@ -160,7 +167,7 @@ class CompressedCalculatorFormInputs(BaseRequestHandler):
except Exception as err: # noqa
self.set_status(400)
return self.finish("Invalid calculator data: it seems incomplete. Was there an error copying & pasting the URL?")
- self.redirect(f'/calculator?{args}')
+ self.redirect(f'{self.settings["calculator_prefix"]}?{args}')
class ReadmeHandler(BaseRequestHandler):
@@ -168,14 +175,15 @@ class ReadmeHandler(BaseRequestHandler):
template = self.settings['template_environment'].get_template("userguide.html.j2")
readme = template.render(
active_page="calculator/user-guide",
- user=self.current_user
+ user=self.current_user,
+ calculator_prefix=self.settings["calculator_prefix"],
)
self.finish(readme)
def make_app(
debug: bool = False,
- prefix: str = '/calculator',
+ calculator_prefix: str = '/calculator',
theme_dir: typing.Optional[Path] = None,
) -> Application:
static_dir = Path(__file__).absolute().parent.parent / 'static'
@@ -185,11 +193,11 @@ def make_app(
(r'/_c/(.*)', CompressedCalculatorFormInputs),
(r'/about', AboutPage),
(r'/static/(.*)', StaticFileHandler, {'path': static_dir}),
- (prefix + r'/?', CalculatorForm),
- (prefix + r'/report', ConcentrationModel),
- (prefix + r'/baseline-model/result', StaticModel),
- (prefix + r'/user-guide', ReadmeHandler),
- (prefix + r'/static/(.*)', StaticFileHandler, {'path': calculator_static_dir}),
+ (calculator_prefix + r'/?', CalculatorForm),
+ (calculator_prefix + r'/report', ConcentrationModel),
+ (calculator_prefix + r'/baseline-model/result', StaticModel),
+ (calculator_prefix + r'/user-guide', ReadmeHandler),
+ (calculator_prefix + r'/static/(.*)', StaticFileHandler, {'path': calculator_static_dir}),
]
cara_templates = Path(__file__).parent.parent / "templates"
@@ -210,9 +218,10 @@ def make_app(
return Application(
urls,
debug=debug,
+ calculator_prefix=calculator_prefix,
template_environment=template_environment,
default_handler_class=Missing404Handler,
- report_generator=ReportGenerator(loader),
+ report_generator=ReportGenerator(loader, calculator_prefix),
xsrf_cookies=True,
# COOKIE_SECRET being undefined will result in no login information being
# presented to the user.
diff --git a/cara/apps/calculator/__main__.py b/cara/apps/calculator/__main__.py
index 0cfafe66..10298c84 100644
--- a/cara/apps/calculator/__main__.py
+++ b/cara/apps/calculator/__main__.py
@@ -16,6 +16,11 @@ def configure_parser(parser) -> argparse.ArgumentParser:
help="A directory containing extensions for templates and static data",
default=None,
)
+ parser.add_argument(
+ "--prefix",
+ help="Change the URL path prefix to the calculator app",
+ default="/calculator"
+ )
return parser
@@ -27,7 +32,7 @@ def main():
theme_dir = Path(theme_dir).absolute()
assert theme_dir.exists()
assert (theme_dir / 'templates').exists()
- app = make_app(debug=args.no_debug, theme_dir=theme_dir)
+ app = make_app(debug=args.no_debug, calculator_prefix=args.prefix, theme_dir=theme_dir)
app.listen(8080)
IOLoop.instance().start()
diff --git a/cara/apps/calculator/report_generator.py b/cara/apps/calculator/report_generator.py
index 4029e54f..ab206ec9 100644
--- a/cara/apps/calculator/report_generator.py
+++ b/cara/apps/calculator/report_generator.py
@@ -71,7 +71,7 @@ def calculate_report_data(model: models.ExposureModel):
}
-def generate_qr_code(prefix, form: FormData):
+def generate_qr_code(base_url, calculator_prefix, form: FormData):
form_dict = FormData.to_dict(form, strip_defaults=True)
# Generate the calculator URL arguments that would be needed to re-create this
@@ -80,10 +80,9 @@ def generate_qr_code(prefix, form: FormData):
# Then zlib compress + base64 encode the string. To be inverted by the
# /_c/ endpoint.
- qr_url = prefix + "/_c/" + base64.b64encode(
- zlib.compress(args.encode())
- ).decode()
- url = prefix + "/calculator?" + args
+ compressed_args = base64.b64encode(zlib.compress(args.encode())).decode()
+ qr_url = f"{base_url}/_c/{compressed_args}"
+ url = f"{base_url}{calculator_prefix}?{args}"
qr = qrcode.QRCode(
version=1,
@@ -281,6 +280,7 @@ def comparison_report(scenarios: typing.Dict[str, models.ExposureModel]):
@dataclasses.dataclass
class ReportGenerator:
jinja_loader: jinja2.BaseLoader
+ calculator_prefix: str
def build_report(self, base_url: str, form: FormData) -> str:
model = form.build_model()
@@ -300,7 +300,8 @@ class ReportGenerator:
context.update(calculate_report_data(model))
alternative_scenarios = manufacture_alternative_scenarios(form)
context['alternative_scenarios'] = comparison_report(alternative_scenarios)
- context['qr_code'] = generate_qr_code(base_url, form)
+ context['qr_code'] = generate_qr_code(base_url, self.calculator_prefix, form)
+ context['calculator_prefix'] = self.calculator_prefix
return context
def _template_environment(self) -> jinja2.Environment:
diff --git a/cara/apps/calculator/templates/base/calculator.report.html.j2 b/cara/apps/calculator/templates/base/calculator.report.html.j2
index cb57d045..043578e6 100644
--- a/cara/apps/calculator/templates/base/calculator.report.html.j2
+++ b/cara/apps/calculator/templates/base/calculator.report.html.j2
@@ -6,7 +6,7 @@
Disclaimer:
Disclaimer:
CARA is a risk assessment tool developed to model the concentration of viruses in enclosed spaces, in order to inform space-management decisions. diff --git a/cara/apps/calculator/templates/calculator.form.html.j2 b/cara/apps/calculator/templates/calculator.form.html.j2 index b03dc47d..bf7840ab 100644 --- a/cara/apps/calculator/templates/calculator.form.html.j2 +++ b/cara/apps/calculator/templates/calculator.form.html.j2 @@ -5,12 +5,12 @@ {% block extra_headers %} - + {% endblock extra_headers %} {% block body_scripts %} - + {% endblock body_scripts %} @@ -28,7 +28,7 @@ v{{ calculator_version }} Please sen {% if DEBUG %}