Skip to content

Commit

Permalink
Bump to version 0.1.1, edit docs (#50)
Browse files Browse the repository at this point in the history
* Bump to version 0.1.1, edit docs

* Update frontend

- New KPI display formats
- Display version number in navbar
- Navbar: "Developer" links open in new tab
  • Loading branch information
yinchi authored Jul 27, 2024
1 parent 899df5b commit e767185
Show file tree
Hide file tree
Showing 10 changed files with 82 additions and 27 deletions.
18 changes: 11 additions & 7 deletions dighosp-des/dighosp_des/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@
from pymongo import MongoClient
from rq import Queue

from .conf import MONGO_CLIENT_ARGS, APP_VERSION
from .conf import APP_VERSION, MONGO_CLIENT_ARGS
from .config import Config
from .kpis import lab_tats_fig, utilisation_fig, wips_fig
from .kpis import lab_tats_fig, lab_tats_table, utilisation_fig, utilisation_table, wips_fig
from .model import Model
from .redis_worker import RedisSingleton

Expand Down Expand Up @@ -209,10 +209,12 @@ def save_dash_objs(job_id: ObjectIdStr):
data = [get_result(job_id, idx) for idx in range(n)]
kpi_objs = {
'utilisation': utilisation_fig(data),
'utilisation_table': utilisation_table(data),
'wip': wips_fig(data, wip='Total WIP'),
'tat': lab_tats_fig(data)
'tat': lab_tats_fig(data),
'tat_table': lab_tats_table(data)
}

with MongoClient(**MONGO_CLIENT_ARGS) as client:
coll = client['sim']['sim_jobs']
obj = coll.find_one({'_id': ObjectId(job_id)})
Expand All @@ -221,18 +223,19 @@ def save_dash_objs(job_id: ObjectIdStr):
status_code=status.HTTP_404_NOT_FOUND,
detail=f"No simulation job with ObjectId {job_id}."
)

output_bytes = orjson.dumps(kpi_objs)

fs = GridFS(client['sim'])
obj_id = fs.put(output_bytes)

coll.find_one_and_update(
filter={'_id': ObjectId(job_id)},
update={'$set': {'results_kpi_obj_id': str(obj_id)}}
)
return obj_id


@app.get(
'/jobs/{job_id}/results/dash_objs',
summary='Get Plotly Dash objects'
Expand All @@ -251,11 +254,12 @@ def get_dash_objs(job_id: ObjectIdStr):
status_code=status.HTTP_404_NOT_FOUND,
detail=f"No simulation job with ObjectId {job_id}."
)

fs = GridFS(client['sim'])
obj_id = ObjectId(obj['results_kpi_obj_id'])
return json.load(fs.get(obj_id))


