Merge branch 'changes/deprecate_expert_apps' into 'master'

Deprecate ExpertApplication and CO2Application

See merge request caimira/caimira!501
This commit is contained in:
Andre Henriques 2024-07-05 09:51:59 +02:00
commit a28b4d8310
19 changed files with 68 additions and 254 deletions

View file

@ -66,7 +66,7 @@ The information also features a distribution diagram of licenses and a brief des
A risk assessment tool which simulates the airborne spread of the SARS-CoV-2 virus for space managers.
### CAiMIRA Expert App
### CAiMIRA Expert App and CO₂ App
A tool to interact with various parameters of the CAiMIRA model.
@ -139,13 +139,32 @@ If any of the `.rst` files under the `caimira/docs` folder is changed, this comm
Then, right click on `caimira/docs/_build/html/index.html` and select `Open with` your preferred web browser.
### Running the CAiMIRA Expert-App app in development mode
### Running the CAiMIRA Expert-App or CO2-App apps in development mode
#### Disclaimer
The `ExpertApplication` and `CO2Application` are no longer actively maintained but will remain in the codebase for legacy purposes.
Please note that the functionality of these applications might be compromised due to deprecation issues.
#### Running the Applications
These applications only work within Jupyter notebooks. Attempting to run them outside of a Jupyter environment may result in errors or degraded functionality.
##### Prerequisites
Make sure you have the needed dependencies intalled:
```
voila caimira/apps/expert/caimira.ipynb --port=8080
pip install notebook jupyterlab
```
Then visit http://localhost:8080.
Running with Visual Studio Code (VSCode):
1. Ensure you have the following extensions installed in VSCode: `Jupyter` and `Python`.
2. Open VSCode and navigate to the directory containing the notebook.
3. Open the notebook (e.g. `caimira/apps/expert/caimira.ipynb`) and run the cells by clicking the `run` button next to each cell.
### Running the tests

View file

