From patchwork Thu Sep 6 21:14:01 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Stephen Finucane X-Patchwork-Id: 967117 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.ozlabs.org (lists.ozlabs.org [203.11.71.2]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 425tff4Wrpz9s47 for ; Fri, 7 Sep 2018 07:16:26 +1000 (AEST) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=that.guru Authentication-Results: ozlabs.org; dkim=fail reason="key not found in DNS" (0-bit key; unprotected) header.d=that.guru header.i=@that.guru header.b="AD7+4YpO"; dkim-atps=neutral Received: from lists.ozlabs.org (unknown [IPv6:2401:3900:2:1::3]) by lists.ozlabs.org (Postfix) with ESMTP id 425tff2QjbzF3Sf for ; Fri, 7 Sep 2018 07:16:26 +1000 (AEST) Authentication-Results: lists.ozlabs.org; dmarc=none (p=none dis=none) header.from=that.guru Authentication-Results: lists.ozlabs.org; dkim=fail reason="key not found in DNS" (0-bit key; unprotected) header.d=that.guru header.i=@that.guru header.b="AD7+4YpO"; dkim-atps=neutral X-Original-To: patchwork@lists.ozlabs.org Delivered-To: patchwork@lists.ozlabs.org Authentication-Results: lists.ozlabs.org; spf=none (mailfrom) smtp.mailfrom=that.guru (client-ip=185.234.75.7; helo=relay-direct7.mxroute.com; envelope-from=stephen@that.guru; receiver=) Authentication-Results: lists.ozlabs.org; dmarc=none (p=none dis=none) header.from=that.guru Authentication-Results: lists.ozlabs.org; dkim=fail reason="key not found in DNS" (0-bit key; unprotected) header.d=that.guru header.i=@that.guru header.b="AD7+4YpO"; dkim-atps=neutral Received: from relay-direct7.mxroute.com (relay-direct7.mxroute.com [185.234.75.7]) (using TLSv1.2 with cipher ADH-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by lists.ozlabs.org (Postfix) with ESMTPS id 425tdC0slVzF3SJ for ; Fri, 7 Sep 2018 07:15:09 +1000 (AEST) Received: from filter002.mxroute.com (unknown [185.133.192.179]) by relay-direct7.mxroute.com (Postfix) with ESMTP id 3CD193F35B for ; Thu, 6 Sep 2018 21:14:36 +0000 (UTC) Received: from one.mxroute.com (one.mxroute.com [195.201.59.211]) by filter002.mxroute.com (Postfix) with ESMTPS id 249693F544 for ; Thu, 6 Sep 2018 21:14:36 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=that.guru; s=default; h=References:In-Reply-To:Message-Id:Date:Subject:Cc:To:From: Sender:Reply-To:MIME-Version:Content-Type:Content-Transfer-Encoding: Content-ID:Content-Description:Resent-Date:Resent-From:Resent-Sender: Resent-To:Resent-Cc:Resent-Message-ID:List-Id:List-Help:List-Unsubscribe: List-Subscribe:List-Post:List-Owner:List-Archive; bh=Gym6Fs7omb9Ms8sLq3yiIsdk8izyqyXZ+MR+R1jhOv4=; b=AD7+4YpO8Qyt4dKn+i5Dtd4Wh9 bCn/4Q5lNFiYlYMbFjZpy/5UQ1tmAqf7YYRyN/lSzYem3BaPs08uF8cgUMofM95bCzLyMR/LF5eLU 0OWji2vsq/itasqkS6tVPjmYk0Us4TpNiW9bnUh3pY8uadgiMBkqIe8s3nZoRP/zhA2GzrBErVheh f/y4QBCGBh8PxPUcXgGGHnzvvaQ+VZpZYbXxYiAupnlynzKQCmyHM0GCTtmMNP5UqHlCumguythLZ RrCYBOeV2aMfR6xDLeOpNmv+x9M+IshJdT9YhPT0dSL8I8JNapYKEx8PdL7hhDoRLFp93JG+7pndz IqwywS8Q==; From: Stephen Finucane To: patchwork@lists.ozlabs.org Subject: [PATCH 1/6] tests: Add more tests for series-ified mbox views Date: Thu, 6 Sep 2018 22:14:01 +0100 Message-Id: <20180906211406.14363-2-stephen@that.guru> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20180906211406.14363-1-stephen@that.guru> References: <20180906211406.14363-1-stephen@that.guru> X-AuthUser: stephen@that.guru X-BeenThere: patchwork@lists.ozlabs.org X-Mailman-Version: 2.1.27 Precedence: list List-Id: Patchwork development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: patchwork-bounces+incoming=patchwork.ozlabs.org@lists.ozlabs.org Sender: "Patchwork" Cover some testing gaps identified during the migration from a M:N to a 1:N series-patch relationship, namely: - Downloading a patch's mbox with dependencies using a numerical series ID - Downloading a series' mbox Signed-off-by: Stephen Finucane --- patchwork/tests/test_mboxviews.py | 40 ++++++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/patchwork/tests/test_mboxviews.py b/patchwork/tests/test_mboxviews.py index b7af746b..ecf46674 100644 --- a/patchwork/tests/test_mboxviews.py +++ b/patchwork/tests/test_mboxviews.py @@ -31,6 +31,7 @@ from patchwork.tests.utils import create_comment from patchwork.tests.utils import create_patch from patchwork.tests.utils import create_project from patchwork.tests.utils import create_person +from patchwork.tests.utils import create_series from patchwork.tests.utils import create_series_patch from patchwork.tests.utils import create_user @@ -211,16 +212,40 @@ class MboxCommentPostcriptUnchangedTest(TestCase): class MboxSeriesDependencies(TestCase): - def test_patch_with_dependencies(self): + @staticmethod + def _create_patches(): patch_a = create_series_patch() patch_b = create_series_patch(series=patch_a.series) + return patch_a.series, patch_a, patch_b + + def test_patch_with_wildcard_series(self): + _, patch_a, patch_b = self._create_patches() + response = self.client.get('%s?series=*' % reverse( 'patch-mbox', args=[patch_b.patch.id])) self.assertContains(response, patch_a.patch.content) self.assertContains(response, patch_b.patch.content) + def test_patch_with_numeric_series(self): + series, patch_a, patch_b = self._create_patches() + + response = self.client.get('%s?series=%d' % ( + reverse('patch-mbox', args=[patch_b.patch.id]), series.id)) + + self.assertContains(response, patch_a.patch.content) + self.assertContains(response, patch_b.patch.content) + + def test_patch_with_invalid_series(self): + series, patch_a, patch_b = self._create_patches() + + for value in ('foo', str(series.id + 1)): + response = self.client.get('%s?series=%s' % ( + reverse('patch-mbox', args=[patch_b.patch.id]), value)) + + self.assertEqual(response.status_code, 404) + def test_legacy_patch(self): """Validate a patch with non-existent dependencies raises a 404.""" # we're explicitly creating a patch without a series @@ -230,3 +255,16 @@ class MboxSeriesDependencies(TestCase): 'patch-mbox', args=[patch.id])) self.assertEqual(response.status_code, 404) + + +class MboxSeries(TestCase): + + def test_series(self): + series = create_series() + patch_a = create_series_patch(series=series) + patch_b = create_series_patch(series=series) + + response = self.client.get(reverse('series-mbox', args=[series.id])) + + self.assertContains(response, patch_a.patch.content) + self.assertContains(response, patch_b.patch.content) From patchwork Thu Sep 6 21:14:02 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Stephen Finucane X-Patchwork-Id: 967115 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.ozlabs.org (lists.ozlabs.org [203.11.71.2]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 425tdp1ytMz9s47 for ; Fri, 7 Sep 2018 07:15:42 +1000 (AEST) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=that.guru Authentication-Results: ozlabs.org; dkim=fail reason="key not found in DNS" (0-bit key; unprotected) header.d=that.guru header.i=@that.guru header.b="U4JcjgQ5"; dkim-atps=neutral Received: from lists.ozlabs.org (unknown [IPv6:2401:3900:2:1::3]) by lists.ozlabs.org (Postfix) with ESMTP id 425tdn5xp1zF3Sr for ; Fri, 7 Sep 2018 07:15:41 +1000 (AEST) Authentication-Results: lists.ozlabs.org; dmarc=none (p=none dis=none) header.from=that.guru Authentication-Results: lists.ozlabs.org; dkim=fail reason="key not found in DNS" (0-bit key; unprotected) header.d=that.guru header.i=@that.guru header.b="U4JcjgQ5"; dkim-atps=neutral X-Original-To: patchwork@lists.ozlabs.org Delivered-To: patchwork@lists.ozlabs.org Authentication-Results: lists.ozlabs.org; spf=none (mailfrom) smtp.mailfrom=that.guru (client-ip=185.234.75.7; helo=relay-direct7.mxroute.com; envelope-from=stephen@that.guru; receiver=) Authentication-Results: lists.ozlabs.org; dmarc=none (p=none dis=none) header.from=that.guru Authentication-Results: lists.ozlabs.org; dkim=fail reason="key not found in DNS" (0-bit key; unprotected) header.d=that.guru header.i=@that.guru header.b="U4JcjgQ5"; dkim-atps=neutral Received: from relay-direct7.mxroute.com (relay-direct7.mxroute.com [185.234.75.7]) (using TLSv1.2 with cipher ADH-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by lists.ozlabs.org (Postfix) with ESMTPS id 425tdC0PdRzF3SB for ; Fri, 7 Sep 2018 07:15:09 +1000 (AEST) Received: from filter002.mxroute.com (unknown [185.133.192.179]) by relay-direct7.mxroute.com (Postfix) with ESMTP id C7D443F35C for ; Thu, 6 Sep 2018 21:14:36 +0000 (UTC) Received: from one.mxroute.com (one.mxroute.com [195.201.59.211]) by filter002.mxroute.com (Postfix) with ESMTPS id B1C943F054 for ; Thu, 6 Sep 2018 21:14:36 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=that.guru; s=default; h=References:In-Reply-To:Message-Id:Date:Subject:Cc:To:From: Sender:Reply-To:MIME-Version:Content-Type:Content-Transfer-Encoding: Content-ID:Content-Description:Resent-Date:Resent-From:Resent-Sender: Resent-To:Resent-Cc:Resent-Message-ID:List-Id:List-Help:List-Unsubscribe: List-Subscribe:List-Post:List-Owner:List-Archive; bh=ZnrDyD3wtXBdc3Aqk/qvcB8fBrCVC9R+A3x3N0FEmtA=; b=U4JcjgQ5SGiabLlnEgvRt+6msa M5oijHSKbBDJBMl2X8+wXZdJEh5rFDDFrDKp2StjPsGJKeHMphPNAFCuINEVT3V6J3U07/jwgBVJl xNu+xjvLI3BcL/fpdkdoEa3KHeB4aHhuESz9Rx3rPbc2iZ1xI9+Yapf8r8qHu+cqvTg1VtOypkkNT uu4jomstNrtJbohD6yy6atFswtV2ukX4wkYvK35z4k8MGwj5d1/G9ABE+v7w9HQN0OhFhj5dEkaqr f1887fHvQ+nPOzquymy9rhs32nIBdAHvNkPcbgCkqk0o6xNnLS7zTZ9k9LqEO7NrOsQLe6DFCQ9uQ C9+4HDZA==; From: Stephen Finucane To: patchwork@lists.ozlabs.org Subject: [PATCH 2/6] tests: Hardcode expected values Date: Thu, 6 Sep 2018 22:14:02 +0100 Message-Id: <20180906211406.14363-3-stephen@that.guru> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20180906211406.14363-1-stephen@that.guru> References: <20180906211406.14363-1-stephen@that.guru> X-AuthUser: stephen@that.guru X-BeenThere: patchwork@lists.ozlabs.org X-Mailman-Version: 2.1.27 Precedence: list List-Id: Patchwork development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: patchwork-bounces+incoming=patchwork.ozlabs.org@lists.ozlabs.org Sender: "Patchwork" Compare against known values to ensure bugs introduced in the function are caught. Signed-off-by: Stephen Finucane --- patchwork/tests/test_series.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/patchwork/tests/test_series.py b/patchwork/tests/test_series.py index 9b5c0129..cdebf254 100644 --- a/patchwork/tests/test_series.py +++ b/patchwork/tests/test_series.py @@ -637,10 +637,6 @@ class SeriesNameTestCase(TestCase): def _parse_mail(self, mail): return parser.parse_mail(mail, self.project.listid) - @staticmethod - def _format_name(cover): - return models.Series._format_name(cover) - def test_cover_letter(self): """Cover letter name set as series name. @@ -656,7 +652,7 @@ class SeriesNameTestCase(TestCase): mbox = self._get_mbox('base-cover-letter.mbox') cover = self._parse_mail(mbox[0]) - cover_name = self._format_name(cover) + cover_name = 'A sample series' self.assertEqual(cover.latest_series.name, cover_name) self._parse_mail(mbox[1]) @@ -711,7 +707,7 @@ class SeriesNameTestCase(TestCase): self.assertEqual(patch.latest_series.name, patch.name) cover = self._parse_mail(mbox[2]) - self.assertEqual(cover.latest_series.name, self._format_name(cover)) + self.assertEqual(cover.latest_series.name, 'A sample series') mbox.close() From patchwork Thu Sep 6 21:14:03 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Stephen Finucane X-Patchwork-Id: 967116 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.ozlabs.org (lists.ozlabs.org [203.11.71.2]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 425tfC6ysnz9s3l for ; Fri, 7 Sep 2018 07:16:03 +1000 (AEST) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=that.guru Authentication-Results: ozlabs.org; dkim=fail reason="key not found in DNS" (0-bit key; unprotected) header.d=that.guru header.i=@that.guru header.b="D4H8Of75"; dkim-atps=neutral Received: from lists.ozlabs.org (unknown [IPv6:2401:3900:2:1::3]) by lists.ozlabs.org (Postfix) with ESMTP id 425tfC5Qr2zF3ST for ; Fri, 7 Sep 2018 07:16:03 +1000 (AEST) Authentication-Results: lists.ozlabs.org; dmarc=none (p=none dis=none) header.from=that.guru Authentication-Results: lists.ozlabs.org; dkim=fail reason="key not found in DNS" (0-bit key; unprotected) header.d=that.guru header.i=@that.guru header.b="D4H8Of75"; dkim-atps=neutral X-Original-To: patchwork@lists.ozlabs.org Delivered-To: patchwork@lists.ozlabs.org Authentication-Results: lists.ozlabs.org; spf=none (mailfrom) smtp.mailfrom=that.guru (client-ip=185.234.75.7; helo=relay-direct7.mxroute.com; envelope-from=stephen@that.guru; receiver=) Authentication-Results: lists.ozlabs.org; dmarc=none (p=none dis=none) header.from=that.guru Authentication-Results: lists.ozlabs.org; dkim=fail reason="key not found in DNS" (0-bit key; unprotected) header.d=that.guru header.i=@that.guru header.b="D4H8Of75"; dkim-atps=neutral Received: from relay-direct7.mxroute.com (relay-direct7.mxroute.com [185.234.75.7]) (using TLSv1.2 with cipher ADH-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by lists.ozlabs.org (Postfix) with ESMTPS id 425tdC0krdzF3SH for ; Fri, 7 Sep 2018 07:15:09 +1000 (AEST) Received: from filter002.mxroute.com (unknown [185.133.192.179]) by relay-direct7.mxroute.com (Postfix) with ESMTP id AE9C43F35D; Thu, 6 Sep 2018 21:14:37 +0000 (UTC) Received: from one.mxroute.com (one.mxroute.com [195.201.59.211]) by filter002.mxroute.com (Postfix) with ESMTPS id 7D77C3F054; Thu, 6 Sep 2018 21:14:37 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=that.guru; s=default; h=References:In-Reply-To:Message-Id:Date:Subject:Cc:To:From: Sender:Reply-To:MIME-Version:Content-Type:Content-Transfer-Encoding: Content-ID:Content-Description:Resent-Date:Resent-From:Resent-Sender: Resent-To:Resent-Cc:Resent-Message-ID:List-Id:List-Help:List-Unsubscribe: List-Subscribe:List-Post:List-Owner:List-Archive; bh=WkD7ROlyqLLR6TiCM3kJGrCQB0TfvJZ840P9nav2I24=; b=D4H8Of75au7SXd+LslwgRp15tj B7v1PKVMQ92vNMMgVvuUwJBvHyTEC8H3mQumrfiVt8zFLUqaaocLtXQ5OAUmCLUqlCaPaC2OvJdYd 5IpDs+criVzGTNB6DlkVAQzsshe4pS87aozjFDEgvve8YRoL1NCVp8txt3fUrBWxXcDj4nOb3Jm5w xHGy/DXN6t4lQMaHSquQufn1GQHhEIdk0J6I2V9NrlfWZWAfHaOw+WN1GghvSEG4p6ztb6CdHOQ9B SNoSy6K0blk8scBqjuQI+t3yyMSLkk7JydGLPynFsrM317QGyHV80V2BYRvrpwsyUOfvg+v/Tzfpk 11SW6U7w==; From: Stephen Finucane To: patchwork@lists.ozlabs.org Subject: [PATCH 3/6] models: Convert Series-Patch relationship to 1:N Date: Thu, 6 Sep 2018 22:14:03 +0100 Message-Id: <20180906211406.14363-4-stephen@that.guru> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20180906211406.14363-1-stephen@that.guru> References: <20180906211406.14363-1-stephen@that.guru> X-AuthUser: stephen@that.guru X-BeenThere: patchwork@lists.ozlabs.org X-Mailman-Version: 2.1.27 Precedence: list List-Id: Patchwork development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: patchwork-bounces+incoming=patchwork.ozlabs.org@lists.ozlabs.org Sender: "Patchwork" Late in the development of the series feature, it was decided that there were advantages to allowing an N:M relationship between series and patches would be desirable. This would allow us to do things like create complete series where a sole vN patch was sent to a list rather than the full series. After some time using series in the wild, it's apparent that such features are very difficult to implement correctly and will likely never be implemented. As such, it's time to start cleaning up the mess, paving the way for things like an improved tagging feature. There are some significant changes to the model required: - models.py, migrations/0027, migrations/0028, migrations/0029 The migrations make the following changes: 1. - Add 'Patch.series_alt' and 'Patch.number' fields. 2. - Populate the 'Patch.series_alt' and 'Patch.number' fields from their 'SeriesPatch' equivalents. 3. - Remove the 'SeriesPatch' model. - Rename 'Patch.series_alt' to 'Patch.series'. - Change 'Series.cover_letter' to a 'OneToOneField' since a cover letter can no longer be assigned to multiple series. Note that the migrations have to be split into multiple parts as the combined migration raises an OperationalError as below. (1072, "Key column 'series_alt_id' doesn't exist in table") This is due to Django's penchant for creating indexes for newly created fields, as noted here: https://stackoverflow.com/q/35158530/ Aside from the model changes, there are numerous other changes required: - admin.py Reflect model changes for the 'PatchInline' inline used by 'SeriesAdmin' - api/cover.py, api/patch.py Update the 'series' field for the cover letter and patch resources to reflect the model changes. A 'to_representation' function is added in both cases to post-process this field and make it look like a list again. This is necessary to avoid breaking clients. - parser.py Update to reflect the replacement of 'SeriesPatch' with 'Patch'. - signals.py Update to filter on changes to 'Patch' instead of 'SeriesPatch'. This requires some reworking due to how we set these fields now, as we can no longer receive on 'post_save' signals for 'SeriesPatch' and must instead watch for 'pre_save' on 'Patch', which is what we do for delegate and state changes on same. - templates/patchwork/*.html Remove logic that handled multiple series in favour of the (simpler) single series logic. - tests/* Modify the 'create_series_patch' helper to reflect the removal of the 'SeriesPatch' model. This entire helper will be removed in a future change. Improve some tests to cover edge cases that were highlighted during development Unfortunately, all of the above changes must go in at the same time, otherwise we end up with either (a) broken views, API etc. or (b) split brain because we need to keep the new single-series fields alongside the older multi-series fields and models while we rework the views. It's unfortunate but there's not much to be done here. Signed-off-by: Stephen Finucane Cc: Daniel Axtens Cc: Stewart Smith Cc: Veronika Kabatova Cc: Don Zickus --- Daniel, Stewart: This is a big change but I think it's (a) worthwhile and (b) is more mechanical than anything. I would appreciate your eyes on the migrations though, assuming you have time, to make sure we're not shooting ourselves in the foot here. Veronika, Don: This is the thing I've been holding off on the improved tagging support for. This should allow us to simplify that patch and reason about it a little more. I'd like to get this in first but tagging would be my next priority and I would be more than happy to help with the rebasing of that patch, if necessary. --- patchwork/admin.py | 2 +- patchwork/api/cover.py | 19 ++++-- patchwork/api/patch.py | 20 ++++-- .../0027_add_patch_series_fields.py | 32 +++++++++ ...migrate_data_from_series_patch_to_patch.py | 35 ++++++++++ .../0029_remove_patch_series_model.py | 58 +++++++++++++++++ patchwork/models.py | 65 +++++++------------ patchwork/parser.py | 6 +- patchwork/signals.py | 40 ++++++------ .../templates/patchwork/download_buttons.html | 13 +--- patchwork/templates/patchwork/patch-list.html | 12 ++-- patchwork/templates/patchwork/submission.html | 18 +---- patchwork/tests/test_detail.py | 16 ----- patchwork/tests/test_events.py | 11 +++- patchwork/tests/test_series.py | 8 +++ patchwork/tests/utils.py | 24 ++++--- patchwork/views/patch.py | 2 +- patchwork/views/utils.py | 17 ++--- 18 files changed, 249 insertions(+), 149 deletions(-) create mode 100644 patchwork/migrations/0027_add_patch_series_fields.py create mode 100644 patchwork/migrations/0028_migrate_data_from_series_patch_to_patch.py create mode 100644 patchwork/migrations/0029_remove_patch_series_model.py diff --git a/patchwork/admin.py b/patchwork/admin.py index d43cc4dd..cb1feb24 100644 --- a/patchwork/admin.py +++ b/patchwork/admin.py @@ -129,7 +129,7 @@ admin.site.register(Comment, CommentAdmin) class PatchInline(admin.StackedInline): - model = Series.patches.through + model = Patch extra = 0 diff --git a/patchwork/api/cover.py b/patchwork/api/cover.py index b497fd85..3a9fc003 100644 --- a/patchwork/api/cover.py +++ b/patchwork/api/cover.py @@ -38,7 +38,7 @@ class CoverLetterListSerializer(BaseHyperlinkedModelSerializer): project = ProjectSerializer(read_only=True) submitter = PersonSerializer(read_only=True) mbox = SerializerMethodField() - series = SeriesSerializer(many=True, read_only=True) + series = SeriesSerializer(read_only=True) comments = SerializerMethodField() def get_web_url(self, instance): @@ -53,6 +53,15 @@ class CoverLetterListSerializer(BaseHyperlinkedModelSerializer): return self.context.get('request').build_absolute_uri( reverse('api-cover-comment-list', kwargs={'pk': cover.id})) + def to_representation(self, instance): + # NOTE(stephenfin): This is here to ensure our API looks the same even + # after we changed the series-patch relationship from M:N to 1:N. It + # will be removed in API v2 + data = super(CoverLetterListSerializer, self).to_representation( + instance) + data['series'] = [data['series']] + return data + class Meta: model = CoverLetter fields = ('id', 'url', 'web_url', 'project', 'msgid', 'date', 'name', @@ -103,8 +112,8 @@ class CoverLetterList(ListAPIView): ordering = 'id' def get_queryset(self): - return CoverLetter.objects.all().prefetch_related('series')\ - .select_related('project', 'submitter')\ + return CoverLetter.objects.all()\ + .select_related('project', 'submitter', 'series')\ .defer('content', 'headers') @@ -114,5 +123,5 @@ class CoverLetterDetail(RetrieveAPIView): serializer_class = CoverLetterDetailSerializer def get_queryset(self): - return CoverLetter.objects.all().prefetch_related('series')\ - .select_related('project', 'submitter') + return CoverLetter.objects.all()\ + .select_related('project', 'submitter', 'series') diff --git a/patchwork/api/patch.py b/patchwork/api/patch.py index 9d890eb1..549ec4fa 100644 --- a/patchwork/api/patch.py +++ b/patchwork/api/patch.py @@ -83,7 +83,7 @@ class PatchListSerializer(BaseHyperlinkedModelSerializer): submitter = PersonSerializer(read_only=True) delegate = UserSerializer() mbox = SerializerMethodField() - series = SeriesSerializer(many=True, read_only=True) + series = SeriesSerializer(read_only=True) comments = SerializerMethodField() check = SerializerMethodField() checks = SerializerMethodField() @@ -113,6 +113,14 @@ class PatchListSerializer(BaseHyperlinkedModelSerializer): # model return {} + def to_representation(self, instance): + # NOTE(stephenfin): This is here to ensure our API looks the same even + # after we changed the series-patch relationship from M:N to 1:N. It + # will be removed in API v2 + data = super(PatchListSerializer, self).to_representation(instance) + data['series'] = [data['series']] + return data + class Meta: model = Patch fields = ('id', 'url', 'web_url', 'project', 'msgid', 'date', 'name', @@ -175,8 +183,9 @@ class PatchList(ListAPIView): def get_queryset(self): return Patch.objects.all()\ - .prefetch_related('series', 'check_set')\ - .select_related('project', 'state', 'submitter', 'delegate')\ + .prefetch_related('check_set')\ + .select_related('project', 'state', 'submitter', 'delegate', + 'series')\ .defer('content', 'diff', 'headers') @@ -188,5 +197,6 @@ class PatchDetail(RetrieveUpdateAPIView): def get_queryset(self): return Patch.objects.all()\ - .prefetch_related('series', 'check_set')\ - .select_related('project', 'state', 'submitter', 'delegate') + .prefetch_related('check_set')\ + .select_related('project', 'state', 'submitter', 'delegate', + 'series') diff --git a/patchwork/migrations/0027_add_patch_series_fields.py b/patchwork/migrations/0027_add_patch_series_fields.py new file mode 100644 index 00000000..4990c31c --- /dev/null +++ b/patchwork/migrations/0027_add_patch_series_fields.py @@ -0,0 +1,32 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import migrations, models +from django.db.models import Count +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('patchwork', '0026_add_user_bundles_backref'), + ] + + operations = [ + # Add Patch.series_alt, Patch.number fields. This will store the fields + # currently stored in SeriesPatch + migrations.AddField( + model_name='patch', + name='number', + field=models.PositiveSmallIntegerField(default=None, help_text=b'The number assigned to this patch in the series', null=True), + ), + migrations.AddField( + model_name='patch', + name='series_alt', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='patchwork.Series'), + ), + migrations.AlterUniqueTogether( + name='patch', + unique_together=set([('series_alt', 'number')]), + ), + ] diff --git a/patchwork/migrations/0028_migrate_data_from_series_patch_to_patch.py b/patchwork/migrations/0028_migrate_data_from_series_patch_to_patch.py new file mode 100644 index 00000000..31d1e225 --- /dev/null +++ b/patchwork/migrations/0028_migrate_data_from_series_patch_to_patch.py @@ -0,0 +1,35 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import migrations, models +from django.db.models import Count +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('patchwork', '0027_add_patch_series_fields'), + ] + + operations = [ + # Copy SeriesPatch.series, SeriesPatch.number to Patch.series_alt, + # Patch.number. Note that there is no uniqueness check here because no + # code actually allowed us to save multiple series + migrations.RunSQL( + """UPDATE patchwork_patch SET series_alt_id = + (SELECT series_id from patchwork_seriespatch + WHERE patchwork_seriespatch.patch_id = + patchwork_patch.submission_ptr_id); + UPDATE patchwork_patch SET number = + (SELECT number from patchwork_seriespatch + WHERE patchwork_seriespatch.patch_id = + patchwork_patch.submission_ptr_id); + """, + """INSERT INTO patchwork_seriespatch + (patch_id, series_id, number) + SELECT submission_ptr_id, series_alt_id, number + FROM patchwork_patch; + """, + ), + ] diff --git a/patchwork/migrations/0029_remove_patch_series_model.py b/patchwork/migrations/0029_remove_patch_series_model.py new file mode 100644 index 00000000..58410cdb --- /dev/null +++ b/patchwork/migrations/0029_remove_patch_series_model.py @@ -0,0 +1,58 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('patchwork', '0028_migrate_data_from_series_patch_to_patch'), + ] + + operations = [ + # Remove SeriesPatch + migrations.AlterUniqueTogether( + name='seriespatch', + unique_together=set([]), + ), + migrations.RemoveField( + model_name='seriespatch', + name='patch', + ), + migrations.RemoveField( + model_name='seriespatch', + name='series', + ), + migrations.RemoveField( + model_name='series', + name='patches', + ), + migrations.DeleteModel( + name='SeriesPatch', + ), + # Now that SeriesPatch has been removed, we can use the now-unused + # Patch.series field and add a backreference + migrations.RenameField( + model_name='patch', + old_name='series_alt', + new_name='series', + ), + migrations.AlterField( + model_name='patch', + name='series', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='patches', related_query_name='patch', to='patchwork.Series'), + ), + migrations.AlterUniqueTogether( + name='patch', + unique_together=set([('series', 'number')]), + ), + # Migrate CoverLetter to OneToOneField as a cover letter can no longer + # be assigned to multiple series + migrations.AlterField( + model_name='series', + name='cover_letter', + field=models.OneToOneField(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='series', to='patchwork.CoverLetter'), + ), + ] diff --git a/patchwork/models.py b/patchwork/models.py index 0409d4bf..a52fc4f6 100644 --- a/patchwork/models.py +++ b/patchwork/models.py @@ -399,9 +399,8 @@ class SeriesMixin(object): in list templates as doing so will result in a new query for each item in the list. """ - # NOTE(stephenfin): We don't use 'latest()' here, as this can raise an - # exception if no series exist - return self.series.order_by('-date').first() + # TODO(stephenfin): Remove this as it's no longer necessary + return self.series class CoverLetter(SeriesMixin, Submission): @@ -434,6 +433,15 @@ class Patch(SeriesMixin, Submission): # patches in a project without needing to do a JOIN. patch_project = models.ForeignKey(Project, on_delete=models.CASCADE) + # series metadata + + series = models.ForeignKey( + 'Series', null=True, blank=True, on_delete=models.CASCADE, + related_name='patches', related_query_name='patch') + number = models.PositiveSmallIntegerField( + default=None, null=True, + help_text='The number assigned to this patch in the series') + objects = PatchManager() @staticmethod @@ -590,6 +598,7 @@ class Patch(SeriesMixin, Submission): class Meta: verbose_name_plural = 'Patches' base_manager_name = 'objects' + unique_together = [('series', 'number')] class Comment(EmailMixin, models.Model): @@ -622,19 +631,16 @@ class Comment(EmailMixin, models.Model): @python_2_unicode_compatible class Series(FilenameMixin, models.Model): - """An collection of patches.""" + """A collection of patches.""" # parent project = models.ForeignKey(Project, related_name='series', null=True, blank=True, on_delete=models.CASCADE) # content - cover_letter = models.ForeignKey(CoverLetter, - related_name='series', - null=True, blank=True, - on_delete=models.CASCADE) - patches = models.ManyToManyField(Patch, through='SeriesPatch', - related_name='series') + cover_letter = models.OneToOneField(CoverLetter, related_name='series', + null=True, + on_delete=models.CASCADE) # metadata name = models.CharField(max_length=255, blank=True, null=True, @@ -700,9 +706,8 @@ class Series(FilenameMixin, models.Model): self.name = self._format_name(cover) else: try: - name = SeriesPatch.objects.get(series=self, - number=1).patch.name - except SeriesPatch.DoesNotExist: + name = Patch.objects.get(series=self, number=1).name + except Patch.DoesNotExist: name = None if self.name == name: @@ -712,20 +717,16 @@ class Series(FilenameMixin, models.Model): def add_patch(self, patch, number): """Add a patch to the series.""" - # see if the patch is already in this series - if SeriesPatch.objects.filter(series=self, patch=patch).count(): - # TODO(stephenfin): We may wish to raise an exception here in the - # future - return - # both user defined names and cover letter-based names take precedence if not self.name and number == 1: self.name = patch.name # keep the prefixes for patch-based names self.save() - return SeriesPatch.objects.create(series=self, - patch=patch, - number=number) + patch.series = self + patch.number = number + patch.save() + + return patch def get_absolute_url(self): # TODO(stephenfin): We really need a proper series view @@ -744,26 +745,6 @@ class Series(FilenameMixin, models.Model): verbose_name_plural = 'Series' -@python_2_unicode_compatible -class SeriesPatch(models.Model): - """A patch in a series. - - Patches can belong to many series. This allows for things like - auto-completion of partial series. - """ - patch = models.ForeignKey(Patch, on_delete=models.CASCADE) - series = models.ForeignKey(Series, on_delete=models.CASCADE) - number = models.PositiveSmallIntegerField( - help_text='The number assigned to this patch in the series') - - def __str__(self): - return self.patch.name - - class Meta: - unique_together = [('series', 'patch'), ('series', 'number')] - ordering = ['number'] - - @python_2_unicode_compatible class SeriesReference(models.Model): """A reference found in a series. diff --git a/patchwork/parser.py b/patchwork/parser.py index 40a80f26..505f2227 100644 --- a/patchwork/parser.py +++ b/patchwork/parser.py @@ -41,7 +41,6 @@ from patchwork.models import Person from patchwork.models import Project from patchwork.models import Series from patchwork.models import SeriesReference -from patchwork.models import SeriesPatch from patchwork.models import State from patchwork.models import Submission @@ -1043,8 +1042,7 @@ def parse_mail(mail, list_id=None): # - there is no existing series to assign this patch to, or # - there is an existing series, but it already has a patch with this # number in it - if not series or ( - SeriesPatch.objects.filter(series=series, number=x).count()): + if not series or Patch.objects.filter(series=series, number=x).count(): series = Series(project=project, date=date, submitter=author, @@ -1078,6 +1076,8 @@ def parse_mail(mail, list_id=None): # patch. Don't add unnumbered patches (for example diffs sent # in reply, or just messages with random refs/in-reply-tos) if series and x: + # TODO(stephenfin): Remove 'series' from the conditional as we will + # always have a series series.add_patch(patch, x) return patch diff --git a/patchwork/signals.py b/patchwork/signals.py index f7b4f547..f6160a87 100644 --- a/patchwork/signals.py +++ b/patchwork/signals.py @@ -29,7 +29,6 @@ from patchwork.models import Event from patchwork.models import Patch from patchwork.models import PatchChangeNotification from patchwork.models import Series -from patchwork.models import SeriesPatch @receiver(pre_save, sender=Patch) @@ -147,39 +146,46 @@ def create_patch_delegated_event(sender, instance, raw, **kwargs): create_event(instance, orig_patch.delegate, instance.delegate) -@receiver(post_save, sender=SeriesPatch) -def create_patch_completed_event(sender, instance, created, raw, **kwargs): - """Create patch completed event for patches with series.""" +@receiver(pre_save, sender=Patch) +def create_patch_completed_event(sender, instance, raw, **kwargs): - def create_event(patch, series): + def create_event(patch): return Event.objects.create( category=Event.CATEGORY_PATCH_COMPLETED, project=patch.project, patch=patch, - series=series) + series=patch.series) - # don't trigger for items loaded from fixtures or existing items - if raw or not created: + # don't trigger for items loaded from fixtures, new items or items that + # (still) don't have a series + if raw or not instance.pk or not instance.series: + return + + orig_patch = Patch.objects.get(pk=instance.pk) + + # we don't currently allow users to change a series, though this might + # change in the future. However, we handle that here nonetheless + if orig_patch.series == instance.series: return # if dependencies not met, don't raise event. There's also no point raising # events for successors since they'll have the same issue - predecessors = SeriesPatch.objects.filter( + predecessors = Patch.objects.filter( series=instance.series, number__lt=instance.number) if predecessors.count() != instance.number - 1: return - create_event(instance.patch, instance.series) + create_event(instance) # if this satisfies dependencies for successor patch, raise events for # those count = instance.number + 1 - for successor in SeriesPatch.objects.filter( + for successor in Patch.objects.order_by('number').filter( series=instance.series, number__gt=instance.number): if successor.number != count: break - create_event(successor.patch, successor.series) + create_event(successor) count += 1 @@ -218,15 +224,9 @@ def create_series_created_event(sender, instance, created, raw, **kwargs): create_event(instance) -@receiver(post_save, sender=SeriesPatch) +@receiver(post_save, sender=Patch) def create_series_completed_event(sender, instance, created, raw, **kwargs): - # NOTE(stephenfin): We subscribe to the SeriesPatch.post_save signal - # instead of Series.m2m_changed to minimize the amount of times this is - # fired. The m2m_changed signal doesn't support a 'changed' parameter, - # which we could use to quick skip the signal when a patch is merely - # updated instead of added to the series. - # NOTE(stephenfin): It's actually possible for this event to be fired # multiple times for a given series. To trigger this case, you would need # to send an additional patch to already exisiting series. This pattern @@ -243,5 +243,5 @@ def create_series_completed_event(sender, instance, created, raw, **kwargs): if raw or not created: return - if instance.series.received_all: + if instance.series and instance.series.received_all: create_event(instance.series) diff --git a/patchwork/templates/patchwork/download_buttons.html b/patchwork/templates/patchwork/download_buttons.html index 4809db54..6a6a1960 100644 --- a/patchwork/templates/patchwork/download_buttons.html +++ b/patchwork/templates/patchwork/download_buttons.html @@ -15,20 +15,9 @@ class="btn btn-default" role="button" title="Download cover mbox" >mbox {% endif %} - {% if submission.series.all|length == 1 %} + {% if submission.series %} series - {% elif submission.series.all|length > 1 %} - - {% endif %} diff --git a/patchwork/templates/patchwork/patch-list.html b/patchwork/templates/patchwork/patch-list.html index 71c1ba92..9dcee1c8 100644 --- a/patchwork/templates/patchwork/patch-list.html +++ b/patchwork/templates/patchwork/patch-list.html @@ -194,13 +194,11 @@ $(document).ready(function() { - {% with patch.series.all.0 as series %} - {% if series %} - - {{ series|truncatechars:100 }} - - {% endif %} - {% endwith %} + {% if patch.series %} + + {{ patch.series|truncatechars:100 }} + + {% endif %} {{ patch|patch_tags }} {{ patch|patch_checks }} diff --git a/patchwork/templates/patchwork/submission.html b/patchwork/templates/patchwork/submission.html index e817713f..2b4bc719 100644 --- a/patchwork/templates/patchwork/submission.html +++ b/patchwork/templates/patchwork/submission.html @@ -68,21 +68,9 @@ function toggle_div(link_id, headers_id) Series -
-
    - {% for series in submission.series.all %} -
  • - {% if series == submission.latest_series %} - {{ series }} - {% else %} - - {{ series }} - - {% endif %} -
  • - {% endfor %} -
-
+ + {{ submission.series }} + diff --git a/patchwork/tests/test_detail.py b/patchwork/tests/test_detail.py index f119da05..96320c10 100644 --- a/patchwork/tests/test_detail.py +++ b/patchwork/tests/test_detail.py @@ -25,7 +25,6 @@ from django.urls import reverse from patchwork.tests.utils import create_comment from patchwork.tests.utils import create_cover from patchwork.tests.utils import create_patch -from patchwork.tests.utils import create_series class CoverLetterViewTest(TestCase): @@ -51,21 +50,6 @@ class PatchViewTest(TestCase): response = self.client.get(requested_url) self.assertRedirects(response, redirect_url) - def test_series_dropdown(self): - patch = create_patch() - series = [create_series() for x in range(5)] - - for series_ in series: - series_.add_patch(patch, 1) - - response = self.client.get( - reverse('patch-detail', kwargs={'patch_id': patch.id})) - - for series_ in series: - self.assertContains( - response, - reverse('series-mbox', kwargs={'series_id': series_.id})) - class CommentRedirectTest(TestCase): diff --git a/patchwork/tests/test_events.py b/patchwork/tests/test_events.py index 70d563de..49c45158 100644 --- a/patchwork/tests/test_events.py +++ b/patchwork/tests/test_events.py @@ -48,8 +48,8 @@ class PatchCreateTest(_BaseTestCase): """No series, so patch dependencies implicitly exist.""" patch = utils.create_patch() - # This should raise both the CATEGORY_PATCH_CREATED and - # CATEGORY_PATCH_COMPLETED events as there are no specific dependencies + # This should raise the CATEGORY_PATCH_CREATED event only as there is + # no series events = _get_events(patch=patch) self.assertEqual(events.count(), 1) self.assertEqual(events[0].category, Event.CATEGORY_PATCH_CREATED) @@ -71,6 +71,13 @@ class PatchCreateTest(_BaseTestCase): self.assertEventFields(events[0]) self.assertEventFields(events[1]) + # This shouldn't be affected by another update to the patch + series_patch.patch.commit_ref = 'aac76f0b0f8dd657ff07bb' + series_patch.patch.save() + + events = _get_events(patch=series_patch.patch) + self.assertEqual(events.count(), 2) + def test_patch_dependencies_out_of_order(self): series = utils.create_series() series_patch_3 = utils.create_series_patch(series=series, number=3) diff --git a/patchwork/tests/test_series.py b/patchwork/tests/test_series.py index cdebf254..62f49cb6 100644 --- a/patchwork/tests/test_series.py +++ b/patchwork/tests/test_series.py @@ -89,6 +89,14 @@ class _BaseTestCase(TestCase): for patch in patches_: self.assertEqual(patch.latest_series, series[idx]) + # TODO(stephenfin): Rework this function into two different + # functions - we're clearly not always testing patches here + if isinstance(patch, models.Patch): + self.assertEqual(series[idx].patches.get(id=patch.id), + patch) + else: + self.assertEqual(series[idx].cover_letter, patch) + start_idx = end_idx diff --git a/patchwork/tests/utils.py b/patchwork/tests/utils.py index 00eb6c2a..be8c62a3 100644 --- a/patchwork/tests/utils.py +++ b/patchwork/tests/utils.py @@ -33,7 +33,6 @@ from patchwork.models import Patch from patchwork.models import Person from patchwork.models import Project from patchwork.models import Series -from patchwork.models import SeriesPatch from patchwork.models import SeriesReference from patchwork.models import State from patchwork.tests import TEST_PATCH_DIR @@ -242,17 +241,24 @@ def create_series(**kwargs): def create_series_patch(**kwargs): - """Create 'SeriesPatch' object.""" + """Create 'Patch' object and associate with a series.""" + # TODO(stephenfin): Remove this and all callers num = 1 if 'series' not in kwargs else kwargs['series'].patches.count() + 1 + if 'number' in kwargs: + num = kwargs['number'] - values = { - 'series': create_series() if 'series' not in kwargs else None, - 'number': num, - 'patch': create_patch() if 'patch' not in kwargs else None, - } - values.update(**kwargs) + series = create_series() if 'series' not in kwargs else kwargs['series'] + patch = create_patch() if 'patch' not in kwargs else kwargs['patch'] + + series.add_patch(patch, num) + + class SeriesPatch(object): + """Simple wrapper to avoid needing to update all tests at once.""" + def __init__(self, series, patch): + self.series = series + self.patch = patch - return SeriesPatch.objects.create(**values) + return SeriesPatch(series=series, patch=patch) def create_series_reference(**kwargs): diff --git a/patchwork/views/patch.py b/patchwork/views/patch.py index 6921882e..a8cab049 100644 --- a/patchwork/views/patch.py +++ b/patchwork/views/patch.py @@ -138,7 +138,7 @@ def patch_mbox(request, patch_id): response = HttpResponse(content_type='text/plain') if series_id: - if not patch.series.count(): + if not patch.series: raise Http404('Patch does not have an associated series. This is ' 'because the patch was processed with an older ' 'version of Patchwork. It is not possible to ' diff --git a/patchwork/views/utils.py b/patchwork/views/utils.py index 2357ab86..bfcf6aba 100644 --- a/patchwork/views/utils.py +++ b/patchwork/views/utils.py @@ -32,7 +32,6 @@ from django.utils import six from patchwork.models import Comment from patchwork.models import Patch -from patchwork.models import Series if settings.ENABLE_REST_API: from rest_framework.authtoken.models import Token @@ -142,26 +141,22 @@ def series_patch_to_mbox(patch, series_id): Returns: A string for the mbox file. """ - if series_id == '*': - series = patch.latest_series - else: + if series_id != '*': try: series_id = int(series_id) except ValueError: raise Http404('Expected integer series value or *. Received: %r' % series_id) - try: - series = patch.series.get(id=series_id) - except Series.DoesNotExist: + if patch.series.id != series_id: raise Http404('Patch does not belong to series %d' % series_id) mbox = [] # get the series-ified patch - number = series.seriespatch_set.get(patch=patch).number - for dep in series.seriespatch_set.filter(number__lt=number): - mbox.append(patch_to_mbox(dep.patch)) + for dep in patch.series.patches.filter( + number__lt=patch.number).order_by('number'): + mbox.append(patch_to_mbox(dep)) mbox.append(patch_to_mbox(patch)) @@ -179,7 +174,7 @@ def series_to_mbox(series): """ mbox = [] - for dep in series.seriespatch_set.all(): + for dep in series.patches.all().order_by('number'): mbox.append(patch_to_mbox(dep.patch)) return '\n'.join(mbox) From patchwork Thu Sep 6 21:14:04 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Stephen Finucane X-Patchwork-Id: 967118 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.ozlabs.org (lists.ozlabs.org [203.11.71.2]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 425tg90LR0z9s3l for ; Fri, 7 Sep 2018 07:16:53 +1000 (AEST) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=that.guru Authentication-Results: ozlabs.org; dkim=fail reason="key not found in DNS" (0-bit key; unprotected) header.d=that.guru header.i=@that.guru header.b="maI5tI3s"; dkim-atps=neutral Received: from lists.ozlabs.org (unknown [IPv6:2401:3900:2:1::3]) by lists.ozlabs.org (Postfix) with ESMTP id 425tg85pcgzF3SH for ; Fri, 7 Sep 2018 07:16:52 +1000 (AEST) Authentication-Results: lists.ozlabs.org; dmarc=none (p=none dis=none) header.from=that.guru Authentication-Results: lists.ozlabs.org; dkim=fail reason="key not found in DNS" (0-bit key; unprotected) header.d=that.guru header.i=@that.guru header.b="maI5tI3s"; dkim-atps=neutral X-Original-To: patchwork@lists.ozlabs.org Delivered-To: patchwork@lists.ozlabs.org Authentication-Results: lists.ozlabs.org; spf=none (mailfrom) smtp.mailfrom=that.guru (client-ip=185.234.75.7; helo=relay-direct7.mxroute.com; envelope-from=stephen@that.guru; receiver=) Authentication-Results: lists.ozlabs.org; dmarc=none (p=none dis=none) header.from=that.guru Authentication-Results: lists.ozlabs.org; dkim=fail reason="key not found in DNS" (0-bit key; unprotected) header.d=that.guru header.i=@that.guru header.b="maI5tI3s"; dkim-atps=neutral Received: from relay-direct7.mxroute.com (relay-direct7.mxroute.com [185.234.75.7]) (using TLSv1.2 with cipher ADH-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by lists.ozlabs.org (Postfix) with ESMTPS id 425tdC4Bc3zF3SK for ; Fri, 7 Sep 2018 07:15:11 +1000 (AEST) Received: from filter002.mxroute.com (unknown [185.133.192.179]) by relay-direct7.mxroute.com (Postfix) with ESMTP id 217A93F35A for ; Thu, 6 Sep 2018 21:14:39 +0000 (UTC) Received: from one.mxroute.com (one.mxroute.com [195.201.59.211]) by filter002.mxroute.com (Postfix) with ESMTPS id 066CD3F054 for ; Thu, 6 Sep 2018 21:14:39 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=that.guru; s=default; h=References:In-Reply-To:Message-Id:Date:Subject:Cc:To:From: Sender:Reply-To:MIME-Version:Content-Type:Content-Transfer-Encoding: Content-ID:Content-Description:Resent-Date:Resent-From:Resent-Sender: Resent-To:Resent-Cc:Resent-Message-ID:List-Id:List-Help:List-Unsubscribe: List-Subscribe:List-Post:List-Owner:List-Archive; bh=A/8bZBpeoWpB0SVD7/f6AM5GuRzhcXhrxViknCscGxM=; b=maI5tI3st5wB7rxyhUYYoPT95h UNahDlmHdkEC4u1shIRJcArSnbLRx5dZWCx8Jv7hKM6IEyJSKmckclPdibe3V5HUUp8Bzx6P3muYF W7pa4JNTh3VoDEi4COp7YI+qTI1oaOOgX/oNb0Jt0WIJUkdLJwyFyDDC6oSmis0125wK6siQfaBBs Z/hcmtf3O5Js47YacoLTivyRpQGw+GTnI34sSKEKWt9lW+zikEkdbLSO7NvYZ6yIBzGNm7+O4cHLL 6CTyjbkOWpk+W8XsfSEAwAhqNuCeLc0YBwR1Sz5JFMROX7jwmelhYjvYejZxmbXdnz/13XEKzr4ny MSpBt+og==; From: Stephen Finucane To: patchwork@lists.ozlabs.org Subject: [PATCH 4/6] models: Remove 'SeriesMixin' Date: Thu, 6 Sep 2018 22:14:04 +0100 Message-Id: <20180906211406.14363-5-stephen@that.guru> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20180906211406.14363-1-stephen@that.guru> References: <20180906211406.14363-1-stephen@that.guru> X-AuthUser: stephen@that.guru X-BeenThere: patchwork@lists.ozlabs.org X-Mailman-Version: 2.1.27 Precedence: list List-Id: Patchwork development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: patchwork-bounces+incoming=patchwork.ozlabs.org@lists.ozlabs.org Sender: "Patchwork" The property this provides, 'latest_series', is now a no-op and can be removed along with the mixin itself. Signed-off-by: Stephen Finucane --- patchwork/models.py | 22 ++--------------- .../templates/patchwork/download_buttons.html | 2 +- patchwork/templates/patchwork/submission.html | 6 ++--- patchwork/tests/test_series.py | 24 +++++++++---------- 4 files changed, 18 insertions(+), 36 deletions(-) diff --git a/patchwork/models.py b/patchwork/models.py index a52fc4f6..48a2f650 100644 --- a/patchwork/models.py +++ b/patchwork/models.py @@ -385,25 +385,7 @@ class Submission(FilenameMixin, EmailMixin, models.Model): unique_together = [('msgid', 'project')] -class SeriesMixin(object): - - @property - def latest_series(self): - """Get the latest series this is a member of. - - Return the last series that (ordered by date) that this - submission is a member of. - - .. warning:: - Be judicious in your use of this. For example, do not use it - in list templates as doing so will result in a new query for - each item in the list. - """ - # TODO(stephenfin): Remove this as it's no longer necessary - return self.series - - -class CoverLetter(SeriesMixin, Submission): +class CoverLetter(Submission): def get_absolute_url(self): return reverse('cover-detail', kwargs={'cover_id': self.id}) @@ -413,7 +395,7 @@ class CoverLetter(SeriesMixin, Submission): @python_2_unicode_compatible -class Patch(SeriesMixin, Submission): +class Patch(Submission): # patch metadata diff = models.TextField(null=True, blank=True) diff --git a/patchwork/templates/patchwork/download_buttons.html b/patchwork/templates/patchwork/download_buttons.html index 6a6a1960..21933bd2 100644 --- a/patchwork/templates/patchwork/download_buttons.html +++ b/patchwork/templates/patchwork/download_buttons.html @@ -16,7 +16,7 @@ >mbox {% endif %} {% if submission.series %} - series {% endif %} diff --git a/patchwork/templates/patchwork/submission.html b/patchwork/templates/patchwork/submission.html index 2b4bc719..9a22665d 100644 --- a/patchwork/templates/patchwork/submission.html +++ b/patchwork/templates/patchwork/submission.html @@ -64,7 +64,7 @@ function toggle_div(link_id, headers_id) -{% if submission.latest_series %} +{% if submission.series %} Series @@ -81,7 +81,7 @@ function toggle_div(link_id, headers_id) >show