Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update docs #627

Merged
merged 32 commits into from
Aug 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
731380d
Add initial glossary for docs
rebkwok Aug 14, 2024
b20f63a
Add state machine diagram to docs
rebkwok Aug 14, 2024
5eea6de
Update the About section
rebkwok Aug 14, 2024
51dba96
Rearrange existing docs according to proposed new structure
rebkwok Aug 14, 2024
0c8876e
Screenshot the login form in functional tests
rebkwok Aug 14, 2024
813611e
Screenshot workspace pages in tests
rebkwok Aug 14, 2024
26f3cea
Access Airlock how-to
rebkwok Aug 16, 2024
b18fcda
Add how-to view workspace files
rebkwok Aug 16, 2024
57d4607
Add previous/next links for how-tos
rebkwok Aug 16, 2024
1546b93
Add a temporary note for out of date pages
rebkwok Aug 16, 2024
0c2d90b
Separate nav sections for researchers and output checkers
rebkwok Aug 16, 2024
934d1b8
Consolidate some reviewer sections
rebkwok Aug 16, 2024
6487210
Add pages for viewing files in new tab/plain text/source code
rebkwok Aug 20, 2024
a74957e
Split glossary and terms and definitions
rebkwok Aug 20, 2024
c8089ee
Create and submit release request How To page
rebkwok Aug 20, 2024
092870b
How to withdraw file from request doc
rebkwok Aug 20, 2024
4fa94ea
Move the manually created screenshots to their own folder
rebkwok Aug 20, 2024
759291e
How-to respond to a returned request
rebkwok Aug 20, 2024
3b6d283
Add How to edit file on request page
rebkwok Aug 21, 2024
473b6c0
Add how to withdraw request page
rebkwok Aug 21, 2024
32f540c
Move screenshotting functional tests to their own tests
rebkwok Aug 21, 2024
a8d2961
Skip screenshot tests by default
rebkwok Aug 21, 2024
e6a97df
Auto-include the glossary on all pages
rebkwok Aug 22, 2024
6c1f7c5
How-to page on reviewing a request
rebkwok Aug 22, 2024
0194372
Add how-to release files
rebkwok Aug 27, 2024
a6e16c6
Add reference page on file colours and icons
rebkwok Aug 27, 2024
50e09e4
Add reference page on downloading files
rebkwok Aug 27, 2024
f1d9862
workflow and permissions docs
rebkwok Aug 27, 2024
342b695
Remaining explanation docs pages
rebkwok Aug 27, 2024
cd95e2c
Fix job-server link in access how-to
rebkwok Aug 28, 2024
208106d
Screenshot more context around the Add file button
rebkwok Aug 28, 2024
411d634
Update screenshots
rebkwok Aug 27, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ jobs:

- name: Run tests
run: |
just test-all
RUN_SCREENSHOT_TESTS=True just test-all

# not actually needed for tests, but we want to make sure the dev tooling
# is still working
Expand Down
2 changes: 2 additions & 0 deletions airlock/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -438,3 +438,5 @@ def filter(self, record): # pragma: no cover
},
},
}

