Skip to content

Commit db61a74

Browse files
committed
Migration 'next' close action to transitions
Remove all usage of close/next in the UI (just administrative actions). Remove all 'next' detection logic. The UI will provide valid options to the user to choose between and they can make an informed choice; especially since for the typical user there will only be a single choice, to the Future Commitfest or back to the Parked one. Committers can also choose to bring any patch into the In Progress or Open/Interlude. The implicit constraint 'next' applied to the system was that, practically speaking, as Patches moved they always move to a Commitfest that was started in the future: thus current_patch_on_commitfest could just use a descending start date sort to find the primary Commitfest. The transition action uses the Workflow implementation which allows for a Patch to re-enter the same Commitfest. This changes the entrydate on the POC which is now used to find the current Commitfest. The Workflow module tries to improve upon the History messages being generated. Additional thoughts forthcoming in this area. Should be simple enough to retain the status quo on these if desired, especially since I'm disliking the absence of a cfid column; which is why i added the relevant POC Commitfest name to the messages. The new code is a bit chatty in terms of feedback to the user. Though, on the flip side, it intentionally doesn't feel the need to verify that the button just clicked was intended. That needs to be tidied up based upon the guidelines I need to review.
1 parent 87ab07c commit db61a74

File tree

5 files changed

+103
-108
lines changed

5 files changed

+103
-108
lines changed

pgcommitfest/commitfest/models.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,7 @@ class Patch(models.Model, DiffableModel):
180180
}
181181

182182
def current_commitfest(self):
183-
return self.commitfests.order_by("-startdate").first()
183+
return self.commitfests.order_by("-patchoncommitfest__enterdate").first()
184184

185185
def current_patch_on_commitfest(self):
186186
cf = self.current_commitfest()

pgcommitfest/commitfest/templates/patch_administrative.inc

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,37 @@
2121
<li role="presentation"><a href="close/reject/" onclick="return verify_reject()">Rejected</a></li>
2222
<li role="presentation"><a href="close/withdrawn/" onclick="return verify_withdrawn()">Withdrawn</a></li>
2323
<li role="presentation"><a href="close/feedback/" onclick="return verify_returned()">Returned with feedback</a></li>
24-
<li role="presentation"><a href="close/next/?cfid={{cf.id}}" onclick="return verify_next()">Move to next CF</a></li>
2524
<li role="presentation"><a href="close/committed/" onclick="return flagCommitted({%if patch.committer%}'{{patch.committer}}'{%elif is_committer%}'{{user.username}}'{%else%}null{%endif%})">Committed</a></li>
25+
<li role="presentation" class="divider"></li>
26+
<li role="presentation" class="dropdown-header">Move To Options</li>
27+
{%if not cf.isfuture and workflow.future %}
28+
<li role="presentation">
29+
<a
30+
href="transition/?fromcfid={{poc.commitfest.id}}&tocfid={{workflow.future.id}}">
31+
{{workflow.future.name}}</a>
32+
</li>
33+
{%endif%}
34+
{%if not cf.isopen and workflow.open %}
35+
<li role="presentation">
36+
<a
37+
href="transition/?fromcfid={{poc.commitfest.id}}&tocfid={{workflow.open.id}}">
38+
{{workflow.open.name}}</a>
39+
</li>
40+
{%endif%}
41+
{%if not cf.isinprogress and workflow.progress %}
42+
<li role="presentation">
43+
<a
44+
href="transition/?fromcfid={{poc.commitfest.id}}&tocfid={{workflow.progress.id}}">
45+
{{workflow.progress.name}}</a>
46+
</li>
47+
{%endif%}
48+
{%if not cf.isparked and workflow.parked %}
49+
<li role="presentation">
50+
<a
51+
href="transition/?fromcfid={{poc.commitfest.id}}&tocfid={{workflow.parked.id}}">
52+
{{workflow.parked.name}}</a>
53+
</li>
54+
{%endif%}
2655
</ul>
2756
</div>
2857

