~launchpad-pqm/launchpad/devel

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
# Copyright 2009-2011 Canonical Ltd.  This software is licensed under the
# GNU Affero General Public License version 3 (see the file LICENSE).

# pylint: disable-msg=E0211,E0213

"""Interfaces including and related to IDistroSeries."""

__metaclass__ = type

__all__ = [
    'DerivationError',
    'IDistroSeries',
    'IDistroSeriesEditRestricted',
    'IDistroSeriesPublic',
    'IDistroSeriesSet',
    ]

from lazr.enum import DBEnumeratedType
from lazr.restful.declarations import (
    call_with,
    export_as_webservice_entry,
    export_factory_operation,
    export_read_operation,
    export_write_operation,
    exported,
    LAZR_WEBSERVICE_EXPORTED,
    operation_parameters,
    operation_returns_collection_of,
    operation_returns_entry,
    rename_parameters_as,
    REQUEST_USER,
    webservice_error,
    )
from lazr.restful.fields import (
    CollectionField,
    Reference,
    ReferenceChoice,
    )
from lazr.restful.interface import copy_field
from zope.component import getUtility
from zope.interface import (
    Attribute,
    Interface,
    )
from zope.schema import (
    Bool,
    Choice,
    Datetime,
    List,
    Object,
    TextLine,
    )

from canonical.launchpad import _
from canonical.launchpad.interfaces.launchpad import IHasAppointedDriver
from lp.app.interfaces.launchpad import IServiceUsage
from lp.app.validators import LaunchpadValidationError
from lp.app.validators.email import email_validator
from lp.app.validators.name import name_validator
from lp.app.validators.version import sane_version
from lp.blueprints.interfaces.specificationtarget import ISpecificationGoal
from lp.bugs.interfaces.bugtarget import (
    IBugTarget,
    IHasBugs,
    IHasOfficialBugTags,
    )
from lp.bugs.interfaces.structuralsubscription import (
    IStructuralSubscriptionTarget,
    )
from lp.registry.errors import NoSuchDistroSeries
from lp.registry.interfaces.milestone import (
    IHasMilestones,
    IMilestone,
    )
from lp.registry.interfaces.person import IPerson
from lp.registry.interfaces.role import IHasOwner
from lp.registry.interfaces.series import (
    ISeriesMixin,
    SeriesStatus,
    )
from lp.registry.interfaces.sourcepackage import ISourcePackage
from lp.services.fields import (
    ContentNameField,
    Description,
    PublicPersonChoice,
    Title,
    UniqueField,
    )
from lp.soyuz.interfaces.buildrecords import IHasBuildRecords
from lp.translations.interfaces.hastranslationimports import (
    IHasTranslationImports,
    )
from lp.translations.interfaces.hastranslationtemplates import (
    IHasTranslationTemplates,
    )
from lp.translations.interfaces.languagepack import ILanguagePack


class DistroSeriesNameField(ContentNameField):
    """A class to ensure `IDistroSeries` has unique names."""
    errormessage = _("%s is already in use by another series.")

    @property
    def _content_iface(self):
        """See `IField`."""
        return IDistroSeries

    def _getByName(self, name):
        """See `IField`."""
        try:
            if self._content_iface.providedBy(self.context):
                return self.context.distribution.getSeries(name)
            else:
                return self.context.getSeries(name)
        except NoSuchDistroSeries:
            # The name is available for the new series.
            return None


