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

feat: Add PeerReviewer model for improved peer review support #748

Merged
merged 28 commits into from
Oct 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
556d06f
feat: add PeerReviewer model to replace basic member search
sgfost Jul 11, 2024
08ba752
feat: create ReviewerSearch component
Karthik99999 Jul 15, 2024
b3e5b12
feat: add Manage Reviewers page
Karthik99999 Jul 26, 2024
698aabe
feat: create modal for add/edit form
Karthik99999 Jul 29, 2024
5d252d5
fix: remove extra migration
Karthik99999 Jul 31, 2024
9c32d94
fix: remove pagination and queryset from jinja template
Karthik99999 Jul 31, 2024
bfb9225
refactor: remove ReviewerAddModal component
Karthik99999 Aug 1, 2024
ab6a0d2
feat: add filters
Karthik99999 Aug 2, 2024
3c51954
feat: add tabs to switch between review dashboard and reviewers page
Karthik99999 Aug 6, 2024
968dc2f
feat: PeerReviewer permissions and create logic
sgfost Aug 7, 2024
7e422fc
refactor: change filters to apply immediately and use checkboxes for …
Karthik99999 Aug 8, 2024
5b8dd7f
refactor: convert reviewer card to component
Karthik99999 Aug 9, 2024
1673f72
fix: clone reviewer prop on edit form
Karthik99999 Aug 13, 2024
4de99df
feat: allow Peer Reviewer data to be edited in profile edit page
Karthik99999 Aug 23, 2024
dfb4eb3
feat: add link to profile edit page on peer reviewer help pages
Karthik99999 Sep 3, 2024
609eaad
fix: add id to MemberProfileSerializer
Karthik99999 Sep 6, 2024
c579e41
fix: remove write only from member_profile_id
Karthik99999 Sep 6, 2024
fc85cf9
refactor: Use PeerReviewer object on PeerReviewInvitation
Karthik99999 Sep 20, 2024
fd51071
fix: create PeerReviewer object in test mock data
Karthik99999 Sep 30, 2024
44c5b3a
fix: remove unnecessary comments
Karthik99999 Oct 18, 2024
20e0136
fix: unpack tuple returned by get_or_create
Karthik99999 Oct 18, 2024
8fee78b
style: change formatting of reviewer card
Karthik99999 Oct 18, 2024
eec026b
feat: remove details of inactive reviewers
Karthik99999 Oct 19, 2024
9c60842
chore: register reviewer list vue app
sgfost Oct 28, 2024
036afdd
fix: don't paginate reviewers list
sgfost Oct 28, 2024
8ffe36f
fix: allow members to view/edit their own reviewer object
sgfost Oct 28, 2024
d105df4
refactor: add MemberProfile.is_reviewer back
alee Oct 29, 2024
0e81d70
refactor: rename field value to be meaningful in field components
sgfost Oct 29, 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
3 changes: 1 addition & 2 deletions django/core/jinja2/core/member_profiles/retrieve.jinja
Original file line number Diff line number Diff line change
Expand Up @@ -158,8 +158,7 @@
<p class='lead p-3'>
You haven't been invited to review other CoMSES member's models yet. If you would like to be added
to the pool of CoMSES Computational Model Library
<a href='{{url("library:peer-review-overview")}}'>Peer Reviewers</a> please
<a href='{{slugurl("contact")}}'>let us know</a>.
<a href='{{url("library:peer-review-overview")}}'>Peer Reviewers</a> please visit your <a href='{{ profile.get_edit_url() }}'>profile edit page</a> and create a peer reviewer profile.
</p>
{% endif %}
</div>
Expand Down
9 changes: 4 additions & 5 deletions django/core/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@ class ComsesGroups(Enum):
MODERATOR = "Moderators"
EDITOR = "Editors"
FULL_MEMBER = "Full Members"
REVIEWER = "Reviewers"

@staticmethod
@transaction.atomic
Expand Down Expand Up @@ -437,6 +436,10 @@ def discourse_username(self):
def is_active(self):
return self.user.is_active

@property
def is_reviewer(self):
return hasattr(self, "peer_reviewer")

