Skip to content

Commit

Permalink
feat: keep release that just passed review unpublished, add publish link
Browse files Browse the repository at this point in the history
"review_complete" release status keeps the release unpublished but does
not allowing modifying files

- added a quick link to the publish modal which is given in the email
  after a review completes and on the detail page if the release is
  publishable + editable

- added link to edit release in the revisions requested email
  • Loading branch information
sgfost committed Sep 22, 2023
1 parent 6760457 commit 862345b
Show file tree
Hide file tree
Showing 15 changed files with 83 additions and 23 deletions.
4 changes: 3 additions & 1 deletion django/library/jinja2/library/codebases/releases/edit.jinja
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@
<div id="release-editor" data-version-number="{{ release.version_number }}"
data-identifier="{{ release.codebase.identifier }}"
data-review-status="{{ release.get_review().get_status_display() if release.get_review() else 'Unreviewed' }}"
data-is-live="{{ release.live }}"></div>
data-is-live="{{ release.live }}"
data-can-edit-originals="{{ release.can_edit_originals }}"
></div>
{% endblock %}

{% block js %}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,10 @@
{% endif %}
{% elif release.is_under_review %}
<div class="alert alert-danger mt-2">This release is currently undergoing peer review and must remain unpublished until complete.</div>
{% elif release.is_review_complete %}
<div class="alert alert-warning mt-2">This release has undergone peer review and is currently unpublished. You can
<a href="{{ release.get_publish_url() }}">publish it here</a>
</div>
{% elif release.is_draft %}
<div class="alert alert-danger mt-2">The release you are viewing is currently a draft.</div>
{% else %}
Expand Down Expand Up @@ -265,6 +269,11 @@
<a href="{{ release.get_edit_url() }}">
<div class="btn btn-secondary my-1 w-100">Edit {{ "Draft" if release.editable else "Metadata" }}</div>
</a>
{% if not release.live and release.is_publishable %}
<a href="{{ release.get_publish_url() }}">
<div class="btn btn-danger my-1 w-100">Publish</div>
</a>
{% endif %}
{% endif %}
{% endwith %}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
{% set release = review.codebase_release %}
{% set release_url = build_absolute_uri(release.get_absolute_url() if release.is_published else release.share_url) %}
{% set release_edit_url = build_absolute_uri(release.get_edit_url()) %}
{% set release_publish_url = build_absolute_uri(release.get_publish_url()) %}

Dear {{ review.submitter.name }},

Congratulations! The peer review of your model [{{ release.title }}]({{ release_url }}) is now complete and your release has been marked as peer reviewed. If your model was previously live the newly reviewed release has been automatically published, otherwise you may [publish it yourself]({{release_edit_url}}). An automatic DOI registration workflow that automatically assigns a DOI to all peer reviewed models is under development but if you need a DOI immediately please feel free to [contact us]({{ build_absolute_uri(slugurl('contact')) }}).
Congratulations! The peer review of your model [{{ release.title }}]({{ release_url }}) is now complete and your release has been marked as peer reviewed. Your release will remain private until you [publish it yourself]({{release_publish_url}}). An automatic DOI registration workflow that automatically assigns a DOI to all peer reviewed models is under development but if you need a DOI immediately please feel free to [contact us]({{ build_absolute_uri(slugurl('contact')) }}).

Thank you for submitting your computational model for review to CoMSES Net! We hope you will be willing to serve as a reviewer of model code as well in future model peer reviews.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,6 @@ The reviewer for your computational model [{{ release.title }}]({{ build_absolut
{{ feedback.notes_to_author }}
{% endautoescape %}

You can follow [this link]({{ build_absolute_uri(release.get_edit_url()) }}) to make changes to your model.

On behalf of the [email protected], thank you for submitting your computational model(s) to CoMSES Net! Our peer review service is intended to serve the community and we hope that you find the requested changes will improve your model's accessibility and potential for reuse. If you have any questions or concerns about this process, please feel free to [contact us]({{ build_absolute_uri(slugurl("contact")) }}).
7 changes: 7 additions & 0 deletions django/library/migrations/0024_add_release_status.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@ def translate_status_to_flags(apps, schema):
CodebaseRelease.objects.filter(status="draft").update(draft=True, live=False)
CodebaseRelease.objects.filter(status="published").update(draft=False, live=True)
CodebaseRelease.objects.filter(status="unpublished").update(draft=False, live=False)
CodebaseRelease.objects.filter(status="under_review").update(
draft=False, live=False
)
CodebaseRelease.objects.filter(status="review_complete").update(
draft=False, live=False
)


class Migration(migrations.Migration):
Expand All @@ -33,6 +39,7 @@ class Migration(migrations.Migration):
("under_review", "Under review"),
("published", "Published"),
("unpublished", "Unpublished"),
("review_complete", "Review complete"),
],
default="draft",
help_text="The current status of this codebase release.",
Expand Down
36 changes: 27 additions & 9 deletions django/library/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -1065,9 +1065,11 @@ class Status(models.TextChoices):
# equivalent to draft but indicates that a release is under review and should not
# block the normal flow of creating/publishing new releases
UNDER_REVIEW = "under_review", _("Under review")
# status given after completing the review process, not editable and not live
REVIEW_COMPLETE = "review_complete", _("Review complete")
# not editable and live
PUBLISHED = "published", _("Published")
# not editable and not live
# indicates a release that was once published but has been unpublished
UNPUBLISHED = "unpublished", _("Unpublished")

date_created = models.DateTimeField(default=timezone.now)
Expand Down Expand Up @@ -1199,6 +1201,9 @@ def get_edit_url(self):
},
)