class DistroSeriesVersionField(UniqueField):
    """A class to ensure `IDistroSeries` has unique versions."""
    errormessage = _(
        "%s is already in use by another version in this distribution.")
    attribute = 'version'

    @property
    def _content_iface(self):
        return IDistroSeries

    @property
    def _distribution(self):
        if self._content_iface.providedBy(self.context):
            return self.context.distribution
        else:
            return self.context

    def _getByName(self, version):
        """Return the `IDistroSeries` for the specified distribution version.

        The distribution is the context's distribution (which may
        the context itself); A version is unique to a distribution.
        """
        found_series = None
        for series in getUtility(IDistroSeriesSet).findByVersion(version):
            if (series.distribution == self._distribution
                and series != self.context):
                # A version is unique to a distribution, but a distroseries
                # may edit itself.
                found_series = series
                break
        return found_series

    def _getByAttribute(self, version):
        """Return the content object with the given attribute."""
        return self._getByName(version)

    def _validate(self, version):
        """See `UniqueField`."""
        super(DistroSeriesVersionField, self)._validate(version)
        if not sane_version(version):
            raise LaunchpadValidationError(
                "%s is not a valid version" % version)
        # Avoid circular import hell.
        from lp.archivepublisher.debversion import Version, VersionError
        try:
            # XXX sinzui 2009-07-25 bug=404613: DistributionMirror and buildd
            # have stricter version rules than the schema. The version must
            # be a debversion.
            Version(version)
        except VersionError, error:
            raise LaunchpadValidationError(
                "'%s': %s" % (version, error))