@ -14,8 +14,6 @@ COPY ./app-config/caimira-public-docker-image/run_caimira.sh /opt/caimira/start.
# To ensure that we have installed the full requirements, re-run the pip install.
# In the best case this will be a no-op.
RUN cd /opt/caimira/src/ && /opt/caimira/app/bin/pip install -r /opt/caimira/src/requirements.txt
RUN /opt/caimira/app/bin/jupyter trust /opt/caimira/src/caimira/apps/expert/*.ipynb
RUN /opt/caimira/app/bin/jupyter trust /opt/caimira/src/caimira/apps/expert_co2/*.ipynb
COPY ./app-config/caimira-public-docker-image/nginx.conf /opt/caimira/nginx.conf
EXPOSE 8080

View file

@ -44,18 +44,6 @@ http {
large_client_header_buffers 4 16k;
location /voila-server/ {
proxy_pass http://localhost:8082/voila-server/;
}
rewrite ^/expert-app$ /voila-server/ last;
rewrite ^/(files/static)/(.*)$ /voila-server/voila/$1/$2 last;
location /co2-voila-server/ {
proxy_pass http://localhost:8083/co2-voila-server/;
}
rewrite ^/co2-app$ /voila-server/ last;
rewrite ^/(files/static)/(.*)$ /voila-server/voila/$1/$2 last;
location / {
proxy_pass http://localhost:8081;
}

View file

@ -5,17 +5,6 @@ echo 'Please see https://gitlab.cern.ch/caimira/caimira for terms of use.'
# Run a proxy for the apps (listening on 8080).
nginx -c /opt/caimira/nginx.conf
# Run the expert app in the background.
cd /opt/caimira/src/caimira
/opt/caimira/app/bin/python -m voila /opt/caimira/src/caimira/apps/expert/caimira.ipynb \
--port=8082 --no-browser --base_url=/voila-server/ \
--Voila.tornado_settings 'allow_origin=*' \
>> /var/log/expert-app.log 2>&1 &
/opt/caimira/app/bin/python -m voila /opt/caimira/src/caimira/apps/expert_co2/caimira.ipynb \
--port=8083 --no-browser --base_url=/co2-voila-server/ \
--Voila.tornado_settings 'allow_origin=*' \
>> /var/log/co2-app.log 2>&1 &
# Run the calculator in the foreground.
/opt/caimira/app/bin/python -m caimira.apps.calculator --port 8081 --no-debug

View file

@ -28,12 +28,7 @@ if [[ "$APP_NAME" == "calculator-app" ]]; then
echo "Starting the caimira webservice with: python -m caimira.apps.calculator ${args[@]}"
python -m caimira.apps.calculator "${args[@]}"
elif [[ "$APP_NAME" == "caimira-voila" ]]; then
echo "Starting the voila service"
voila caimira/apps/expert/ --port=8080 --no-browser --base_url=/voila-server/ --tornado_settings 'allow_origin=*'
elif [[ "$APP_NAME" == "caimira-co2-voila" ]]; then
echo "Starting the CO2 voila service"
voila caimira/apps/expert_co2/ --port=8080 --no-browser --base_url=/co2-voila-server/ --tornado_settings 'allow_origin=*'
else
echo "No APP_NAME specified"
exit 1

View file

@ -1,16 +1,5 @@
version: "3.8"
services:
expert-app:
image: calculator-app
environment:
- APP_NAME=caimira-voila
user: ${CURRENT_UID:?"Please run as follows 'CURRENT_UID=$(id -u):$(id -g) docker-compose up'"}
expert-co2-app:
image: calculator-app
environment:
- APP_NAME=caimira-co2-voila
user: ${CURRENT_UID:?"Please run as follows 'CURRENT_UID=$(id -u):$(id -g) docker-compose up'"}
calculator-app:
image: calculator-app
@ -54,10 +43,6 @@ services:
condition: service_started
calculator-open-app:
condition: service_started
expert-app:
condition: service_started
expert-co2-app:
condition: service_started
auth-service:
condition: service_started
user: ${CURRENT_UID}

View file

@ -73,44 +73,8 @@ http {
proxy_pass http://calculator-app:8080/$request_uri;
}
location /voila-server/ {
proxy_intercept_errors on;
# Anything under voila-server or expert-app is authenticated.
auth_request /auth/probe;
error_page 401 = @error401;
error_page 404 = @proxy_404_error_handler;
# expert-app is the name of the voila server in each of docker-compose,
# caimira-test.web.cern.ch and caimira.web.cern.ch.
proxy_pass http://expert-app:8080/voila-server/;
}
rewrite ^/expert-app$ /voila-server/voila/render/caimira.ipynb last;
rewrite ^/(files/static)/(.*)$ /voila-server/voila/$1/$2 last;
# Before implementing the nginx router we could access /voila/render/caimira.ipynb.
# Redirect this (and all other) URLs to the new scheme.
# Redirect URLs to the new scheme.
absolute_redirect off;
rewrite ^/voila/(.*)$ /voila-server/voila/$1 redirect;
location /co2-voila-server/ {
proxy_intercept_errors on;
# Anything under voila-server or co2-app is authenticated.
auth_request /auth/probe;
error_page 401 = @error401;
error_page 404 = @proxy_404_error_handler;
# expert-co2-app is the name of the voila server in each of docker-compose,
# caimira-test.web.cern.ch and caimira.web.cern.ch.
proxy_pass http://expert-co2-app:8080/co2-voila-server/;
}
rewrite ^/co2-app$ /co2-voila-server/voila/render/caimira.ipynb last;
rewrite ^/(files/static)/(.*)$ /co2-voila-server/voila/$1/$2 last;
# Before implementing the nginx router we could access /voila/render/caimira.ipynb.
# Redirect this (and all other) URLs to the new scheme.
rewrite ^/voila/(.*)$ /co2-voila-server/voila/$1 redirect;
location / {
# By default we have no authentication.

View file

@ -68,120 +68,6 @@
kind: ImageStreamTag
name: 'auth-service:latest'
namespace: ${PROJECT_NAME}
-
apiVersion: apps.openshift.io/v1
kind: DeploymentConfig
metadata:
name: expert-app
labels: {app: expert-app}
spec:
replicas: 1
template:
metadata:
labels:
app: expert-app
spec:
containers:
- name: calculator-app
env:
- name: APP_NAME
value: caimira-voila
image: '${PROJECT_NAME}/calculator-app'
ports:
- containerPort: 8080
protocol: TCP
imagePullPolicy: Always
resources:
limits: { cpu: '1', memory: 1Gi }
requests: { cpu: 1m, memory: 512Mi }
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
dnsPolicy: ClusterFirst
restartPolicy: Always
schedulerName: default-scheduler
securityContext: { }
terminationGracePeriodSeconds: 30
strategy:
activeDeadlineSeconds: 21600
resources: { }
rollingParams:
intervalSeconds: 1
maxSurge: 25%
maxUnavailable: 25%
timeoutSeconds: 600
updatePeriodSeconds: 1
type: Rolling
test: false
selector:
app: expert-app
triggers:
- type: ConfigChange
- type: ImageChange
imageChangeParams:
automatic: true
containerNames:
- calculator-app
from:
kind: ImageStreamTag
name: 'calculator-app:latest'
namespace: ${PROJECT_NAME}
-
apiVersion: apps.openshift.io/v1
kind: DeploymentConfig
metadata:
name: expert-co2-app
labels: {app: expert-co2-app}
spec:
replicas: 1
template:
metadata:
labels:
app: expert-co2-app
spec:
containers:
- name: calculator-app
env:
- name: APP_NAME
value: caimira-co2-voila
image: '${PROJECT_NAME}/calculator-app'
ports:
- containerPort: 8080
protocol: TCP
imagePullPolicy: Always
resources:
limits: { cpu: '1', memory: 1Gi }
requests: { cpu: 1m, memory: 512Mi }
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
dnsPolicy: ClusterFirst
restartPolicy: Always
schedulerName: default-scheduler
securityContext: { }
terminationGracePeriodSeconds: 30
strategy:
activeDeadlineSeconds: 21600
resources: { }
rollingParams:
intervalSeconds: 1
maxSurge: 25%
maxUnavailable: 25%
timeoutSeconds: 600
updatePeriodSeconds: 1
type: Rolling
test: false
selector:
app: expert-co2-app
triggers:
- type: ConfigChange
- type: ImageChange
imageChangeParams:
automatic: true
containerNames:
- calculator-app
from:
kind: ImageStreamTag
name: 'calculator-app:latest'
namespace: ${PROJECT_NAME}
-
apiVersion: apps.openshift.io/v1
kind: DeploymentConfig

View file

@ -27,40 +27,6 @@
deploymentconfig: auth-service
sessionAffinity: 'None'
type: 'ClusterIP'
-
apiVersion: v1
kind: Service
metadata:
labels:
app: expert-app
name: expert-app
spec:
ports:
- name: 8080-tcp
port: 8080
protocol: TCP
targetPort: 8080
selector:
deploymentconfig: expert-app
sessionAffinity: 'None'
type: 'ClusterIP'
-
apiVersion: v1
kind: Service
metadata:
labels:
app: expert-co2-app
name: expert-co2-app
spec:
ports:
- name: 8080-tcp
port: 8080
protocol: TCP
targetPort: 8080
selector:
deploymentconfig: expert-co2-app
sessionAffinity: 'None'
type: 'ClusterIP'
-
apiVersion: v1
kind: Service

View file

@ -42,7 +42,7 @@ from .user import AuthenticatedUser, AnonymousUser
# calculator version. If the calculator needs to make breaking changes (e.g. change
# form attributes) then it can also increase its MAJOR version without needing to
# increase the overall CAiMIRA version (found at ``caimira.__version__``).
__version__ = "4.15.3"
__version__ = "4.16.0"
LOG = logging.getLogger("Calculator")
@ -517,6 +517,9 @@ def make_app(
(get_root_calculator_url(r'/user-guide'), GenericExtraPage, {
'active_page': 'calculator/user-guide',
'filename': 'userguide.html.j2'}),
(get_root_url(r'/expert-app'), GenericExtraPage, {
'active_page': 'expert-app',
'filename': 'expert-app.html.j2'}),
]
profiler_enabled = int(os.environ.get('CAIMIRA_PROFILER_ENABLED', 0))

View file

@ -12,10 +12,13 @@ from matplotlib import pyplot as plt
import numpy as np
import datetime
import pandas as pd
import logging
from caimira import data, models, state
from caimira.store.data_registry import DataRegistry
LOG = logging.getLogger(__name__)
def collapsible(widgets_to_collapse: typing.List, title: str, start_collapsed=False):
collapsed = widgets.Accordion([widgets.VBox(widgets_to_collapse)])
@ -929,6 +932,10 @@ class CAIMIRAStateBuilder(state.StateBuilder):
class ExpertApplication(Controller):
def __init__(self) -> None:
LOG.warning(
"ExpertApplication is currently deactivated and will no longer be maintained. It remains in the codebase for legacy purposes."
)
self._data_registry = DataRegistry()
#: A list of scenario name and ModelState instances. This is intended to be
#: mutated. Any mutation should notify the appropriate Views for handling.

View file

@ -2,6 +2,7 @@ import dataclasses
import ipywidgets as widgets
import typing
import numpy as np
import logging
from caimira import data, models, state
from caimira.store.data_registry import DataRegistry
@ -11,6 +12,8 @@ import matplotlib.lines as mlines
import matplotlib.patches as patches
from .expert import generate_presence_widget, collapsible, ipympl_canvas, WidgetGroup, CAIMIRAStateBuilder
LOG = logging.getLogger(__name__)
def baseline_model(data_registry: DataRegistry):
return models.CO2ConcentrationModel(
@ -188,6 +191,10 @@ class ExposureComparisonResult(View):
class CO2Application(Controller):
def __init__(self) -> None:
LOG.warning(
"CO2Application is currently deactivated and will no longer be maintained. It remains in the codebase for legacy purposes."
)
self._data_registry = DataRegistry()
# self._debug_output = widgets.Output()

View file

@ -15,7 +15,7 @@ CAiMIRA stands for CERN Airborne Model for Indoor Risk Assessment, previously kn
Since then, the model has evolved and now is capable of simulating the short-range component. CAiMIRA comes with different applications that allow more or less flexibility in the input parameters:
<ul>
<li><a href='{{ get_calculator_url() }}'>CAiMIRA calculator app</a></li>
<li><a href='/expert-app'>CAiMIRA expert app</a></li>
<li><a href='{{ get_url() }}/expert-app'>CAiMIRA expert app</a></li>
</ul>
The mathematical and physical model simulate the airborne spread of SARS-CoV-2 virus in a finite volume, assuming a homogenous mixture and a two-stage exhaled jet model, and estimates the risk of COVID-19 airborne transmission therein. The results DO NOT include other known modes of SARS-CoV-2 transmission. Hence, the output from this model is only valid when the other recommended public health & safety instructions are observed, such as good hand hygiene and other barrier measures.<br>

View file

@ -32,7 +32,7 @@
<div class="d-flex flex-row" >
<div class="pr-3"><a href="{{ get_calculator_url() }}" role="button" class="btn btn-lg btn-outline-primary"><div class="d-flex d-row"><i class="icon-calculator"></i><span class="pl-2">Calculator</div></a></div>
<br>
<div class="expert_app_button"><a href="/expert-app" role="button" class="btn btn-lg btn-outline-secondary"><div class="d-flex d-row"><i class="icon-expert"></i><span class="pl-2">Expert app</div></a></div>
<div class="expert_app_button"><a href="{{ get_url() }}/expert-app" role="button" class="btn btn-lg btn-outline-secondary"><div class="d-flex d-row"><i class="icon-expert"></i><span class="pl-2">Expert app</div></a></div>
</div>
<br>
</div>
@ -46,7 +46,7 @@
<div class="split">
<div class="w-25 mobile-sub-section"><hr width="95%">
<div class="d-flex m-2 align-items-center"><i class="bi bi-window" style="font-size: 25px"></i><div><p class="paragraph-title ml-2">Applications</p></div></div>
<div class="m-2">CAiMIRA is composed of two applications, the <a href="{{ get_calculator_url() }}">Calculator</a> and the <a href="/expert-app">Expert App</a>.</div>
<div class="m-2">CAiMIRA is composed of two applications, the <a href="{{ get_calculator_url() }}">Calculator</a> and the <a href="{{ get_url() }}/expert-app">Expert App</a>.</div>
</div>
<div class="w-25 mobile-sub-section"><hr width="95%">
<div class="d-flex m-2 align-items-center"><i class="bi bi-info-square-fill" style="font-size: 25px"></i><div><p class="paragraph-title ml-2">About</p></div></div>

View file

@ -45,8 +45,8 @@
<ul class="dropdown-menu dropwown-navbar-colors" style="min-width: 12rem;" aria-labelledby="navbarDropdown">
<li><a href="{{ get_calculator_url() }}" class="{{ "header-navbar nav-link active" if "calculator/" == active_page else "header-navbar nav-link" }}">Calculator</a></li>
<li><div class="d-flex"><span class="d-flex align-self-center submenu-division"></span><a href="{{ get_calculator_url() }}/user-guide" class="{{ "header-navbar nav-link active" if "user-guide" in active_page else "header-navbar nav-link" }}">User Guide</a></div></li>
<li><a href="/expert-app" class="{{ "header-navbar nav-link active" if "/expert-app" == active_page else "header-navbar nav-link" }}">Expert app</a></li>
<li><a href="/co2-app" class="{{ "header-navbar nav-link active" if "/co2-app" == active_page else "header-navbar nav-link" }}">CO₂ Simulator</a></li>
<li><a href="{{ get_url() }}/expert-app" class="{{ "header-navbar nav-link active" if "{{ get_url() }}/expert-app" == active_page else "header-navbar nav-link" }}">Expert app</a></li>
<li><a href="{{ get_url() }}/expert-app" class="{{ "header-navbar nav-link active" if "{{ get_url() }}/expert-app" == active_page else "header-navbar nav-link" }}">CO₂ Simulator</a></li>
</ul>
</li>
</div>

View file

@ -0,0 +1,18 @@
{% extends "base/layout.html.j2" %}
{% block main %}
<div class="container container--padding" style="word-wrap:break-word";>
<h1 class="paragraph-title">CAiMIRA Expert Apps currently deactivated</h1><br>
With the latest feature implementations in the core CAiMIRA engine, the <b>ExpertApplication</b> and <b>CO2Application</b> apps are no longer actively maintained.
For legacy purposes, the source code is still available in the <a href="https://gitlab.cern.ch/caimira/caimira/">GitLab</a> repository.
<br><br>
For any query, please let us know by sending an email to <a href="mailto:CAiMIRA-dev@cern.ch">CAiMIRA-dev@cern.ch</a>.
<br><br>
<div class="pr-3"><a href="{{ get_calculator_url() }}" role="button" class="btn btn-lg btn-outline-primary"><div class="d-flex d-row"><i class="icon-calculator"></i><span class="pl-2">Calculator</div></a></div>
</div>
{% endblock main %}

View file

@ -8,6 +8,7 @@ def expert_app():
return caimira.apps.ExpertApplication()
@pytest.mark.skip(reason="ExpertApplication is deactivated")
def test_app(expert_app):
# To start with, let's just test that the application runs. We don't try to
# do anything fancy to verify how it looks etc., we leave that for manual
@ -15,6 +16,7 @@ def test_app(expert_app):
assert expert_app._model_scenarios[0][0] == "Scenario 1"
@pytest.mark.skip(reason="ExpertApplication is deactivated")
def test_new_scenario_changes_tab(expert_app):
# Adding a new scenario should change the tab index of the multi-model view.
assert expert_app.multi_model_view.widget.selected_index == 0

View file

@ -49,16 +49,6 @@ json5==0.9.14
jsonpointer==2.4
jsonschema==4.21.1
jsonschema-specifications==2023.12.1
jupyter_client==8.6.0
jupyter_core==5.7.1
jupyter-events==0.9.0
jupyter-lsp==2.2.2
jupyter_server==2.12.5
jupyter_server_terminals==0.5.2
jupyterlab==4.1.1
jupyterlab_pygments==0.3.0
jupyterlab_server==2.25.3
jupyterlab-widgets==1.1.7
kiwisolver==1.4.5
loky==3.4.1
MarkupSafe==2.1.5
@ -124,7 +114,6 @@ typing_extensions==4.9.0
tzdata==2024.1
uri-template==1.3.0
urllib3==2.2.0
voila==0.5.5
wcwidth==0.2.13
webcolors==1.13
webencodings==0.5.1

View file

@ -40,7 +40,6 @@ REQUIREMENTS: dict = {
'timezonefinder',
'tornado',
'types-retry',
'voila',
],
'app': [],
'test': [
@ -54,7 +53,6 @@ REQUIREMENTS: dict = {
'types-requests',
],
'dev': [
'jupyterlab',
],
'doc': [
'sphinx',