@app.get(
'/jobs',
summary='List jobs'
Expand Down
28 changes: 28 additions & 0 deletions dighosp-des/dighosp_des/kpis.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,18 @@ def utilisation_fig(data):
fig.update_layout(title='Resource utilisation')
return json.loads(fig.to_json())

def utilisation_table(data):
"""Create a table of mean resource utilisation by resource."""
res_names = list(data[0]['resources']['n_claimed'].keys())
res_means = [
np.mean([mean_claimed(dd, res)/mean_available(dd, res) for dd in data])
for res in res_names
]
return {
'Resource': res_names,
'Utilisation': [f'{x:.3%}' for x in res_means]
}


def wip_df(data, wip):
"""Get the hourly means for a given WIP counter, for a single simulation replication."""
Expand Down Expand Up @@ -143,3 +155,19 @@ def lab_tats_fig(data):
fig.update_xaxes(title='Days')
fig.update_yaxes(title='Probability')
return json.loads(fig.to_json())


def lab_tats_table(data):
"""Create a table of lab turnaround times, showing the percentage of specimens completed within
7, 10, 14, or 21 days."""
lab_tats = [[
(x['qc_end']-x['reception_start'])/24.0
for x in data[n]['specimen_data'].values()
if 'reporting_end' in x
] for n in range(len(data))]
lab_tats = np.array(list(chain(*lab_tats)))
days = [7, 10, 14, 21]
return {
'Days': days,
'Specimens completed': [f'{np.mean(lab_tats < x):.3%}' for x in days]
}
2 changes: 1 addition & 1 deletion dighosp-des/pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "dighosp-des"
version = "0.1.1a0"
version = "0.1.1"
description = ""
authors = ["Yin-Chi Chan <ycc39@cam.ac.uk>"]
readme = "README.md"
Expand Down
2 changes: 1 addition & 1 deletion dighosp-docs/pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "dighosp-docs"
version = "0.1.1a0"
version = "0.1.1"
description = "# Internal (developer) documentation"
authors = ["Yin-Chi Chan <ycc39@cam.ac.uk>"]
readme = "README.md"
Expand Down
4 changes: 4 additions & 0 deletions dighosp-docs/source/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

## 0.1

### 0.1.1
- Modify DES service to precompute KPI figure/table data for improved page loading speed
- Add version numbers to saved KPI data

### 0.1.0

- Functioning frontend (based on existing [digital-hosp-frontend](https://github.com/cam-digital-hospitals/digital-hosp-frontend))
Expand Down
9 changes: 0 additions & 9 deletions dighosp-docs/source/roadmap.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,6 @@

## 0.1

### 0.1.1
- Modify DES service to precompute KPIs for improved page loading speed
- Actually, a entire Plotly Figure object is saved in `dict` form
- [x] Run automatically when all simulation replications finished, save results in database
- [x] New API endpoint for fetching precomputed KPI values from database; modify frontend accordingly
- Keep or remove full simulation results???
- If kept, can recompute KPIs if changes are made (e.g. to add more KPIs to the output)
- [x] Add version numbers to saved KPI values

### 0.1.2
- [ ] Containerise the DES workers
- See: https://github.com/yinchi/container-queue
Expand Down
8 changes: 5 additions & 3 deletions dighosp-frontend/dighosp_frontend/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ def app_main():
style={'max-width': '1600px'}
) as ret:
with dbc.NavbarSimple(
brand='Digital Hospitals Demo',
brand=f'Digital Hospitals Demo v{conf.APP_VERSION.base_version}',
brand_href=dash.get_relative_path('/#'),
color='primary',
dark=True,
Expand All @@ -47,8 +47,10 @@ def app_main():
href=dash.get_relative_path(service_data['href'])
)
with dbc.DropdownMenu(nav=True, in_navbar=True, label='Developer'):
yield dbc.DropdownMenuItem("Docs", href='/docs', external_link=True)
yield dbc.DropdownMenuItem("MongoDB admin", href='/mongoadmin', external_link=True)
yield dbc.DropdownMenuItem(
"Docs", href='/docs', external_link=True, target='_blank')
yield dbc.DropdownMenuItem(
"MongoDB admin", href='/mongoadmin', external_link=True, target='_blank')
yield dash.page_container
return ret

Expand Down
14 changes: 14 additions & 0 deletions dighosp-frontend/dighosp_frontend/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@
import os
from pathlib import Path

import toml
from dotenv import find_dotenv, load_dotenv
from packaging.version import Version

env_get = os.environ.get

Expand All @@ -23,3 +25,15 @@
if __name__ == "__main__":
print(ASSETS_DIRNAME)
print(ASSETS_DIRNAME.is_dir())


# APP VERSION
APP_VERSION = None
try:
data = toml.load('/app/pyproject.toml')
if 'project' in data and 'version' in data['project']:
APP_VERSION = Version(data['project']['version'])
elif 'tool' in data and 'poetry' in data['tool'] and 'version' in data['tool']['poetry']:
APP_VERSION = Version(data['tool']['poetry']['version'])
except Exception:
pass # Keep default of None
21 changes: 16 additions & 5 deletions dighosp-frontend/dighosp_frontend/pages/des_result_single.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,23 +46,34 @@ def layout(job_id: str):
@composition
def populate_card(job_id: str):
"""Generate the Card layout showing KPIs for the simulation job."""
fig_data = get_kpi_figs(job_id)
kpi_objs = get_kpi_objs(job_id)

with dbc.Card(class_name='mb-3') as ret1:
with dbc.CardBody():
yield dcc.Graph(figure=fig_data['tat'])
yield html.H3('Lab Turnaround Times')
yield dbc.Table.from_dataframe(
pd.DataFrame(kpi_objs['tat_table']),
striped=True, bordered=True, hover=True
)
yield dcc.Graph(figure=kpi_objs['tat'])

with dbc.Card(class_name='mb-3') as ret2:
with dbc.CardBody():
yield dcc.Graph(figure=fig_data['wip'])
yield html.H3('Work in Progress')
yield dcc.Graph(figure=kpi_objs['wip'])
n = get_num_reps(job_id)
yield html.P(f"""\
Bands denote the lower and upper deciles (light blue) and quantiles (dark blue); the black line \
denotes the median ({n} simulation runs).""")

with dbc.Card(class_name='mb-3') as ret3:
with dbc.CardBody():
yield dcc.Graph(figure=fig_data['utilisation'])
yield html.H3('Resource Utilisation')
yield dbc.Table.from_dataframe(
pd.DataFrame(kpi_objs['utilisation_table']),
striped=True, bordered=True, hover=True
)
yield dcc.Graph(figure=kpi_objs['utilisation'])

return [ret1, ret2, ret3]

Expand All @@ -78,7 +89,7 @@ def get_num_reps(job_id: str) -> dict:



def get_kpi_figs(job_id: str) -> dict:
def get_kpi_objs(job_id: str) -> dict:
"""Get the KPI-related figure objects for a given job."""
url = f'{DES_FASTAPI_URL}/jobs/{job_id}/results/dash_objs'
response = requests.get(url, timeout=100)
Expand Down
3 changes: 2 additions & 1 deletion dighosp-frontend/pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "dighosp-frontend"
version = "0.1.1a0"
version = "0.1.1"
description = ""
authors = ["Yin-Chi Chan <ycc39@cam.ac.uk>"]
readme = "README.md"
Expand All @@ -24,6 +24,7 @@ gunicorn = "^22.0.0"

[tool.poetry.group.dev.dependencies]
pylint = "^3.2.5"
ipykernel = "^6.29.5"

[build-system]
requires = ["poetry-core"]
Expand Down

0 comments on commit e767185

Please sign in to comment.