# Urls
@property
def orcid_url(self):
Expand Down Expand Up @@ -510,10 +513,6 @@ def primary_affiliation_name(self):
def submitter(self):
return self.user

@cached_property
def is_reviewer(self):
return ComsesGroups.REVIEWER.is_member(self.user)

@cached_property
def name(self):
return self.user.get_full_name() or self.user.username
Expand Down
6 changes: 5 additions & 1 deletion django/core/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,9 @@ class MemberProfileSerializer(serializers.ModelSerializer):
profile_url = serializers.URLField(source="get_absolute_url", read_only=True)
bio = MarkdownField()
research_interests = MarkdownField()
peer_reviewer_id = serializers.IntegerField(
source="peer_reviewer.id", read_only=True, allow_null=True, default=None
)

def validate_affiliations(self, value):
return validate_affiliations(value)
Expand Down Expand Up @@ -240,6 +243,7 @@ class Meta:
model = MemberProfile
fields = (
# User
"id",
"date_joined",
"family_name",
"given_name",
Expand All @@ -261,12 +265,12 @@ class Meta:
"orcid_url",
"github_url",
"personal_url",
"is_reviewer",
"professional_url",
"profile_url",
"research_interests",
"affiliations",
"name",
"peer_reviewer_id",
)


Expand Down
1 change: 1 addition & 0 deletions django/library/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ class Meta:
"review",
"editor",
"candidate_reviewer",
"reviewer",
]


Expand Down
16 changes: 16 additions & 0 deletions django/library/jinja2/library/review/dashboard.jinja
Original file line number Diff line number Diff line change
@@ -1,10 +1,26 @@
{% extends 'sidebar_layout.jinja' %}
{% from "common.jinja" import breadcrumb %}
{% from "library/review/includes/macros.jinja" import display_closed_status %}

{% block introduction %}
<h1>Peer Review Editor Dashboard</h1>
{% endblock %}

{% block top %}
{{ breadcrumb([
{'url': '/reviews/', 'text': 'Reviews'},
{'text': 'Review Editor Dashboard' }])
}}
<ul class="nav nav-tabs justify-content-center mb-4">
<li class="nav-item">
<a href="/reviews/dashboard" class="nav-link active">Reviews</a>
</li>
<li class="nav-item">
<a href="/reviews/reviewers" class="nav-link">Reviewers</a>
</li>
</ul>
{% endblock %}

{% block content %}
{% for codebase in codebases %}
<div class="card">
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Dear {{ invitation.invitee }},

Thank you for letting us know you were unable to accept the invitation to provide peer review for this model. If you no longer wish to serve as a CoMSES Net peer reviewer please [let us know]({{build_absolute_uri(slugurl("contact"))}}) and we will remove you from this opt-in group. *Note:* submitting your own computational model for peer review automatically adds you to the pool of candidate peer reviewers.
Thank you for letting us know you were unable to accept the invitation to provide peer review for this model. If you no longer wish to serve as a CoMSES Net peer reviewer please visit your <a href='{{ profile.get_edit_url() }}'>profile edit page</a> and deactivate your peer reviewer profile.

Best regards,

Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
Dear {{invitation.editor.name}},

The reviewer {{ invitation.candidate_reviewer }} has submitted their feedback. Action is needed on your part to continue the peer review process and send the recommendation back to the original submitter. You can do this at the [review management page]({{ build_absolute_uri(review.get_absolute_url()) }}).
The reviewer {{ invitation.reviewer.member_profile }} has submitted their feedback. Action is needed on your part to continue the peer review process and send the recommendation back to the original submitter. You can do this at the [review management page]({{ build_absolute_uri(review.get_absolute_url()) }}).
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
{% block content %}
<h2>Editor Peer Review Management</h2>
<p class='lead'>
Feedback submitted by <mark>{{review_feedback.invitation.candidate_reviewer}}</mark> on {{format_datetime(review_feedback.last_modified)}}.
Feedback submitted by <mark>{{review_feedback.invitation.reviewer.member_profile}}</mark> on {{format_datetime(review_feedback.last_modified)}}.
</p>
<h2>{{ review_feedback.invitation.review.title }}<a href="{{ review_feedback.invitation.review.codebase_release.share_url }}">(click to view in a new window/tab)</a>
</h2>
Expand Down
26 changes: 26 additions & 0 deletions django/library/jinja2/library/review/reviewers.jinja
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{% extends 'base.jinja' %}
{% from "common.jinja" import breadcrumb %}