class IDistroSeriesPublic(
    ISeriesMixin, IHasAppointedDriver, IHasOwner, IBugTarget,
    ISpecificationGoal, IHasMilestones, IHasOfficialBugTags,
    IHasBuildRecords, IHasTranslationImports, IHasTranslationTemplates,
    IServiceUsage):
    """Public IDistroSeries properties."""

    id = Attribute("The distroseries's unique number.")
    name = exported(
        DistroSeriesNameField(
            title=_("Name"), required=True,
            description=_("The name of this series."),
            constraint=name_validator))
    displayname = exported(
        TextLine(
            title=_("Display name"), required=True,
            description=_("The series displayname.")))
    fullseriesname = exported(
        TextLine(
            title=_("Series full name"), required=False,
            description=_("The series full name, e.g. Ubuntu Warty")))
    title = exported(
        Title(
            title=_("Title"), required=True,
            description=_(
                "The title of this series. It should be distinctive "
                "and designed to look good at the top of a page.")))
    description = exported(
        Description(title=_("Description"), required=True,
            description=_("A detailed description of this series, with "
                          "information on the architectures covered, the "
                          "availability of security updates and any other "
                          "relevant information.")))
    version = exported(
        DistroSeriesVersionField(
            title=_("Version"), required=True,
            description=_("The version string for this series.")))
    distribution = exported(
        Reference(
            Interface, # Really IDistribution, see circular import fix below.
            title=_("Distribution"), required=True,
            description=_("The distribution for which this is a series.")))
    distributionID = Attribute('The distribution ID.')
    named_version = Attribute('The combined display name and version.')
    parent = Attribute('The structural parent of this series - the distro')
    components = Attribute("The series components.")
    upload_components = Attribute("The series components that can be "
                                  "uploaded to.")
    sections = Attribute("The series sections.")
    status = exported(
        Choice(
            title=_("Status"), required=True,
            vocabulary=SeriesStatus))
    is_derived_series = Bool(
        title=u'Is this series a derived series?', readonly=True,
        description=(u"Whether or not this series is a derived series."))
    is_initialising = Bool(
        title=u'Is this series initialising?', readonly=True,
        description=(u"Whether or not this series is initialising."))
    datereleased = exported(
        Datetime(title=_("Date released")))
    previous_series = exported(
        ReferenceChoice(
            title=_("Parent series"),
            description=_("The series from which this one was branched."),
            required=True, schema=Interface, # Really IDistroSeries, see below
            vocabulary='DistroSeries'),
        ("devel", dict(exported_as="previous_series")),
        ("1.0", dict(exported_as="parent_series")),
        ("beta", dict(exported_as="parent_series")),
        readonly=True)
    registrant = exported(
        PublicPersonChoice(
            title=_("Registrant"), vocabulary='ValidPersonOrTeam'))
    owner = exported(Reference(
        IPerson, title=_("Owning team of the derived series"), readonly=True,
        description=_(
            "This attribute mirrors the owner of the distribution.")))
    date_created = exported(
        Datetime(title=_("The date this series was registered.")))
    driver = exported(
        ReferenceChoice(
            title=_("Driver"),
            description=_(
                "The person or team responsible for decisions about features "
                "and bugs that will be targeted to this series of the "
                "distribution."),
            required=False, vocabulary='ValidPersonOrTeam', schema=IPerson))
    changeslist = exported(
        TextLine(
            title=_("E-mail changes to"), required=True,
            description=_("The mailing list or other e-mail address that "
                          "Launchpad should notify about new uploads."),
            constraint=email_validator))
    sourcecount = Attribute("Source Packages Counter")
    defer_translation_imports = Bool(
        title=_("Defer translation imports"),
        description=_("Suspends any translation imports for this series"),
        default=True,
        required=True,
        )
    binarycount = Attribute("Binary Packages Counter")

    architecturecount = Attribute("The number of architectures in this "
        "series.")
    nominatedarchindep = Attribute(
        "DistroArchSeries designed to build architecture-independent "
        "packages whithin this distroseries context.")
    messagecount = Attribute("The total number of translatable items in "
        "this series.")
    distroserieslanguages = Attribute("The set of dr-languages in this "
        "series.")

    hide_all_translations = Bool(
        title=u'Hide translations for this release', required=True,
        description=(
            u"You may hide all translation for this distribution series so"
             " that only Launchpad administrators will be able to see them."
             " For example, you should hide these translations while they are"
             " being imported from a previous series so that translators"
             " will not be confused by imports that are in progress."),
        default=True)

    language_pack_base = Choice(
        title=_('Language pack base'), required=False,
        description=_('''
            Language pack with the export of all translations
            available for this distribution series when it was generated. The
            subsequent update exports will be generated based on this one.
            '''), vocabulary='FilteredFullLanguagePack')

    language_pack_delta = Choice(
        title=_('Language pack update'), required=False,
        description=_('''
            Language pack with the export of all translation updates
            available for this distribution series since the language pack
            base was generated.
            '''), vocabulary='FilteredDeltaLanguagePack')

    language_pack_proposed = Choice(
        title=_('Proposed language pack update'), required=False,
        description=_('''
            Base or update language pack export that is being tested and
            proposed to be used as the new language pack base or
            language pack update for this distribution series.
            '''), vocabulary='FilteredLanguagePack')

    language_pack_full_export_requested = Bool(
        title=_('Request a full language pack export'), required=True,
        description=_('''
            Whether next language pack generation will be a full export. This
            information is useful when update packs are too big and want to
            merge all those changes in the base pack.
            '''))

    last_full_language_pack_exported = Object(
        title=_('Latest exported language pack with all translation files.'),
        required=False, readonly=True, schema=ILanguagePack)

    last_delta_language_pack_exported = Object(
        title=_(
            'Lastest exported language pack with updated translation files.'),
        required=False, readonly=True, schema=ILanguagePack)

    # related joins
    packagings = Attribute("All of the Packaging entries for this "
        "distroseries.")
    specifications = Attribute("The specifications targeted to this "
        "series.")

    language_packs = Attribute(
        "All language packs associated with this distribution series.")

    backports_not_automatic = Bool(
        title=_("Don't upgrade to backports automatically"), required=True,
        description=_("""
            Set NotAutomatic: yes and ButAutomaticUpgrades: yes in Release
            files generated for the backports pocket. This tells apt to
            automatically upgrade within backports, but not into it.
            """))

    # other properties
    prior_series = Attribute(
        "Prior series *by date* from the same distribution.")

    main_archive = exported(
        Reference(
            Interface, # Really IArchive, see below for circular import fix.
            title=_('Distribution Main Archive')))

    supported = exported(
        Bool(
            title=_("Supported"),
            description=_(
                "Whether or not this series is currently supported.")))

    def isUnstable():
        """Whether or not a distroseries is unstable.

        The distribution is "unstable" until it is released; after that
        point, all development on the Release pocket is stopped and
        development moves on to the other pockets.
        """

    def canUploadToPocket(pocket):
        """Decides whether or not allow uploads for a given pocket.

        Only allow uploads for RELEASE pocket in unreleased
        distroseries and the opposite, only allow uploads for
        non-RELEASE pockets in released distroseries.
        For instance, in edgy time :

                warty         -> DENY
                edgy          -> ALLOW
                warty-updates -> ALLOW
                edgy-security -> DENY

        Note that FROZEN is not considered either 'stable' or 'unstable'
        state.  Uploads to a FROZEN distroseries will end up in the
        UNAPPROVED queue.

        Return True if the upload is allowed and False if denied.
        """

    def getLatestUploads():
        """Return the latest five source uploads for this DistroSeries.

        It returns a list containing up to five elements as
        IDistroSeriesSourcePackageRelease instances
        """

    # DistroArchSeries lookup properties/methods.
    architectures = exported(
        CollectionField(
            title=_("All architectures in this series."),
            value_type=Reference(schema=Interface), # IDistroArchSeries.
            readonly=True))

    enabled_architectures = Attribute(
        "All architectures in this series with the 'enabled' flag set.")

    virtualized_architectures = Attribute(
        "All architectures in this series where PPA is supported.")

    buildable_architectures = Attribute(
        "All architectures in this series with available chroot tarball.")

    def __getitem__(archtag):
        """Return the distroarchseries for this distroseries with the
        given architecturetag.
        """

    def __str__():
        """Return the name of the distroseries."""

    def getDistroArchSeriesByProcessor(processor):
        """Return the distroarchseries for this distroseries with the
        given architecturetag from a `IProcessor`.

        :param processor: An `IProcessor`
        :return: An `IDistroArchSeries` or None when none was found.
        """

    @operation_parameters(
        archtag=TextLine(
            title=_("The architecture tag"), required=True))
    @operation_returns_entry(Interface)
    @export_read_operation()
    def getDistroArchSeries(archtag):
        """Return the distroarchseries for this distroseries with the
        given architecturetag.
        """
    # End of DistroArchSeries lookup methods.

    def updateStatistics(ztm):
        """Update all the Rosetta stats for this distro series."""

    def updatePackageCount():
        """Update the binary and source package counts for this distro
        series."""

    @operation_parameters(
        name=TextLine(
            title=_("The name of the source package"), required=True))
    @operation_returns_entry(ISourcePackage)
    @export_read_operation()
    def getSourcePackage(name):
        """Return a source package in this distro series by name.

        The name given may be a string or an ISourcePackageName-providing
        object. The source package may not be published in the distro series.
        """

    def getTranslatableSourcePackages():
        """Return a list of Source packages in this distribution series
        that can be translated.
        """

    def getPrioritizedUnlinkedSourcePackages():
        """Return a list of package summaries that need packaging links.

        A summary is a dict of package (`ISourcePackage`), total_bugs,
        and total_messages (translatable messages).
        """

    def getPrioritizedPackagings():
        """Return a list of packagings that need more upstream information."""

    def getMostRecentlyLinkedPackagings():
        """Return a list of packagings that are the most recently linked.

        At most five packages are returned of those most recently linked to an
        upstream.
        """

    @operation_parameters(
        created_since_date=Datetime(
            title=_("Created Since Timestamp"),
            description=_(
                "Return items that are more recent than this timestamp."),
            required=False),
        status=Choice(
            # Really PackageUploadCustomFormat, patched in
            # _schema_circular_imports.py
            vocabulary=DBEnumeratedType,
            title=_("Package Upload Status"),
            description=_("Return only items that have this status."),
            required=False),
        archive=Reference(
            # Really IArchive, patched in _schema_circular_imports.py
            schema=Interface,
            title=_("Archive"),
            description=_("Return only items for this archive."),
            required=False),
        pocket=Choice(
            # Really PackagePublishingPocket, patched in
            # _schema_circular_imports.py
            vocabulary=DBEnumeratedType,
            title=_("Pocket"),
            description=_("Return only items targeted to this pocket"),
            required=False),
        custom_type=Choice(
            # Really PackageUploadCustomFormat, patched in
            # _schema_circular_imports.py
            vocabulary=DBEnumeratedType,
            title=_("Custom Type"),
            description=_("Return only items with custom files of this "
                          "type."),
            required=False),
        )
    # Really IPackageUpload, patched in _schema_circular_imports.py
    @operation_returns_collection_of(Interface)
    @export_read_operation()
    def getPackageUploads(created_since_date, status, archive, pocket,
                          custom_type):
        """Get package upload records for this distribution series.

        :param created_since_date: If specified, only returns items uploaded
            since the timestamp supplied.
        :param status: Filter results by this `PackageUploadStatus`
        :param archive: Filter results for this `IArchive`
        :param pocket: Filter results by this `PackagePublishingPocket`
        :param custom_type: Filter results by this `PackageUploadCustomFormat`
        :return: A result set containing `IPackageUpload`
        """

    def getUnlinkedTranslatableSourcePackages():
        """Return a list of source packages that can be translated in
        this distribution series but which lack Packaging links.
        """

    def getBinaryPackage(name):
        """Return a DistroSeriesBinaryPackage for this name.

        The name given may be an IBinaryPackageName or a string.  The
        binary package may not be published in the distro series.
        """

    def getSourcePackageRelease(sourcepackagerelease):
        """Return a IDistroSeriesSourcePackageRelease

        sourcepackagerelease is an ISourcePackageRelease.
        """

    def getCurrentSourceReleases(source_package_names):
        """Get the current release of a list of source packages.

        :param source_package_names: a list of `ISourcePackageName`
            instances.

        :return: a dict where the key is a `ISourcePackage`
            and the value is a `IDistroSeriesSourcePackageRelease`.
        """

    def getPublishedSources(sourcepackage_or_name, pocket=None, version=None,
                            include_pending=False, exclude_pocket=None,
                            archive=None):
        """Return the SourcePackagePublishingHistory(s)

        Deprecated.  Use IArchive.getPublishedSources instead.

        Given a ISourcePackageName or name.

        If pocket is not specified, we look in all pockets.

        If version is not specified, return packages with any version.

        If exclude_pocket is specified we exclude results matching that
        pocket.

        If 'include_pending' is True, we return also the pending publication
        records, those packages that will get published in the next publisher
        run (it's only useful when we need to know if a given package is
        known during a publisher run, mostly in pre-upload checks)

        If 'archive' is not specified consider publication in the
        main_archive, otherwise respect the given value.
        """

    def getAllPublishedSources():
        """Return all currently published sources for the distroseries.

        Return publications in the main archives only.
        """

    def getAllPublishedBinaries():
        """Return all currently published binaries for the distroseries.

        Return publications in the main archives only.
        """

    def getSourcesPublishedForAllArchives():
        """Return all sourcepackages published across all the archives.

        It's only used in the buildmaster/master.py context for calculating
        the publication that are still missing build records.

        It will consider all publishing records in PENDING or PUBLISHED status
        as part of the 'build-unpublished-source' specification.

        For 'main_archive' candidates it will automatically exclude RELEASE
        pocket records of released distroseries (ensuring that we won't waste
        time with records that can't be accepted).

        Return a SelectResult of SourcePackagePublishingHistory.
        """

    def getDistroSeriesLanguage(language):
        """Return the DistroSeriesLanguage for this distroseries and the
        given language, or None if there's no DistroSeriesLanguage for this
        distribution and the given language.
        """

    def getDistroSeriesLanguageOrDummy(language):
        """Return the DistroSeriesLanguage for this distroseries and the
        given language, or a DummyDistroSeriesLanguage.
        """

    def createUploadedSourcePackageRelease(
        sourcepackagename, version, maintainer, builddepends,
        builddependsindep, architecturehintlist, component, creator, urgency,
        changelog, changelog_entry, dsc, dscsigningkey, section,
        dsc_maintainer_rfc822, dsc_standards_version, dsc_format,
        dsc_binaries, archive, copyright, build_conflicts,
        build_conflicts_indep, dateuploaded=None,
        source_package_recipe_build=None, user_defined_fields=None,
        homepage=None):
        """Create an uploads `SourcePackageRelease`.

        Set this distroseries set to be the uploadeddistroseries.

        All arguments are mandatory, they are extracted/built when
        processing and uploaded source package:

         :param dateuploaded: timestamp, if not provided will be UTC_NOW
         :param sourcepackagename: `ISourcePackageName`
         :param version: string, a debian valid version
         :param maintainer: IPerson designed as package maintainer
         :param creator: IPerson, package uploader
         :param component: IComponent
         :param section: ISection
         :param urgency: dbschema.SourcePackageUrgency
         :param dscsigningkey: IGPGKey used to sign the DSC file
         :param dsc: string, original content of the dsc file
         :param copyright: string, the original debian/copyright content
         :param changelog: LFA ID of the debian/changelog file in librarian
         :param changelog_entry: string, changelog extracted from the
                                 changesfile
         :param architecturehintlist: string, DSC architectures
         :param builddepends: string, DSC build dependencies
         :param builddependsindep: string, DSC architecture independent build
                                   dependencies.
         :param build_conflicts: string, DSC Build-Conflicts content
         :param build_conflicts_indep: string, DSC Build-Conflicts-Indep
                                       content
         :param dsc_maintainer_rfc822: string, DSC maintainer field
         :param dsc_standards_version: string, DSC standards version field
         :param dsc_format: string, DSC format version field
         :param dsc_binaries:  string, DSC binaries field
         :param archive: IArchive to where the upload was targeted
         :param dateuploaded: optional datetime, if omitted assumed nowUTC
         :param source_package_recipe_build: optional SourcePackageRecipeBuild
         :param user_defined_fields: optional sequence of key-value pairs with
                                     user defined fields.
         :param homepage: optional string with (unchecked) upstream homepage
                          URL
         :return: the just creates `SourcePackageRelease`
        """

    def getComponentByName(name):
        """Get the named component.

        Raise NotFoundError if the component is not in the permitted component
        list for this distroseries.
        """

    def getSectionByName(name):
        """Get the named section.

        Raise NotFoundError if the section is not in the permitted section
        list for this distroseries.
        """

    def addSection(section):
        """SQLObject provided method to fill a related join key section."""

    def getBinaryPackagePublishing(
        name=None, version=None, archtag=None, sourcename=None, orderBy=None,
        pocket=None, component=None, archive=None):
        """Get BinaryPackagePublishings in a DistroSeries.

        Can optionally restrict the results by name, version,
        architecturetag, pocket and/or component.

        If sourcename is passed, only packages that are built from
        source packages by that name will be returned.
        If archive is passed, restricted the results to the given archive,
        if it is suppressed the results will be restricted to the
        distribution 'main_archive'.
        """

    def getSourcePackagePublishing(status, pocket, component=None,
                                   archive=None):
        """Return a selectResult of ISourcePackagePublishingHistory.

        According status and pocket.
        If archive is passed, restricted the results to the given archive,
        if it is suppressed the results will be restricted to the
        distribution 'main_archive'.
        """

    def getBinaryPackageCaches(archive=None):
        """All of the cached binary package records for this distroseries.

        If 'archive' is not given it will return all caches stored for the
        distroseries main archives (PRIMARY and PARTNER).
        """

    def removeOldCacheItems(archive, log):
        """Delete any records that are no longer applicable.

        Consider all binarypackages marked as REMOVED.

        Also purges all existing cache records for disabled archives.

        :param archive: target `IArchive`.
        :param log: the context logger object able to print DEBUG level
            messages.
        """

    def updateCompletePackageCache(archive, log, ztm, commit_chunk=500):
        """Update the binary package cache

        Consider all binary package names published in this distro series
        and entirely skips updates for disabled archives

        :param archive: target `IArchive`;
        :param log: logger object for printing debug level information;
        :param ztm:  transaction used for partial commits, every chunk of
            'commit_chunk' updates is committed;
        :param commit_chunk: number of updates before commit, defaults to 500.

        :return the number of packages updated.
        """

    def updatePackageCache(binarypackagename, archive, log):
        """Update the package cache for a given IBinaryPackageName

        'log' is required, it should be a logger object able to print
        DEBUG level messages.
        'ztm' is the current trasaction manager used for partial commits
        (in full batches of 100 elements)
        """

    def searchPackages(text):
        """Search through the packge cache for this distroseries and return
        DistroSeriesBinaryPackage objects that match the given text.
        """

    def createQueueEntry(pocket, changesfilename, changesfilecontent,
                         archive, signingkey=None):
        """Create a queue item attached to this distroseries.

        Create a new records respecting the given pocket and archive.

        The default state is NEW, sorted sqlobject declaration, any
        modification should be performed via Queue state-machine.

        The changesfile argument should be the text of the .changes for this
        upload. The contents of this may be used later.

        'signingkey' is the IGPGKey used to sign the changesfile or None if
        the changesfile is unsigned.
        """

    def newArch(architecturetag, processorfamily, official, owner,
                supports_virtualized=False, enabled=True):
        """Create a new port or DistroArchSeries for this DistroSeries."""

    def copyTranslationsFromParent(ztm):
        """Copy any translation done in parent that we lack.

        If there is another translation already added to this one, we ignore
        the one from parent.

        The supplied transaction manager will be used for intermediate
        commits to break up large copying jobs into palatable smaller
        chunks.

        This method starts and commits transactions, so don't rely on `self`
        or any other database object remaining valid across this call!
        """

    def getPOFileContributorsByLanguage(language):
        """People who translated strings to the given language.

        The people that translated only IPOTemplate objects that are not
        current will not appear in the returned list.
        """

    def getSuite(pocket):
        """Return the suite for this distro series and the given pocket.

        :param pocket: A `DBItem` of `PackagePublishingPocket`.
        :return: A string.
        """

    def isSourcePackageFormatPermitted(format):
        """Check if the specified source format is allowed in this series.

        :param format: The SourcePackageFormat to check.
        """

    @operation_returns_collection_of(Interface)
    @export_read_operation()
    def getDerivedSeries():
        """Get all `DistroSeries` derived from this one."""


