diff --git a/physionet-django/console/forms.py b/physionet-django/console/forms.py
index 758a4b90c..4ac02b705 100644
--- a/physionet-django/console/forms.py
+++ b/physionet-django/console/forms.py
@@ -26,6 +26,7 @@
SubmissionStatus,
exists_project_slug,
InternalNote,
+ PeerReview,
)
from project.validators import MAX_PROJECT_SLUG_LENGTH, validate_doi, validate_slug
from user.models import CodeOfConduct, CredentialApplication, CredentialReview, User, TrainingQuestion
@@ -83,6 +84,15 @@ class Meta:
fields = ['content']
+class PeerReviewForm(forms.ModelForm):
+ class Meta:
+ widgets = {
+ 'content': forms.Textarea(attrs={'class': 'form-control', 'rows': 6}),
+ }
+ model = PeerReview
+ fields = ['content']
+
+
class AssignEditorForm(forms.Form):
"""
Assign an editor to a project under submission
diff --git a/physionet-django/console/templates/console/submission_info_card.html b/physionet-django/console/templates/console/submission_info_card.html
index 01e3ebbb6..d07e9a563 100644
--- a/physionet-django/console/templates/console/submission_info_card.html
+++ b/physionet-django/console/templates/console/submission_info_card.html
@@ -33,6 +33,11 @@
{{ notes|task_count_badge|safe }}
+
+ Peer reviews
+ {{ reviews|task_count_badge|safe }}
+
+
@@ -158,6 +163,40 @@
Uploaded Documents
+ {# Reviews #}
+
+
Reviews posted here are visible only to the editorial team. They are not visible to authors.
+
+
+
+
+
+
+
{# Permanent Reassign #}
{% if project.editor == user %}
diff --git a/physionet-django/console/views.py b/physionet-django/console/views.py
index 7311353de..a48bd4ec4 100644
--- a/physionet-django/console/views.py
+++ b/physionet-django/console/views.py
@@ -54,7 +54,8 @@
SubmissionStatus,
Topic,
exists_project_slug,
- InternalNote
+ InternalNote,
+ PeerReview
)
from project.authorization.access import can_view_project_files
from project.utility import readable_size
@@ -278,6 +279,7 @@ def submission_info(request, project_slug):
"""
project = get_object_or_404(ActiveProject, slug=project_slug)
notes = project.internal_notes.all().order_by('-created_at')
+ reviews = project.peer_reviews.all().order_by('-created_at')
user = request.user
authors, author_emails, storage_info, edit_logs, copyedit_logs, latest_version = project.info_card()
@@ -285,6 +287,7 @@ def submission_info(request, project_slug):
data = request.POST or None
reassign_editor_form = forms.ReassignEditorForm(user, data=data)
internal_note_form = forms.InternalNoteForm(data)
+ peer_review_form = forms.PeerReviewForm(data)
embargo_form = forms.EmbargoFilesDaysForm()
passphrase = ''
anonymous_url = project.get_anonymous_url()
@@ -331,6 +334,24 @@ def submission_info(request, project_slug):
else:
messages.error(request, "You are not authorized to delete this note.")
return redirect(f'{request.path}?tab=notes')
+ if 'add_peer_review' in request.POST:
+ if peer_review_form.is_valid():
+ review = peer_review_form.save(commit=False)
+ review.project = project
+ review.created_by = request.user
+ review.save()
+ messages.success(request, "Review added.")
+ peer_review_form = forms.PeerReviewForm()
+ return redirect(f'{request.path}?tab=reviews')
+ if 'delete_peer_review' in request.POST:
+ review_id = request.POST['review_id']
+ review = get_object_or_404(PeerReview, pk=review_id, project=project)
+ if review.created_by == request.user:
+ review.delete()
+ messages.success(request, "Review deleted.")
+ else:
+ messages.error(request, "You are not authorized to delete this review.")
+ return redirect(f'{request.path}?tab=reviews')
url_prefix = notification.get_url_prefix(request)
bulk_url_prefix = notification.get_url_prefix(request, bulk_download=True)
@@ -344,7 +365,10 @@ def submission_info(request, project_slug):
'reassign_editor_form': reassign_editor_form,
'embargo_form': embargo_form,
'notes': notes,
- 'internal_note_form': internal_note_form})
+ 'internal_note_form': internal_note_form,
+ 'reviews': reviews,
+ 'peer_review_form': peer_review_form})
+
@handling_editor
def edit_submission(request, project_slug, *args, **kwargs):
diff --git a/physionet-django/project/modelcomponents/activeproject.py b/physionet-django/project/modelcomponents/activeproject.py
index 4ecd6942f..851e2d9ac 100644
--- a/physionet-django/project/modelcomponents/activeproject.py
+++ b/physionet-django/project/modelcomponents/activeproject.py
@@ -654,3 +654,18 @@ class InternalNote(models.Model):
def __str__(self):
return f"Note by {self.created_by} on {self.created_at}"
+
+
+class PeerReview(models.Model):
+ """
+ Allow reviews to be created for active projects.
+
+ These reviews should only be viewable by people with editor status.
+ """
+ project = models.ForeignKey('project.ActiveProject', on_delete=models.CASCADE, related_name='peer_reviews')
+ created_by = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
+ created_at = models.DateTimeField(auto_now_add=True)
+ content = models.TextField()
+
+ def __str__(self):
+ return f"Review by {self.created_by} on {self.created_at}"