{% block introduction %}
<h1>Manage Peer Reviewers</h1>
{% endblock %}

{% block content %}
Karthik99999 marked this conversation as resolved.
Show resolved Hide resolved
{{ breadcrumb([
{'url': '/reviews/', 'text': 'Reviews'},
{'text': 'Manage Reviewers' }])
}}
<ul class="nav nav-tabs justify-content-center mb-4">
<li class="nav-item">
<a href="/reviews/dashboard" class="nav-link">Reviews</a>
</li>
<li class="nav-item">
<a href="/reviews/reviewers" class="nav-link active">Reviewers</a>
</li>
</ul>
<div id="reviewer-list"></div>
{% endblock %}

{% block js %}
{{ vite_asset("apps/reviewer_list.ts") }}
{% endblock %}
66 changes: 66 additions & 0 deletions django/library/migrations/0029_peerreviewer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# Generated by Django 4.2.14 on 2024-07-11 17:58

import django.contrib.postgres.fields
from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

dependencies = [
("core", "0021_add_spam_moderation"),
("library", "0028_contributor_json_affiliations"),
]

operations = [
migrations.CreateModel(
name="PeerReviewer",
fields=[
(
"id",
models.AutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("date_created", models.DateTimeField(auto_now_add=True)),
("is_active", models.BooleanField(default=True)),
(
"programming_languages",
django.contrib.postgres.fields.ArrayField(
base_field=models.CharField(max_length=100),
blank=True,
default=list,
help_text="Programming Languages this reviewer knows, e.g. Python, Java",
size=None,
),
),
(
"subject_areas",
django.contrib.postgres.fields.ArrayField(
base_field=models.CharField(max_length=100),
blank=True,
default=list,
help_text="Areas of expertise, e.g. social science, biology",
size=None,
),
),
(
"notes",
models.TextField(
blank=True, help_text="Any additional notes about this reviewer"
),
),
(
"member_profile",
models.OneToOneField(
on_delete=django.db.models.deletion.CASCADE,
related_name="peer_reviewer",
to="core.memberprofile",
),
),
],
),
]
52 changes: 52 additions & 0 deletions django/library/migrations/0030_peerreviewinvitation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# Generated by Django 4.2.14 on 2024-09-20 23:12

from django.db import migrations, models
import django.db.models.deletion
from django.utils import timezone
from datetime import timedelta


def create_reviewers(apps, schema):
PeerReviewInvitation = apps.get_model("library", "PeerReviewInvitation")
PeerReviewer = apps.get_model("library", "PeerReviewer")
PeerReviewerFeedback = apps.get_model("library", "PeerReviewerFeedback")

one_year_ago = timezone.now() - timedelta(days=365)
for invitation in PeerReviewInvitation.objects.all():
member_profile = invitation.candidate_reviewer
reviewer, created = PeerReviewer.objects.get_or_create(
member_profile=member_profile,
defaults={
"is_active": PeerReviewerFeedback.objects.filter(
invitation__candidate_reviewer=member_profile,
last_modified__gte=one_year_ago,
).exists()
},
)
invitation.reviewer = reviewer
invitation.save()


class Migration(migrations.Migration):

dependencies = [
("library", "0029_peerreviewer"),
]

operations = [
migrations.AddField(
model_name="peerreviewinvitation",
name="reviewer",
field=models.ForeignKey(
null=True,
on_delete=django.db.models.deletion.PROTECT,
related_name="invitation_set",
to="library.peerreviewer",
),
),
migrations.RunPython(create_reviewers),
migrations.AlterUniqueTogether(
name="peerreviewinvitation",
unique_together={("review", "reviewer")},
),
]
Loading
Loading