From patchwork Thu Apr 9 17:12:57 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Stephen Finucane X-Patchwork-Id: 1268691 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.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 48ynm371gnz9sSM for ; Fri, 10 Apr 2020 03:13:23 +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" header.d=that.guru header.i=@that.guru header.a=rsa-sha256 header.s=default header.b=K1TMLFIW; dkim-atps=neutral Received: from lists.ozlabs.org (lists.ozlabs.org [IPv6:2401:3900:2:1::3]) by lists.ozlabs.org (Postfix) with ESMTP id 48ynm35s8hzDqjc for ; Fri, 10 Apr 2020 03:13:23 +1000 (AEST) X-Original-To: patchwork@lists.ozlabs.org Delivered-To: patchwork@lists.ozlabs.org Authentication-Results: lists.ozlabs.org; spf=none (no SPF record) smtp.mailfrom=that.guru (client-ip=172.82.139.228; helo=qrelay228.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" header.d=that.guru header.i=@that.guru header.a=rsa-sha256 header.s=default header.b=K1TMLFIW; dkim-atps=neutral Received: from qrelay228.mxroute.com (qrelay228.mxroute.com [172.82.139.228]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by lists.ozlabs.org (Postfix) with ESMTPS id 48ynlx6WQxzDqH6 for ; Fri, 10 Apr 2020 03:13:16 +1000 (AEST) Received: from filter003.mxroute.com ([168.235.111.26] 168-235-111-26.cloud.ramnode.com) (Authenticated sender: mN4UYu2MZsgR) by qrelay228.mxroute.com (ZoneMTA) with ESMTPA id 1715fee69ca0000d83.001 for ; Thu, 09 Apr 2020 17:13:12 +0000 X-Zone-Loop: dcc06ae7dd9186b15252b7e36b3fc1a4d94062900e10 X-Originating-IP: [168.235.111.26] Received: from one.mxroute.com (one.mxroute.com [195.201.59.211]) by filter003.mxroute.com (Postfix) with ESMTPS id 1384D60038 for ; Thu, 9 Apr 2020 17:13:11 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=that.guru; s=default; h=Content-Transfer-Encoding:MIME-Version:References:In-Reply-To: Message-Id:Date:Subject:Cc:To:From:Sender:Reply-To:Content-Type: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=S+b86Yu9K8tBrzKLdpjM7yvpuw+IewHJwNmcqz0EltM=; b=K1TMLFIW3pFurg3dbVHA4t8xDI EwisWcjiwyqBTDh0usSH/5duATeUGx5/UUWTG0VF9DEHId9OtTuabqe5sKIPl426vKmKwRLh/qvxe cp34QHKySe/d3mvqVKwtxLWKclydDSmBchJYB95AKi3S7awej6LwRqQumOq1IU4WocIDyqoFgSzsc TrUtkbMC1j0qjK+R17DQbl7dfZKbqW2aJ93hWAiYZKfbkuUgWXsQ3bFWL3GmrBqm37b9mc76NzLUr Fix26yWgefE5abNTPcI0HVDWQwixTRniyUXtgkn7HCVoUs/w8MEysVWXH342FCg408WROLOZvwiQD 8OCIpntw==; From: Stephen Finucane To: patchwork@lists.ozlabs.org Subject: [PATCH v2 1/6] trivial: Rename 'CoverLetter' references to 'Cover' Date: Thu, 9 Apr 2020 18:12:57 +0100 Message-Id: <20200409171302.105869-2-stephen@that.guru> X-Mailer: git-send-email 2.25.2 In-Reply-To: <20200409171302.105869-1-stephen@that.guru> References: <20200409171302.105869-1-stephen@that.guru> MIME-Version: 1.0 X-AuthUser: stephen@that.guru X-BeenThere: patchwork@lists.ozlabs.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Patchwork development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: patchwork-bounces+incoming=patchwork.ozlabs.org@lists.ozlabs.org Sender: "Patchwork" We're going to be doing some model surgery shortly. Do the necessary renaming of variables ahead of this. Signed-off-by: Stephen Finucane --- docs/api/schemas/latest/patchwork.yaml | 14 +++++++------- docs/api/schemas/patchwork.j2 | 14 +++++++------- docs/api/schemas/v1.0/patchwork.yaml | 14 +++++++------- docs/api/schemas/v1.1/patchwork.yaml | 14 +++++++------- docs/api/schemas/v1.2/patchwork.yaml | 14 +++++++------- patchwork/api/cover.py | 24 ++++++++++++------------ patchwork/api/embedded.py | 2 +- patchwork/api/event.py | 4 ++-- patchwork/api/filters.py | 2 +- patchwork/api/series.py | 4 ++-- patchwork/tests/api/test_cover.py | 2 +- patchwork/tests/test_detail.py | 2 +- patchwork/urls.py | 4 ++-- 13 files changed, 57 insertions(+), 57 deletions(-) diff --git docs/api/schemas/latest/patchwork.yaml docs/api/schemas/latest/patchwork.yaml index 13cdc9cd..082248a3 100644 --- docs/api/schemas/latest/patchwork.yaml +++ docs/api/schemas/latest/patchwork.yaml @@ -257,7 +257,7 @@ paths: schema: type: array items: - $ref: '#/components/schemas/CoverLetterList' + $ref: '#/components/schemas/CoverList' tags: - covers /api/covers/{id}/: @@ -278,7 +278,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/CoverLetterDetail' + $ref: '#/components/schemas/CoverDetail' '404': description: Not found content: @@ -1573,7 +1573,7 @@ components: items: type: string readOnly: true - CoverLetterList: + CoverList: type: object properties: id: @@ -1635,9 +1635,9 @@ components: type: string format: uri readOnly: true - CoverLetterDetail: + CoverDetail: allOf: - - $ref: '#/components/schemas/CoverLetterList' + - $ref: '#/components/schemas/CoverList' - properties: headers: title: Headers @@ -2169,7 +2169,7 @@ components: format: uri readOnly: true cover_letter: - $ref: '#/components/schemas/CoverLetterEmbedded' + $ref: '#/components/schemas/CoverEmbedded' patches: title: Patches type: array @@ -2282,7 +2282,7 @@ components: maxLength: 255 minLength: 1 readOnly: true - CoverLetterEmbedded: + CoverEmbedded: type: object properties: id: diff --git docs/api/schemas/patchwork.j2 docs/api/schemas/patchwork.j2 index bd714d5e..91faef88 100644 --- docs/api/schemas/patchwork.j2 +++ docs/api/schemas/patchwork.j2 @@ -262,7 +262,7 @@ paths: schema: type: array items: - $ref: '#/components/schemas/CoverLetterList' + $ref: '#/components/schemas/CoverList' tags: - covers /api/{{ version_url }}covers/{id}/: @@ -283,7 +283,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/CoverLetterDetail' + $ref: '#/components/schemas/CoverDetail' '404': description: Not found content: @@ -1629,7 +1629,7 @@ components: items: type: string readOnly: true - CoverLetterList: + CoverList: type: object properties: id: @@ -1699,9 +1699,9 @@ components: format: uri readOnly: true {% endif %} - CoverLetterDetail: + CoverDetail: allOf: - - $ref: '#/components/schemas/CoverLetterList' + - $ref: '#/components/schemas/CoverList' - properties: headers: title: Headers @@ -2253,7 +2253,7 @@ components: format: uri readOnly: true cover_letter: - $ref: '#/components/schemas/CoverLetterEmbedded' + $ref: '#/components/schemas/CoverEmbedded' patches: title: Patches type: array @@ -2368,7 +2368,7 @@ components: maxLength: 255 minLength: 1 readOnly: true - CoverLetterEmbedded: + CoverEmbedded: type: object properties: id: diff --git docs/api/schemas/v1.0/patchwork.yaml docs/api/schemas/v1.0/patchwork.yaml index 6c7c068a..642c7773 100644 --- docs/api/schemas/v1.0/patchwork.yaml +++ docs/api/schemas/v1.0/patchwork.yaml @@ -142,7 +142,7 @@ paths: schema: type: array items: - $ref: '#/components/schemas/CoverLetterList' + $ref: '#/components/schemas/CoverList' tags: - covers /api/1.0/covers/{id}/: @@ -163,7 +163,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/CoverLetterDetail' + $ref: '#/components/schemas/CoverDetail' '404': description: Not found content: @@ -1389,7 +1389,7 @@ components: items: type: string readOnly: true - CoverLetterList: + CoverList: type: object properties: id: @@ -1431,9 +1431,9 @@ components: items: $ref: '#/components/schemas/SeriesEmbedded' readOnly: true - CoverLetterDetail: + CoverDetail: allOf: - - $ref: '#/components/schemas/CoverLetterList' + - $ref: '#/components/schemas/CoverList' - properties: headers: title: Headers @@ -1881,7 +1881,7 @@ components: format: uri readOnly: true cover_letter: - $ref: '#/components/schemas/CoverLetterEmbedded' + $ref: '#/components/schemas/CoverEmbedded' patches: title: Patches type: array @@ -1967,7 +1967,7 @@ components: maxLength: 255 minLength: 1 readOnly: true - CoverLetterEmbedded: + CoverEmbedded: type: object properties: id: diff --git docs/api/schemas/v1.1/patchwork.yaml docs/api/schemas/v1.1/patchwork.yaml index 6b497aba..e91457e5 100644 --- docs/api/schemas/v1.1/patchwork.yaml +++ docs/api/schemas/v1.1/patchwork.yaml @@ -142,7 +142,7 @@ paths: schema: type: array items: - $ref: '#/components/schemas/CoverLetterList' + $ref: '#/components/schemas/CoverList' tags: - covers /api/1.1/covers/{id}/: @@ -163,7 +163,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/CoverLetterDetail' + $ref: '#/components/schemas/CoverDetail' '404': description: Not found content: @@ -1399,7 +1399,7 @@ components: items: type: string readOnly: true - CoverLetterList: + CoverList: type: object properties: id: @@ -1456,9 +1456,9 @@ components: type: string format: uri readOnly: true - CoverLetterDetail: + CoverDetail: allOf: - - $ref: '#/components/schemas/CoverLetterList' + - $ref: '#/components/schemas/CoverList' - properties: headers: title: Headers @@ -1950,7 +1950,7 @@ components: format: uri readOnly: true cover_letter: - $ref: '#/components/schemas/CoverLetterEmbedded' + $ref: '#/components/schemas/CoverEmbedded' patches: title: Patches type: array @@ -2036,7 +2036,7 @@ components: maxLength: 255 minLength: 1 readOnly: true - CoverLetterEmbedded: + CoverEmbedded: type: object properties: id: diff --git docs/api/schemas/v1.2/patchwork.yaml docs/api/schemas/v1.2/patchwork.yaml index db2ed122..ed1cb5af 100644 --- docs/api/schemas/v1.2/patchwork.yaml +++ docs/api/schemas/v1.2/patchwork.yaml @@ -257,7 +257,7 @@ paths: schema: type: array items: - $ref: '#/components/schemas/CoverLetterList' + $ref: '#/components/schemas/CoverList' tags: - covers /api/1.2/covers/{id}/: @@ -278,7 +278,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/CoverLetterDetail' + $ref: '#/components/schemas/CoverDetail' '404': description: Not found content: @@ -1573,7 +1573,7 @@ components: items: type: string readOnly: true - CoverLetterList: + CoverList: type: object properties: id: @@ -1635,9 +1635,9 @@ components: type: string format: uri readOnly: true - CoverLetterDetail: + CoverDetail: allOf: - - $ref: '#/components/schemas/CoverLetterList' + - $ref: '#/components/schemas/CoverList' - properties: headers: title: Headers @@ -2169,7 +2169,7 @@ components: format: uri readOnly: true cover_letter: - $ref: '#/components/schemas/CoverLetterEmbedded' + $ref: '#/components/schemas/CoverEmbedded' patches: title: Patches type: array @@ -2282,7 +2282,7 @@ components: maxLength: 255 minLength: 1 readOnly: true - CoverLetterEmbedded: + CoverEmbedded: type: object properties: id: diff --git patchwork/api/cover.py patchwork/api/cover.py index c0c27fe8..66d2146c 100644 --- patchwork/api/cover.py +++ patchwork/api/cover.py @@ -11,14 +11,14 @@ from rest_framework.reverse import reverse from rest_framework.serializers import SerializerMethodField from patchwork.api.base import BaseHyperlinkedModelSerializer -from patchwork.api.filters import CoverLetterFilterSet +from patchwork.api.filters import CoverFilterSet from patchwork.api.embedded import PersonSerializer from patchwork.api.embedded import ProjectSerializer from patchwork.api.embedded import SeriesSerializer from patchwork.models import CoverLetter -class CoverLetterListSerializer(BaseHyperlinkedModelSerializer): +class CoverListSerializer(BaseHyperlinkedModelSerializer): web_url = SerializerMethodField() project = ProjectSerializer(read_only=True) @@ -43,7 +43,7 @@ class CoverLetterListSerializer(BaseHyperlinkedModelSerializer): # 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( + data = super(CoverListSerializer, self).to_representation( instance) data['series'] = [data['series']] if data['series'] else [] return data @@ -63,7 +63,7 @@ class CoverLetterListSerializer(BaseHyperlinkedModelSerializer): } -class CoverLetterDetailSerializer(CoverLetterListSerializer): +class CoverDetailSerializer(CoverListSerializer): headers = SerializerMethodField() @@ -83,18 +83,18 @@ class CoverLetterDetailSerializer(CoverLetterListSerializer): class Meta: model = CoverLetter - fields = CoverLetterListSerializer.Meta.fields + ( + fields = CoverListSerializer.Meta.fields + ( 'headers', 'content') read_only_fields = fields - extra_kwargs = CoverLetterListSerializer.Meta.extra_kwargs - versioned_fields = CoverLetterListSerializer.Meta.versioned_fields + extra_kwargs = CoverListSerializer.Meta.extra_kwargs + versioned_fields = CoverListSerializer.Meta.versioned_fields -class CoverLetterList(ListAPIView): +class CoverList(ListAPIView): """List cover letters.""" - serializer_class = CoverLetterListSerializer - filter_class = filterset_class = CoverLetterFilterSet + serializer_class = CoverListSerializer + filter_class = filterset_class = CoverFilterSet search_fields = ('name',) ordering_fields = ('id', 'name', 'date', 'submitter') ordering = 'id' @@ -106,10 +106,10 @@ class CoverLetterList(ListAPIView): .defer('content', 'headers') -class CoverLetterDetail(RetrieveAPIView): +class CoverDetail(RetrieveAPIView): """Show a cover letter.""" - serializer_class = CoverLetterDetailSerializer + serializer_class = CoverDetailSerializer def get_queryset(self): return CoverLetter.objects.all()\ diff --git patchwork/api/embedded.py patchwork/api/embedded.py index 85a30cae..d2300dff 100644 --- patchwork/api/embedded.py +++ patchwork/api/embedded.py @@ -103,7 +103,7 @@ class CheckSerializer(SerializedRelatedField): } -class CoverLetterSerializer(SerializedRelatedField): +class CoverSerializer(SerializedRelatedField): class _Serializer(MboxMixin, WebURLMixin, BaseHyperlinkedModelSerializer): diff --git patchwork/api/event.py patchwork/api/event.py index d7a99c7d..7ed9efb1 100644 --- patchwork/api/event.py +++ patchwork/api/event.py @@ -11,7 +11,7 @@ from rest_framework.serializers import SerializerMethodField from rest_framework.serializers import SlugRelatedField from patchwork.api.embedded import CheckSerializer -from patchwork.api.embedded import CoverLetterSerializer +from patchwork.api.embedded import CoverSerializer from patchwork.api.embedded import PatchSerializer from patchwork.api.embedded import PatchRelationSerializer from patchwork.api.embedded import ProjectSerializer @@ -27,7 +27,7 @@ class EventSerializer(ModelSerializer): actor = UserSerializer() patch = PatchSerializer(read_only=True) series = SeriesSerializer(read_only=True) - cover = CoverLetterSerializer(read_only=True) + cover = CoverSerializer(read_only=True) previous_state = SlugRelatedField(slug_field='slug', read_only=True) current_state = SlugRelatedField(slug_field='slug', read_only=True) previous_delegate = UserSerializer() diff --git patchwork/api/filters.py patchwork/api/filters.py index deb5ace1..80b17232 100644 --- patchwork/api/filters.py +++ patchwork/api/filters.py @@ -184,7 +184,7 @@ class SeriesFilterSet(TimestampMixin, BaseFilterSet): fields = ('submitter', 'project') -class CoverLetterFilterSet(TimestampMixin, BaseFilterSet): +class CoverFilterSet(TimestampMixin, BaseFilterSet): project = ProjectFilter(queryset=Project.objects.all(), distinct=False) # NOTE(stephenfin): We disable the select-based HTML widgets for these diff --git patchwork/api/series.py patchwork/api/series.py index e4cffaa3..106e60f0 100644 --- patchwork/api/series.py +++ patchwork/api/series.py @@ -10,7 +10,7 @@ from rest_framework.serializers import SerializerMethodField from patchwork.api.base import BaseHyperlinkedModelSerializer from patchwork.api.base import PatchworkPermission from patchwork.api.filters import SeriesFilterSet -from patchwork.api.embedded import CoverLetterSerializer +from patchwork.api.embedded import CoverSerializer from patchwork.api.embedded import PatchSerializer from patchwork.api.embedded import PersonSerializer from patchwork.api.embedded import ProjectSerializer @@ -23,7 +23,7 @@ class SeriesSerializer(BaseHyperlinkedModelSerializer): project = ProjectSerializer(read_only=True) submitter = PersonSerializer(read_only=True) mbox = SerializerMethodField() - cover_letter = CoverLetterSerializer(read_only=True) + cover_letter = CoverSerializer(read_only=True) patches = PatchSerializer(read_only=True, many=True) def get_web_url(self, instance): diff --git patchwork/tests/api/test_cover.py patchwork/tests/api/test_cover.py index 5eeb1902..4f3ad841 100644 --- patchwork/tests/api/test_cover.py +++ patchwork/tests/api/test_cover.py @@ -21,7 +21,7 @@ if settings.ENABLE_REST_API: @unittest.skipUnless(settings.ENABLE_REST_API, 'requires ENABLE_REST_API') -class TestCoverLetterAPI(utils.APITestCase): +class TestCoverAPI(utils.APITestCase): fixtures = ['default_tags'] @staticmethod diff --git patchwork/tests/test_detail.py patchwork/tests/test_detail.py index 92fe2d7e..ddc2b937 100644 --- patchwork/tests/test_detail.py +++ patchwork/tests/test_detail.py @@ -12,7 +12,7 @@ from patchwork.tests.utils import create_patch from patchwork.tests.utils import create_project -class CoverLetterViewTest(TestCase): +class CoverViewTest(TestCase): def test_redirect(self): patch = create_patch() diff --git patchwork/urls.py patchwork/urls.py index dcdcfb49..9c3b85f0 100644 --- patchwork/urls.py +++ patchwork/urls.py @@ -207,10 +207,10 @@ if settings.ENABLE_REST_API: api_person_views.PersonDetail.as_view(), name='api-person-detail'), url(r'^covers/$', - api_cover_views.CoverLetterList.as_view(), + api_cover_views.CoverList.as_view(), name='api-cover-list'), url(r'^covers/(?P[^/]+)/$', - api_cover_views.CoverLetterDetail.as_view(), + api_cover_views.CoverDetail.as_view(), name='api-cover-detail'), url(r'^patches/$', api_patch_views.PatchList.as_view(), From patchwork Thu Apr 9 17:12:58 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Stephen Finucane X-Patchwork-Id: 1268692 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.ozlabs.org (lists.ozlabs.org [IPv6:2401:3900:2:1::3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 48ynmR0jgyz9sSk for ; Fri, 10 Apr 2020 03:13:43 +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" header.d=that.guru header.i=@that.guru header.a=rsa-sha256 header.s=default header.b=rOY2KVyk; dkim-atps=neutral Received: from lists.ozlabs.org (lists.ozlabs.org [IPv6:2401:3900:2:1::3]) by lists.ozlabs.org (Postfix) with ESMTP id 48ynmQ5fvXzDqg9 for ; Fri, 10 Apr 2020 03:13:42 +1000 (AEST) X-Original-To: patchwork@lists.ozlabs.org Delivered-To: patchwork@lists.ozlabs.org Authentication-Results: lists.ozlabs.org; spf=none (no SPF record) smtp.mailfrom=that.guru (client-ip=45.152.178.121; helo=chi01-121.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" header.d=that.guru header.i=@that.guru header.a=rsa-sha256 header.s=default header.b=rOY2KVyk; dkim-atps=neutral Received: from chi01-121.mxroute.com (chi01-121.mxroute.com [45.152.178.121]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by lists.ozlabs.org (Postfix) with ESMTPS id 48ynly46W0zDqPQ for ; Fri, 10 Apr 2020 03:13:18 +1000 (AEST) Received: from filter004.mxroute.com ([149.28.56.236] 149.28.56.236.vultr.com) (Authenticated sender: mN4UYu2MZsgR) by chi01-121.mxroute.com (ZoneMTA) with ESMTPSA id 1715fee71520001c89.001 for (version=TLSv1/SSLv3 cipher=ECDHE-RSA-AES128-GCM-SHA256); Thu, 09 Apr 2020 17:13:14 +0000 X-Zone-Loop: 94804585dca1aa5ff6ba5194fb5555a5fb4ca2e1a657 X-Originating-IP: [149.28.56.236] Received: from one.mxroute.com (one.mxroute.com [195.201.59.211]) by filter004.mxroute.com (Postfix) with ESMTPS id 92A953EACB for ; Thu, 9 Apr 2020 17:13:13 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=that.guru; s=default; h=Content-Transfer-Encoding:MIME-Version:References:In-Reply-To: Message-Id:Date:Subject:Cc:To:From:Sender:Reply-To:Content-Type: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=CJtLr6m4thFqJi5ytgr/SHlCDq8KJ7Iu+lU4T2Krq+Y=; b=rOY2KVykckIV3XlpqHMn/rcRVr sq3RyRR/1I83ELSboafC4t1SlytQx7zwGtjdbL4G2mevQ+tG9ky+d8n41PDmKMTheVvmRJipyypbN MPYgkbJsqemSdKHGb6OgFojw29MHlel+StRf7j3uJkprtNCHqzlonFokC2WVgc3W8BJj2VX14D6Ky IKmJWCcX1eZ78htV2v4RBUlelFFCPKHylCnUVPevVlgy/YF70Xg4ZMUPJ9r4ua7kmNEH5zKGnzpoW ai0/NI6wOhKHp3gnCm1vzK7z3ZhK21ewd/5if4Q2mQEhjqxQldSVKvhYrDSFXh/80N+1FkGgUDm/d L6SgNJlA==; From: Stephen Finucane To: patchwork@lists.ozlabs.org Subject: [PATCH v2 2/6] Remove unnecessary references to Submission model Date: Thu, 9 Apr 2020 18:12:58 +0100 Message-Id: <20200409171302.105869-3-stephen@that.guru> X-Mailer: git-send-email 2.25.2 In-Reply-To: <20200409171302.105869-1-stephen@that.guru> References: <20200409171302.105869-1-stephen@that.guru> MIME-Version: 1.0 X-AuthUser: stephen@that.guru X-BeenThere: patchwork@lists.ozlabs.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Patchwork development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: patchwork-bounces+incoming=patchwork.ozlabs.org@lists.ozlabs.org Sender: "Patchwork" We want to drop this in future changes. Start by removing any unnecessary references. Signed-off-by: Stephen Finucane --- patchwork/admin.py | 6 ++---- patchwork/views/cover.py | 10 ++++++---- patchwork/views/patch.py | 10 ++++++---- 3 files changed, 14 insertions(+), 12 deletions(-) diff --git patchwork/admin.py patchwork/admin.py index c3d45240..fb798085 100644 --- patchwork/admin.py +++ patchwork/admin.py @@ -20,7 +20,6 @@ from patchwork.models import Project from patchwork.models import Series from patchwork.models import SeriesReference from patchwork.models import State -from patchwork.models import Submission from patchwork.models import Tag from patchwork.models import UserProfile @@ -76,15 +75,14 @@ class StateAdmin(admin.ModelAdmin): admin.site.register(State, StateAdmin) -class SubmissionAdmin(admin.ModelAdmin): +class CoverLetterAdmin(admin.ModelAdmin): list_display = ('name', 'submitter', 'project', 'date') list_filter = ('project', ) search_fields = ('name', 'submitter__name', 'submitter__email') date_hierarchy = 'date' -admin.site.register(Submission, SubmissionAdmin) -admin.site.register(CoverLetter, SubmissionAdmin) +admin.site.register(CoverLetter, CoverLetterAdmin) class PatchAdmin(admin.ModelAdmin): diff --git patchwork/views/cover.py patchwork/views/cover.py index 54962abb..e90b7373 100644 --- patchwork/views/cover.py +++ patchwork/views/cover.py @@ -11,8 +11,8 @@ from django.shortcuts import render from django.urls import reverse from patchwork.models import CoverLetter +from patchwork.models import Patch from patchwork.models import Project -from patchwork.models import Submission from patchwork.views.utils import cover_to_mbox @@ -25,9 +25,11 @@ def cover_detail(request, project_id, msgid): cover = get_object_or_404(CoverLetter, project_id=project.id, msgid=db_msgid) except Http404 as exc: - submissions = Submission.objects.filter(project_id=project.id, - msgid=db_msgid) - if submissions: + patches = Patch.objects.filter( + project_id=project.id, + msgid=db_msgid, + ) + if patches: return HttpResponseRedirect( reverse('patch-detail', kwargs={'project_id': project.linkname, diff --git patchwork/views/patch.py patchwork/views/patch.py index 470ad197..9fdbbf9b 100644 --- patchwork/views/patch.py +++ patchwork/views/patch.py @@ -15,9 +15,9 @@ from django.urls import reverse from patchwork.forms import CreateBundleForm from patchwork.forms import PatchForm from patchwork.models import Bundle +from patchwork.models import CoverLetter from patchwork.models import Patch from patchwork.models import Project -from patchwork.models import Submission from patchwork.views import generic_list from patchwork.views.utils import patch_to_mbox from patchwork.views.utils import series_patch_to_mbox @@ -42,9 +42,11 @@ def patch_detail(request, project_id, msgid): try: patch = Patch.objects.get(project_id=project.id, msgid=db_msgid) except Patch.DoesNotExist: - submissions = Submission.objects.filter(project_id=project.id, - msgid=db_msgid) - if submissions: + covers = CoverLetter.objects.filter( + project_id=project.id, + msgid=db_msgid, + ) + if covers: return HttpResponseRedirect( reverse('cover-detail', kwargs={'project_id': project.linkname, From patchwork Thu Apr 9 17:12:59 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Stephen Finucane X-Patchwork-Id: 1268695 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.ozlabs.org (lists.ozlabs.org [IPv6:2401:3900:2:1::3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 48ynnY6ZNwz9sSM for ; Fri, 10 Apr 2020 03:14:41 +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" header.d=that.guru header.i=@that.guru header.a=rsa-sha256 header.s=default header.b=Y0kXXCfE; dkim-atps=neutral Received: from lists.ozlabs.org (lists.ozlabs.org [IPv6:2401:3900:2:1::3]) by lists.ozlabs.org (Postfix) with ESMTP id 48ynnY5p2bzDqgF for ; Fri, 10 Apr 2020 03:14:41 +1000 (AEST) X-Original-To: patchwork@lists.ozlabs.org Delivered-To: patchwork@lists.ozlabs.org Authentication-Results: lists.ozlabs.org; spf=none (no SPF record) smtp.mailfrom=that.guru (client-ip=199.181.239.202; helo=relay0202.mxlogin.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" header.d=that.guru header.i=@that.guru header.a=rsa-sha256 header.s=default header.b=Y0kXXCfE; dkim-atps=neutral Received: from relay0202.mxlogin.com (relay0202.mxlogin.com [199.181.239.202]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by lists.ozlabs.org (Postfix) with ESMTPS id 48ynm348VbzDqg9 for ; Fri, 10 Apr 2020 03:13:23 +1000 (AEST) Received: from filter004.mxroute.com ([149.28.56.236] 149.28.56.236.vultr.com) (Authenticated sender: mN4UYu2MZsgR) by relay0202.mxlogin.com (ZoneMTA) with ESMTPSA id 1715fee756a0000766.002 for (version=TLSv1/SSLv3 cipher=ECDHE-RSA-AES128-GCM-SHA256); Thu, 09 Apr 2020 17:13:15 +0000 X-Zone-Loop: 45caf47408220f8c8562021b2e6631e3b80a92f5b695 X-Originating-IP: [149.28.56.236] Received: from one.mxroute.com (one.mxroute.com [195.201.59.211]) by filter004.mxroute.com (Postfix) with ESMTPS id 500B93ED95; Thu, 9 Apr 2020 17:13:14 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=that.guru; s=default; h=Content-Transfer-Encoding:MIME-Version:References:In-Reply-To: Message-Id:Date:Subject:Cc:To:From:Sender:Reply-To:Content-Type: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=n97Ul/ucNaVvIzz8BmD7TJTgZEVrME4MJUEGlW8y7eY=; b=Y0kXXCfEjGANMVp8HDKuFjHnCT y/9t2RRz6qF49RUOlTwogry/NTK1WE43Px8krmq6La5XT8T/kJPnnqXdv6IwgbBLywKMnOn2CcwjC CgDX5MIHlhNWEAlam8lfReb3VuXVqdXpXz+uvGKGZj5Qjui2DGEIVLQMdSwPsvYi9pFqItNluOXcA xX98JjyDRlMrH4I5EM27WuIRCWWRMrkiuA2LTIP7hchqf1rqEpuC5+PKcgedT6OwDtvFn+bGkLES6 pfvLo7/1CXhJhdlBfuh7WwVgf3o5/2RuzS8EdLzlV5FFczltVTzb90oxzY6b72JAnNMV6TrNDyrYC Qb40f6nA==; From: Stephen Finucane To: patchwork@lists.ozlabs.org Subject: [PATCH v2 3/6] Revert "Be sensible computing project patch counts" Date: Thu, 9 Apr 2020 18:12:59 +0100 Message-Id: <20200409171302.105869-4-stephen@that.guru> X-Mailer: git-send-email 2.25.2 In-Reply-To: <20200409171302.105869-1-stephen@that.guru> References: <20200409171302.105869-1-stephen@that.guru> MIME-Version: 1.0 X-AuthUser: stephen@that.guru X-BeenThere: patchwork@lists.ozlabs.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Patchwork development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: patchwork-bounces+incoming=patchwork.ozlabs.org@lists.ozlabs.org Sender: "Patchwork" This reverts commit cfcf2f2a80ac0709f1a5fd9aa212c8403daa5a18. This will no longer be necessary once we remove the Patch-Submission split. Revert it now to avoid needing to rejig this later. Conflicts: patchwork/views/project.py NOTE(stephenfin): Conflicts are due to commit 880ec8c5 ("Fetch maintainer information in one query") which changed nearby lines. Signed-off-by: Stephen Finucane Cc: Daniel Axtens --- patchwork/views/project.py | 29 ++++------------------------- 1 file changed, 4 insertions(+), 25 deletions(-) diff --git patchwork/views/project.py patchwork/views/project.py index 8fa41794..4c25f715 100644 --- patchwork/views/project.py +++ patchwork/views/project.py @@ -10,10 +10,9 @@ from django.shortcuts import get_object_or_404 from django.shortcuts import render from django.urls import reverse +from patchwork.models import Patch from patchwork.models import Project -from django.db import connection - def project_list(request): projects = Project.objects.all() @@ -31,34 +30,14 @@ def project_list(request): def project_detail(request, project_id): project = get_object_or_404(Project, linkname=project_id) - - # So, we revert to raw sql because if we do what you'd think would - # be the correct thing in Django-ese, it ends up doing a *pointless* - # join with patchwork_submissions that ends up ruining the query. - # So, we do not do this, as this is wrong: - # - # patches = Patch.objects.filter( - # patch_project_id=project.id).only('archived') - # patches = patches.annotate(c=Count('archived')) - # - # and instead do this, because it's simple and fast - - n_patches = {} - with connection.cursor() as cursor: - cursor.execute('SELECT archived,COUNT(submission_ptr_id) as c ' - 'FROM patchwork_patch ' - 'WHERE patch_project_id=%s GROUP BY archived', - [project.id]) - - for r in cursor: - n_patches[r[0]] = r[1] + patches = Patch.objects.filter(project=project) context = { 'project': project, 'maintainers': User.objects.filter( profile__maintainer_projects=project).select_related('profile'), - 'n_patches': n_patches[False] if False in n_patches else 0, - 'n_archived_patches': n_patches[True] if True in n_patches else 0, + 'n_patches': patches.filter(archived=False).count(), + 'n_archived_patches': patches.filter(archived=True).count(), 'enable_xmlrpc': settings.ENABLE_XMLRPC, } return render(request, 'patchwork/project.html', context) From patchwork Thu Apr 9 17:13:00 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Stephen Finucane X-Patchwork-Id: 1268693 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.ozlabs.org (lists.ozlabs.org [IPv6:2401:3900:2:1::3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 48ynmn6pcgz9sSq for ; Fri, 10 Apr 2020 03:14:01 +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" header.d=that.guru header.i=@that.guru header.a=rsa-sha256 header.s=default header.b=Z8PToVRs; dkim-atps=neutral Received: from lists.ozlabs.org (lists.ozlabs.org [IPv6:2401:3900:2:1::3]) by lists.ozlabs.org (Postfix) with ESMTP id 48ynmn6T7GzDqg9 for ; Fri, 10 Apr 2020 03:14:01 +1000 (AEST) X-Original-To: patchwork@lists.ozlabs.org Delivered-To: patchwork@lists.ozlabs.org Authentication-Results: lists.ozlabs.org; spf=none (no SPF record) smtp.mailfrom=that.guru (client-ip=172.82.139.47; helo=qrelay47.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" header.d=that.guru header.i=@that.guru header.a=rsa-sha256 header.s=default header.b=Z8PToVRs; dkim-atps=neutral Received: from qrelay47.mxroute.com (qrelay47.mxroute.com [172.82.139.47]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by lists.ozlabs.org (Postfix) with ESMTPS id 48ynm06cbqzDqH6 for ; Fri, 10 Apr 2020 03:13:20 +1000 (AEST) Received: from filter003.mxroute.com ([168.235.111.26] 168-235-111-26.cloud.ramnode.com) (Authenticated sender: mN4UYu2MZsgR) by qrelay47.mxroute.com (ZoneMTA) with ESMTPA id 1715fee785d0000d83.001 for ; Thu, 09 Apr 2020 17:13:16 +0000 X-Zone-Loop: 29c1d08c1f7b9ea081fc8867607cacb702e5572bdb7d X-Originating-IP: [168.235.111.26] Received: from one.mxroute.com (one.mxroute.com [195.201.59.211]) by filter003.mxroute.com (Postfix) with ESMTPS id 4C6BB60038 for ; Thu, 9 Apr 2020 17:13:15 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=that.guru; s=default; h=Content-Transfer-Encoding:MIME-Version:References:In-Reply-To: Message-Id:Date:Subject:Cc:To:From:Sender:Reply-To:Content-Type: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=IoYa7HQZxjhApyN2KaQksIiJpGCU52ppambyq/JX1nc=; b=Z8PToVRsAX0JFvj5LCsoWdl/WL ckAjEJYIpab7AQ0cVHgjjSGcVDJpL+VzO9FyxKCeF7O128B/Z0pLFrLs8uKKu5LZKDZ7ztYSiyMZg JJly5GOTuaKxoOXgbB7VBlGD3O9p8XL7yiM3Amaq1AanLSRi4C2dlD+MXyWO0rbsGTJYMKHoNtnVz kDyXVyyyH8qImLZaX6aB6hb7itaR4hRjoJnbBSwZq0LjweiiQ0tzAESS/OJLgl72bnUCWa6SwJunZ 1Vj8DD0vLRHle6c/muyLSIR5Gn23VxKga0+PiUxJzveIpL+AgOgRHo2dxELMrb2PYj4xeRDh4MuD3 qf7VBfbQ==; From: Stephen Finucane To: patchwork@lists.ozlabs.org Subject: [PATCH v2 4/6] models: Split 'CoverLetter' from 'Submission' Date: Thu, 9 Apr 2020 18:13:00 +0100 Message-Id: <20200409171302.105869-5-stephen@that.guru> X-Mailer: git-send-email 2.25.2 In-Reply-To: <20200409171302.105869-1-stephen@that.guru> References: <20200409171302.105869-1-stephen@that.guru> MIME-Version: 1.0 X-AuthUser: stephen@that.guru X-BeenThere: patchwork@lists.ozlabs.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Patchwork development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: patchwork-bounces+incoming=patchwork.ozlabs.org@lists.ozlabs.org Sender: "Patchwork" We want to get rid of the split between 'Patch' and 'Submission' because of the cost of using JOINs basically everywhere we use 'Patch'. Before we do that, we need to move the other users of 'Submission' to other models and other models that rely on these users sharing the common 'Submission' base. For the former, there is only one user, 'CoverLetter', while for the latter there is only the 'Comment' model. As a result, we must do the following: - Create a new 'Cover' model - Create a new 'CoverComment' model - Move everything from 'CoverLetter' to 'Cover' and all entries associated with a 'CoverLetter' from 'Comment' to 'CoverComment' - Delete the 'CoverLetter' model - Rename the 'Comment' model to 'PatchComment' This means our model "hierarchy" goes from: Submission Patch CoverLetter Comment To: Submission Patch PatchComment Cover CoverComment A future change will flatten the 'Submission' and 'Patch' model. Note that this actually highlighted a bug in Django, which has since been reported upstream [1]. As noted there, the issue stems from MySQL's refusal to remove an index from a foreign key when DB constraints are used and the workaround is to remove the foreign key constraint before altering the indexes and then re-add the constraint after. [1] https://code.djangoproject.com/ticket/31335 Signed-off-by: Stephen Finucane --- patchwork/admin.py | 26 +- patchwork/api/comment.py | 52 +++- patchwork/api/cover.py | 10 +- patchwork/api/embedded.py | 2 +- patchwork/api/filters.py | 6 +- patchwork/management/commands/parsearchive.py | 11 +- patchwork/migrations/0042_add_cover_model.py | 247 ++++++++++++++++++ patchwork/models.py | 120 ++++++--- patchwork/parser.py | 77 ++++-- patchwork/signals.py | 4 +- patchwork/tests/api/test_comment.py | 11 +- patchwork/tests/test_detail.py | 31 ++- patchwork/tests/test_mboxviews.py | 14 +- patchwork/tests/test_parser.py | 4 +- patchwork/tests/test_series.py | 2 +- patchwork/tests/test_tags.py | 6 +- patchwork/tests/utils.py | 32 ++- patchwork/urls.py | 4 +- patchwork/views/comment.py | 43 ++- patchwork/views/cover.py | 12 +- patchwork/views/patch.py | 7 +- patchwork/views/utils.py | 17 +- 22 files changed, 595 insertions(+), 143 deletions(-) create mode 100644 patchwork/migrations/0042_add_cover_model.py diff --git patchwork/admin.py patchwork/admin.py index fb798085..a6502224 100644 --- patchwork/admin.py +++ patchwork/admin.py @@ -10,10 +10,11 @@ from django.db.models import Prefetch from patchwork.models import Bundle from patchwork.models import Check -from patchwork.models import Comment -from patchwork.models import CoverLetter +from patchwork.models import Cover +from patchwork.models import CoverComment from patchwork.models import DelegationRule from patchwork.models import Patch +from patchwork.models import PatchComment from patchwork.models import PatchRelation from patchwork.models import Person from patchwork.models import Project @@ -75,14 +76,14 @@ class StateAdmin(admin.ModelAdmin): admin.site.register(State, StateAdmin) -class CoverLetterAdmin(admin.ModelAdmin): +class CoverAdmin(admin.ModelAdmin): list_display = ('name', 'submitter', 'project', 'date') list_filter = ('project', ) search_fields = ('name', 'submitter__name', 'submitter__email') date_hierarchy = 'date' -admin.site.register(CoverLetter, CoverLetterAdmin) +admin.site.register(Cover, CoverAdmin) class PatchAdmin(admin.ModelAdmin): @@ -104,13 +105,22 @@ class PatchAdmin(admin.ModelAdmin): admin.site.register(Patch, PatchAdmin) -class CommentAdmin(admin.ModelAdmin): - list_display = ('submission', 'submitter', 'date') - search_fields = ('submission__name', 'submitter__name', 'submitter__email') +class CoverCommentAdmin(admin.ModelAdmin): + list_display = ('cover', 'submitter', 'date') + search_fields = ('cover__name', 'submitter__name', 'submitter__email') date_hierarchy = 'date' -admin.site.register(Comment, CommentAdmin) +admin.site.register(CoverComment, CoverCommentAdmin) + + +class PatchCommentAdmin(admin.ModelAdmin): + list_display = ('patch', 'submitter', 'date') + search_fields = ('patch__name', 'submitter__name', 'submitter__email') + date_hierarchy = 'date' + + +admin.site.register(PatchComment, PatchCommentAdmin) class PatchInline(admin.StackedInline): diff --git patchwork/api/comment.py patchwork/api/comment.py index 290b9cd3..3802dab9 100644 --- patchwork/api/comment.py +++ patchwork/api/comment.py @@ -12,11 +12,13 @@ from rest_framework.serializers import SerializerMethodField from patchwork.api.base import BaseHyperlinkedModelSerializer from patchwork.api.base import PatchworkPermission from patchwork.api.embedded import PersonSerializer -from patchwork.models import Comment +from patchwork.models import Cover +from patchwork.models import CoverComment from patchwork.models import Submission +from patchwork.models import PatchComment -class CommentListSerializer(BaseHyperlinkedModelSerializer): +class BaseCommentListSerializer(BaseHyperlinkedModelSerializer): web_url = SerializerMethodField() subject = SerializerMethodField() @@ -46,7 +48,6 @@ class CommentListSerializer(BaseHyperlinkedModelSerializer): return headers class Meta: - model = Comment fields = ('id', 'web_url', 'msgid', 'list_archive_url', 'date', 'subject', 'submitter', 'content', 'headers') read_only_fields = fields @@ -56,11 +57,48 @@ class CommentListSerializer(BaseHyperlinkedModelSerializer): } -class CommentList(ListAPIView): +class CoverCommentListSerializer(BaseCommentListSerializer): + + class Meta: + model = CoverComment + fields = BaseCommentListSerializer.Meta.fields + read_only_fields = fields + versioned_fields = BaseCommentListSerializer.Meta.versioned_fields + + +class PatchCommentListSerializer(BaseCommentListSerializer): + + class Meta: + model = PatchComment + fields = BaseCommentListSerializer.Meta.fields + read_only_fields = fields + versioned_fields = BaseCommentListSerializer.Meta.versioned_fields + + +class CoverCommentList(ListAPIView): + """List cover comments""" + + permission_classes = (PatchworkPermission,) + serializer_class = CoverCommentListSerializer + search_fields = ('subject',) + ordering_fields = ('id', 'subject', 'date', 'submitter') + ordering = 'id' + lookup_url_kwarg = 'pk' + + def get_queryset(self): + if not Cover.objects.filter(pk=self.kwargs['pk']).exists(): + raise Http404 + + return CoverComment.objects.filter( + cover=self.kwargs['pk'] + ).select_related('submitter') + + +class PatchCommentList(ListAPIView): """List comments""" permission_classes = (PatchworkPermission,) - serializer_class = CommentListSerializer + serializer_class = PatchCommentListSerializer search_fields = ('subject',) ordering_fields = ('id', 'subject', 'date', 'submitter') ordering = 'id' @@ -70,6 +108,6 @@ class CommentList(ListAPIView): if not Submission.objects.filter(pk=self.kwargs['pk']).exists(): raise Http404 - return Comment.objects.filter( - submission=self.kwargs['pk'] + return PatchComment.objects.filter( + patch=self.kwargs['pk'] ).select_related('submitter') diff --git patchwork/api/cover.py patchwork/api/cover.py index 66d2146c..b4a3a8f7 100644 --- patchwork/api/cover.py +++ patchwork/api/cover.py @@ -15,7 +15,7 @@ from patchwork.api.filters import CoverFilterSet from patchwork.api.embedded import PersonSerializer from patchwork.api.embedded import ProjectSerializer from patchwork.api.embedded import SeriesSerializer -from patchwork.models import CoverLetter +from patchwork.models import Cover class CoverListSerializer(BaseHyperlinkedModelSerializer): @@ -49,7 +49,7 @@ class CoverListSerializer(BaseHyperlinkedModelSerializer): return data class Meta: - model = CoverLetter + model = Cover fields = ('id', 'url', 'web_url', 'project', 'msgid', 'list_archive_url', 'date', 'name', 'submitter', 'mbox', 'series', 'comments') @@ -82,7 +82,7 @@ class CoverDetailSerializer(CoverListSerializer): return headers class Meta: - model = CoverLetter + model = Cover fields = CoverListSerializer.Meta.fields + ( 'headers', 'content') read_only_fields = fields @@ -100,7 +100,7 @@ class CoverList(ListAPIView): ordering = 'id' def get_queryset(self): - return CoverLetter.objects.all()\ + return Cover.objects.all()\ .prefetch_related('series__project')\ .select_related('project', 'submitter', 'series')\ .defer('content', 'headers') @@ -112,5 +112,5 @@ class CoverDetail(RetrieveAPIView): serializer_class = CoverDetailSerializer def get_queryset(self): - return CoverLetter.objects.all()\ + return Cover.objects.all()\ .select_related('project', 'submitter', 'series') diff --git patchwork/api/embedded.py patchwork/api/embedded.py index d2300dff..01990179 100644 --- patchwork/api/embedded.py +++ patchwork/api/embedded.py @@ -108,7 +108,7 @@ class CoverSerializer(SerializedRelatedField): class _Serializer(MboxMixin, WebURLMixin, BaseHyperlinkedModelSerializer): class Meta: - model = models.CoverLetter + model = models.Cover fields = ('id', 'url', 'web_url', 'msgid', 'list_archive_url', 'date', 'name', 'mbox') read_only_fields = fields diff --git patchwork/api/filters.py patchwork/api/filters.py index 80b17232..7aa6059a 100644 --- patchwork/api/filters.py +++ patchwork/api/filters.py @@ -18,7 +18,7 @@ from rest_framework import exceptions from patchwork.api import utils from patchwork.models import Bundle from patchwork.models import Check -from patchwork.models import CoverLetter +from patchwork.models import Cover from patchwork.models import Event from patchwork.models import Patch from patchwork.models import Person @@ -194,7 +194,7 @@ class CoverFilterSet(TimestampMixin, BaseFilterSet): submitter = PersonFilter(queryset=Person.objects.all(), distinct=False) class Meta: - model = CoverLetter + model = Cover fields = ('project', 'series', 'submitter') @@ -247,7 +247,7 @@ class EventFilterSet(TimestampMixin, BaseFilterSet): patch = BaseFilter(queryset=Patch.objects.all(), widget=MultipleHiddenInput, distinct=False) - cover = BaseFilter(queryset=CoverLetter.objects.all(), + cover = BaseFilter(queryset=Cover.objects.all(), widget=MultipleHiddenInput, distinct=False) diff --git patchwork/management/commands/parsearchive.py patchwork/management/commands/parsearchive.py index b7f1ea73..f53b8db4 100644 --- patchwork/management/commands/parsearchive.py +++ patchwork/management/commands/parsearchive.py @@ -32,8 +32,9 @@ class Command(BaseCommand): def handle(self, *args, **options): results = { models.Patch: 0, - models.CoverLetter: 0, - models.Comment: 0, + models.Cover: 0, + models.PatchComment: 0, + models.CoverComment: 0, } duplicates = 0 dropped = 0 @@ -118,9 +119,11 @@ class Command(BaseCommand): ' %(errors)4d errors\n' 'Total: %(new)s new entries' % { 'total': count, - 'covers': results[models.CoverLetter], + 'covers': results[models.Cover], 'patches': results[models.Patch], - 'comments': results[models.Comment], + 'comments': ( + results[models.CoverComment] + results[models.PatchComment] + ), 'duplicates': duplicates, 'dropped': dropped, 'errors': errors, diff --git patchwork/migrations/0042_add_cover_model.py patchwork/migrations/0042_add_cover_model.py new file mode 100644 index 00000000..53196c18 --- /dev/null +++ patchwork/migrations/0042_add_cover_model.py @@ -0,0 +1,247 @@ +import datetime + +from django.db import migrations, models +import django.db.models.deletion +import patchwork.models + + +class Migration(migrations.Migration): + + dependencies = [ + ('patchwork', '0041_python3'), + ] + + operations = [ + # create a new, separate cover (letter) model + + migrations.CreateModel( + name='Cover', + fields=[ + ( + 'id', + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name='ID', + ), + ), + ('msgid', models.CharField(max_length=255)), + ( + 'date', + models.DateTimeField(default=datetime.datetime.utcnow), + ), + ('headers', models.TextField(blank=True)), + ('content', models.TextField(blank=True, null=True)), + ('name', models.CharField(max_length=255)), + ( + 'project', + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to='patchwork.Project', + ), + ), + ( + 'submitter', + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to='patchwork.Person', + ), + ), + ], + options={'ordering': ['date']}, + bases=(patchwork.models.FilenameMixin, models.Model), + ), + migrations.AddIndex( + model_name='cover', + index=models.Index( + fields=['date', 'project', 'submitter', 'name'], + name='cover_covering_idx', + ), + ), + migrations.AlterUniqueTogether( + name='cover', unique_together=set([('msgid', 'project')]), + ), + + # create a new, separate cover (letter) comment model + + migrations.CreateModel( + name='CoverComment', + fields=[ + ( + 'id', + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name='ID', + ), + ), + ('msgid', models.CharField(max_length=255)), + ( + 'date', + models.DateTimeField(default=datetime.datetime.utcnow), + ), + ('headers', models.TextField(blank=True)), + ('content', models.TextField(blank=True, null=True)), + ( + 'cover', + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name='comments', + related_query_name='comment', + to='patchwork.Cover', + ), + ), + ( + 'submitter', + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to='patchwork.Person', + ), + ), + ], + options={'ordering': ['date']}, + ), + migrations.AddIndex( + model_name='covercomment', + index=models.Index( + fields=['cover', 'date'], name='cover_date_idx' + ), + ), + migrations.AlterUniqueTogether( + name='covercomment', unique_together=set([('msgid', 'cover')]), + ), + + # copy all entries from the 'CoverLetter' model to the new 'Cover' + # model; note that it's not possible to reverse this since we can't + # guarantee IDs will be unique after the split + + migrations.RunSQL( + """ + INSERT INTO patchwork_cover + (id, msgid, name, date, headers, project_id, submitter_id, + content) + SELECT s.id, s.msgid, s.name, s.date, s.headers, s.project_id, + s.submitter_id, s.content + FROM patchwork_coverletter c + INNER JOIN patchwork_submission s ON s.id = c.submission_ptr_id + """, + None, + ), + + # copy all 'CoverLetter'-related comments to the new 'CoverComment' + # table; as above, this is non-reversible + + migrations.RunSQL( + """ + INSERT INTO patchwork_covercomment + (id, msgid, date, headers, content, cover_id, submitter_id) + SELECT c.id, c.msgid, c.date, c.headers, c.content, + c.submission_id, c.submitter_id + FROM patchwork_comment c + INNER JOIN patchwork_coverletter p + ON p.submission_ptr_id = c.submission_id + """, + None, + ), + + # update all references to the 'CoverLetter' model to point to the new + # 'Cover' model instead + + migrations.AlterField( + model_name='event', + name='cover', + field=models.ForeignKey( + blank=True, + help_text='The cover letter that this event was created for.', + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name='+', + to='patchwork.Cover', + ), + ), + 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.Cover' + ), + ), + + # remove all the old 'CoverLetter'-related entries from the 'Comment' + # table + + migrations.RunSQL( + """ + DELETE patchwork_comment FROM + patchwork_comment + INNER JOIN patchwork_coverletter + ON patchwork_coverletter.submission_ptr_id = patchwork_comment.submission_id + """, # noqa + None + ), + + # delete the old 'CoverLetter' model + + migrations.DeleteModel( + name='CoverLetter', + ), + + # rename the 'Comment.submission' field to 'Comment.patch'; note our + # use of 'AlterField' before and after to work around bug #31335 + # + # https://code.djangoproject.com/ticket/31335 + + migrations.AlterField( + model_name='comment', + name='submission', + field=models.ForeignKey( + db_constraint=False, + on_delete=django.db.models.deletion.CASCADE, + related_name='comments', + related_query_name='comment', + to='patchwork.Submission', + ), + ), + migrations.RemoveIndex( + model_name='comment', + name='submission_date_idx', + ), + migrations.RenameField( + model_name='comment', + old_name='submission', + new_name='patch', + ), + migrations.AlterUniqueTogether( + name='comment', + unique_together=set([('msgid', 'patch')]), + ), + migrations.AddIndex( + model_name='comment', + index=models.Index( + fields=['patch', 'date'], + name='patch_date_idx', + ), + ), + migrations.AlterField( + model_name='comment', + name='patch', + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name='comments', + related_query_name='comment', + to='patchwork.Submission', + ), + ), + + # rename the 'Comment' model to 'PatchComment' + + migrations.RenameModel( + old_name='Comment', + new_name='PatchComment', + ), + ] diff --git patchwork/models.py patchwork/models.py index 769f602f..3755b654 100644 --- patchwork/models.py +++ patchwork/models.py @@ -358,7 +358,7 @@ class FilenameMixin(object): return fname -class Submission(FilenameMixin, EmailMixin, models.Model): +class SubmissionMixin(models.Model): # parent project = models.ForeignKey(Project, on_delete=models.CASCADE) @@ -385,19 +385,10 @@ class Submission(FilenameMixin, EmailMixin, models.Model): return self.name class Meta: - ordering = ['date'] - unique_together = [('msgid', 'project')] - indexes = [ - # This is a covering index for the /list/ query - # Like what we have for Patch, but used for displaying what we want - # rather than for working out the count (of course, this all - # depends on the SQL optimiser of your db engine) - models.Index(fields=['date', 'project', 'submitter', 'name'], - name='submission_covering_idx'), - ] + abstract = True -class CoverLetter(Submission): +class Cover(FilenameMixin, EmailMixin, SubmissionMixin): def get_absolute_url(self): return reverse('cover-detail', @@ -409,6 +400,35 @@ class CoverLetter(Submission): kwargs={'project_id': self.project.linkname, 'msgid': self.url_msgid}) + class Meta: + ordering = ['date'] + unique_together = [('msgid', 'project')] + indexes = [ + # This is a covering index for the /list/ query + # Like what we have for Patch, but used for displaying what we want + # rather than for working out the count (of course, this all + # depends on the SQL optimiser of your DB engine) + models.Index( + fields=['date', 'project', 'submitter', 'name'], + name='cover_covering_idx', + ), + ] + + +class Submission(SubmissionMixin, FilenameMixin, EmailMixin): + + class Meta: + ordering = ['date'] + unique_together = [('msgid', 'project')] + indexes = [ + # This is a covering index for the /list/ query + # Like what we have for Patch, but used for displaying what we want + # rather than for working out the count (of course, this all + # depends on the SQL optimiser of your db engine) + models.Index(fields=['date', 'project', 'submitter', 'name'], + name='submission_covering_idx'), + ] + class Patch(Submission): # patch metadata @@ -619,44 +639,79 @@ class Patch(Submission): ] -class Comment(EmailMixin, models.Model): +class CoverComment(EmailMixin, models.Model): + + cover = models.ForeignKey( + Cover, + related_name='comments', + related_query_name='comment', + on_delete=models.CASCADE, + ) + + @property + def list_archive_url(self): + if not self.cover.project.list_archive_url_format: + return None + if not self.msgid: + return None + return self.project.list_archive_url_format.format(self.url_msgid) + + def get_absolute_url(self): + return reverse('comment-redirect', kwargs={'comment_id': self.id}) + + def is_editable(self, user): + return False + + class Meta: + ordering = ['date'] + unique_together = [('msgid', 'cover')] + indexes = [ + models.Index(name='cover_date_idx', fields=['cover', 'date']), + ] + + +class PatchComment(EmailMixin, models.Model): # parent - submission = models.ForeignKey(Submission, related_name='comments', - related_query_name='comment', - on_delete=models.CASCADE) + patch = models.ForeignKey( + Submission, + related_name='comments', + related_query_name='comment', + on_delete=models.CASCADE, + ) @property def list_archive_url(self): - if not self.submission.project.list_archive_url_format: + if not self.patch.project.list_archive_url_format: return None if not self.msgid: return None - return self.project.list_archive_url_format.format( + return self.patch.list_archive_url_format.format( self.url_msgid) def get_absolute_url(self): return reverse('comment-redirect', kwargs={'comment_id': self.id}) def save(self, *args, **kwargs): - super(Comment, self).save(*args, **kwargs) - if hasattr(self.submission, 'patch'): - self.submission.patch.refresh_tag_counts() + super(PatchComment, self).save(*args, **kwargs) + # TODO(stephenfin): Update this once patch is flattened + if hasattr(self.patch, 'patch'): + self.patch.patch.refresh_tag_counts() def delete(self, *args, **kwargs): - super(Comment, self).delete(*args, **kwargs) - if hasattr(self.submission, 'patch'): - self.submission.patch.refresh_tag_counts() + super(PatchComment, self).delete(*args, **kwargs) + # TODO(stephenfin): Update this once patch is flattened + if hasattr(self.patch, 'patch'): + self.patch.patch.refresh_tag_counts() def is_editable(self, user): return False class Meta: ordering = ['date'] - unique_together = [('msgid', 'submission')] + unique_together = [('msgid', 'patch')] indexes = [ - models.Index(name='submission_date_idx', - fields=['submission', 'date']) + models.Index(name='patch_date_idx', fields=['patch', 'date']), ] @@ -668,9 +723,12 @@ class Series(FilenameMixin, models.Model): blank=True, on_delete=models.CASCADE) # content - cover_letter = models.OneToOneField(CoverLetter, related_name='series', - null=True, - on_delete=models.CASCADE) + cover_letter = models.OneToOneField( + Cover, + related_name='series', + null=True, + on_delete=models.CASCADE + ) # metadata name = models.CharField(max_length=255, blank=True, null=True, @@ -987,7 +1045,7 @@ class Event(models.Model): on_delete=models.CASCADE, help_text='The series that this event was created for.') cover = models.ForeignKey( - CoverLetter, related_name='+', null=True, blank=True, + Cover, related_name='+', null=True, blank=True, on_delete=models.CASCADE, help_text='The cover letter that this event was created for.') diff --git patchwork/parser.py patchwork/parser.py index a09fd754..ae46e687 100644 --- patchwork/parser.py +++ patchwork/parser.py @@ -18,11 +18,12 @@ from django.contrib.auth.models import User from django.db.utils import IntegrityError from django.db import transaction -from patchwork.models import Comment -from patchwork.models import CoverLetter +from patchwork.models import Cover +from patchwork.models import CoverComment from patchwork.models import DelegationRule from patchwork.models import get_default_initial_patch_state from patchwork.models import Patch +from patchwork.models import PatchComment from patchwork.models import Person from patchwork.models import Project from patchwork.models import Series @@ -657,10 +658,12 @@ def find_submission_for_comment(project, refs): # see if we have comments that refer to a patch try: - comment = Comment.objects.get(submission__project=project, - msgid=ref) + comment = PatchComment.objects.get( + patch__project=project, + msgid=ref, + ) return comment.submission - except Comment.MultipleObjectsReturned: + except PatchComment.MultipleObjectsReturned: # NOTE(stephenfin): This is a artifact of prior lack of support # for cover letters in Patchwork. Previously all replies to # patches were saved as comments. However, it's possible that @@ -674,11 +677,36 @@ def find_submission_for_comment(project, refs): # apply the comment to the cover letter. Note that this only # happens when running 'parsearchive' or similar, so it should not # affect every day use in any way. - comments = Comment.objects.filter(submission__project=project, - msgid=ref) + comments = PatchComment.objects.filter( + patch__project=project, + msgid=ref, + ) # The latter item will be the cover letter return comments.reverse()[0].submission - except Comment.DoesNotExist: + except PatchComment.DoesNotExist: + pass + + return None + + +def find_cover_for_comment(project, refs): + for ref in refs: + ref = ref[:255] + # first, check for a direct reply + try: + cover = Cover.objects.get(project=project, msgid=ref) + return cover + except Cover.DoesNotExist: + pass + + # see if we have comments that refer to a patch + try: + comment = CoverComment.objects.get( + cover__project=project, + msgid=ref, + ) + return comment.cover + except CoverComment.DoesNotExist: pass return None @@ -1182,11 +1210,11 @@ def parse_mail(mail, list_id=None): if not is_comment: if not refs == []: try: - CoverLetter.objects.all().get(name=name) - except CoverLetter.DoesNotExist: + Cover.objects.all().get(name=name) + except Cover.DoesNotExist: # if no match, this is a new cover letter is_cover_letter = True - except CoverLetter.MultipleObjectsReturned: + except Cover.MultipleObjectsReturned: # if multiple cover letters are found, just ignore pass else: @@ -1220,7 +1248,7 @@ def parse_mail(mail, list_id=None): msgid=msgid, project=project, series=series) try: - cover_letter = CoverLetter.objects.create( + cover_letter = Cover.objects.create( msgid=msgid, project=project, name=name[:255], @@ -1241,14 +1269,33 @@ def parse_mail(mail, list_id=None): # we only save comments if we have the parent email submission = find_submission_for_comment(project, refs) - if not submission: + if submission: + author = get_or_create_author(mail, project) + + try: + comment = PatchComment.objects.create( + patch=submission, + msgid=msgid, + date=date, + headers=headers, + submitter=author, + content=message) + except IntegrityError: + raise DuplicateMailError(msgid=msgid) + + logger.debug('Comment saved') + + return comment + + cover = find_cover_for_comment(project, refs) + if not cover: return author = get_or_create_author(mail, project) try: - comment = Comment.objects.create( - submission=submission, + comment = CoverComment.objects.create( + cover=cover, msgid=msgid, date=date, headers=headers, diff --git patchwork/signals.py patchwork/signals.py index 3a2f0fbd..4db9909f 100644 --- patchwork/signals.py +++ patchwork/signals.py @@ -10,7 +10,7 @@ from django.db.models.signals import pre_save from django.dispatch import receiver from patchwork.models import Check -from patchwork.models import CoverLetter +from patchwork.models import Cover from patchwork.models import Event from patchwork.models import Patch from patchwork.models import PatchChangeNotification @@ -54,7 +54,7 @@ def patch_change_callback(sender, instance, raw, **kwargs): notification.save() -@receiver(post_save, sender=CoverLetter) +@receiver(post_save, sender=Cover) def create_cover_created_event(sender, instance, created, raw, **kwargs): def create_event(cover): diff --git patchwork/tests/api/test_comment.py patchwork/tests/api/test_comment.py index f48bfce1..dfbf9049 100644 --- patchwork/tests/api/test_comment.py +++ patchwork/tests/api/test_comment.py @@ -10,9 +10,10 @@ from django.urls import NoReverseMatch from django.urls import reverse from patchwork.tests.api import utils -from patchwork.tests.utils import create_comment from patchwork.tests.utils import create_cover +from patchwork.tests.utils import create_cover_comment from patchwork.tests.utils import create_patch +from patchwork.tests.utils import create_patch_comment from patchwork.tests.utils import SAMPLE_CONTENT if settings.ENABLE_REST_API: @@ -47,7 +48,7 @@ class TestCoverComments(utils.APITestCase): def test_list(self): """List cover letter comments.""" cover = create_cover() - comment = create_comment(submission=cover) + comment = create_cover_comment(cover=cover) resp = self.client.get(self.api_url(cover)) self.assertEqual(status.HTTP_200_OK, resp.status_code) @@ -57,7 +58,7 @@ class TestCoverComments(utils.APITestCase): def test_list_version_1_0(self): """List cover letter comments using API v1.0.""" cover = create_cover() - create_comment(submission=cover) + create_cover_comment(cover=cover) # check we can't access comments using the old version of the API with self.assertRaises(NoReverseMatch): @@ -98,7 +99,7 @@ class TestPatchComments(utils.APITestCase): def test_list(self): """List patch comments.""" patch = create_patch() - comment = create_comment(submission=patch) + comment = create_patch_comment(patch=patch) resp = self.client.get(self.api_url(patch)) self.assertEqual(status.HTTP_200_OK, resp.status_code) @@ -108,7 +109,7 @@ class TestPatchComments(utils.APITestCase): def test_list_version_1_0(self): """List patch comments using API v1.0.""" patch = create_patch() - create_comment(submission=patch) + create_patch_comment(patch=patch) # check we can't access comments using the old version of the API with self.assertRaises(NoReverseMatch): diff --git patchwork/tests/test_detail.py patchwork/tests/test_detail.py index ddc2b937..393ebc76 100644 --- patchwork/tests/test_detail.py +++ patchwork/tests/test_detail.py @@ -6,9 +6,10 @@ from django.test import TestCase 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_cover_comment from patchwork.tests.utils import create_patch +from patchwork.tests.utils import create_patch_comment from patchwork.tests.utils import create_project @@ -159,24 +160,32 @@ class PatchViewTest(TestCase): class CommentRedirectTest(TestCase): - def _test_redirect(self, submission, submission_url): - comment_id = create_comment(submission=submission).id + def test_patch_redirect(self): + patch = create_patch() + comment_id = create_patch_comment(patch=patch).id requested_url = reverse('comment-redirect', kwargs={'comment_id': comment_id}) redirect_url = '%s#%d' % ( - reverse(submission_url, - kwargs={'project_id': submission.project.linkname, - 'msgid': submission.url_msgid}), + reverse('patch-detail', + kwargs={'project_id': patch.project.linkname, + 'msgid': patch.url_msgid}), comment_id) response = self.client.get(requested_url) self.assertRedirects(response, redirect_url) - def test_patch_redirect(self): - patch = create_patch() - self._test_redirect(patch, 'patch-detail') - def test_cover_redirect(self): cover = create_cover() - self._test_redirect(cover, 'cover-detail') + comment_id = create_cover_comment(cover=cover).id + + requested_url = reverse('comment-redirect', + kwargs={'comment_id': comment_id}) + redirect_url = '%s#%d' % ( + reverse('cover-detail', + kwargs={'project_id': cover.project.linkname, + 'msgid': cover.url_msgid}), + comment_id) + + response = self.client.get(requested_url) + self.assertRedirects(response, redirect_url) diff --git patchwork/tests/test_mboxviews.py patchwork/tests/test_mboxviews.py index 1e7bfb01..a7b0186a 100644 --- patchwork/tests/test_mboxviews.py +++ patchwork/tests/test_mboxviews.py @@ -13,8 +13,8 @@ import email from django.test import TestCase from django.urls import reverse -from patchwork.tests.utils import create_comment from patchwork.tests.utils import create_patch +from patchwork.tests.utils import create_patch_comment from patchwork.tests.utils import create_project from patchwork.tests.utils import create_person from patchwork.tests.utils import create_series @@ -34,8 +34,8 @@ class MboxPatchResponseTest(TestCase): project=self.project, submitter=self.person, content='comment 1 text\nAcked-by: 1\n') - create_comment( - submission=patch, + create_patch_comment( + patch=patch, submitter=self.person, content='comment 2 text\nAcked-by: 2\n') response = self.client.get( @@ -48,8 +48,8 @@ class MboxPatchResponseTest(TestCase): project=self.project, submitter=self.person, content='patch text\n') - create_comment( - submission=patch, + create_patch_comment( + patch=patch, submitter=self.person, content=u'comment\nAcked-by:\u00A0 foo') response = self.client.get( @@ -71,8 +71,8 @@ class MboxPatchSplitResponseTest(TestCase): submitter=self.person, diff='', content='comment 1 text\nAcked-by: 1\n---\nupdate\n') - self.comment = create_comment( - submission=self.patch, + self.comment = create_patch_comment( + patch=self.patch, submitter=self.person, content='comment 2 text\nAcked-by: 2\n') diff --git patchwork/tests/test_parser.py patchwork/tests/test_parser.py index f5631bee..12d2da4e 100644 --- patchwork/tests/test_parser.py +++ patchwork/tests/test_parser.py @@ -15,8 +15,8 @@ import unittest from django.test import TestCase from django.test import TransactionTestCase -from patchwork.models import Comment from patchwork.models import Patch +from patchwork.models import PatchComment from patchwork.models import Person from patchwork.models import State from patchwork.parser import clean_subject @@ -524,7 +524,7 @@ class MultipleProjectPatchCommentTest(MultipleProjectPatchTest): patch = Patch.objects.filter(project=project)[0] # we should see the reply comment only self.assertEqual( - Comment.objects.filter(submission=patch).count(), 1) + PatchComment.objects.filter(patch=patch).count(), 1) class ListIdHeaderTest(TestCase): diff --git patchwork/tests/test_series.py patchwork/tests/test_series.py index 2e86e47c..e68ee88e 100644 --- patchwork/tests/test_series.py +++ patchwork/tests/test_series.py @@ -35,7 +35,7 @@ class _BaseTestCase(TestCase): mbox = mailbox.mbox(os.path.join(TEST_SERIES_DIR, name), create=False) for msg in mbox: obj = parser.parse_mail(msg, project.listid) - if type(obj) == models.CoverLetter: + if type(obj) == models.Cover: results[0].append(obj) elif type(obj) == models.Patch: results[1].append(obj) diff --git patchwork/tests/test_tags.py patchwork/tests/test_tags.py index 97afba0a..6c13687f 100644 --- patchwork/tests/test_tags.py +++ patchwork/tests/test_tags.py @@ -9,8 +9,8 @@ from django.test import TransactionTestCase from patchwork.models import Patch from patchwork.models import PatchTag from patchwork.models import Tag -from patchwork.tests.utils import create_comment from patchwork.tests.utils import create_patch +from patchwork.tests.utils import create_patch_comment class ExtractTagsTest(TestCase): @@ -107,8 +107,8 @@ class PatchTagsTest(TransactionTestCase): return '%s-by: Test Tagger \n' % tags[tagtype] def create_tag_comment(self, patch, tagtype=None): - comment = create_comment( - submission=patch, + comment = create_patch_comment( + patch=patch, content=self.create_tag(tagtype)) return comment diff --git patchwork/tests/utils.py patchwork/tests/utils.py index 7759c8f3..ea184a9d 100644 --- patchwork/tests/utils.py +++ patchwork/tests/utils.py @@ -13,9 +13,10 @@ from django.contrib.auth.models import User from patchwork.models import Bundle from patchwork.models import Check -from patchwork.models import Comment -from patchwork.models import CoverLetter +from patchwork.models import Cover +from patchwork.models import CoverComment from patchwork.models import Patch +from patchwork.models import PatchComment from patchwork.models import PatchRelation from patchwork.models import Person from patchwork.models import Project @@ -203,8 +204,8 @@ def create_patch(**kwargs): def create_cover(**kwargs): - """Create 'CoverLetter' object.""" - num = CoverLetter.objects.count() + """Create 'Cover' object.""" + num = Cover.objects.count() # NOTE(stephenfin): Despite first appearances, passing 'series' to the # 'create' function doesn't actually cause the relationship to be created. @@ -232,7 +233,7 @@ def create_cover(**kwargs): } values.update(kwargs) - cover = CoverLetter.objects.create(**values) + cover = Cover.objects.create(**values) if series: series.add_cover_letter(cover) @@ -240,17 +241,30 @@ def create_cover(**kwargs): return cover -def create_comment(**kwargs): - """Create 'Comment' object.""" +def create_cover_comment(**kwargs): + """Create 'CoverComment' object.""" values = { 'submitter': create_person() if 'submitter' not in kwargs else None, - 'submission': create_patch() if 'submission' not in kwargs else None, + 'cover': create_cover() if 'cover' not in kwargs else None, 'msgid': make_msgid(), 'content': SAMPLE_CONTENT, } values.update(kwargs) - return Comment.objects.create(**values) + return CoverComment.objects.create(**values) + + +def create_patch_comment(**kwargs): + """Create 'PatchComment' object.""" + values = { + 'submitter': create_person() if 'submitter' not in kwargs else None, + 'patch': create_patch() if 'patch' not in kwargs else None, + 'msgid': make_msgid(), + 'content': SAMPLE_CONTENT, + } + values.update(kwargs) + + return PatchComment.objects.create(**values) def create_check(**kwargs): diff --git patchwork/urls.py patchwork/urls.py index 9c3b85f0..7d888d4a 100644 --- patchwork/urls.py +++ patchwork/urls.py @@ -249,10 +249,10 @@ if settings.ENABLE_REST_API: api_1_1_patterns = [ url(r'^patches/(?P[^/]+)/comments/$', - api_comment_views.CommentList.as_view(), + api_comment_views.PatchCommentList.as_view(), name='api-patch-comment-list'), url(r'^covers/(?P[^/]+)/comments/$', - api_comment_views.CommentList.as_view(), + api_comment_views.CoverCommentList.as_view(), name='api-cover-comment-list'), ] diff --git patchwork/views/comment.py patchwork/views/comment.py index 7dee8dc4..4f699224 100644 --- patchwork/views/comment.py +++ patchwork/views/comment.py @@ -4,20 +4,41 @@ # SPDX-License-Identifier: GPL-2.0-or-later from django import http -from django import shortcuts from django.urls import reverse from patchwork import models def comment(request, comment_id): - submission = shortcuts.get_object_or_404(models.Comment, - id=comment_id).submission - if models.Patch.objects.filter(id=submission.id).exists(): - url = 'patch-detail' - else: - url = 'cover-detail' - - return http.HttpResponseRedirect('%s#%s' % ( - reverse(url, kwargs={'project_id': submission.project.linkname, - 'msgid': submission.url_msgid}), comment_id)) + patch = None + cover = None + + try: + patch = models.PatchComment.objects.get(id=comment_id).patch + except models.PatchComment.DoesNotExist: + try: + cover = models.CoverComment.objects.get(id=comment_id).cover + except models.CoverComment.DoesNotExist: + pass + + if not patch and not cover: + raise http.Http404('No comment matches the given query.') + + if patch: + url = reverse( + 'patch-detail', + kwargs={ + 'project_id': patch.project.linkname, + 'msgid': patch.url_msgid, + }, + ) + else: # cover + url = reverse( + 'cover-detail', + kwargs={ + 'project_id': cover.project.linkname, + 'msgid': cover.url_msgid, + }, + ) + + return http.HttpResponseRedirect('%s#%s' % (url, comment_id)) diff --git patchwork/views/cover.py patchwork/views/cover.py index e90b7373..8ab0ba99 100644 --- patchwork/views/cover.py +++ patchwork/views/cover.py @@ -10,7 +10,7 @@ from django.shortcuts import get_object_or_404 from django.shortcuts import render from django.urls import reverse -from patchwork.models import CoverLetter +from patchwork.models import Cover from patchwork.models import Patch from patchwork.models import Project from patchwork.views.utils import cover_to_mbox @@ -22,7 +22,7 @@ def cover_detail(request, project_id, msgid): # redirect to patches where necessary try: - cover = get_object_or_404(CoverLetter, project_id=project.id, + cover = get_object_or_404(Cover, project_id=project.id, msgid=db_msgid) except Http404 as exc: patches = Patch.objects.filter( @@ -44,7 +44,7 @@ def cover_detail(request, project_id, msgid): comments = cover.comments.all() comments = comments.select_related('submitter') comments = comments.only('submitter', 'date', 'id', 'content', - 'submission') + 'cover') context['comments'] = comments return render(request, 'patchwork/submission.html', context) @@ -53,7 +53,7 @@ def cover_detail(request, project_id, msgid): def cover_mbox(request, project_id, msgid): db_msgid = ('<%s>' % msgid) project = get_object_or_404(Project, linkname=project_id) - cover = get_object_or_404(CoverLetter, project_id=project.id, + cover = get_object_or_404(Cover, project_id=project.id, msgid=db_msgid) response = HttpResponse(content_type='text/plain') @@ -65,7 +65,7 @@ def cover_mbox(request, project_id, msgid): def cover_by_id(request, cover_id): - cover = get_object_or_404(CoverLetter, id=cover_id) + cover = get_object_or_404(Cover, id=cover_id) url = reverse('cover-detail', kwargs={'project_id': cover.project.linkname, 'msgid': cover.url_msgid}) @@ -74,7 +74,7 @@ def cover_by_id(request, cover_id): def cover_mbox_by_id(request, cover_id): - cover = get_object_or_404(CoverLetter, id=cover_id) + cover = get_object_or_404(Cover, id=cover_id) url = reverse('cover-mbox', kwargs={'project_id': cover.project.linkname, 'msgid': cover.url_msgid}) diff --git patchwork/views/patch.py patchwork/views/patch.py index 9fdbbf9b..5d772fdf 100644 --- patchwork/views/patch.py +++ patchwork/views/patch.py @@ -15,7 +15,7 @@ from django.urls import reverse from patchwork.forms import CreateBundleForm from patchwork.forms import PatchForm from patchwork.models import Bundle -from patchwork.models import CoverLetter +from patchwork.models import Cover from patchwork.models import Patch from patchwork.models import Project from patchwork.views import generic_list @@ -42,7 +42,7 @@ def patch_detail(request, project_id, msgid): try: patch = Patch.objects.get(project_id=project.id, msgid=db_msgid) except Patch.DoesNotExist: - covers = CoverLetter.objects.filter( + covers = Cover.objects.filter( project_id=project.id, msgid=db_msgid, ) @@ -109,8 +109,7 @@ def patch_detail(request, project_id, msgid): comments = patch.comments.all() comments = comments.select_related('submitter') - comments = comments.only('submitter', 'date', 'id', 'content', - 'submission') + comments = comments.only('submitter', 'date', 'id', 'content', 'patch') if patch.related: related_same_project = patch.related.patches.only( diff --git patchwork/views/utils.py patchwork/views/utils.py index 4419702e..f02948c2 100644 --- patchwork/views/utils.py +++ patchwork/views/utils.py @@ -15,8 +15,9 @@ import re from django.conf import settings from django.http import Http404 -from patchwork.models import Comment +from patchwork.models import CoverComment from patchwork.models import Patch +from patchwork.models import PatchComment from patchwork.parser import split_from_header if settings.ENABLE_REST_API: @@ -34,12 +35,12 @@ class PatchMbox(MIMENonMultipart): def _submission_to_mbox(submission): - """Get an mbox representation of a single Submission. + """Get an mbox representation of a single submission. - Handles both Patch and CoverLetter objects. + Handles both Patch and Cover objects. Arguments: - submission: The Patch object to convert. + submission: The Patch or Cover object to convert. Returns: A string for the mbox file. @@ -61,8 +62,12 @@ def _submission_to_mbox(submission): postscript = '' # TODO(stephenfin): Make this use the tags infrastructure - for comment in Comment.objects.filter(submission=submission): - body += comment.patch_responses + if is_patch: + for comment in PatchComment.objects.filter(patch=submission): + body += comment.patch_responses + else: + for comment in CoverComment.objects.filter(cover=submission): + body += comment.patch_responses if postscript: body += '---\n' + postscript + '\n' From patchwork Thu Apr 9 17:13:01 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Stephen Finucane X-Patchwork-Id: 1268694 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.ozlabs.org (lists.ozlabs.org [IPv6:2401:3900:2:1::3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 48ynnC1M06z9sSc for ; Fri, 10 Apr 2020 03:14:23 +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" header.d=that.guru header.i=@that.guru header.a=rsa-sha256 header.s=default header.b=iN9IGpCV; dkim-atps=neutral Received: from lists.ozlabs.org (lists.ozlabs.org [IPv6:2401:3900:2:1::3]) by lists.ozlabs.org (Postfix) with ESMTP id 48ynnC0TljzDqbc for ; Fri, 10 Apr 2020 03:14:23 +1000 (AEST) X-Original-To: patchwork@lists.ozlabs.org Delivered-To: patchwork@lists.ozlabs.org Authentication-Results: lists.ozlabs.org; spf=none (no SPF record) smtp.mailfrom=that.guru (client-ip=199.181.239.230; helo=relay0230.mxlogin.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" header.d=that.guru header.i=@that.guru header.a=rsa-sha256 header.s=default header.b=iN9IGpCV; dkim-atps=neutral Received: from relay0230.mxlogin.com (relay0230.mxlogin.com [199.181.239.230]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by lists.ozlabs.org (Postfix) with ESMTPS id 48ynm32zB9zDqby for ; Fri, 10 Apr 2020 03:13:23 +1000 (AEST) Received: from filter003.mxroute.com ([168.235.111.26] 168-235-111-26.cloud.ramnode.com) (Authenticated sender: mN4UYu2MZsgR) by relay0230.mxlogin.com (ZoneMTA) with ESMTPSA id 1715fee7cd60000766.003 for (version=TLSv1/SSLv3 cipher=ECDHE-RSA-AES128-GCM-SHA256); Thu, 09 Apr 2020 17:13:17 +0000 X-Zone-Loop: f4b0edc92e9d71dca613502c59ae7f32db16dc913f0f X-Originating-IP: [168.235.111.26] Received: from one.mxroute.com (one.mxroute.com [195.201.59.211]) by filter003.mxroute.com (Postfix) with ESMTPS id 41CD460048; Thu, 9 Apr 2020 17:13:16 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=that.guru; s=default; h=Content-Transfer-Encoding:MIME-Version:References:In-Reply-To: Message-Id:Date:Subject:Cc:To:From:Sender:Reply-To:Content-Type: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=94CMx9HptyWniu+cR9cKk+0LAZoB7Vuvznxe+VdpZGw=; b=iN9IGpCV2lNKtyyuF9k242KpO6 UGbRIecJoCdedR2cWWodKnElexT0twozxLrnCAqQ7OMYpEwqJ8fv4dhBU9gR0XJLAR+AExHBBnxWc sg+IGTqjq399sL/QnV+sPFjNr9211ghzlVAiVYp9VjF2jhtqvKmoL6cEmgL7pnW6ivjhUlGKd+bSB UJZkFOeML855lQta6AtinegvSNv9ofnEN9aoxVAop4uaxhdZrkyPRj0jzhsOdArD8+Mtffa5ylT2M NYh/GQDGLaSdsaSb63TfYOxcTKVxZO5XMP6pRoIGPBb223zlGYHpIpnkCeTXO/oAKXoGcNs/wU1tF Gubf8cbA==; From: Stephen Finucane To: patchwork@lists.ozlabs.org Subject: [PATCH v2 5/6] models: Merge 'Patch' and 'Submission' Date: Thu, 9 Apr 2020 18:13:01 +0100 Message-Id: <20200409171302.105869-6-stephen@that.guru> X-Mailer: git-send-email 2.25.2 In-Reply-To: <20200409171302.105869-1-stephen@that.guru> References: <20200409171302.105869-1-stephen@that.guru> MIME-Version: 1.0 X-AuthUser: stephen@that.guru X-BeenThere: patchwork@lists.ozlabs.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Patchwork development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Stewart Smith Errors-To: patchwork-bounces+incoming=patchwork.ozlabs.org@lists.ozlabs.org Sender: "Patchwork" Oh, the follies of youth. Time to undo the damage of 2.0.0, specifically commit 86172ccc16, which split Patch into two separate models using concrete inheritance. As noted previously, this introduced a large number of unavoidable JOINs across large tables and the performance impacts these introduce are blocking other features we want, such as improved tagging functionality. To combine these two models, we must do the following: - Update any references to the 'Patch' model to point to the 'Submission' model instead - Move everything from 'Patch' to 'Submission', including both fields and options - Delete the 'Patch' model - Rename the 'Submission' model to 'Patch' With this change, our model "hierarchy" goes from: Submission Patch PatchComment Cover CoverComment To a nice, flat: Patch PatchComment Cover CoverComment I expect this migration to be intensive, particularly for MySQL users who will see their entire tables rewritten. Unfortunately I don't see any way to resolve this in an easier manner. Signed-off-by: Stephen Finucane Cc: Daniel Axtens Cc: Stewart Smith --- v2: - Combine the migrations since we've dropped Django 1.11 support - Update migration to include 'related' field - Fix typo with PostgreSQL migration TODO: - Figure out if the indexes are correct (Stewart?). I've just included everything that's displayed on the '/list' page. --- patchwork/api/comment.py | 4 +- patchwork/api/filters.py | 3 +- patchwork/management/commands/dumparchive.py | 2 +- .../migrations/0043_merge_patch_submission.py | 292 ++++++++++++++++++ patchwork/models.py | 94 +++--- patchwork/parser.py | 16 +- patchwork/tests/test_mboxviews.py | 26 +- patchwork/tests/utils.py | 5 +- patchwork/views/__init__.py | 2 +- patchwork/views/utils.py | 4 +- 10 files changed, 371 insertions(+), 77 deletions(-) create mode 100644 patchwork/migrations/0043_merge_patch_submission.py diff --git patchwork/api/comment.py patchwork/api/comment.py index 3802dab9..43b26c61 100644 --- patchwork/api/comment.py +++ patchwork/api/comment.py @@ -14,7 +14,7 @@ from patchwork.api.base import PatchworkPermission from patchwork.api.embedded import PersonSerializer from patchwork.models import Cover from patchwork.models import CoverComment -from patchwork.models import Submission +from patchwork.models import Patch from patchwork.models import PatchComment @@ -105,7 +105,7 @@ class PatchCommentList(ListAPIView): lookup_url_kwarg = 'pk' def get_queryset(self): - if not Submission.objects.filter(pk=self.kwargs['pk']).exists(): + if not Patch.objects.filter(pk=self.kwargs['pk']).exists(): raise Http404 return PatchComment.objects.filter( diff --git patchwork/api/filters.py patchwork/api/filters.py index 7aa6059a..7538aaad 100644 --- patchwork/api/filters.py +++ patchwork/api/filters.py @@ -200,8 +200,7 @@ class CoverFilterSet(TimestampMixin, BaseFilterSet): class PatchFilterSet(TimestampMixin, BaseFilterSet): - project = ProjectFilter(queryset=Project.objects.all(), distinct=False, - field_name='patch_project') + project = ProjectFilter(queryset=Project.objects.all(), distinct=False) # NOTE(stephenfin): We disable the select-based HTML widgets for these # filters as the resulting query is _huge_ series = BaseFilter(queryset=Series.objects.all(), diff --git patchwork/management/commands/dumparchive.py patchwork/management/commands/dumparchive.py index 9ee80c8b..e9445eab 100644 --- patchwork/management/commands/dumparchive.py +++ patchwork/management/commands/dumparchive.py @@ -58,7 +58,7 @@ class Command(BaseCommand): i + 1, len(projects), project.linkname)) with tempfile.NamedTemporaryFile(delete=False) as mbox: - patches = Patch.objects.filter(patch_project=project) + patches = Patch.objects.filter(project=project) count = patches.count() for j, patch in enumerate(patches): if not (j % 10): diff --git patchwork/migrations/0043_merge_patch_submission.py patchwork/migrations/0043_merge_patch_submission.py new file mode 100644 index 00000000..25e741d6 --- /dev/null +++ patchwork/migrations/0043_merge_patch_submission.py @@ -0,0 +1,292 @@ +from django.conf import settings +from django.db import connection, migrations, models +import django.db.models.deletion + +import patchwork.fields + + +def migrate_data(apps, schema_editor): + if connection.vendor == 'postgresql': + schema_editor.execute( + """ + UPDATE patchwork_submission + SET archived = patchwork_patch.archived2, + commit_ref = patchwork_patch.commit_ref2, + delegate_id = patchwork_patch.delegate2_id, + diff = patchwork_patch.diff2, + hash = patchwork_patch.hash2, + number = patchwork_patch.number2, + pull_url = patchwork_patch.pull_url2, + related_id = patchwork_patch.related2_id, + series_id = patchwork_patch.series2_id, + state_id = patchwork_patch.state2_id + FROM patchwork_patch + WHERE patchwork_submission.id = patchwork_patch.submission_ptr_id + """ + ) + elif connection.vendor == 'mysql': + schema_editor.execute( + """ + UPDATE patchwork_submission, patchwork_patch + SET patchwork_submission.archived = patchwork_patch.archived2, + patchwork_submission.commit_ref = patchwork_patch.commit_ref2, + patchwork_submission.delegate_id = patchwork_patch.delegate2_id, + patchwork_submission.diff = patchwork_patch.diff2, + patchwork_submission.hash = patchwork_patch.hash2, + patchwork_submission.number = patchwork_patch.number2, + patchwork_submission.pull_url = patchwork_patch.pull_url2, + patchwork_submission.related_id = patchwork_patch.related2_id, + patchwork_submission.series_id = patchwork_patch.series2_id, + patchwork_submission.state_id = patchwork_patch.state2_id + WHERE patchwork_submission.id = patchwork_patch.submission_ptr_id + """ # noqa + ) + else: + raise Exception('DB not supported') + + +class Migration(migrations.Migration): + + dependencies = [ + ('patchwork', '0042_add_cover_model'), + ] + + operations = [ + # move the 'PatchTag' model to point to 'Submission' + + migrations.RemoveField(model_name='patch', name='tags',), + migrations.AddField( + model_name='submission', + name='tags', + field=models.ManyToManyField( + through='patchwork.PatchTag', to='patchwork.Tag' + ), + ), + migrations.AlterField( + model_name='patchtag', + name='patch', + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to='patchwork.Submission', + ), + ), + + # do the same for any other field that references 'Patch' + + migrations.AlterField( + model_name='bundle', + name='patches', + field=models.ManyToManyField( + through='patchwork.BundlePatch', to='patchwork.Submission' + ), + ), + migrations.AlterField( + model_name='bundlepatch', + name='patch', + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to='patchwork.Submission', + ), + ), + migrations.AlterField( + model_name='check', + name='patch', + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to='patchwork.Submission', + ), + ), + migrations.AlterField( + model_name='event', + name='patch', + field=models.ForeignKey( + blank=True, + help_text='The patch that this event was created for.', + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name='+', + to='patchwork.Submission', + ), + ), + migrations.AlterField( + model_name='patchchangenotification', + name='patch', + field=models.OneToOneField( + on_delete=django.db.models.deletion.CASCADE, + primary_key=True, + serialize=False, + to='patchwork.Submission', + ), + ), + + # rename all the fields on 'Patch' so we don't have duplicates when we + # add them to 'Submission' + + migrations.RemoveIndex( + model_name='patch', name='patch_list_covering_idx', + ), + migrations.AlterUniqueTogether(name='patch', unique_together=set([]),), + migrations.RenameField( + model_name='patch', old_name='archived', new_name='archived2', + ), + migrations.RenameField( + model_name='patch', old_name='commit_ref', new_name='commit_ref2', + ), + migrations.RenameField( + model_name='patch', old_name='delegate', new_name='delegate2', + ), + migrations.RenameField( + model_name='patch', old_name='diff', new_name='diff2', + ), + migrations.RenameField( + model_name='patch', old_name='hash', new_name='hash2', + ), + migrations.RenameField( + model_name='patch', old_name='number', new_name='number2', + ), + migrations.RenameField( + model_name='patch', old_name='pull_url', new_name='pull_url2', + ), + migrations.RenameField( + model_name='patch', old_name='related', new_name='related2', + ), + migrations.RenameField( + model_name='patch', old_name='series', new_name='series2', + ), + migrations.RenameField( + model_name='patch', old_name='state', new_name='state2', + ), + + # add the fields found on 'Patch' to 'Submission' + + migrations.AddField( + model_name='submission', + name='archived', + field=models.BooleanField(default=False), + ), + migrations.AddField( + model_name='submission', + name='commit_ref', + field=models.CharField(blank=True, max_length=255, null=True), + ), + migrations.AddField( + model_name='submission', + name='delegate', + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + to=settings.AUTH_USER_MODEL, + ), + ), + migrations.AddField( + model_name='submission', + name='diff', + field=models.TextField(blank=True, null=True), + ), + migrations.AddField( + model_name='submission', + name='hash', + field=patchwork.fields.HashField( + blank=True, max_length=40, null=True + ), + ), + migrations.AddField( + model_name='submission', + name='number', + field=models.PositiveSmallIntegerField( + default=None, + help_text='The number assigned to this patch in the series', + null=True, + ), + ), + migrations.AddField( + model_name='submission', + name='pull_url', + field=models.CharField(blank=True, max_length=255, null=True), + ), + migrations.AddField( + model_name='submission', + name='related', + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name='patches', + related_query_name='patch', + to='patchwork.PatchRelation', + ), + ), + migrations.AddField( + model_name='submission', + 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.AddField( + model_name='submission', + name='state', + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.CASCADE, + to='patchwork.State', + ), + ), + + # copy the data from 'Patch' to 'Submission' + + migrations.RunPython(migrate_data, None, atomic=False), + + # configure metadata for the 'Submission' model + + migrations.AlterModelOptions( + name='submission', + options={ + 'base_manager_name': 'objects', + 'ordering': ['date'], + 'verbose_name_plural': 'Patches', + }, + ), + migrations.AlterUniqueTogether( + name='submission', + unique_together=set([('series', 'number'), ('msgid', 'project')]), + ), + migrations.RemoveIndex( + model_name='submission', name='submission_covering_idx', + ), + migrations.AddIndex( + model_name='submission', + index=models.Index( + fields=[ + 'archived', + 'state', + 'delegate', + 'date', + 'project', + 'submitter', + 'name', + ], + name='patch_covering_idx', + ), + ), + + # remove the foreign key fields from the 'Patch' model + + migrations.RemoveField(model_name='patch', name='delegate2',), + migrations.RemoveField(model_name='patch', name='patch_project',), + migrations.RemoveField(model_name='patch', name='related2',), + migrations.RemoveField(model_name='patch', name='series2',), + migrations.RemoveField(model_name='patch', name='state2',), + migrations.RemoveField(model_name='patch', name='submission_ptr',), + + # drop the 'Patch' model and rename 'Submission' to 'Patch' + + migrations.DeleteModel(name='Patch',), + migrations.RenameModel(old_name='Submission', new_name='Patch',), + ] diff --git patchwork/models.py patchwork/models.py index 3755b654..a5e7bea7 100644 --- patchwork/models.py +++ patchwork/models.py @@ -164,7 +164,7 @@ class UserProfile(models.Model): @property def contributor_projects(self): submitters = Person.objects.filter(user=self.user) - return Project.objects.filter(id__in=Submission.objects.filter( + return Project.objects.filter(id__in=Patch.objects.filter( submitter__in=submitters).values('project_id').query) @property @@ -285,8 +285,7 @@ class PatchQuerySet(models.query.QuerySet): select[tag.attr_name] = ( "coalesce(" "(SELECT count FROM patchwork_patchtag" - " WHERE patchwork_patchtag.patch_id=" - "patchwork_patch.submission_ptr_id" + " WHERE patchwork_patchtag.patch_id=patchwork_patch.id" " AND patchwork_patchtag.tag_id=%s), 0)") select_params.append(tag.id) @@ -415,23 +414,7 @@ class Cover(FilenameMixin, EmailMixin, SubmissionMixin): ] -class Submission(SubmissionMixin, FilenameMixin, EmailMixin): - - class Meta: - ordering = ['date'] - unique_together = [('msgid', 'project')] - indexes = [ - # This is a covering index for the /list/ query - # Like what we have for Patch, but used for displaying what we want - # rather than for working out the count (of course, this all - # depends on the SQL optimiser of your db engine) - models.Index(fields=['date', 'project', 'submitter', 'name'], - name='submission_covering_idx'), - ] - - -class Patch(Submission): - # patch metadata +class Patch(FilenameMixin, EmailMixin, SubmissionMixin): diff = models.TextField(null=True, blank=True) commit_ref = models.CharField(max_length=255, null=True, blank=True) @@ -440,24 +423,31 @@ class Patch(Submission): # patchwork metadata - delegate = models.ForeignKey(User, blank=True, null=True, - on_delete=models.CASCADE) + delegate = models.ForeignKey( + User, + blank=True, + null=True, + on_delete=models.CASCADE, + ) state = models.ForeignKey(State, null=True, on_delete=models.CASCADE) archived = models.BooleanField(default=False) hash = HashField(null=True, blank=True) - # duplicate project from submission in subclass so we can count the - # 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') + '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') + default=None, + null=True, + help_text='The number assigned to this patch in the series', + ) # related patches metadata @@ -628,14 +618,23 @@ class Patch(Submission): class Meta: verbose_name_plural = 'Patches' + ordering = ['date'] base_manager_name = 'objects' - unique_together = [('series', 'number')] - + unique_together = [('msgid', 'project'), ('series', 'number')] indexes = [ # This is a covering index for the /list/ query - models.Index(fields=['archived', 'patch_project', 'state', - 'delegate'], - name='patch_list_covering_idx'), + models.Index( + fields=[ + 'archived', + 'state', + 'delegate', + 'date', + 'project', + 'submitter', + 'name', + ], + name='patch_covering_idx', + ), ] @@ -674,7 +673,7 @@ class PatchComment(EmailMixin, models.Model): # parent patch = models.ForeignKey( - Submission, + Patch, related_name='comments', related_query_name='comment', on_delete=models.CASCADE, @@ -694,15 +693,11 @@ class PatchComment(EmailMixin, models.Model): def save(self, *args, **kwargs): super(PatchComment, self).save(*args, **kwargs) - # TODO(stephenfin): Update this once patch is flattened - if hasattr(self.patch, 'patch'): - self.patch.patch.refresh_tag_counts() + self.patch.refresh_tag_counts() def delete(self, *args, **kwargs): super(PatchComment, self).delete(*args, **kwargs) - # TODO(stephenfin): Update this once patch is flattened - if hasattr(self.patch, 'patch'): - self.patch.patch.refresh_tag_counts() + self.patch.refresh_tag_counts() def is_editable(self, user): return False @@ -744,10 +739,10 @@ class Series(FilenameMixin, models.Model): @staticmethod def _format_name(obj): - # The parser ensure 'Submission.name' will always take the form - # 'subject' or '[prefix_a,prefix_b,...] subject'. There will never be - # multiple prefixes (text inside brackets), thus, we don't need to - # account for multiple prefixes here. + # The parser ensure 'Cover.name' will always take the form 'subject' or + # '[prefix_a,prefix_b,...] subject'. There will never be multiple + # prefixes (text inside brackets), thus, we don't need to account for + # multiple prefixes here. prefix_re = re.compile(r'^\[([^\]]*)\]\s*(.*)$') match = prefix_re.match(obj.name) if match: @@ -1133,7 +1128,10 @@ class EmailOptout(models.Model): class PatchChangeNotification(models.Model): - patch = models.OneToOneField(Patch, primary_key=True, - on_delete=models.CASCADE) + patch = models.OneToOneField( + Patch, + primary_key=True, + on_delete=models.CASCADE, + ) last_modified = models.DateTimeField(default=datetime.datetime.utcnow) orig_state = models.ForeignKey(State, on_delete=models.CASCADE) diff --git patchwork/parser.py patchwork/parser.py index ae46e687..0ffb42e1 100644 --- patchwork/parser.py +++ patchwork/parser.py @@ -29,7 +29,6 @@ from patchwork.models import Project from patchwork.models import Series from patchwork.models import SeriesReference from patchwork.models import State -from patchwork.models import Submission _hunk_re = re.compile(r'^\@\@ -\d+(?:,(\d+))? \+\d+(?:,(\d+))? \@\@') @@ -646,14 +645,14 @@ def find_comment_content(mail): return None, commentbuf -def find_submission_for_comment(project, refs): +def find_patch_for_comment(project, refs): for ref in refs: ref = ref[:255] # first, check for a direct reply try: - submission = Submission.objects.get(project=project, msgid=ref) - return submission - except Submission.DoesNotExist: + patch = Patch.objects.get(project=project, msgid=ref) + return patch + except Patch.DoesNotExist: pass # see if we have comments that refer to a patch @@ -1094,7 +1093,6 @@ def parse_mail(mail, list_id=None): patch = Patch.objects.create( msgid=msgid, project=project, - patch_project=project, name=name[:255], date=date, headers=headers, @@ -1268,13 +1266,13 @@ def parse_mail(mail, list_id=None): # comments # we only save comments if we have the parent email - submission = find_submission_for_comment(project, refs) - if submission: + patch = find_patch_for_comment(project, refs) + if patch: author = get_or_create_author(mail, project) try: comment = PatchComment.objects.create( - patch=submission, + patch=patch, msgid=msgid, date=date, headers=headers, diff --git patchwork/tests/test_mboxviews.py patchwork/tests/test_mboxviews.py index a7b0186a..1535c5cb 100644 --- patchwork/tests/test_mboxviews.py +++ patchwork/tests/test_mboxviews.py @@ -268,9 +268,12 @@ class MboxSeriesDependencies(TestCase): 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.project.linkname, - patch_b.patch.url_msgid])) + response = self.client.get( + '%s?series=*' % reverse( + 'patch-mbox', + args=[patch_b.project.linkname, patch_b.url_msgid], + ), + ) self.assertContains(response, patch_a.content) self.assertContains(response, patch_b.content) @@ -279,9 +282,12 @@ class MboxSeriesDependencies(TestCase): series, patch_a, patch_b = self._create_patches() response = self.client.get('%s?series=%d' % ( - reverse('patch-mbox', args=[patch_b.patch.project.linkname, - patch_b.patch.url_msgid]), - series.id)) + reverse( + 'patch-mbox', + args=[patch_b.project.linkname, patch_b.url_msgid], + ), + series.id, + )) self.assertContains(response, patch_a.content) self.assertContains(response, patch_b.content) @@ -291,8 +297,12 @@ class MboxSeriesDependencies(TestCase): for value in ('foo', str(series.id + 1)): response = self.client.get('%s?series=%s' % ( - reverse('patch-mbox', args=[patch_b.patch.project.linkname, - patch_b.patch.url_msgid]), value)) + reverse( + 'patch-mbox', + args=[patch_b.project.linkname, patch_b.url_msgid] + ), + value, + )) self.assertEqual(response.status_code, 404) diff --git patchwork/tests/utils.py patchwork/tests/utils.py index ea184a9d..fd4dba74 100644 --- patchwork/tests/utils.py +++ patchwork/tests/utils.py @@ -191,9 +191,6 @@ def create_patch(**kwargs): } values.update(kwargs) - if 'patch_project' not in values: - values['patch_project'] = values['project'] - patch = Patch.objects.create(**values) if series: @@ -312,7 +309,7 @@ def create_series_reference(**kwargs): def _create_submissions(create_func, count=1, **kwargs): - """Create 'count' Submission-based objects. + """Create 'count' SubmissionMixin-based objects. Args: count (int): Number of patches to create diff --git patchwork/views/__init__.py patchwork/views/__init__.py index ad17a070..3efe90cd 100644 --- patchwork/views/__init__.py +++ patchwork/views/__init__.py @@ -257,7 +257,7 @@ def generic_list(request, project, view, view_args=None, filter_settings=None, context['filters'].set_status(filterclass, setting) if patches is None: - patches = Patch.objects.filter(patch_project=project) + patches = Patch.objects.filter(project=project) # annotate with tag counts patches = patches.with_tag_counts(project) diff --git patchwork/views/utils.py patchwork/views/utils.py index f02948c2..2bf65252 100644 --- patchwork/views/utils.py +++ patchwork/views/utils.py @@ -179,8 +179,8 @@ def series_to_mbox(series): """ mbox = [] - for dep in series.patches.all().order_by('number'): - mbox.append(patch_to_mbox(dep.patch)) + for patch in series.patches.all().order_by('number'): + mbox.append(patch_to_mbox(patch)) return '\n'.join(mbox) From patchwork Thu Apr 9 17:13:02 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Stephen Finucane X-Patchwork-Id: 1268697 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.ozlabs.org (lists.ozlabs.org [IPv6:2401:3900:2:1::3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 48yntJ2R4Xz9sSc for ; Fri, 10 Apr 2020 03:18:48 +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" header.d=that.guru header.i=@that.guru header.a=rsa-sha256 header.s=default header.b=c9z81/yh; dkim-atps=neutral Received: from lists.ozlabs.org (lists.ozlabs.org [IPv6:2401:3900:2:1::3]) by lists.ozlabs.org (Postfix) with ESMTP id 48yntJ1ny0zDrCF for ; Fri, 10 Apr 2020 03:18:48 +1000 (AEST) X-Original-To: patchwork@lists.ozlabs.org Delivered-To: patchwork@lists.ozlabs.org Authentication-Results: lists.ozlabs.org; spf=none (no SPF record) smtp.mailfrom=that.guru (client-ip=64.137.75.137; helo=chi02-137.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" header.d=that.guru header.i=@that.guru header.a=rsa-sha256 header.s=default header.b=c9z81/yh; dkim-atps=neutral Received: from chi02-137.mxroute.com (chi02-137.mxroute.com [64.137.75.137]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by lists.ozlabs.org (Postfix) with ESMTPS id 48ynsw4tzgzDrCm for ; Fri, 10 Apr 2020 03:18:28 +1000 (AEST) Received: from filter004.mxroute.com ([149.28.56.236] 149.28.56.236.vultr.com) (Authenticated sender: mN4UYu2MZsgR) by chi02-137.mxroute.com (ZoneMTA) with ESMTPSA id 1715fee7e760001c89.001 for (version=TLSv1/SSLv3 cipher=ECDHE-RSA-AES128-GCM-SHA256); Thu, 09 Apr 2020 17:13:17 +0000 X-Zone-Loop: b156b31cc02b74cc370829d7d68023463713e1cd0f16 X-Originating-IP: [149.28.56.236] Received: from one.mxroute.com (one.mxroute.com [195.201.59.211]) by filter004.mxroute.com (Postfix) with ESMTPS id DA1CE3EDA1 for ; Thu, 9 Apr 2020 17:13:16 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=that.guru; s=default; h=Content-Transfer-Encoding:MIME-Version:References:In-Reply-To: Message-Id:Date:Subject:Cc:To:From:Sender:Reply-To:Content-Type: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=/uTCecGsGjTYdRxj1yXYm9mTgXRbRqWY4x0DMutoXkU=; b=c9z81/yhqhV1f70gGx/3L9Kt41 UfvjHxUAPD0/7EFV0aTQNfvw/Lwrn+s9XylaJA6wgHi7qzfpTMyxZHrK5FR/ikgVOQ18d0WVt60LO Irq1tRCLTIQC2uq2tSCkjy4LJCicRcjh02cVYOUGAqvPKNvBDAPUOegb5Yp6B1tnfk9tsOYYxEgp3 SRZ0br0ZRWVQ5Jiy4g9Xx6oo1UbYqd4fTjkerz7ScK6AR0lZ2VfEiCnqA3vy+hhFxHxBlEfAJ7BHg 9aJWP5MddqTa0TNaOOffPkqUbLZDVcEOD5PfjBJJK3ZCqp2dimYTdZes2XD/6L5DPt4hpW55wRjMb kYbQ36og==; From: Stephen Finucane To: patchwork@lists.ozlabs.org Subject: [PATCH v2 6/6] models: Trivial post-merge cleanup Date: Thu, 9 Apr 2020 18:13:02 +0100 Message-Id: <20200409171302.105869-7-stephen@that.guru> X-Mailer: git-send-email 2.25.2 In-Reply-To: <20200409171302.105869-1-stephen@that.guru> References: <20200409171302.105869-1-stephen@that.guru> MIME-Version: 1.0 X-AuthUser: stephen@that.guru X-BeenThere: patchwork@lists.ozlabs.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Patchwork development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: patchwork-bounces+incoming=patchwork.ozlabs.org@lists.ozlabs.org Sender: "Patchwork" Signed-off-by: Stephen Finucane --- patchwork/models.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git patchwork/models.py patchwork/models.py index a5e7bea7..77ab9241 100644 --- patchwork/models.py +++ patchwork/models.py @@ -357,7 +357,7 @@ class FilenameMixin(object): return fname -class SubmissionMixin(models.Model): +class SubmissionMixin(FilenameMixin, EmailMixin, models.Model): # parent project = models.ForeignKey(Project, on_delete=models.CASCADE) @@ -387,7 +387,7 @@ class SubmissionMixin(models.Model): abstract = True -class Cover(FilenameMixin, EmailMixin, SubmissionMixin): +class Cover(SubmissionMixin): def get_absolute_url(self): return reverse('cover-detail', @@ -414,7 +414,7 @@ class Cover(FilenameMixin, EmailMixin, SubmissionMixin): ] -class Patch(FilenameMixin, EmailMixin, SubmissionMixin): +class Patch(SubmissionMixin): diff = models.TextField(null=True, blank=True) commit_ref = models.CharField(max_length=255, null=True, blank=True)