[MOD-109][Feature] Reviews, Volume I: Moderation#7807
Conversation
[#OSF-8498] Return relationship data for those relations of the form... proto://host:port/v2/<namespace>/<namespace_id>/
* [MOD-20][Feature] Add PreprintService.state field * [MOD-21][Feature] Add ReviewProviderMixin * Responding to review * Use Enum * Isolate transitions machine * comments_public -> comments_private, for clarity
* [MOD-12] Setup ember-osf-reviews app *Update docker setup *Add osf-reviews campaign *Add reviews route *Add osf-reviews logo *Update registration templates *Update osf navbar *Add mako landing page * fix
* Add reviews fields to preprint/provider endpoints * Add permissions... * Cleaner permissions * Apply permissions to ReviewLog endpoints * Preprint permissions... * Use reviews permissions on all preprint endpoints * Filter preprint providers by reviews permission * Add provider setup endpoint * Allow node admins to see logs on their submissions Assuming the provider settings allow it * Hide log creators if comments are anonymous * Add script to generate fake ReviewLogs * Replace log state fields with action * Creating review logs to trigger state transitions * Allow resubmitting * Fix admin * Filter permissions correctly * Responding to review * Fix reviews migration * Responding to review * Fix stupid mistakes * Cleaner PermissionHelper * Fix provider log list view * Add preprint list permissions tests * Fix migrations after rebase * Fix a couple things... * No reviews workflow is None instead of 'none' * Create review logs at /v2/reviews/review_logs/ * Add read-only title and contributors to preprints * Hide preprints with private nodes from moderators * Add date_last_transitioned field to preprints * Expose live reload port for reviews * Better permission names * Tests for creating review logs, transitions
* Add related counts for reviewables * Fixes * CR
* More reviews tests * plac8 flake8 * Filter providers by multiple permissions with OR * Tests and cleanup... * More cleanup * Responding to review
* ReviewLog => Action * Move Action model to osf.models * Fix up migrations * Fix failing tests * Fix up migrations again * Make /actions/ useful in the browsable API
This reverts commit 51d8b4d.
* Set abandoned to False on submission * Optimize query
| target = validated_data.pop('target') | ||
| comment = validated_data.pop('comment', '') | ||
| try: | ||
| if trigger == Triggers.ACCEPT.value: |
There was a problem hiding this comment.
I feel like this will be simplified one day, as it seems like it's having to maintain a redundant list. But not a big deal right now.
|
|
||
| this.nextLink = link ? | ||
| this.nextLink = link ? | ||
| link + '&version=2.2' : |
There was a problem hiding this comment.
It wouldn't be a bad idea to upgrade to 2.6 for this, but I don't think it would hold anything up leaving it at 2.2.
| <div style="margin: 40px;"> | ||
| <p>Hello ${user.fullname},</p> | ||
| % if workflow == 'pre-moderation': | ||
| <p>Your submission "${reviewable.node.title}", submitted to ${reviewable.provider.name} has ${'not been accepted. You may edit the '+ reviewable.provider.preprint_word+ ' and resubmit, at which time it will becoming pending moderation.' if is_rejected else 'been accepted by the moderator and is now discoverable to others.'}</p> |
There was a problem hiding this comment.
Almost always prefer str.format to string concatenation, even in templates.
http://cosdev.readthedocs.io/en/latest/style_guides/python.html#string-formatting
| @@ -0,0 +1,31 @@ | |||
| # -*- coding: utf-8 -*- | |||
There was a problem hiding this comment.
Minor: Can you rename this file to something more descriptive? Custom migrations should not use the autogenerated names. Something like 0065_set_abandoned_false_for_all_submitted_preprints.py
| @@ -0,0 +1,49 @@ | |||
| # -*- coding: utf-8 -*- | |||
There was a problem hiding this comment.
Same here: Can you rename this file to something more descriptive?
| ] | ||
|
|
||
| operations = [ | ||
| migrations.RunPython(add_reviews_notification_setting), |
There was a problem hiding this comment.
This is a very slow migration--after 30 minutes, I'm still in the users whose GUIDS start with "a". And since there are schema changes in later migrations, the app would need to be down until this migration finished.
One workaround would be to make this a mgmt command instead of a migration. Just be clear document in the ticket that the command needs to be run when this is deployed.
| send_type = models.CharField(max_length=50, db_index=True, validators=[validate_subscription_type, ]) | ||
| event = models.CharField(max_length=50) | ||
| message = models.CharField(max_length=2048) | ||
| message = models.CharField(max_length=10000) |
There was a problem hiding this comment.
I think this field should just be changed to a TextField, which doesn't have a db-level limit on character length.
Besides, there's no performance difference between the vachar and text columns in Postgres: https://www.postgresql.org/docs/9.0/static/datatype-character.html
| for user_id in context['email_recipients']: | ||
| user = OSFUser.load(user_id) | ||
| context['is_creator'] = user == context.get('reviewable').node.creator | ||
| email = mails.Mail(template, subject='Confirmation of your submission to {provider}'.format(provider=context.get('reviewable').provider.name)) |
There was a problem hiding this comment.
This should be a constant in website.mails.mails.
| title = ser.CharField(source='node.title', required=False) | ||
| description = ser.CharField(required=False, allow_blank=True, allow_null=True, source='node.description') | ||
| tags = JSONAPIListField(child=NodeTagField(), required=False, source='node.tags') | ||
| node_is_public = ser.BooleanField(read_only=True, source='node__is_public') |
There was a problem hiding this comment.
Do we actually need this field? If you're just using it for filtering, can you do ?filter[node__is_public]=1 instead?
There was a problem hiding this comment.
If there's a way to make that work, that'd be lovely, but I get a 400:
{
"errors": [
{
"source": {
"parameter": "filter"
},
"meta": {},
"detail": "'node__is_public' is not a valid field for this endpoint."
}
]
}even if I add node__is_public to filterable_fields
There was a problem hiding this comment.
I was about to be very surprised and pleased and possibly a little concerned if that worked.
There was a problem hiding this comment.
I don't think it would take a lot to update the filtering to handle this; it's probably just the verification that a field is named what you think it is, but it would be a bit to do.
There was a problem hiding this comment.
Ah, it looks like we check if the passed filter name is one of the serializer fields here:
def _get_field_or_error(self, field_name):
"""
Check that the attempted filter field is valid
:raises InvalidFilterError: If the filter field is not valid
"""
serializer_class = self.serializer_class
if field_name not in serializer_class._declared_fields:
raise InvalidFilterError(detail="'{0}' is not a valid field for this endpoint.".format(field_name))
if field_name not in getattr(serializer_class, 'filterable_fields', set()):
raise InvalidFilterFieldError(parameter='filter', value=field_name)
return serializer_class._declared_fields[field_name]Does DRF support hidden fields? Maybe if read_only=True and write_only=True?
There was a problem hiding this comment.
No need for this to block merging this PR, but it's worth a few minutes to see if we can avoid adding the extra field, especially if we intend to ever support nested filtering (node__is_public), which I think we should.
| def save_changes(self, ev): | ||
| now = self.action.date_created if self.action is not None else timezone.now() | ||
| should_publish = self.reviewable.in_public_reviews_state | ||
| self.reviewable.node._has_abandoned_preprint = False |
There was a problem hiding this comment.
Minor: to avoid potentially repeating queries, save self.reviewable.node to a variable.
node = self.reviewable.node
node._has_abandoned_preprint = False
....
node.save()|
|
||
| Your ${reviewable.provider.name} and OSF teams | ||
|
|
||
| Center for Open Science |
There was a problem hiding this comment.
You need to put one empty line between 'Center for Open Science' and '210 Ridge McIntire Road, Suite 500, Charlottesville, VA 22903-5083' or gmail render will just think they are at the same line.
|
|
||
| Center for Open Science | ||
| 210 Ridge McIntire Road, Suite 500, Charlottesville, VA 22903-5083 | ||
|
|
There was a problem hiding this comment.
You need to add a link to the privacy police here. An example will be https://github.com/CenterForOpenScience/osf.io/pull/7613/files#diff-dfa2a5104ecad07e986bd711b1d9464fR10
|
|
||
| Your ${reviewable.provider.name} and OSF teams | ||
|
|
||
| Center for Open Science |
|
|
||
| Center for Open Science | ||
| 210 Ridge McIntire Road, Suite 500, Charlottesville, VA 22903-5083 | ||
|
|
There was a problem hiding this comment.
A link to privacy policy.
|
|
||
| Your ${reviewable.provider.name} and OSF teams | ||
|
|
||
| Center for Open Science |
|
|
||
| Center for Open Science | ||
| 210 Ridge McIntire Road, Suite 500, Charlottesville, VA 22903-5083 | ||
|
|
There was a problem hiding this comment.
A link to privacy policy
| Your ${reviewable.provider.name} and OSF teams | ||
| <p> | ||
| Center for Open Science<br> | ||
| 210 Ridge McIntire Road, Suite 500, Charlottesville, VA 22903-5083 |
There was a problem hiding this comment.
This is fine for now. But after #7613 got merged in. There will be duplicate in html email templates for COS physical address and privacy policy. Create a ticket for the later on clean up. https://openscience.atlassian.net/browse/OSF-8826
| @@ -0,0 +1,31 @@ | |||
| Hello ${user.fullname}, | |||
There was a problem hiding this comment.
Product team has specified all emails to be in html email template instead of txt. There is a ticket to change all current existing txt email to html, which is https://openscience.atlassian.net/browse/OSF-8770. This is fine for now. But we should stop writing any more txt email template.
Rename migrations Use variable for related node Use constant in mails.py Change message field on NotificationDigest from char to text Add management command for adding subscriptions to users
* Update templates and mixins * Remove .txt templates and Apply other requested changes * Apply requested changes
Respond to Reviews CR
* * Add new re-submission template * Add new method notify_resubmit * Update workflow * Remove zipcode * Apply requested changes * Apply requested changes
|
|
||
| # Handle email notifications including: update comment, accept, and reject of submission. | ||
| @reviews_signals.reviews_email.connect | ||
| def reviews_notification(self, context): |
There was a problem hiding this comment.
Note: This will be refactored in the 0.1.0 release to use a mails.notify-like function instead of checking subscriptions and creating NotificationDigests manually.
https://openscience.atlassian.net/browse/MOD-150
|
|
||
| REVIEWS_SUBMISSION_CONFIRMATION = lambda provider_name: Mail( | ||
| 'reviews_submission_confirmation', | ||
| subject='Confirmation of your submission to {}'.format(provider_name) |
There was a problem hiding this comment.
@laurenbarker Did subject='Confirmation of your submission to ${provider_name}' not work?
There was a problem hiding this comment.
Woops, disregard. Didn't see this one done in a future commit.
| mails.send_mail(user.username, email, mimetype='html', user=user, **context) | ||
| mails.send_mail( | ||
| user.username, | ||
| getattr(mails, 'REVIEWS_SUBMISSION_CONFIRMATION')(context.get('reviewable').provider.name), |
|
🚢 🇮🇹 ! |
Replaces #7708
https://openscience.atlassian.net/browse/MOD-109
Purpose
All the backend changes required for OSF Reviews. Adds workflows for moderators to control which preprints are publicly available in their branded interface.
Changes
New changes since #7708 was approved
metaobject in API responsesget_renderer_contextin the view, something like:/preprint_providers/<id>/preprints/, addmeta[reviews_state_counts]=truein the query string to get counts of that provider's preprints grouped byreviews_stateto top-levelmeta_has_abandoned_preprint=Falseon submission to providers using a reviews workflowfilter[node_is_public], so we can exclude preprints with private nodes from the reviews interfaceOriginal changes
Actiontop-level objectsubmitted preprint X for review at time Y, changing its state frominitialtopending"/actions//actions/<id>//preprints/<guid>/actions//users/me/actions/ReviewableMixinfor objects which can be reviewedPreprintServicereviews_state: Current state of the preprintActionsReviewProviderMixinfor objects which own Reviewables and can choose a workflow for themPreprintProviderreviews_workflow: Provider's chosen workflow (see below)reviews_comments_private: Whether comments made by moderators are visible to the submitter/contributorsreviews_comments_anonymous: Whether the name/ID of the moderators who accept/reject/act on a preprint are visible to the submitter/contributors/preprint_providers/<id>/is once-writablereviews_workflowis non-null, the endpoint is read-onlypre-moderation: Preprints are publicly visible as soon as they're submitted, but can later be removed by moderatorspost-moderation: Preprints are visible only after approval by a moderatorinitial: Default state, created but not yet submittedpending: Submitted for and awaiting review, may be publicly visible depending on the workflowaccepted: Has explicit moderator approval, publicly visible under the providerrejected: Has explicit moderator disapproval, only visible to contributors and moderatorssubmitinitaltopending, or fromrejectedtopendingif resubmission is allowed by the workflowsubmitacceptpendingorrejectedtoacceptedacceptrejectpendingoracceptedtorejectedrejectedit_commentedit_commentadminandmoderatorgroups for each provider, assign group various object permissions on that providerSide Effects
Preprints workflow
If a provider has non-null
reviews_workflow, authors can no longer publish their preprints directly (by settingis_published). Instead, they must create asubmitaction at/actions/. The corresponding changes to ember-preprints are here: CenterForOpenScience/ember-osf-preprints#465If a provider has null
reviews_workflow, the existing behavior is unchanged. Workflows are entirely opt-in per provider, for now.Ember OSF
ember-osf changes: CenterForOpenScience/ember-osf#272