class IDistroSeriesEditRestricted(Interface):
    """IDistroSeries properties which require launchpad.Edit."""

    @rename_parameters_as(dateexpected='date_targeted')
    @export_factory_operation(
        IMilestone, ['name', 'dateexpected', 'summary', 'code_name'])
    def newMilestone(name, dateexpected=None, summary=None, code_name=None):
        """Create a new milestone for this DistroSeries."""

    @operation_parameters(
        name=copy_field(IDistroSeriesPublic['name'], required=True),
        displayname=copy_field(
            IDistroSeriesPublic['displayname'], required=False),
        title=copy_field(IDistroSeriesPublic['title'], required=False),
        summary=TextLine(
            title=_("The summary of the distroseries to derive."),
            required=False),
        description=copy_field(
            IDistroSeriesPublic['description'], required=False),
        version=copy_field(
            IDistroSeriesPublic['version'], required=False),
        distribution=copy_field(
            IDistroSeriesPublic['distribution'], required=False),
        architectures=List(
            title=_("The list of architectures to copy to the derived "
            "distroseries."), value_type=TextLine(),
            required=False),
        packagesets=List(
            title=_("The list of packagesets to copy to the derived "
            "distroseries"), value_type=TextLine(),
            required=False),
        rebuild=Bool(
            title=_("If binaries will be copied to the derived "
            "distroseries."),
            required=True),
        )
    @call_with(user=REQUEST_USER)
    @export_write_operation()
    def deriveDistroSeries(user, name, displayname, title, summary,
                           description, version, distribution,
                           architectures, packagesets, rebuild):
        """Derive a distroseries from this one.

        This method performs checks, can create the new distroseries if
        necessary, and then creates a job to populate the new
        distroseries.

        :param name: The name of the new distroseries we will create if it
            doesn't exist, or the name of the distroseries we will look
            up, and then initialise.
        :param displayname: The Display Name for the new distroseries.
            If the distroseries already exists this parameter is ignored.
        :param title: The Title for the new distroseries. If the
            distroseries already exists this parameter is ignored.
        :param summary: The Summary for the new distroseries. If the
            distroseries already exists this parameter is ignored.
        :param description: The Description for the new distroseries. If the
            distroseries already exists this parameter is ignored.
        :param version: The version for the new distroseries. If the
            distroseries already exists this parameter is ignored.
        :param distribution: The distribution the derived series will
            belong to. If it isn't specified this distroseries'
            distribution is used.
        :param architectures: The architectures to copy to the derived
            series. If not specified, all of the architectures are copied.
        :param packagesets: The packagesets to copy to the derived series.
            If not specified, all of the packagesets are copied.
        :param rebuild: Whether binaries will be copied to the derived
            series. If it's true, they will not be, and if it's false, they
            will be.
        """



