From c7e6edb5f5140b3095eed3012663db109bed1fef Mon Sep 17 00:00:00 2001 From: andrepapoti Date: Mon, 22 Apr 2024 15:23:39 -0300 Subject: [PATCH 1/7] forms: Add SeriesBulkUpdatePatchesForm This form is similar to the MultiplePatchForm but is meant to apply chages to all of thje patches contained in a series. bulk_update_patches() was also added to simplify applying changes to multiple patches Signed-off-by: andrepapoti --- patchwork/forms.py | 52 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/patchwork/forms.py b/patchwork/forms.py index ed06d0d1..2948b002 100644 --- a/patchwork/forms.py +++ b/patchwork/forms.py @@ -224,3 +224,55 @@ def save(self, instance, commit=True): if commit: instance.save() return instance + + +class SeriesBulkUpdatePatchesForm(forms.Form): + action = 'update' + archived = OptionalBooleanField( + choices=[ + ('*', 'no change'), + ('True', 'Archived'), + ('False', 'Unarchived'), + ], + coerce=lambda x: x == 'True', + empty_value='*', + ) + + def __init__(self, project, *args, **kwargs): + super(SeriesBulkUpdatePatchesForm, self).__init__(*args, **kwargs) + self.fields['delegate'] = OptionalModelChoiceField( + queryset=_get_delegate_qs(project=project), required=False + ) + self.fields['state'] = OptionalModelChoiceField( + queryset=State.objects.all() + ) + + def save(self, series, request): + update_values = {} + data = self.cleaned_data + + for name, obj in self.fields.items(): + value = data[name] + if not obj.is_no_change(value): + update_values[name] = value + + patches = Patch.objects.filter(series=series) + err = bulk_update_patches(patches, request, update_values) + + return err + + +def bulk_update_patches(patches, request, values_to_update): + errors = [] + for patch in patches: + if not patch.is_editable(request.user): + errors.append( + "You don't have permissions to edit patch '%s'" % patch.name + ) + continue + + if len(errors) > 0: + return errors + + patches.update(**values_to_update) + return errors From bb8b21ff0d0301c3a0e0547fd02be925d8d4d48d Mon Sep 17 00:00:00 2001 From: andrepapoti Date: Mon, 22 Apr 2024 15:25:34 -0300 Subject: [PATCH 2/7] views: add series-list view This view is meant to give a quickoverview on a project since some of them can have hundreds of patches actives. This view also allows the user to apply changes on all of the series patches at once Signed-off-by: andrepapoti --- .../templates/patchwork/series-list.html | 99 +++++++++++++++++++ patchwork/urls.py | 5 + patchwork/views/series.py | 56 +++++++++++ 3 files changed, 160 insertions(+) create mode 100644 patchwork/templates/patchwork/series-list.html diff --git a/patchwork/templates/patchwork/series-list.html b/patchwork/templates/patchwork/series-list.html new file mode 100644 index 00000000..af0eedcd --- /dev/null +++ b/patchwork/templates/patchwork/series-list.html @@ -0,0 +1,99 @@ +{% extends "base.html" %} + +{% load person %} +{% load static %} + +{% block title %}{{project.name}}{% endblock %} +{% block series_active %}active{% endblock %} + +{% block body %} + +{% load person %} +{% load listurl %} +{% load patch %} +{% load project %} +{% load static %} + +{% include "patchwork/partials/pagination.html" %} + + + + + + + +{% if user.is_authenticated and user.profile.show_ids %} + +{% endif %} + + + + + + + + + + + + + + + + + + +{% for series in series_list %} + +{% if user.is_authenticated and user.profile.show_ids %} + +{% endif %} + + + + + + + + +{% empty %} + + + +{% endfor %} + +
+ ID + + Version + + Series + + Cover Letter + + Number of Patches + + Date + + Submitter + + Edit patches +
+ + + {{ series.version|default:"-"}} + + + {{ series.name|default:"[no subject]"|truncatechars:100 }} + + {{ series.cover_letter.content|default:"No cover letter available" }}{{ series.received_total}}{{ series.date|date:"Y-m-d" }}{{ series.submitter|personify:project }} +
+ {% csrf_token %} + {{ form.as_p }} + +
+
No series to display
+ +{% endblock %} diff --git a/patchwork/urls.py b/patchwork/urls.py index ecd3668d..adacbab3 100644 --- a/patchwork/urls.py +++ b/patchwork/urls.py @@ -36,6 +36,11 @@ patch_views.patch_list, name='patch-list', ), + path( + 'project//series-list/', + series_views.series_list, + name='series-list', + ), path( 'project//bundles/', bundle_views.bundle_list, diff --git a/patchwork/views/series.py b/patchwork/views/series.py index a8892ae6..8ee79939 100644 --- a/patchwork/views/series.py +++ b/patchwork/views/series.py @@ -5,9 +5,12 @@ from django.http import HttpResponse from django.shortcuts import get_object_or_404 +from django.shortcuts import render from patchwork.models import Series +from patchwork.models import Project from patchwork.views.utils import series_to_mbox +from patchwork.forms import SeriesBulkUpdatePatchesForm def series_mbox(request, series_id): @@ -20,3 +23,56 @@ def series_mbox(request, series_id): ) return response + + +def series_list(request, project_id): + project = get_object_or_404(Project, linkname=project_id) + + form = SeriesBulkUpdatePatchesForm(project) + context = {} + + if request.method == 'POST': + form = SeriesBulkUpdatePatchesForm(project, request.POST) + errors = _update_series_patches(request, form) + if len(errors) > 0: + context.update({'errors': errors}) + else: + form = SeriesBulkUpdatePatchesForm(project) + + series_list = ( + Series.objects.filter(project=project) + .only( + 'submitter', + 'project', + 'version', + 'name', + 'date', + 'id', + ) + .select_related('project') + ) + + context.update( + { + 'project': project, + 'projects': Project.objects.all(), + 'series_list': series_list, + 'form': form, + } + ) + + return render(request, 'patchwork/series-list.html', context) + + +def _update_series_patches(request, form): + pk = request.POST.get('save') + series = Series.objects.get(id=pk) + + if not form.is_valid(): + errors = ['The submitted form data was invalid'] + for field_name, error_message in form.errors.items(): + errors.append(f'{field_name}: {error_message}') + + return errors + + return form.save(series, request) From 7bea49b995d1ccacb609f9586c5df7c64803c116 Mon Sep 17 00:00:00 2001 From: andrepapoti Date: Mon, 22 Apr 2024 15:26:04 -0300 Subject: [PATCH 3/7] tests: Add tests for series list view Signed-off-by: andrepapoti --- patchwork/tests/views/test_series.py | 84 ++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 patchwork/tests/views/test_series.py diff --git a/patchwork/tests/views/test_series.py b/patchwork/tests/views/test_series.py new file mode 100644 index 00000000..dfc7ce50 --- /dev/null +++ b/patchwork/tests/views/test_series.py @@ -0,0 +1,84 @@ +# Patchwork - automated patch tracking system +# Copyright (C) 2012 Jeremy Kerr +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from datetime import datetime as dt + +from django.test import TestCase +from django.urls import reverse + +from patchwork.models import Patch +from patchwork.models import Person +from patchwork.tests.utils import create_patch +from patchwork.tests.utils import create_cover +from patchwork.tests.utils import create_person +from patchwork.tests.utils import create_project +from patchwork.tests.utils import create_series +from patchwork.tests.utils import create_user + + +class SeriesList(TestCase): + def setUp(self): + self.project = create_project() + self.user = create_user() + self.person_1 = Person.objects.get(user=self.user) + self.person_2 = create_person() + self.series_1 = create_series(project=self.project) + self.series_2 = create_series(project=self.project) + create_cover(project=self.project, series=self.series_1) + + for i in range(5): + create_patch( + submitter=self.person_1, + project=self.project, + series=self.series_1, + date=dt(2014, 3, 16, 13, 4, 50, 155643), + ) + create_patch( + submitter=self.person_2, + project=self.project, + series=self.series_2, + date=dt(2014, 3, 16, 13, 4, 50, 155643), + ) + + # with open('output.html', "w", encoding="utf-8") as file: + # file.write(response.content.decode('utf-8')) + + def test_series_list(self): + requested_url = reverse( + 'series-list', + kwargs={'project_id': self.project.linkname}, + ) + response = self.client.get(requested_url) + + self.assertEqual(response.status_code, 200) + + def test_update_series_list_unauth(self): + requested_url = reverse( + 'series-list', + kwargs={'project_id': self.project.linkname}, + ) + + data = {'save': self.series_1.id, 'archived': 'True', 'state': '*'} + response = self.client.post(requested_url, data) + + self.assertContains( + response, 'You don't have permissions to edit patch' + ) + + def test_update_series_list(self): + requested_url = reverse( + 'series-list', + kwargs={'project_id': self.project.linkname}, + ) + + data = {'save': self.series_1.id, 'archived': 'True', 'state': '*'} + self.client.login( + username=self.user.username, password=self.user.username + ) + _ = self.client.post(requested_url, data) + + patches = Patch.objects.filter(series=self.series_1) + for patch in patches: + self.assertEqual(patch.archived, True) From ff6cfa33a3592add8c2c9bbfa489fc94bbc3b571 Mon Sep 17 00:00:00 2001 From: andrepapoti Date: Tue, 23 Apr 2024 02:28:28 -0300 Subject: [PATCH 4/7] views: add series-detail view Signed-off-by: andrepapoti --- .../templates/patchwork/series-detail.html | 41 +++++++++++++++++++ patchwork/urls.py | 5 +++ patchwork/views/series.py | 20 +++++++++ 3 files changed, 66 insertions(+) create mode 100644 patchwork/templates/patchwork/series-detail.html diff --git a/patchwork/templates/patchwork/series-detail.html b/patchwork/templates/patchwork/series-detail.html new file mode 100644 index 00000000..720df3be --- /dev/null +++ b/patchwork/templates/patchwork/series-detail.html @@ -0,0 +1,41 @@ +{% extends "base.html" %} + +{% load humanize %} +{% load syntax %} +{% load person %} +{% load patch %} +{% load static %} +{% load utils %} + +{% block title %}{{series.name}}{% endblock %} + +{% block body %} + +
+