def get_publish_url(self):
return f"{self.get_edit_url()}?publish"

def get_list_url(self):
return reverse(
"library:codebaserelease-list",
Expand Down Expand Up @@ -1364,11 +1369,20 @@ def is_peer_review_requestable(self):
2. has not already been peer reviewed
3. a related PeerReview does not exist
"""
return (
self.is_publishable and not self.peer_reviewed and self.get_review() is None
)

@property
def is_publishable(self):
"""
Returns true if this release is ready to be published
"""
try:
self.validate_publishable()
return True
except ValidationError:
return False
return not self.peer_reviewed and self.get_review() is None

@property
def is_latest_version(self):
Expand Down Expand Up @@ -1473,13 +1487,18 @@ def is_published(self):
def is_under_review(self):
return self.status == self.Status.UNDER_REVIEW

@property
def is_review_complete(self):
return self.status == self.Status.REVIEW_COMPLETE

@property
def live(self):
return self.is_published

@property
def editable(self):
return self.is_draft or self.is_under_review
def can_edit_originals(self):
"""return true if the original (unpublished) files are editable"""
return not self.live and not self.is_review_complete

def get_status_display(self):
return self.Status(self.status).label
Expand All @@ -1490,6 +1509,7 @@ def get_status_color(self):
self.Status.UNPUBLISHED: "gray",
self.Status.UNDER_REVIEW: "danger",
self.Status.PUBLISHED: "success",
self.Status.REVIEW_COMPLETE: "primary",
}
return COLOR_MAP.get(self.status)

Expand Down Expand Up @@ -1813,11 +1833,9 @@ def set_complete_status(self, editor: MemberProfile):
action=PeerReviewEvent.RELEASE_CERTIFIED,
message="Model has been certified as peer reviewed",
)
# automatically publish the release if previous versions are live
if self.codebase_release.codebase.live:
self.codebase_release.publish()
else:
self.codebase_release.status = CodebaseRelease.Status.UNPUBLISHED
# dont un-publish releases with a review started before the new review process
if self.codebase_release.is_under_review:
self.codebase_release.status = CodebaseRelease.Status.REVIEW_COMPLETE
self.codebase_release.peer_reviewed = True
self.codebase_release.save()
self.codebase_release.codebase.peer_reviewed = True
Expand Down
2 changes: 2 additions & 0 deletions django/library/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -506,6 +506,7 @@ class CodebaseReleaseSerializer(serializers.ModelSerializer):
)
license = LicenseSerializer()
live = serializers.ReadOnlyField()
can_edit_originals = serializers.ReadOnlyField()
os_display = serializers.ReadOnlyField(source="get_os_display")
platforms = TagSerializer(many=True, source="platform_tags")
programming_languages = TagSerializer(many=True)
Expand Down Expand Up @@ -535,6 +536,7 @@ class Meta:
model = CodebaseRelease
fields = (
"absolute_url",
"can_edit_originals",
"citation_text",
"release_contributors",
"date_created",
Expand Down
6 changes: 5 additions & 1 deletion django/library/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -594,11 +594,15 @@ def has_permission(self, request, view):


class NestedCodebaseReleaseUnpublishedFilesPermission(permissions.BasePermission):
def has_object_permission(self, request, view, obj):
def has_object_permission(self, request, view, obj: CodebaseRelease):
if obj.live:
raise DrfPermissionDenied(
"Cannot access unpublished files of published release"
)
if obj.is_review_complete:
raise DrfPermissionDenied(
"Cannot modify unpublished files of a release that has been peer reviewed"
)
if request.method == "GET" and not request.user.has_perm(
"library.change_codebaserelease", obj=obj
):
Expand Down
15 changes: 11 additions & 4 deletions frontend/src/apps/release_editor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,25 @@ const props = extractDataParams("release-editor", [
"identifier",
"reviewStatus",
"isLive",
"canEditOriginals",
]);
console.log(props.reviewStatus);

// check if ?publish is in the url, set prop, and clear from the url
const urlParams = new URLSearchParams(window.location.search);
props.showPublishModal = urlParams.has("publish");
window.history.replaceState({}, "", window.location.pathname + window.location.hash);

const app = createApp(App, props);
const pinia = createPinia();

const router = createRouter({
history: createWebHashHistory(),
routes: [
// only include upload route when release is unpublished
{ path: "/", redirect: { name: props.isLive ? "metadata" : "upload" } },
...(props.isLive ? [] : [{ path: "/upload", component: UploadFormPage, name: "upload" }]),
// only include upload route when original files are editable
{ path: "/", redirect: { name: props.canEditOriginals ? "upload" : "metadata" } },
...(props.canEditOriginals
? [{ path: "/upload", component: UploadFormPage, name: "upload" }]
: []),
{ path: "/metadata", component: MetadataFormPage, name: "metadata" },
{ path: "/contributors", component: ContributorsPage, name: "contributors" },
],
Expand Down
6 changes: 4 additions & 2 deletions frontend/src/components/releaseEditor/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -42,13 +42,13 @@
:files="store.files.media"
/>
</span>
<PublishModal button-class="btn btn-danger" />
<PublishModal :show="showPublishModal" button-class="btn btn-danger" />
</div>
</div>
<hr />
<div class="row mt-3">
<div class="col-md-3">
<ProgressSidebar :isLive="isLive" />
<ProgressSidebar :showUpload="canEditOriginals" />
</div>
<div class="col-md-9">
<div>
Expand Down Expand Up @@ -77,6 +77,8 @@ const props = defineProps<{
versionNumber: string;
reviewStatus: string;
isLive: boolean;
canEditOriginals: boolean;
showPublishModal: boolean;
}>();
const store = useReleaseEditorStore();
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/components/releaseEditor/ProgressSidebar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<div>
<nav class="nav flex-md-column">
<router-link
v-if="!isLive"
v-if="showUpload"
to="/upload"
class="card text-decoration-none flex-grow-1 mb-3 me-3 me-md-0"
active-class="border-secondary"
Expand Down Expand Up @@ -68,7 +68,7 @@ import { useReleaseEditorStore } from "@/stores/releaseEditor";
import ProgressCheck from "@/components/releaseEditor/ProgressCheck.vue";
const props = defineProps<{
isLive: boolean;
showUpload: boolean;
}>();
const store = useReleaseEditorStore();
Expand Down
4 changes: 4 additions & 0 deletions frontend/src/components/releaseEditor/PublishModal.vue
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ import { useReleaseEditorStore } from "@/stores/releaseEditor";
const props = defineProps<{
buttonClass: string;
show: boolean;
}>();
const store = useReleaseEditorStore();
Expand Down Expand Up @@ -91,6 +92,9 @@ const { errors, handleSubmit, values, setValues } = useForm<PublishFields>({
});
onMounted(() => {
if (props.show) {
publishModal.value?.show();
}
if (store.isInitialized) {
setValues({ versionNumber: store.release.versionNumber });
}
Expand Down
4 changes: 3 additions & 1 deletion frontend/src/stores/releaseEditor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ export const useReleaseEditorStore = defineStore("releaseEditor", () => {
programmingLanguages: release.value.programmingLanguages,
outputDataUrl: release.value.outputDataUrl,
live: release.value.live,
canEditOriginals: release.value.canEditOriginals,
license: release.value.license || undefined,
};
});
Expand Down Expand Up @@ -90,7 +91,7 @@ export const useReleaseEditorStore = defineStore("releaseEditor", () => {
isInitialized.value = false;
await fetchCodebaseRelease(identifier, versionNumber);
await fetchMediaFiles();
if (!release.value.live) {
if (release.value.canEditOriginals) {
for (const category of ["data", "code", "docs", "results"]) {
await fetchOriginalFiles(category as FileCategory);
}
Expand Down Expand Up @@ -173,6 +174,7 @@ const INITIAL_STATE: CodebaseReleaseEditorState = {
tags: [],
title: "",
},
canEditOriginals: false,
dateCreated: new Date("2006-01-01"),
dependencies: null,
documentation: null,
Expand Down
1 change: 1 addition & 0 deletions frontend/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,7 @@ export interface RelatedUser {

export interface CodebaseRelease {
absoluteUrl: string;
canEditOriginals: boolean;
citationText?: string;
codebase: Codebase;
dateCreated: Date;
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/util.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
type ExtractedAttributes = {
[key: string]: string | object;
[key: string]: any;
};

export function extractDataParams(elementId: string, names: string[]): ExtractedAttributes {
Expand Down

0 comments on commit 862345b

Please sign in to comment.