SCREENSHOT_DIR = BASE_DIR / "docs" / "screenshots"
1 change: 1 addition & 0 deletions airlock/templates/file_browser/file.html
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,7 @@
height=1000
style="width: 100%;"
sandbox="{{ path_item.iframe_sandbox }} allow-same-origin"
id="content-iframe"
></iframe>
</div>
{% /card %}
163 changes: 82 additions & 81 deletions airlock/templates/file_browser/group.html
Original file line number Diff line number Diff line change
Expand Up @@ -8,101 +8,102 @@
}
</style>
{% #card title=group.title container=True class="group_modal" %}
<form action="{{ group.c2_edit_url }}" method="POST" aria-label="group-edit-form">
{% csrf_token %}
<div style="display: flex; flex-direction: row; width: 100%; flex-wrap: wrap; gap: 1rem;">
<div style="display: flex: flex-direction: column; flex-basis: 100%; flex: 1;">
{% form_textarea field=group.c2_edit_form.context show_placeholder=True resize=True placeholder="Describe the data to be released in this group of files" class="w-full max-w-lg mx-auto" rows=6 disabled=group.c2_readonly readonly=group.c2_readonly %}
<div data-testid="c3">
<form action="{{ group.c2_edit_url }}" method="POST" aria-label="group-edit-form">
{% csrf_token %}
<div style="display: flex; flex-direction: row; width: 100%; flex-wrap: wrap; gap: 1rem;">
<div style="display: flex: flex-direction: column; flex-basis: 100%; flex: 1;">
{% form_textarea field=group.c2_edit_form.context show_placeholder=True resize=True placeholder="Describe the data to be released in this group of files" class="w-full max-w-lg mx-auto" rows=6 disabled=group.c2_readonly readonly=group.c2_readonly %}
</div>
<div style="display: flex: flex-direction: column; flex-basis: 100%; flex: 1; gap: 1rem;">
{% form_textarea field=group.c2_edit_form.controls resize=True show_placeholder=True placeholder="Describe the disclosure controls that have been applied to these files" class="w-full max-w-lg mx-auto" rows=6 disabled=group.c2_readonly readonly=group.c2_readonly %}
</div>
</div>
<div style="display: flex: flex-direction: column; flex-basis: 100%; flex: 1; gap: 1rem;">
{% form_textarea field=group.c2_edit_form.controls resize=True show_placeholder=True placeholder="Describe the disclosure controls that have been applied to these files" class="w-full max-w-lg mx-auto" rows=6 disabled=group.c2_readonly readonly=group.c2_readonly %}
</div>
</div>
{% if not group.c2_readonly %}
<div class="mt-2">
{% #button type="submit" variant="success" id="edit-group-button" disabled=group.c2_readonly %}Save{% /button %}
</div>
{% endif %}
</form>
{% if not group.c2_readonly %}
<div class="mt-2">
{% #button type="submit" variant="success" id="edit-group-button" disabled=group.c2_readonly %}Save{% /button %}
</div>
{% endif %}
</form>

<div class="comments" style="margin-top: 1rem;">
{% #list_group %}
{% for comment, comment_class in group.comments %}
<div class="comments" style="margin-top: 1rem;">
{% #list_group %}
{% for comment, comment_class in group.comments %}

{% fragment as comment_status %}
<span>
{% if request.user.output_checker %}
{% if release_request.get_turn_phase.name == "INDEPENDENT" and comment.review_turn == release_request.review_turn %}
{% #pill variant="info" text="Blinded" class="group" %}
{% fragment as comment_status %}
<span>
{% if request.user.output_checker %}
{% if release_request.get_turn_phase.name == "INDEPENDENT" and comment.review_turn == release_request.review_turn %}
{% #pill variant="info" text="Blinded" class="group" %}
{% comment%}
TODO: get tooltips working for pills
{% tooltip content=comment.visibility.blinded_description %}
{% endcomment%}
{% /pill%}
{% endif %}
{% #pill variant="info" class="group" text=comment.visibility.name.title %}
{% /pill%}
{% endif %}
{% #pill variant="info" class="group" text=comment.visibility.name.title %}
{% comment%}
TODO: get tooltips working for pills
{%tooltip content=comment.visibility.description %}
{% endcomment%}
{% /pill%}
{% endif %}
{% pill variant="info" text=comment.created_at|date:"Y-m-d H:i" %}
</span>
{% endfragment %}
{% #list_group_rich_item custom_status=comment_status class=comment_class title=comment.author %}
{{ comment.comment }}
{% if request.user.username == comment.author %}
{% if comment.visibility.name == "PRIVATE" and comment.review_turn == release_request.review_turn %}
<div>
<form action="{{ group.comment_visibility_public_url }}" method="POST">
{% csrf_token %}
<input type="hidden" name="comment_id" value="{{ comment.id }}">
{% #button variant="danger" type="cancel" %}Make comment visible to all users{% /button %}
</form>
</div>
{% endif %}
{% if group.user_can_comment %}
<div>
<form action="{{ group.comment_delete_url }}" method="POST">
{% csrf_token %}
<input type="hidden" name="comment_id" value="{{ comment.id }}">
{% #button variant="danger" type="submit" %}Delete comment{% /button %}
</form>
</div>
{% endif %}
{% endif %}
{% /list_group_rich_item %}
{% endfor %}
{% if group.user_can_comment and not group.inline %}
{% #list_group_item %}
<form action="{{ group.comment_create_url }}" method="POST" aria-label="group-comment-form">
{% csrf_token %}
{% if request.user.output_checker and release_request.get_turn_phase.name == "INDEPENDENT" %}
{% #alert variant="warning" title="Comments are hidden" dismissible=True %}
Only you will see this comment until two independent reviews have been submitted
{% /alert %}
{% else %}
{% #alert variant="info" title="Comments are pending" no_icon=True %}
Any comments will be shown to other users once you submit or return a request
{% /alert %}
{% /pill%}
{% endif %}
{% pill variant="info" text=comment.created_at|date:"Y-m-d H:i" %}
</span>
{% endfragment %}
{% #list_group_rich_item custom_status=comment_status class=comment_class title=comment.author %}
{{ comment.comment }}
{% if request.user.username == comment.author %}
{% if comment.visibility.name == "PRIVATE" and comment.review_turn == release_request.review_turn %}
<div>
<form action="{{ group.comment_visibility_public_url }}" method="POST">
{% csrf_token %}
<input type="hidden" name="comment_id" value="{{ comment.id }}">
{% #button variant="danger" type="submit" %}Make comment visible to all users{% /button %}
</form>
</div>
{% endif %}
{% if group.user_can_comment %}
<div>
<form action="{{ group.comment_delete_url }}" method="POST">
{% csrf_token %}
<input type="hidden" name="comment_id" value="{{ comment.id }}">
{% #button variant="danger" type="submit" %}Delete comment{% /button %}
</form>
</div>
{% endif %}
{% endif %}
{% /list_group_rich_item %}
{% endfor %}
{% if group.user_can_comment and not group.inline %}
{% #list_group_item %}
<form action="{{ group.comment_create_url }}" method="POST" aria-label="group-comment-form">
{% csrf_token %}
{% if request.user.output_checker and release_request.get_turn_phase.name == "INDEPENDENT" %}
{% #alert variant="warning" title="Comments are hidden" dismissible=True %}
Only you will see this comment until two independent reviews have been submitted
{% /alert %}
{% else %}
{% #alert variant="info" title="Comments are pending" no_icon=True %}
Any comments will be shown to other users once you submit or return a request
{% /alert %}
{% endif %}

{% form_textarea field=group.comment_form.comment placeholder=" " label="Add Comment" show_placeholder=True class="w-full max-w-lg" rows=6 required=False %}
{% if group.comment_form.visibility.field.choices|length == 1 %}
<input type="hidden" name="visibility" value="{{ group.comment_form.visibility.field.choices.0.0 }}"/>
{% else %}
{% form_radios field=group.comment_form.visibility choices=group.comment_form.visibility.field.choices class="w-full max-w-lg" %}
{% endif%}
<div class="mt-2">
{% #button type="submit" variant="success" id="edit-comment-button" %}Comment{% /button %}
</div>
</form>
{% /list_group_item %}
{% endif %}
{% /list_group %}
{% form_textarea field=group.comment_form.comment placeholder=" " label="Add Comment" show_placeholder=True class="w-full max-w-lg" rows=6 required=False %}
{% if group.comment_form.visibility.field.choices|length == 1 %}
<input type="hidden" name="visibility" value="{{ group.comment_form.visibility.field.choices.0.0 }}"/>
{% else %}
{% form_radios field=group.comment_form.visibility choices=group.comment_form.visibility.field.choices class="w-full max-w-lg" %}
{% endif%}
<div class="mt-2">
{% #button type="submit" variant="success" id="edit-comment-button" %}Comment{% /button %}
</div>
</form>
{% /list_group_item %}
{% endif %}
{% /list_group %}
</div>
</div>

{% if not group.inline %}
{% include "activity.html" with activity=group.activity %}
{% else %}
Expand Down
2 changes: 1 addition & 1 deletion airlock/templates/file_browser/request.html
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@
Please confirm you wish to withdraw this request.
</div>
{% #button type="submit" variant="danger" class="action-button" small=True id="withdraw-request-confirm" %}Withdraw{% /button %}
{% #button variant="primary" type="cancel" %}Cancel{% /button %}
{% #button variant="primary" type="cancel" small=True %}Cancel{% /button %}
</form>
{% /card %}
{% /modal %}
Expand Down
2 changes: 1 addition & 1 deletion airlock/templates/login.html
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
<small><code>{{ dev_users_file }}</code></small>
{% /alert %}
{% endif %}
<form class="flex flex-col items-start gap-4" method="POST" action="{% url 'login' %}" onsubmit="showSpinner()" >
<form class="flex flex-col items-start gap-4" method="POST" action="{% url 'login' %}" onsubmit="showSpinner()" data-testid="loginform">
{% csrf_token %}
<input type="hidden" name="next" value="{{ next_url }}" />
{% form_input type="text" field=token_login_form.user required=True label="GitHub username or OpenSAFELY email address" placeholder="opensafely" class="w-full max-w-md" %}
Expand Down
12 changes: 12 additions & 0 deletions docs/airlock-includes/glossary.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
*[release request]: a request to release one or more output files from a workspace to the Jobs site
*[file group]: A collection of files that share the same context and disclosure control information
*[workspace]: a directory of files generated from the jobs that have been run on the Jobs site
*[output file]: A file that has been added to a release request and is to be released
*[supporting file]: A file that has been added to a release request for additional information or context, and will not be released
*[turn]: A stage of the release request process during which the request is considered to be "owned" by either the researcher (author) or the reviewer (output checker).
*[independent review]: Initial blinded review by output checkers
*[consolidation]: Phase after independent review, when output checkers can discuss and consolidate feedback
*[context]: Contextual description of the output files within a file group
*[controls]: Statistical disclosure control measures that have been applied to the files within a file group
*[vote]: An individual output checker's review of a file (i.e. approve/request changed)
*[decision]: The combined decision on the file, based on the submitted reviews from output checkers (approved, request changes, conflicted.)
77 changes: 0 additions & 77 deletions docs/creating-a-release-request.md

This file was deleted.

6 changes: 6 additions & 0 deletions docs/explanation/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
The explanation provides background knowledge about Airlock and its features.

* [Why Airlock?](why-airlock.md)
* [How does a workspace file differ from a request file?](workspace-vs-request-files.md)
* [Workflow and permissions](workflow-and-permissions.md)
* [Notifications about events](notifications.md)
41 changes: 41 additions & 0 deletions docs/explanation/notifications.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
Notifications are sent at various stags during the release request workflow. Notifications may take the form of:

- Emails to the release request author
- GitHub issues in the relevant output-checking repo
- Slack notifications to the output checking team


## Request submitted

- GitHub issue created in relevant repo
- Slack notification to output checkers

## Independent review submitted

- GitHub issue updated
- Slack notification to output checkers

## Request returned

- GitHub issue updated
- Author notified by email

## Request re-submitted

- GitHub issue updated
- Slack notification to output checkers

## Request rejected

- GitHub issue closed
- Author notified by email

## Request withdrawn

- GitHub issue closed
- Slack notification to output checkers

## Files released

- GitHub issue closed
- Author notified by email
14 changes: 14 additions & 0 deletions docs/explanation/why-airlock.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
## Security

Airlock allows us to enforce some safety controls and policies automatically. This includes things such as the number of required reviews, independence of reviews, and ensuring that researchers who are also output checkers are not able to review their own requests.

## User experience

Airlock provides a simple way for users to view their workspace files and
build a release request. It reduces the burden on both researchers and output checkers,
and reduces the risk of user error by automating the workflow and enforcing who is
permitted to act on the request at each stage.

## Audit log

Airlock logs actions taken by users during the life cycle of the request.
Loading
Loading