{{ series.name }}

+
+ + + + + + + + + + + + + + + + + + +
Cover Letter{{ series.cover_letter.content|default:"No cover letter available" }}
Date{{ series.date }}
Submitter{{ series.submitter }}
Total{{ series.patches }}
+
+

Patches:

+
+{% include "patchwork/partials/patch-list.html" %} + +{% endblock %} diff --git a/patchwork/urls.py b/patchwork/urls.py index adacbab3..5f2a742b 100644 --- a/patchwork/urls.py +++ b/patchwork/urls.py @@ -115,6 +115,11 @@ name='comment-redirect', ), # series views + path( + 'series//', + series_views.series_detail, + name='series-detail', + ), path( 'series//mbox/', series_views.series_mbox, diff --git a/patchwork/views/series.py b/patchwork/views/series.py index 8ee79939..62cc40c4 100644 --- a/patchwork/views/series.py +++ b/patchwork/views/series.py @@ -8,7 +8,9 @@ from django.shortcuts import render from patchwork.models import Series +from patchwork.models import Patch from patchwork.models import Project +from patchwork.views import generic_list from patchwork.views.utils import series_to_mbox from patchwork.forms import SeriesBulkUpdatePatchesForm @@ -25,6 +27,24 @@ def series_mbox(request, series_id): return response +def series_detail(request, series_id): + series = get_object_or_404(Series, id=series_id) + + patches = Patch.objects.filter(series=series) + + context = generic_list( + request, + series.project, + 'series-detail', + view_args={'series_id': series_id}, + patches=patches, + ) + + context.update({'series': series}) + + return render(request, 'patchwork/series-detail.html', context) + + def series_list(request, project_id): project = get_object_or_404(Project, linkname=project_id) From 59b89e87d20941ba3deed556def855d3a041aa45 Mon Sep 17 00:00:00 2001 From: andrepapoti Date: Mon, 22 Apr 2024 15:26:46 -0300 Subject: [PATCH 5/7] views: add shortcut to series-list view on base.html Signed-off-by: andrepapoti --- templates/base.html | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/templates/base.html b/templates/base.html index 747da592..80ca7b16 100644 --- a/templates/base.html +++ b/templates/base.html @@ -48,6 +48,12 @@ {% block navbarmenu %} {% if project %}