diff --git a/.github/workflows/copy_file.yml b/.github/workflows/copy_file.yml
new file mode 100644
index 0000000..6f982e3
--- /dev/null
+++ b/.github/workflows/copy_file.yml
@@ -0,0 +1,26 @@
+name: copy_file
+
+on: workflow_dispatch
+# push:
+# branches:
+# - master
+
+jobs:
+ copy-file:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v2
+
+ - name: Pushes file
+ uses: dmnemec/copy_file_to_another_repo_action@main
+ env:
+ API_TOKEN_GITHUB: ${{ secrets.API_TOKEN_GITHUB }}
+ with:
+ source_file: './documentation/data/website.yml'
+ destination_repo: 'GSTT-CSC/gstt-csc.github.io'
+ destination_folder: '_projects'
+ #email and username of the user associated with the token
+ user_email: ''
+ user_name: ''
+ commit_message: ''
\ No newline at end of file
diff --git a/.github/workflows/render_and_push.yml b/.github/workflows/render_and_push.yml
new file mode 100644
index 0000000..4754775
--- /dev/null
+++ b/.github/workflows/render_and_push.yml
@@ -0,0 +1,61 @@
+# make sure main_image_path in website.yml is correct
+name: render_and_push
+
+# left as manual for testing, to be changed to on push
+on: workflow_dispatch
+# push:
+# branches:
+# - main
+
+jobs:
+ update-and-copy-md:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v2
+ - name: render md and graphs
+ # set up environment, render website_modelcard.md with yml data in data directory, and plot demographics data
+ # NOTE to fix - currently fails if demo_graphs dir already exists in repo AND has files in
+ run: |
+ python -m venv testenv
+ . testenv/bin/activate
+ pip install --upgrade pip
+ pip install rdm
+ pip install matplotlib
+ pip install pillow
+ cd ./documentation
+ make ./release/website_modelcard.md
+ mkdir ../md-to-send
+ mv ./release/website_modelcard.md ../md-to-send/${GITHUB_REPOSITORY#*/}_modelcard.md
+ mkdir demo_graphs
+ python plotyml.py
+ - name: Pushes md
+ uses: SpinyOwl/publish-artifact-to-git@1.0.1
+ with:
+ # need to create a personal access token with read/write access to website repo, and save it to secrets. access tokens expire after 60 days.
+ # a user based naming convention for secrets may be necessary, to ensure each user calls their own access token (if multiple access tokens saved to a repo's secrets)
+ github_pat: '${{ secrets.MODEL_CARD_DEMO }}'
+ repository: GSTT-CSC/gstt-csc.github.io
+ # sending to branch for testing, to be changed to main/master
+ branch: 228_add_modelcard_layout
+ # source folder path needs to match the md-to-send directory created in render md and graphs step above
+ source_folder: ./md-to-send
+ # set target folder in website repo for modelcard md file
+ target_folder: _projects
+ no_delete: true
+ - name: Pushes graphs
+ uses: SpinyOwl/publish-artifact-to-git@1.0.1
+ with:
+ github_pat: '${{ secrets.MODEL_CARD_DEMO }}'
+ repository: GSTT-CSC/gstt-csc.github.io
+ # sending to branch for testing, to be changed to main/master
+ branch: 228_add_modelcard_layout
+ # source folder path needs to match the demo_graphs directory created in render md and graphs step above
+ source_folder: ./documentation/demo_graphs
+ target_folder: assets/img/projects
+ no_delete: true
+
+# works but get this Warning: The `set-output` command is deprecated and will be disabled soon...
+# Please upgrade to using Environment Files...
+# For more information see: https://github.blog/changelog/2022-10-11-github-actions-deprecating-save-state-and-set-output-commands/
+# link says command disable is postponed because of high use
diff --git a/documentation/data/device.yml b/documentation/data/device.yml
index 884287a..f72e052 100644
--- a/documentation/data/device.yml
+++ b/documentation/data/device.yml
@@ -6,6 +6,18 @@ name: "DEVICE"
# Shorthand Name for device, (max 5 characters)
name_sh: "DEV"
+statement_of_purpose: PURPOSE
+
+summary: SUMMARY
+
+modality: MODALITY
+
+pathology: PATHOLOGY
+
+rationale: RATIONALE
+
+patient_pathway: PATHWAY
+
# The current release number for the device
version: "v0.1.0"
@@ -22,9 +34,25 @@ safety_class: B
# "III" -
mhra_class: "IIb"
-# link ot the website page
+# link to the website page
csc_website_url:
#the GitHub URL
github_url:
+#path to logo
+logo:
+
+# Name of or department that approved the project
+Project_approver:
+
+#
+Approval_date:
+
+#is project service development (bool)
+Service_Dev:
+
+#expected completion date
+exp_comp_date:
+
+
diff --git a/documentation/data/documents.yml b/documentation/data/documents.yml
new file mode 100644
index 0000000..f161216
--- /dev/null
+++ b/documentation/data/documents.yml
@@ -0,0 +1,42 @@
+- document: proposal
+ version:
+- document: software_req_spec
+ version:
+- document: hazard_log
+ version:
+- document: software_design_spec
+ version:
+- document: design_plan
+ version:
+- document: verif_and_validation_plan
+ version:
+- document: user_acceptance_criteria
+ version:
+- document: support_agreement
+ version:
+- document: post_deployment_surveillance_plan
+ version:
+- document: clinical_safety_case_report
+ version:
+- document: clinical_risk_management_plan
+ version:
+- document: cyber_security
+ version:
+- document: revision_level_history
+ version:
+- document: risk_management_report
+ version:
+- document: software_description
+ version:
+- document: software_development_and_maintenance_rec
+ version:
+- document: software_plan
+ version:
+- document: software_release_activity_record
+ version:
+- document: test_record
+ version:
+- document: unresolved_anomalies
+ version:
+- document: data_description
+ version:
\ No newline at end of file
diff --git a/documentation/data/model.yml b/documentation/data/model.yml
new file mode 100644
index 0000000..53d060a
--- /dev/null
+++ b/documentation/data/model.yml
@@ -0,0 +1,12 @@
+model_architecture: "model"
+
+MLFlow artefacts:
+# will this path be in project or website repo or elsewhere?
+- image: path/to/performance_image1.png
+ description: "text description"
+- image: image2.png
+ description: "text description"
+
+model_version: "v2.0"
+date:
+pytorch_version:
\ No newline at end of file
diff --git a/documentation/data/roles.yml b/documentation/data/roles.yml
new file mode 100644
index 0000000..6d89cc5
--- /dev/null
+++ b/documentation/data/roles.yml
@@ -0,0 +1,14 @@
+- role: clinical lead
+ people:
+- role: Clinical Safety Officer
+ people:
+- role: csc_development_lead
+ people:
+- role: csc_ml_lead
+ people:
+- role: stakeholder
+ people:
+- role: hod_for_deployment
+ people:
+- role: staff_training_lead
+ people:
\ No newline at end of file
diff --git a/documentation/data/training_data.yml b/documentation/data/training_data.yml
index 0de8e01..4bb08f6 100644
--- a/documentation/data/training_data.yml
+++ b/documentation/data/training_data.yml
@@ -1,10 +1,51 @@
-xnat_project_name:
+xnat_project_name: NAME
-dataset_version:
+dataset_version: VERSION
-preprocess_description:
+preprocess_description: DESCRIPTION
+
+train_dataset_summary: DATASET SUMMARY
+train_data_excl: Exclusion Criteria
+train_dataset_size: SIZE
+train_input: Images
train_dataset:
+- total:
+ subgroups:
+ - gender:
+ Female: 10
+ Male: 10
+ - ethnicity:
+ Not Stated:
+ Asian:
+ Black:
+ Latin American:
+ Mixed:
+ White:
+ - Age:
+ 0-15:
+ 16-20:
+ 21-25:
+ 26-30:
+ 31-35:
+ 36-40:
+ 41-45:
+ 46-50:
+ 51-55:
+ 56-60:
+ 61-65:
+ 66-70:
+ 71-75:
+ 76-80:
+ 81-85:
+ 86-90:
+ 91+:
+- positive:
+ subgroups:
+- postive_female_mixed:
+ subgroups:
+ - age:
+
test_dataset:
diff --git a/documentation/data/website.yml b/documentation/data/website.yml
new file mode 100644
index 0000000..281a2af
--- /dev/null
+++ b/documentation/data/website.yml
@@ -0,0 +1,13 @@
+project_title: PROJECT TITLE
+status: Developing
+
+# image path in website repo
+main_image_path: /assets/img/projects/IMAGE_TITLE.jpg
+
+# logo path in website repo
+logo: path/to/logo
+
+errors: ERRORS
+goals: GOALS
+success_criteria: CRITERIA
+
diff --git a/documentation/documents/website_modelcard.md b/documentation/documents/website_modelcard.md
new file mode 100644
index 0000000..547cfde
--- /dev/null
+++ b/documentation/documents/website_modelcard.md
@@ -0,0 +1,35 @@
+---
+layout: modelcard
+title: {{website.project_title}}
+devicename: {{device.name}}
+status: {{website.status}}
+image: {{website.main_image_path}}
+sex_graph: /assets/img/projects/{{device.name}}_sex_graph.png
+ethn_graph: /assets/img/projects/{{device.name}}_ethn_graph.png
+age_graph: /assets/img/projects/{{device.name}}_age_graph.png
+summary: {{device.summary}}
+modality: {{device.modality}}
+model: {{model.model_architecture}}
+pathology: {{device.pathology}}
+rationale: {{device.rationale}}
+patient-pathway: {{device.patient_pathway}}
+training-data: {{training_data.train_dataset_summary}}
+train_data_excl: {{training_data.train_data_excl}}
+train_input: {{training_data.train_input}}
+train_size: {{training_data.train_dataset_size}}
+errors: {{website.errors}}
+goals: {{website.goals}}
+success-criteria: {{website.success_criteria}}
+
+alternatives: Gleamer, which specialise in trauma x-rays, has been considered for this purpose but was decided not suitable to solve this particular clinical problem. The decision was made to train an in-house algorithm instead.
+
+
+
+
+---
+
+**Project Plan**
+
1. Meeting of all persons involved to determine AI specifications.
2. Setting technical and system requirements for AI model.
3. Dataset curation (retrospective).
4. Model training
5. Model testing
6. Implementation
7. Audit
+
+
+References:
\ No newline at end of file
diff --git a/documentation/documents/website_project.md b/documentation/documents/website_project.md
new file mode 100644
index 0000000..1b27270
--- /dev/null
+++ b/documentation/documents/website_project.md
@@ -0,0 +1,30 @@
+---
+layout: project_page
+title: {{website.project_title}}
+status: {{website.status}}
+image: {{website.main_image_path}}
+summary: {{device.summary}}
+modality: {{device.modality}}
+pathology: {{device.pathology}}
+rationale: {{device.rationale}}
+patient-pathway: {{device.patient_pathway}}
+training-data: {{training_data.train_dataset.summary}}
+errors: {{website.errors}}
+goals: {{website.goals}}
+success-criteria: {{website.success_criteria}}
+
+csc-lead: Dika
+alternatives: Gleamer, which specialise in trauma x-rays, has been considered for this purpose but was decided not suitable to solve this particular clinical problem. The decision was made to train an in-house algorithm instead
+
+---
+MRI imaging is superior in identification of occult carpal fracture, but is not always accessible. Imaging from X-rays can give suboptimal views, and the presentation of arthritis can make small fractures difficult to see. An AI tool to aide clinical diagnosis of occult carpal fractures using x-rays would increase diagnostic sensitivity in areas and situations where MRI is not available.
+A computer aided diagnosis tool which would automatically run when either a scaphoid fracture is suspected or if a patient is referred for a hand/wrist x-ray from A&E would increase sensitivity and confidence of diagnosis. Carpal fractures can be difficult to identify and patients with high clinical suspicion are put in a splint and referred to the fracture clinic even if a fracture isn’t seen on the x-ray by the clinician. Subtle lucency of an un-displaced fracture and the significance of a small bone fragment is currently easily missed. A successful tool would therefore increase diagnostic confidence and accuracy and reduce repeated x-rays and needless fracture clinic referrals
+
+
+Clinical lead: {% for r in roles %}{% if r.role == "clinical lead" %}{{r.people | join('
, ')}}{% endif %}{% endfor %}
+
+**Project Plan**
+1. Meeting of all persons involved to determine AI specifications.
2. Setting technical and system requirements for AI model.
3. Dataset curation (retrospective).
4. Model training
5. Model testing
6. Implementation
7. Audit
+
+
+References:
TOHETI trial results
\ No newline at end of file
diff --git a/documentation/plotyml.py b/documentation/plotyml.py
new file mode 100644
index 0000000..9f25673
--- /dev/null
+++ b/documentation/plotyml.py
@@ -0,0 +1,171 @@
+import matplotlib.pyplot as plt
+import yaml
+from PIL import Image, ImageDraw
+
+# read data from yaml file
+with open('./data/training_data.yml') as file:
+ data = yaml.safe_load(file)
+# needed so that the graphs can be given device specific names in website repo
+with open('./data/device.yml') as file2:
+ device = yaml.safe_load(file2)
+name = device['name']
+
+# plot sex graph
+# would be better to have all plotting in for loop, when doing this note dict_keys is iterable but not indexable
+try:
+ w = data['train_dataset'][0]['total']['subgroups'][0]['gender']
+ y = w.values()
+ x = w.keys()
+
+ fig, ax = plt.subplots()
+ fig.set_size_inches(5.6, 4.8)
+ bars = ax.bar(x, y, color=(0.06, 0.38, 0.94, 0.7))
+
+# formatting graph
+ ax.spines['top'].set_visible(False)
+ ax.spines['right'].set_visible(False)
+ ax.spines['left'].set_visible(False)
+ ax.spines['bottom'].set_color('#DDDDDD')
+
+ ax.tick_params(bottom=False, left=False)
+
+ ax.set_axisbelow(True)
+ ax.yaxis.grid(False)
+ ax.xaxis.grid(False)
+
+# add text above bars, same color as bars
+ bar_color = bars[0].get_facecolor()
+
+ for bar in bars:
+ ax.text(
+ bar.get_x() + bar.get_width() / 2,
+ # may want to add a small number to this param to give a small gap between text and bar
+ bar.get_height(),
+ bar.get_height(),
+ horizontalalignment='center',
+ color=bar_color,
+ weight='bold'
+ )
+
+ ax.set_xlabel('Sex', labelpad=15, color='#333333')
+ ax.set_ylabel('Number of Patients', labelpad=15, color='#333333')
+ ax.set_title('Patients in Training Dataset by Sex', pad=15, color='#333333',
+ weight='bold')
+ fig.tight_layout()
+
+# save fig and clear for next one
+ plt.savefig("demo_graphs/"+name+"_sex_graph.png")
+ plt.clf()
+
+except:
+ img = Image.new(mode="RGBA", size=(480,480), color='lightgrey')
+ draw = ImageDraw.Draw(img)
+ text = "No Image Available"
+ draw.text((10,10), text, fill=(0,0,0))
+ img.save("demo_graphs/"+name+"_sex_graph.png")
+
+
+# plot ethnicity graph
+try:
+ w = data['train_dataset'][0]['total']['subgroups'][1]['ethnicity']
+ y = w.values()
+ x = w.keys()
+
+ fig, ax = plt.subplots()
+ fig.set_size_inches(8, 4.8)
+ bars = ax.bar(x, y, color=(0.06, 0.38, 0.94, 0.7))
+
+ # formatting graph
+ ax.spines['top'].set_visible(False)
+ ax.spines['right'].set_visible(False)
+ ax.spines['left'].set_visible(False)
+ ax.spines['bottom'].set_color('#DDDDDD')
+
+ ax.tick_params(bottom=False, left=False)
+
+ ax.set_axisbelow(True)
+ ax.yaxis.grid(False)
+ ax.xaxis.grid(False)
+
+ # add text above bars, same color as bars
+ bar_color = bars[0].get_facecolor()
+
+ for bar in bars:
+ ax.text(
+ bar.get_x() + bar.get_width() / 2,
+ bar.get_height(),
+ bar.get_height(),
+ horizontalalignment='center',
+ color=bar_color,
+ weight='bold'
+ )
+
+ ax.set_xlabel('Ethnicity', labelpad=15, color='#333333')
+ ax.set_ylabel('Number of Patients', labelpad=15, color='#333333')
+ ax.set_title('Patients in Training Dataset by Ethnicity', pad=15, color='#333333',
+ weight='bold')
+ fig.tight_layout()
+
+ # save fig and clear for next one
+ plt.savefig("demo_graphs/"+name+"_ethn_graph.png")
+ plt.clf()
+
+except:
+ img = Image.new(mode="RGBA", size=(480,480), color='lightgrey')
+ draw = ImageDraw.Draw(img)
+ text = "No Image Available"
+ draw.text((10,10), text, fill=(0,0,0))
+ img.save("demo_graphs/"+name+"_ethn_graph.png")
+
+
+# plot age graph
+try:
+ w = data['train_dataset'][0]['total']['subgroups'][2]['Age']
+ y = w.values()
+ x = w.keys()
+
+ fig, ax = plt.subplots()
+ fig.set_size_inches(11, 4.8)
+ bars = ax.bar(x, y, color=(0.06, 0.38, 0.94, 0.7))
+
+ # formatting graph
+ ax.spines['top'].set_visible(False)
+ ax.spines['right'].set_visible(False)
+ ax.spines['left'].set_visible(False)
+ ax.spines['bottom'].set_color('#DDDDDD')
+
+ ax.tick_params(bottom=False, left=False)
+
+ ax.set_axisbelow(True)
+ ax.yaxis.grid(False)
+ ax.xaxis.grid(False)
+
+ # add text above bars, same color as bars
+ bar_color = bars[0].get_facecolor()
+
+ for bar in bars:
+ ax.text(
+ bar.get_x() + bar.get_width() / 2,
+ bar.get_height(),
+ bar.get_height(),
+ horizontalalignment='center',
+ color=bar_color,
+ weight='bold'
+ )
+
+ ax.set_xlabel('Age', labelpad=15, color='#333333')
+ ax.set_ylabel('Number of Patients', labelpad=15, color='#333333')
+ ax.set_title('Patients in Training Dataset by Age', pad=15, color='#333333',
+ weight='bold')
+ fig.tight_layout()
+
+ # save fig
+ plt.savefig("demo_graphs/"+name+"_age_graph.png")
+
+except:
+ img = Image.new(mode="RGBA", size=(480,480), color='lightgrey')
+ draw = ImageDraw.Draw(img)
+ text = "No Image Available"
+ draw.text((10,10), text, fill=(0,0,0))
+ img.save("demo_graphs/"+name+"_age_graph.png")
+