class IDistroSeries(IDistroSeriesEditRestricted, IDistroSeriesPublic,
                    IStructuralSubscriptionTarget):
    """A series of an operating system distribution."""
    export_as_webservice_entry()


# We assign the schema for an `IHasBugs` method argument here
# in order to avoid circular dependencies.
IHasBugs['searchTasks'].queryTaggedValue(LAZR_WEBSERVICE_EXPORTED)[
    'params']['nominated_for'].schema = IDistroSeries


class IDistroSeriesSet(Interface):
    """The set of distro seriess."""

    def get(distroseriesid):
        """Retrieve the distro series with the given distroseriesid."""

    def translatables():
        """Return a set of distroseriess that can be translated in
        rosetta."""

    def findByName(name):
        """Find a DistroSeries by name.

        Returns a list of matching distributions, which may be empty.
        """

    def queryByName(distribution, name):
        """Query a DistroSeries by name.

        :distribution: An IDistribution.
        :name: A string.

        Returns the matching DistroSeries, or None if not found.
        """

    def findByVersion(version):
        """Find a DistroSeries by version.

        Returns a list of matching distributions, which may be empty.
        """

    def fromSuite(distribution, suite):
        """Return the distroseries and pocket for 'suite' of 'distribution'.

        :param distribution: An `IDistribution`.
        :param suite: A string that forms the name of a suite.
        :return: (`IDistroSeries`, `DBItem`) where the item is from
            `PackagePublishingPocket`.
        """

    def getCurrentSourceReleases(distro_series_source_packagenames):
        """Lookup many distroseries source package releases.

        :param distro_series_to_source_packagenames: A dictionary with
            its keys being `IDistroSeries` and its values a list of
            `ISourcePackageName`.
        :return: A dict as per `IDistroSeries.getCurrentSourceReleases`
        """

    def search(distribution=None, released=None, orderBy=None):
        """Search the set of distro seriess.

        released == True will filter results to only include
        IDistroSeries with status CURRENT or SUPPORTED.

        released == False will filter results to only include
        IDistroSeriess with status EXPERIMENTAL, DEVELOPMENT,
        FROZEN.

        released == None will do no filtering on status.
        """


class DerivationError(Exception):
    """Raised when there is a problem deriving a distroseries."""
    webservice_error(400) # Bad Request
    _message_prefix = "Error deriving distro series"


# Monkey patch for circular import avoidance done in
# _schema_circular_imports.py