pgcommitfest/commitfest/templates/patch_workflow.inc

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,42 @@
2727
<a class="btn btn-default" href="close/reject/" onclick="return verify_reject()">Rejected</a>
2828
<a class="btn btn-default" href="close/withdrawn/" onclick="return verify_withdrawn()">Withdrawn</a>
2929
<a class="btn btn-default" href="close/feedback/" onclick="return verify_returned()">Returned with feedback</a>
30-
<a class="btn btn-default" href="close/next/?cfid={{cf.id}}" onclick="return verify_next()">Move to next CF</a>
3130
<a class="btn btn-default" href="close/committed/" onclick="return flagCommitted({%if patch.committer%}'{{patch.committer}}'{%elif is_committer%}'{{user.username}}'{%else%}null{%endif%})">Committed</a>
3231
</td>
3332
<!-- Move -->
3433
<td>
35-
/transition Actions Will Go Here (Deprecate Next)
34+
{%if not cf.isfuture and workflow.future %}
35+
{%if True %}
36+
<a class="btn btn-default"
37+
href="transition/?fromcfid={{poc.commitfest.id}}&tocfid={{workflow.future.id}}">
38+
{{workflow.future.name}}</a>
39+
{%endif%}
40+
{%endif%}
41+
42+
{%if not cf.isopen and workflow.open %}
43+
{%if is_committer %}
44+
<a class="btn btn-default"
45+
href="transition/?fromcfid={{poc.commitfest.id}}&tocfid={{workflow.open.id}}">
46+
{{workflow.open.name}}</a>
47+
{%endif%}
48+
{%endif%}
49+
50+
{%if not cf.isinprogress and workflow.progress %}
51+
{%if is_committer %}
52+
<a class="btn btn-default"
53+
href="transition/?fromcfid={{poc.commitfest.id}}&tocfid={{workflow.progress.id}}">
54+
{{workflow.progress.name}}</a>
55+
{%endif%}
56+
{%endif%}
57+
58+
{%if not cf.isparked and workflow.parked %}
59+
{%if is_author %}
60+
<a class="btn btn-default"
61+
href="transition/?fromcfid={{poc.commitfest.id}}&tocfid={{workflow.parked.id}}">
62+
{{workflow.parked.name}}</a>
63+
{%endif%}
64+
{%endif%}
65+
3666
</td>
3767
</tr>
3868
<tr>

pgcommitfest/commitfest/views.py

Lines changed: 38 additions & 103 deletions
Original file line numberDiff line numberDiff line change
@@ -725,6 +725,12 @@ def patch(request, patchid):
725725
{"title": cf.title, "href": "/%s/" % cf.pk},
726726
],
727727
"userprofile": getattr(request.user, "userprofile", UserProfile()),
728+
"workflow": {
729+
"open": Workflow.open_cf(),
730+
"future": Workflow.future_cf(),
731+
"progress": Workflow.inprogress_cf(),
732+
"parked": Workflow.parked_cf(),
733+
}
728734
},
729735
)
730736

@@ -1016,29 +1022,7 @@ def status(request, patchid, status):
10161022
@transaction.atomic
10171023
def close(request, patchid, status):
10181024
patch = get_object_or_404(Patch.objects.select_related(), pk=patchid)
1019-
cf = patch.current_commitfest()
1020-
1021-
try:
1022-
request_cfid = int(request.GET.get("cfid", ""))
1023-
except ValueError:
1024-
# int() failed, ignore
1025-
request_cfid = None
1026-
1027-
if request_cfid is not None and request_cfid != cf.id:
1028-
# The cfid parameter is only added to the /next/ link. That's the only
1029-
# close operation where two people pressing the button at the same time
1030-
# can have unintended effects.
1031-
messages.error(
1032-
request,
1033-
"The patch was moved to a new commitfest by someone else. Please double check if you still want to retry this operation.",
1034-
)
1035-
return HttpResponseRedirect("/%s/%s/" % (cf.id, patch.id))
1036-
1037-
poc = get_object_or_404(
1038-
PatchOnCommitFest.objects.select_related(),
1039-
commitfest__id=cf.id,
1040-
patch__id=patchid,
1041-
)
1025+
poc = patch.current_patch_on_commitfest()
10421026

10431027
poc.leavedate = datetime.now()
10441028

@@ -1051,86 +1035,6 @@ def close(request, patchid, status):
10511035
poc.status = PatchOnCommitFest.STATUS_WITHDRAWN
10521036
elif status == "feedback":
10531037
poc.status = PatchOnCommitFest.STATUS_RETURNED
1054-
elif status == "next":
1055-
# Only some patch statuses can actually be moved.
1056-
if poc.status in (
1057-
PatchOnCommitFest.STATUS_COMMITTED,
1058-
PatchOnCommitFest.STATUS_NEXT,
1059-
PatchOnCommitFest.STATUS_RETURNED,
1060-
PatchOnCommitFest.STATUS_REJECTED,
1061-
):
1062-
# Can't be moved!
1063-
messages.error(
1064-
request,
1065-
"A patch in status {0} cannot be moved to next commitfest.".format(
1066-
poc.statusstring
1067-
),
1068-
)
1069-
return HttpResponseRedirect("/%s/%s/" % (poc.commitfest.id, poc.patch.id))
1070-
elif poc.status in (
1071-
PatchOnCommitFest.STATUS_REVIEW,
1072-
PatchOnCommitFest.STATUS_AUTHOR,
1073-
PatchOnCommitFest.STATUS_COMMITTER,
1074-
):
1075-
# This one can be moved
1076-
pass
1077-
else:
1078-
messages.error(request, "Invalid existing patch status")
1079-
1080-
oldstatus = poc.status
1081-
1082-
poc.status = PatchOnCommitFest.STATUS_NEXT
1083-
# Figure out the commitfest to actually put it on
1084-
newcf = CommitFest.objects.filter(status=CommitFest.STATUS_OPEN)
1085-
if len(newcf) == 0:
1086-
# Ok, there is no open CF at all. Let's see if there is a
1087-
# future one.
1088-
newcf = CommitFest.objects.filter(status=CommitFest.STATUS_FUTURE)
1089-
if len(newcf) == 0:
1090-
messages.error(request, "No open and no future commitfest exists!")
1091-
return HttpResponseRedirect(
1092-
"/%s/%s/" % (poc.commitfest.id, poc.patch.id)
1093-
)
1094-
elif len(newcf) != 1:
1095-
messages.error(
1096-
request, "No open and multiple future commitfests exist!"
1097-
)
1098-
return HttpResponseRedirect(
1099-
"/%s/%s/" % (poc.commitfest.id, poc.patch.id)
1100-
)
1101-
elif len(newcf) != 1:
1102-
messages.error(request, "Multiple open commitfests exists!")
1103-
return HttpResponseRedirect("/%s/%s/" % (poc.commitfest.id, poc.patch.id))
1104-
elif newcf[0] == poc.commitfest:
1105-
# The current open CF is the same one that we are already on.
1106-
# In this case, try to see if there is a future CF we can
1107-
# move it to.
1108-
newcf = CommitFest.objects.filter(status=CommitFest.STATUS_FUTURE)
1109-
if len(newcf) == 0:
1110-
messages.error(
1111-
request,
1112-
"Cannot move patch to the same commitfest, and no future commitfests exist!",
1113-
)
1114-
return HttpResponseRedirect(
1115-
"/%s/%s/" % (poc.commitfest.id, poc.patch.id)
1116-
)
1117-
elif len(newcf) != 1:
1118-
messages.error(
1119-
request,
1120-
"Cannot move patch to the same commitfest, and multiple future commitfests exist!",
1121-
)
1122-
return HttpResponseRedirect(
1123-
"/%s/%s/" % (poc.commitfest.id, poc.patch.id)
1124-
)
1125-
# Create a mapping to the new commitfest that we are bouncing
1126-
# this patch to.
1127-
newpoc = PatchOnCommitFest(
1128-
patch=poc.patch,
1129-
commitfest=newcf[0],
1130-
status=oldstatus,
1131-
enterdate=datetime.now(),
1132-
)
1133-
newpoc.save()
11341038
elif status == "committed":
11351039
committer = get_object_or_404(Committer, user__username=request.GET["c"])
11361040
if committer != poc.patch.committer:
@@ -1160,6 +1064,37 @@ def close(request, patchid, status):
11601064
return HttpResponseRedirect("/%s/%s/" % (poc.commitfest.id, poc.patch.id))
11611065

11621066

1067+
@login_required
1068+
@transaction.atomic
1069+
def transition(request, patchid):
1070+
from_cf = Workflow.getCommitfest(request.GET.get("fromcfid", None))
1071+
1072+
if from_cf is None:
1073+
messages.error(request, "Unknown from commitfest id {}".format(request.GET.get("fromcfid", None)))
1074+
return HttpResponseRedirect("/patch/%s/" % (patchid))
1075+
1076+
cur_poc = get_object_or_404(Patch.objects.select_related(), pk=patchid).current_patch_on_commitfest()
1077+
1078+
if from_cf != cur_poc.commitfest:
1079+
messages.error(request, "Transition aborted, Redirect performed. Commitfest for patch already changed.")
1080+
return HttpResponseRedirect("/patch/%s/" % (cur_poc.patch.id))
1081+
1082+
target_cf = Workflow.getCommitfest(request.GET.get("tocfid", None))
1083+
1084+
if target_cf is None:
1085+
messages.error(request, "Unknown destination commitfest id {}".format(request.GET.get("tocfid", None)))
1086+
return HttpResponseRedirect("/patch/%s/" % (cur_poc.patch.id))
1087+
1088+
try:
1089+
new_poc = Workflow.transitionPatch(cur_poc, target_cf, request.user)
1090+
messages.info(request, "Transitioned patch to commitfest %s" % new_poc.commitfest.name)
1091+
except Exception as e:
1092+
messages.error(request, "Failed to transition patch: {}".format(e))
1093+
return HttpResponseRedirect("/patch/%s/" % (cur_poc.patch.id))
1094+
1095+
return HttpResponseRedirect("/patch/%s/" % (new_poc.patch.id))
1096+
1097+
11631098
@login_required
11641099
@transaction.atomic
11651100
def reviewer(request, patchid, status):

pgcommitfest/urls.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,9 @@
2828
re_path(r"^(\d+)/new/$", views.newpatch),
2929
re_path(r"^patch/(\d+)/status/(review|author|committer)/$", views.status),
3030
re_path(
31-
r"^patch/(\d+)/close/(reject|withdrawn|feedback|committed|next)/$", views.close
31+
r"^patch/(\d+)/close/(reject|withdrawn|feedback|committed)/$", views.close
3232
),
33+
re_path(r"^patch/(\d+)/transition/$", views.transition),
3334
re_path(r"^patch/(\d+)/reviewer/(become|remove)/$", views.reviewer),
3435
re_path(r"^patch/(\d+)/committer/(become|remove)/$", views.committer),
3536
re_path(r"^patch/(\d+)/(un)?subscribe/$", views.subscribe),

0 commit comments

Comments
 (0)