Beispiel #1
0
class ICodeImport(Interface):
    """A code import to a Bazaar Branch."""

    export_as_webservice_entry()

    id = Int(readonly=True, required=True)
    date_created = Datetime(title=_("Date Created"),
                            required=True,
                            readonly=True)

    branch = exported(
        ReferenceChoice(title=_('Branch'),
                        required=True,
                        readonly=True,
                        vocabulary='Branch',
                        schema=IBranch,
                        description=_("The Bazaar branch produced by the "
                                      "import system.")))

    registrant = PublicPersonChoice(
        title=_('Registrant'),
        required=True,
        readonly=True,
        vocabulary='ValidPersonOrTeam',
        description=_("The person who initially requested this import."))

    review_status = exported(
        Choice(title=_("Review Status"),
               vocabulary=CodeImportReviewStatus,
               default=CodeImportReviewStatus.REVIEWED,
               readonly=True,
               description=_("Only reviewed imports are processed.")))

    rcs_type = exported(
        Choice(title=_("Type of RCS"),
               readonly=True,
               required=True,
               vocabulary=RevisionControlSystems,
               description=_("The version control system to import from. "
                             "Can be CVS or Subversion.")))

    url = exported(
        URIField(
            title=_("URL"),
            required=False,
            readonly=True,
            description=_("The URL of the VCS branch."),
            allowed_schemes=["http", "https", "svn", "git", "bzr", "ftp"],
            allow_userinfo=True,
            allow_port=True,
            allow_query=False,  # Query makes no sense in Subversion.
            allow_fragment=False,  # Fragment makes no sense in Subversion.
            trailing_slash=False))  # See http://launchpad.net/bugs/56357.

    cvs_root = exported(
        TextLine(
            title=_("Repository"),
            required=False,
            readonly=True,
            constraint=validate_cvs_root,
            description=_(
                "The CVSROOT. "
                "Example: :pserver:[email protected]:/cvs/gnome")))

    cvs_module = exported(
        TextLine(title=_("Module"),
                 required=False,
                 readonly=True,
                 constraint=validate_cvs_module,
                 description=_("The path to import within the repository."
                               " Usually, it is the name of the project.")))

    date_last_successful = exported(
        Datetime(title=_("Last successful"), required=False, readonly=True))

    update_interval = Timedelta(
        title=_("Update interval"),
        required=False,
        description=
        _("The user-specified time between automatic updates of this import. "
          "If this is unspecified, the effective update interval is a default "
          "value selected by Launchpad administrators."))

    effective_update_interval = Timedelta(
        title=_("Effective update interval"),
        required=True,
        readonly=True,
        description=_(
            "The effective time between automatic updates of this import. "
            "If the user did not specify an update interval, this is a default "
            "value selected by Launchpad administrators."))

    def getImportDetailsForDisplay():
        """Get a one-line summary of the location this import is from."""

    import_job = Choice(
        title=_("Current job"),
        readonly=True,
        vocabulary='CodeImportJob',
        description=_(
            "The current job for this import, either pending or running."))

    results = Attribute("The results for this code import.")

    consecutive_failure_count = Attribute(
        "How many times in a row this import has failed.")

    def updateFromData(data, user):
        """Modify attributes of the `CodeImport`.

        Creates and returns a MODIFY `CodeImportEvent` if changes were made.

        This method preserves the invariant that a `CodeImportJob` exists for
        a given import if and only if its review_status is REVIEWED, creating
        and deleting jobs as necessary.

        :param data: dictionary whose keys are attribute names and values are
            attribute values.
        :param user: user who made the change, to record in the
            `CodeImportEvent`.  May be ``None``.
        :return: The MODIFY `CodeImportEvent`, if any changes were made, or
            None if no changes were made.
        """

    def tryFailingImportAgain(user):
        """Try a failing import again.

        This method sets the review_status back to REVIEWED and requests the
        import be attempted as soon as possible.

        The import must be in the FAILING state.

        :param user: the user who is requesting the import be tried again.
        """

    @call_with(requester=REQUEST_USER)
    @export_write_operation()
    def requestImport(requester, error_if_already_requested=False):
        """Request that an import be tried soon.
Beispiel #2
0
class IBinaryPackagePublishingHistory(IBinaryPackagePublishingHistoryPublic,
                                      IBinaryPackagePublishingHistoryEdit):
    """A binary package publishing record."""
    export_as_webservice_entry(publish_web_link=False)
class ITeamMembership(Interface):
    """TeamMembership for Users.

    This table includes *direct* team members only.  Indirect memberships are
    handled by the TeamParticipation table.
    """
    export_as_webservice_entry()

    id = Int(title=_('ID'), required=True, readonly=True)
    team = exported(
        Reference(title=_("Team"),
                  required=True,
                  readonly=True,
                  schema=Interface))  # Specified in interfaces/person.py.
    person = exported(
        Reference(title=_("Member"),
                  required=True,
                  readonly=True,
                  schema=Interface),  # Specified in interfaces/person.py.
        exported_as='member')
    proposed_by = Attribute(_('Proponent'))
    reviewed_by = Attribute(
        _("The team admin who approved/rejected the member."))
    acknowledged_by = Attribute(
        _('The person (usually the member or someone acting on his behalf) '
          'that acknowledged (accepted/declined) a membership invitation.'))
    last_changed_by = exported(
        Reference(title=_('Last person who change this'),
                  required=False,
                  readonly=True,
                  schema=Interface))  # Specified in interfaces/person.py.

    datejoined = exported(Datetime(
        title=_("Date joined"),
        required=False,
        readonly=True,
        description=_("The date in which this membership was made "
                      "active for the first time.")),
                          exported_as='date_joined')
    dateexpires = exported(Datetime(title=_("Date expires"),
                                    required=False,
                                    readonly=True),
                           exported_as='date_expires')
    date_created = Datetime(
        title=_("Date created"),
        required=False,
        readonly=True,
        description=_("The date in which this membership was created."))
    date_proposed = Datetime(
        title=_("Date proposed"),
        required=False,
        readonly=True,
        description=_("The date in which this membership was proposed."))
    date_acknowledged = Datetime(
        title=_("Date acknowledged"),
        required=False,
        readonly=True,
        description=_("The date in which this membership was acknowledged by "
                      "the member (or someone acting on their behalf)."))
    date_reviewed = Datetime(
        title=_("Date reviewed"),
        required=False,
        readonly=True,
        description=_("The date in which this membership was approved/"
                      "rejected by one of the team's admins."))
    date_last_changed = Datetime(
        title=_("Date last changed"),
        required=False,
        readonly=True,
        description=_("The date in which this membership was last changed."))

    last_change_comment = exported(
        Text(title=_("Comment on the last change"),
             required=False,
             readonly=True))
    proponent_comment = Text(title=_("Proponent comment"),
                             required=False,
                             readonly=True)
    acknowledger_comment = Text(title=_("Acknowledger comment"),
                                required=False,
                                readonly=True)
    reviewer_comment = Text(title=_("Reviewer comment"),
                            required=False,
                            readonly=True)
    status = exported(
        Choice(title=_("The state of this membership"),
               required=True,
               readonly=True,
               vocabulary=TeamMembershipStatus))

    def isExpired():
        """Return True if this membership's status is EXPIRED."""

    def canChangeExpirationDate(person):
        """Can the given person change this membership's expiration date?

        A membership's expiration date can be changed by the team owner, by a
        Launchpad admin or by a team admin. In the latter case, though, the
        expiration date can only be changed if the admin is not changing his
        own membership.
        """

    @call_with(user=REQUEST_USER)
    @operation_parameters(date=copy_field(dateexpires))
    @export_write_operation()
    def setExpirationDate(date, user):
        """Set this membership's expiration date.

        The given date must be None or in the future and the given user must
        be allowed to change this membership's expiration date as per the
        rules defined in canChangeExpirationDate().
        """

    def canBeRenewedByMember():
        """Can this membership be renewed by the member himself?

        A membership can be renewed if the team's renewal policy is ONDEMAND,
        the membership itself is active (status = [ADMIN|APPROVED]) and it's
        set to expire in less than DAYS_BEFORE_EXPIRATION_WARNING_IS_SENT
        days.
        """

    def sendSelfRenewalNotification():
        """Send an email to the team admins notifying that this membership
        has been renewed by the member himself.

        This method must not be called if the team's renewal policy is not
        ONDEMAND.
        """

    def sendExpirationWarningEmail():
        """Send the member an email warning that the membership will expire.

        This method cannot be called for memberships without an expiration
        date. Emails are not sent to members if their membership has already
        expired or if the member is no longer active.

        :raises AssertionError: if the member has no expiration date of the
            team or if the TeamMembershipRenewalPolicy is AUTOMATIC.
        """

    @call_with(user=REQUEST_USER)
    @operation_parameters(status=copy_field(status),
                          comment=copy_field(reviewer_comment),
                          silent=Bool(title=_(
                              "Do not send notifications of status change.  "
                              "For use by Launchpad administrators only."),
                                      required=False,
                                      default=False))
    @export_write_operation()
    def setStatus(status, user, comment=None, silent=False):
        """Set the status of this membership.
class IBugSubscriptionFilter(
    IBugSubscriptionFilterAttributes, IBugSubscriptionFilterMethodsProtected,
    IBugSubscriptionFilterMethodsPublic):
    """A bug subscription filter."""
    export_as_webservice_entry()
Beispiel #5
0
class IBugAttachment(IHasBug):
    """A file attachment to an IBug.

    Launchpadlib example of accessing content of an attachment::

        for attachment in bug.attachments:
            buffer = attachment.data.open()
            for line in buffer:
                print line
            buffer.close()

    Launchpadlib example of accessing metadata about an attachment::

        attachment = bug.attachments[0]
        print "title:", attachment.title
        print "ispatch:", attachment.type

    For information about the file-like object returned by
    attachment.data.open() see lazr.restfulclient's documentation of the
    HostedFile object.

    Details about the message associated with an attachment can be found on
    the "message" attribute::

        message = attachment.message
        print "subject:", message.subject.encode('utf-8')
        print "owner:", message.owner.display_name.encode('utf-8')
        print "created:", message.date_created
    """
    export_as_webservice_entry()

    id = Int(title=_('ID'), required=True, readonly=True)
    bug = exported(
        Reference(Interface, title=_('The bug the attachment belongs to.')))
    type = exported(
        Choice(title=_('Attachment Type'),
               description=_(
                   'The type of the attachment, for example Patch or '
                   'Unspecified.'),
               vocabulary=BugAttachmentType,
               default=BugAttachmentType.UNSPECIFIED,
               required=True))
    title = exported(
        Title(title=_('Title'),
              description=_(
                  'A short and descriptive description of the attachment'),
              required=True))
    libraryfile = Bytes(title=_("The attachment content."), required=True)
    data = exported(
        Bytes(title=_("The attachment content."), required=True,
              readonly=True))
    message = exported(
        Reference(IMessage,
                  title=_("The message that was created when we "
                          "added this attachment.")))
    is_patch = Bool(title=_('Patch?'),
                    description=_('Is this attachment a patch?'),
                    readonly=True)

    @call_with(user=REQUEST_USER)
    @export_write_operation()
    def removeFromBug(user):
        """Remove the attachment from the bug."""

    def destroySelf():
        """Delete this record.

        The library file content for this attachment is set to None.
        """

    def getFileByName(filename):
        """Return the `ILibraryFileAlias for the given file name.
Beispiel #6
0
class IWebhookDeliveryJob(IRunnableJob):
    """A Job that delivers an event to a webhook consumer."""

    export_as_webservice_entry('webhook_delivery', as_of='beta')

    webhook = exported(
        Reference(title=_("Webhook"),
                  description=_("The webhook that this delivery is for."),
                  schema=IWebhook,
                  required=True,
                  readonly=True))

    pending = exported(
        Bool(title=_("Pending"),
             description=_("Whether a delivery attempt is in progress."),
             required=True,
             readonly=True))

    successful = exported(
        Bool(
            title=_("Successful"),
            description=_(
                "Whether the most recent delivery attempt succeeded, or null if "
                "no attempts have been made yet."),
            required=False,
            readonly=True))

    error_message = exported(
        TextLine(
            title=_("Error message"),
            description=_(
                "Details of the error encountered by the most recent delivery "
                "attempt."),
            required=False,
            readonly=True))

    date_created = exported(
        Datetime(title=_("Date created"), required=True, readonly=True))

    date_scheduled = exported(
        Datetime(title=_("Date scheduled"),
                 description=_("Timestamp of the next delivery attempt."),
                 required=False,
                 readonly=True))

    date_first_sent = exported(
        Datetime(title=_("Date first sent"),
                 description=_("Timestamp of the first delivery attempt."),
                 required=False,
                 readonly=True))

    date_sent = exported(
        Datetime(title=_("Date sent"),
                 description=_("Timestamp of the last delivery attempt."),
                 required=False,
                 readonly=True))

    event_type = exported(
        TextLine(title=_('Event type'), required=True, readonly=True))

    payload = exported(
        Dict(title=_('Event payload'),
             key_type=TextLine(),
             required=True,
             readonly=True))

    @export_write_operation()
    @operation_parameters(reset=Bool(
        title=_("Reset automatic retries"),
        description=_(
            "Restart the 24 hour automatic retry window as well as trying "
            "again now.")))
    @operation_for_version("devel")
    def retry(reset=False):
        """Attempt to deliver the event again.
class ISourcePackageRecipeBuild(ISourcePackageRecipeBuildView,
                                ISourcePackageRecipeBuildEdit):
    """A build of a source package."""

    export_as_webservice_entry()
class IDistributionMirror(Interface):
    """A mirror of a given distribution."""
    export_as_webservice_entry()

    id = Int(title=_('The unique id'), required=True, readonly=True)
    owner = exported(
        PublicPersonChoice(
            title=_('Owner'),
            readonly=False,
            vocabulary='ValidOwner',
            required=True,
            description=_(
                "The person who is set as the current administrator of this"
                "mirror.")))
    distribution = exported(
        Reference(
            Interface,
            # Really IDistribution, circular import fixed in
            # _schema_circular_imports.
            title=_("Distribution"),
            required=True,
            readonly=True,
            description=_("The distribution that is mirrored")))
    name = exported(
        DistributionMirrorNameField(
            title=_('Name'),
            required=True,
            readonly=False,
            description=_('A short and unique name for this mirror.'),
            constraint=name_validator))
    displayname = exported(
        TextLine(title=_('Organisation'),
                 required=False,
                 readonly=False,
                 description=_(
                     'The name of the organization hosting this mirror.')))
    description = exported(
        TextLine(title=_('Description'), required=False, readonly=False))
    http_base_url = exported(
        DistroMirrorHTTPURIField(
            title=_('HTTP URL'),
            required=False,
            readonly=False,
            allowed_schemes=['http'],
            allow_userinfo=False,
            allow_query=False,
            allow_fragment=False,
            trailing_slash=True,
            description=_('e.g.: http://archive.ubuntu.com/ubuntu/')))
    ftp_base_url = exported(
        DistroMirrorFTPURIField(
            title=_('FTP URL'),
            required=False,
            readonly=False,
            allowed_schemes=['ftp'],
            allow_userinfo=False,
            allow_query=False,
            allow_fragment=False,
            trailing_slash=True,
            description=_('e.g.: ftp://archive.ubuntu.com/ubuntu/')))
    rsync_base_url = exported(
        DistroMirrorRsyncURIField(
            title=_('Rsync URL'),
            required=False,
            readonly=False,
            allowed_schemes=['rsync'],
            allow_userinfo=False,
            allow_query=False,
            allow_fragment=False,
            trailing_slash=True,
            description=_('e.g.: rsync://archive.ubuntu.com/ubuntu/')))
    enabled = exported(
        Bool(title=_('This mirror was probed successfully.'),
             required=False,
             readonly=True,
             default=False))
    speed = exported(
        Choice(title=_('Link Speed'),
               required=True,
               readonly=False,
               vocabulary=MirrorSpeed))
    country = exported(
        ReferenceChoice(
            title=_('Location'),
            description=_("The country in which this mirror is based."),
            required=True,
            readonly=False,
            vocabulary='CountryName',
            schema=ICountry))
    content = exported(
        Choice(title=_('Content'),
               required=True,
               readonly=False,
               description=_(
                   'Choose "CD Image" if this mirror contains CD images of '
                   'this distribution. Choose "Archive" if this is a '
                   'mirror of packages for this distribution.'),
               vocabulary=MirrorContent))
    status = exported(
        Choice(
            title=_('Status'),
            required=True,
            readonly=False,
            vocabulary=MirrorStatus,
            description=_("The current status of a mirror's registration.")))

    title = Attribute('The title of this mirror')
    cdimage_series = Attribute('All MirrorCDImageDistroSeries of this mirror')
    source_series = Attribute('All MirrorDistroSeriesSources of this mirror')
    arch_series = Attribute('All MirrorDistroArchSeries of this mirror')
    last_probe_record = Attribute(
        'The last MirrorProbeRecord for this mirror.')
    all_probe_records = Attribute('All MirrorProbeRecords for this mirror.')
    has_ftp_or_rsync_base_url = Bool(
        title=_('Does this mirror have a FTP or Rsync base URL?'))
    arch_mirror_freshness = Attribute(
        'The freshness of this mirror\'s archive mirrors')
    source_mirror_freshness = Attribute(
        'The freshness of this mirror\'s source mirrors')
    base_url = Attribute('The HTTP or FTP base URL of this mirror')
    date_created = exported(
        Datetime(
            title=_('Date Created'),
            required=True,
            readonly=True,
            description=_("The date on which this mirror was registered.")))
    country_dns_mirror = exported(
        Bool(title=_('Country DNS Mirror'),
             description=_('Whether this is a country mirror in DNS.'),
             required=False,
             readonly=True,
             default=False))

    reviewer = exported(
        PublicPersonChoice(
            title=_('Reviewer'),
            required=False,
            readonly=True,
            vocabulary='ValidPersonOrTeam',
            description=_("The person who last reviewed this mirror.")))
    date_reviewed = exported(
        Datetime(
            title=_('Date reviewed'),
            required=False,
            readonly=True,
            description=_(
                "The date on which this mirror was last reviewed by a mirror "
                "admin.")))

    official_candidate = exported(
        Bool(title=_('Apply to be an official mirror of this distribution'),
             required=False,
             readonly=False,
             default=True))
    whiteboard = exported(
        Whiteboard(title=_('Whiteboard'),
                   required=False,
                   readonly=False,
                   description=_(
                       "Notes on the current status of the mirror (only "
                       "visible to admins and the mirror's registrant).")))

    @export_read_operation()
    def canTransitionToCountryMirror():
        """Verify if a mirror can be set as a country mirror or return
        False."""

    @mutator_for(country_dns_mirror)
    @operation_parameters(country_dns_mirror=copy_field(country_dns_mirror))
    @export_write_operation()
    def transitionToCountryMirror(country_dns_mirror):
        """Method run on changing country_dns_mirror."""

    @invariant
    def mirrorMustHaveHTTPOrFTPURL(mirror):
        if not (mirror.http_base_url or mirror.ftp_base_url):
            raise Invalid('A mirror must have at least an HTTP or FTP URL.')

    def getSummarizedMirroredSourceSeries():
        """Return a summarized list of this distribution_mirror's
        MirrorDistroSeriesSource objects.

        Summarized, in this case, means that it ignores pocket and components
        and returns the MirrorDistroSeriesSource with the worst freshness for
        each distroseries of this distribution mirror.
        """

    def getSummarizedMirroredArchSeries():
        """Return a summarized list of this distribution_mirror's
        MirrorDistroArchSeries objects.

        Summarized, in this case, means that it ignores pocket and components
        and returns the MirrorDistroArchSeries with the worst freshness for
        each distro_arch_series of this distribution mirror.
        """

    @export_read_operation()
    def getOverallFreshness():
        """Return this mirror's overall freshness.

        For ARCHIVE mirrors, the overall freshness is the worst freshness of
        all of this mirror's content objects (MirrorDistroArchSeries,
        MirrorDistroSeriesSource or MirrorCDImageDistroSeriess).

        For RELEASE mirrors, the overall freshness is either UPTODATE, if the
        mirror contains all ISO images that it should or UNKNOWN if it doesn't
        contain one or more ISO images.
        """

    @export_read_operation()
    def isOfficial():
        """Return True if this is an official mirror."""

    def shouldDisable(expected_file_count=None):
        """Should this mirror be marked disabled?

        If this is a RELEASE mirror then expected_file_count must not be None,
        and it should be disabled if the number of cdimage_series it
        contains is smaller than the given expected_file_count.

        If this is an ARCHIVE mirror, then it should be disabled only if it
        has no content at all.

        We could use len(get_expected_cdimage_paths()) to obtain the
        expected_file_count, but that's not a good idea because that method
        gets the expected paths from releases.ubuntu.com, which is something
        we don't have control over.
        """

    def disable(notify_owner, log):
        """Disable this mirror and notify the distro's mirror admins by email.

        :param notify_owner: If True, an identical notification is sent to the
        mirror owner.

        :param log: The log of requests/responses from the last time this
        mirror was probed.  This is only necessary because we want to include
        a snippet of the log in the email notification but the content can
        only be read back from the librarian after we commit the transaction
        (and we really don't want to do it here).

        This method can't be called before a probe record has been created
        because we'll link to the latest probe record in the email we send to
        notify the owner.

        The notification(s) are actually sent only if this mirror was
        previously enabled or if it was probed only once.
        """

    def newProbeRecord(log_file):
        """Create and return a new MirrorProbeRecord for this mirror."""

    def deleteMirrorDistroArchSeries(distro_arch_series, pocket, component):
        """Delete the MirrorDistroArchSeries with the given arch series and
        pocket, in case it exists.
        """

    def ensureMirrorDistroArchSeries(distro_arch_series, pocket, component):
        """Check if we have a MirrorDistroArchSeries with the given arch
        series and pocket, creating one if not.

        Return that MirrorDistroArchSeries.
        """

    def ensureMirrorDistroSeriesSource(distroseries, pocket, component):
        """Check if we have a MirrorDistroSeriesSource with the given distro
        series, creating one if not.

        Return that MirrorDistroSeriesSource.
        """

    def deleteMirrorDistroSeriesSource(distroseries, pocket, component):
        """Delete the MirrorDistroSeriesSource with the given distro series,
        in case it exists.
        """

    def ensureMirrorCDImageSeries(arch_series, flavour):
        """Check if we have a MirrorCDImageDistroSeries with the given
        arch series and flavour, creating one if not.

        Return that MirrorCDImageDistroSeries.
        """

    def deleteMirrorCDImageSeries(arch_series, flavour):
        """Delete the MirrorCDImageDistroSeries with the given arch
        series and flavour, in case it exists.
        """

    def deleteAllMirrorCDImageSeries():
        """Delete all MirrorCDImageDistroSeriess of this mirror."""

    def getExpectedPackagesPaths():
        """Get all paths where we can find Packages.gz files on this mirror.

        Return a list containing, for each path, the DistroArchSeries,
        the PackagePublishingPocket and the Component to which that given
        Packages.gz file refer to and the path to the file itself.
        """

    def getExpectedSourcesPaths():
        """Get all paths where we can find Sources.gz files on this mirror.
Beispiel #9
0
class IDistroSeries(IDistroSeriesEditRestricted, IDistroSeriesPublic,
                    IStructuralSubscriptionTarget):
    """A series of an operating system distribution."""
    export_as_webservice_entry()
class IGitSubscription(Interface):
    """The relationship between a person and a Git repository."""

    # XXX cjwatson 2015-01-19 bug=760849: "beta" is a lie to get WADL
    # generation working.  Individual attributes must set their version to
    # "devel".
    export_as_webservice_entry(as_of="beta")

    id = Int(title=_("ID"), readonly=True, required=True)
    person_id = Int(title=_("Person ID"), required=True, readonly=True)
    person = exported(
        PersonChoice(
            title=_("Person"),
            required=True,
            vocabulary="ValidPersonOrTeam",
            readonly=True,
            description=_(
                'Enter the launchpad id, or email address of the person you '
                'wish to subscribe to this repository. If you are unsure, use '
                'the "Choose..." option to find the person in Launchpad. You '
                'can only subscribe someone who is a registered user of the '
                'system.')))
    repository = exported(
        Reference(title=_("Repository ID"),
                  required=True,
                  readonly=True,
                  schema=IGitRepository))
    notification_level = exported(
        Choice(
            title=_("Notification Level"),
            required=True,
            vocabulary=BranchSubscriptionNotificationLevel,
            default=BranchSubscriptionNotificationLevel.ATTRIBUTEONLY,
            description=_(
                "Attribute notifications are sent when repository details are "
                "changed such as lifecycle status and name.  Revision "
                "notifications are generated when new revisions are found.")))
    max_diff_lines = exported(
        Choice(
            title=_("Generated Diff Size Limit"),
            required=True,
            vocabulary=BranchSubscriptionDiffSize,
            default=BranchSubscriptionDiffSize.ONEKLINES,
            description=_(
                "Diffs greater than the specified number of lines will not "
                "be sent to the subscriber.  The subscriber will still "
                "receive an email with the new revision details even if the "
                "diff is larger than the specified number of lines.")))
    review_level = exported(
        Choice(title=_("Code review Level"),
               required=True,
               vocabulary=CodeReviewNotificationLevel,
               default=CodeReviewNotificationLevel.FULL,
               description=_(
                   "Control the kind of review activity that triggers "
                   "notifications.")))

    subscribed_by = exported(
        PersonChoice(
            title=_("Subscribed by"),
            required=True,
            vocabulary="ValidPersonOrTeam",
            readonly=True,
            description=_("The person who created this subscription.")))

    @call_with(user=REQUEST_USER)
    @export_read_operation()
    @operation_for_version("devel")
    def canBeUnsubscribedByUser(user):
        """Can the user unsubscribe the subscriber from the repository?"""
class IQuestionTarget(IQuestionTargetPublic, IQuestionTargetView):
    """An object that can have a new question asked about it."""
    export_as_webservice_entry(as_of='devel')
class IBranchMergeProposal(IBranchMergeProposalPublic,
                           IBranchMergeProposalView, IBranchMergeProposalEdit,
                           IBranchMergeProposalAnyAllowedPerson):
    """Branch merge proposals show intent of landing one branch on another."""

    export_as_webservice_entry()
Beispiel #13
0
class IMessage(Interface):
    """A message.

    This is like an email (RFC822) message, though it could be created through
    the web as well.
    """
    export_as_webservice_entry('message')

    id = Int(title=_('ID'), required=True, readonly=True)
    datecreated = exported(
        Datetime(title=_('Date Created'), required=True, readonly=True),
        exported_as='date_created')
    subject = exported(
        TextLine(title=_('Subject'), required=True, readonly=True))

    # XXX flacoste 2006-09-08: This attribute is only used for the
    # add form used by MessageAddView.
    content = Text(title=_("Message"), required=True, readonly=True)
    owner = exported(
        Reference(title=_('Person'), schema=Interface,
                  required=False, readonly=True))

    # Schema is really IMessage, but this cannot be declared here. It's
    # fixed below after the IMessage definition is complete.
    parent = exported(
        Reference(title=_('Parent'), schema=Interface,
                  required=False, readonly=True))

    rfc822msgid = TextLine(
        title=_('RFC822 Msg ID'), required=True, readonly=True)
    raw = Reference(title=_('Original unmodified email'),
                    schema=ILibraryFileAlias, required=False, readonly=True)
    bugs = CollectionField(
        title=_('Bug List'),
        value_type=Reference(schema=Interface))  # Redefined in bug.py

    chunks = Attribute(_('Message pieces'))

    text_contents = exported(
        Text(title=_('All the text/plain chunks joined together as a '
                     'unicode string.')),
        exported_as='content')

    title = TextLine(
        title=_('The message title, usually just the subject.'),
        readonly=True)
    visible = Bool(title=u"This message is visible or not.", required=False,
        default=True)

    bugattachments = exported(
        CollectionField(
            title=_("A list of BugAttachments connected to this "
                    "message."),
            value_type=Reference(Interface)),
        exported_as='bug_attachments')

    def __iter__():
        """Iterate over all the message chunks."""

    @accessor_for(parent)
    @export_read_operation()
    @operation_for_version('beta')
    def getAPIParent():
        """Return None because messages are not threaded over the API."""
Beispiel #14
0
class IBugTask(IHasDateCreated, IHasBug, IBugTaskDelete):
    """A bug needing fixing in a particular product or package."""
    export_as_webservice_entry()

    id = Int(title=_("Bug Task #"))
    bug = exported(
        BugField(title=_("Bug"), readonly=True))
    product = Choice(
        title=_('Project'), required=False, vocabulary='Product')
    productID = Attribute('The product ID')
    productseries = Choice(
        title=_('Series'), required=False, vocabulary='ProductSeries')
    productseriesID = Attribute('The product series ID')
    sourcepackagename = Choice(
        title=_("Package"), required=False,
        vocabulary='SourcePackageName')
    sourcepackagenameID = Attribute('The sourcepackagename ID')
    distribution = Choice(
        title=_("Distribution"), required=False, vocabulary='Distribution')
    distributionID = Attribute('The distribution ID')
    distroseries = Choice(
        title=_("Series"), required=False,
        vocabulary='DistroSeries')
    distroseriesID = Attribute('The distroseries ID')
    milestone = exported(ReferenceChoice(
        title=_('Milestone'),
        required=False,
        readonly=True,
        vocabulary='BugTaskMilestone',
        schema=Interface))  # IMilestone
    milestoneID = Attribute('The id of the milestone.')

    # The status and importance's vocabularies do not
    # contain an UNKNOWN item in bugtasks that aren't linked to a remote
    # bugwatch; this would be better described in a separate interface,
    # but adding a marker interface during initialization is expensive,
    # and adding it post-initialization is not trivial.
    # Note that status is a property because the model only exposes INCOMPLETE
    # but the DB stores INCOMPLETE_WITH_RESPONSE and
    # INCOMPLETE_WITHOUT_RESPONSE for query efficiency.
    status = exported(
        Choice(title=_('Status'), vocabulary=BugTaskStatus,
               default=BugTaskStatus.NEW, readonly=True))
    _status = Attribute('The actual status DB column used in queries.')
    importance = exported(
        Choice(title=_('Importance'), vocabulary=BugTaskImportance,
               default=BugTaskImportance.UNDECIDED, readonly=True))
    assignee = exported(
        PersonChoice(
            title=_('Assigned to'), required=False,
            vocabulary='ValidAssignee',
            readonly=True))
    assigneeID = Int(title=_('The assignee ID (for eager loading)'))
    bugtargetdisplayname = exported(
        Text(title=_("The short, descriptive name of the target"),
             readonly=True),
        exported_as='bug_target_display_name')
    bugtargetname = exported(
        Text(title=_("The target as presented in mail notifications"),
             readonly=True),
        exported_as='bug_target_name')
    bugwatch = exported(
        ReferenceChoice(
            title=_("Remote Bug Details"), required=False,
            schema=IBugWatch,
            vocabulary='BugWatch', description=_(
                "Select the bug watch that "
                "represents this task in the relevant bug tracker. If none "
                "of the bug watches represents this particular bug task, "
                "leave it as (None). Linking the remote bug watch with the "
                "task in this way means that a change in the remote bug "
                "status will change the status of this bug task in "
                "Launchpad.")),
        exported_as='bug_watch')
    date_assigned = exported(
        Datetime(title=_("Date Assigned"),
                 description=_("The date on which this task was assigned "
                               "to someone."),
                 readonly=True,
                 required=False))
    datecreated = exported(
        Datetime(title=_("Date Created"),
                 description=_("The date on which this task was created."),
                 readonly=True),
        exported_as='date_created')
    date_confirmed = exported(
        Datetime(title=_("Date Confirmed"),
                 description=_("The date on which this task was marked "
                               "Confirmed."),
                 readonly=True,
                 required=False))
    date_incomplete = exported(
        Datetime(title=_("Date Incomplete"),
                 description=_("The date on which this task was marked "
                               "Incomplete."),
                 readonly=True,
                 required=False))
    date_inprogress = exported(
        Datetime(title=_("Date In Progress"),
                 description=_("The date on which this task was marked "
                               "In Progress."),
                 readonly=True,
                 required=False),
        exported_as='date_in_progress')
    date_closed = exported(
        Datetime(title=_("Date Closed"),
                 description=_("The date on which this task was marked "
                               "either Won't Fix, Invalid or Fix Released."),
                 readonly=True,
                 required=False))
    date_left_new = exported(
        Datetime(title=_("Date left new"),
                 description=_("The date on which this task was marked "
                               "with a status higher than New."),
                 readonly=True,
                 required=False))
    date_triaged = exported(
        Datetime(title=_("Date Triaged"),
                 description=_("The date on which this task was marked "
                               "Triaged."),
                 readonly=True,
                 required=False))
    date_fix_committed = exported(
        Datetime(title=_("Date Fix Committed"),
                 description=_("The date on which this task was marked "
                               "Fix Committed."),
                 readonly=True,
                 required=False))
    date_fix_released = exported(
        Datetime(title=_("Date Fix Released"),
                 description=_("The date on which this task was marked "
                               "Fix Released."),
                 readonly=True,
                 required=False))
    date_left_closed = exported(
        Datetime(title=_("Date left closed"),
                 description=_("The date on which this task was "
                               "last reopened."),
                 readonly=True,
                 required=False))
    age = Datetime(title=_("Age"),
                   description=_("The age of this task, expressed as the "
                                 "length of time between the creation date "
                                 "and now."))
    task_age = Int(title=_("Age of the bug task"),
            description=_("The age of this task in seconds, a delta between "
                         "now and the date the bug task was created."))
    owner = exported(
        Reference(title=_("The owner"), schema=Interface, readonly=True))
    target = exported(Reference(
        title=_('Target'), required=True, schema=Interface,  # IBugTarget
        readonly=True,
        description=_("The software in which this bug should be fixed.")))
    title = exported(
        Text(title=_("The title of the bug related to this bugtask"),
             readonly=True))
    related_tasks = exported(
        CollectionField(
            description=_(
                "IBugTasks related to this one, namely other "
                "IBugTasks on the same IBug."),
            value_type=Reference(schema=Interface),  # Will be specified later
            readonly=True))
    pillar = Choice(
        title=_('Pillar'),
        description=_("The LP pillar (product or distribution) "
                      "associated with this task."),
        vocabulary='DistributionOrProduct', readonly=True)
    other_affected_pillars = Attribute(
        "The other pillars (products or distributions) affected by this bug. "
        "This returns a list of pillars OTHER THAN the pillar associated "
        "with this particular bug.")
    # This property does various database queries. It is a property so a
    # "snapshot" of its value will be taken when a bugtask is modified, which
    # allows us to compare it to the current value and see if there are any
    # new subscribers that should get an email containing full bug details
    # (rather than just the standard change mail.) It is a property on
    # IBugTask because we currently only ever need this value for events
    # handled on IBugTask.
    bug_subscribers = Field(
        title=_("A list of IPersons subscribed to the bug, whether directly "
                "or indirectly."), readonly=True)

    conjoined_master = Attribute(
        "The series-specific bugtask in a conjoined relationship")
    conjoined_slave = Attribute(
        "The generic bugtask in a conjoined relationship")

    is_complete = exported(
        Bool(description=_(
                "True or False depending on whether or not there is more "
                "work required on this bug task."),
             readonly=True))

    @operation_returns_collection_of(Interface)  # Actually IBug.
    @call_with(user=REQUEST_USER, limit=10)
    @export_read_operation()
    def findSimilarBugs(user, limit=10):
        """Return the list of possible duplicates for this BugTask."""

    @call_with(user=REQUEST_USER)
    @operation_parameters(person=copy_field(assignee))
    @export_read_operation()
    @operation_for_version("devel")
    def getContributorInfo(user, person):
        """Is the person a contributor to bugs in this task's pillar?

        :param user: The user doing the search. Private bugs that this
            user doesn't have access to won't be included in the search.
        :param person: The person to check to see if they are a contributor.

        Return a dict with the following values:
        is_contributor: True if the user has any bugs assigned to him in the
        context of this bug task's pillar, either directly or by team
        participation.
        person_name: the displayname of the person
        pillar_name: the displayname of the bug task's pillar

        This API call is provided for use by the client Javascript where the
        calling context does not have access to the person or pillar names.
        """

    def getConjoinedMaster(bugtasks, bugtasks_by_package=None):
        """Return the conjoined master in the given bugtasks, if any.

        :param bugtasks: The bugtasks to be considered when looking for
            the conjoined master.
        :param bugtasks_by_package: A cache, mapping a
            `ISourcePackageName` to a list of bug tasks targeted to such
            a package name. Both distribution and distro series tasks
            should be included in this list.

        This method exists mainly to allow calculating the conjoined
        master from a cached list of bug tasks, reducing the number of
        db queries needed.
        """

    def subscribe(person, subscribed_by):
        """Subscribe this person to the underlying bug.

        This method was documented as being required here so that
        MentorshipOffers could happen on IBugTask. If that was the sole reason
        this method should be deletable. When we move to context-less bug
        presentation (where the bug is at /bugs/n?task=ubuntu) then we can
        eliminate this if it is no longer useful.
        """

    def isSubscribed(person):
        """Return True if the person is an explicit subscriber to the
        underlying bug for this bugtask.

        This method was documented as being required here so that
        MentorshipOffers could happen on IBugTask. If that was the sole
        reason then this method should be deletable.  When we move to
        context-less bug presentation (where the bug is at
        /bugs/n?task=ubuntu) then we can eliminate this if it is no
        longer useful.
        """

    @mutator_for(milestone)
    @rename_parameters_as(new_milestone='milestone')
    @operation_parameters(new_milestone=copy_field(milestone))
    @call_with(user=REQUEST_USER)
    @export_write_operation()
    def transitionToMilestone(new_milestone, user):
        """Set the BugTask milestone.

        Set the bugtask milestone, making sure that the user is
        authorised to do so.
        """

    @mutator_for(importance)
    @rename_parameters_as(new_importance='importance')
    @operation_parameters(new_importance=copy_field(importance))
    @call_with(user=REQUEST_USER)
    @export_write_operation()
    def transitionToImportance(new_importance, user):
        """Set the BugTask importance.

        Set the bugtask importance, making sure that the user is
        authorised to do so.
        """

    def canTransitionToStatus(new_status, user):
        """Return True if the user is allowed to change the status to
        `new_status`.

        :new_status: new status from `BugTaskStatus`
        :user: the user requesting the change

        Some status transitions, e.g. Triaged, require that the user
        be a bug supervisor or the owner of the project.
        """

    @mutator_for(status)
    @rename_parameters_as(new_status='status')
    @operation_parameters(
        new_status=copy_field(status))
    @call_with(user=REQUEST_USER)
    @export_write_operation()
    def transitionToStatus(new_status, user):
        """Perform a workflow transition to the new_status.

        :new_status: new status from `BugTaskStatus`
        :user: the user requesting the change

        For certain statuses, e.g. Confirmed, other actions will
        happen, like recording the date when the task enters this
        status.

        Some status transitions require extra conditions to be met.
        See `canTransitionToStatus` for more details.
        """

    def userCanSetAnyAssignee(user):
        """Check if the current user can set anybody sa a bugtask assignee.

        Owners, drivers, bug supervisors and Launchpad admins can always
        assign to someone else.  Other users can assign to someone else if a
        bug supervisor is not defined.
        """

    def userCanUnassign(user):
        """Check if the current user can set assignee to None."""

    @mutator_for(assignee)
    @operation_parameters(assignee=copy_field(assignee))
    @export_write_operation()
    def transitionToAssignee(assignee, validate=True):
        """Perform a workflow transition to the given assignee.

        When the bugtask assignee is changed from None to an IPerson
        object, the date_assigned is set on the task. If the assignee
        value is set to None, date_assigned is also set to None.
        """

    def validateTransitionToTarget(target):
        """Check whether a transition to this target is legal.

        :raises IllegalTarget: if the new target is not allowed.
        """

    @mutator_for(target)
    @call_with(user=REQUEST_USER)
    @operation_parameters(
        target=copy_field(target))
    @export_write_operation()
    def transitionToTarget(target, user):
        """Convert the bug task to a different bug target."""

    def updateTargetNameCache():
        """Update the targetnamecache field in the database.

        This method is meant to be called when an IBugTask is created or
        modified and will also be called from the update_stats.py cron script
        to ensure that the targetnamecache is properly updated when, for
        example, an IDistribution is renamed.
        """

    def asEmailHeaderValue():
        """Return a value suitable for an email header value for this bugtask.

        The return value is a single line of arbitrary length, so header
        folding should be done by the callsite, as needed.

        For an upstream task, this value might look like:

          product=firefox; status=New; importance=Critical; assignee=None;

        See doc/bugmail-headers.txt for a complete explanation and more
        examples.
        """

    def getDelta(old_task):
        """Compute the delta from old_task to this task.

        Returns an IBugTaskDelta or None if there were no changes between
        old_task and this task.
        """

    def getPackageComponent():
        """Return the task's package's component or None.

        Returns the component associated to the current published
        package in that distribution's current series. If the task is
        not a package task, returns None.
        """

    def userHasDriverPrivileges(user):
        """Does the user have driver privileges on the current bugtask?

        :return: A boolean.
        """

    def userHasBugSupervisorPrivileges(user):
        """Is the user privileged and allowed to change details on a bug?
Beispiel #15
0
class IPOFile(IRosettaStats):
    """A translation file."""

    export_as_webservice_entry(singular_name="translation_file",
                               plural_name="translation_files")

    id = exported(
        Int(title=_('The translation file id.'), required=True, readonly=True))

    potemplate = Object(title=_('The translation file template.'),
                        required=True,
                        readonly=True,
                        schema=IPOTemplate)

    language = Choice(title=_('Language of this PO file.'),
                      vocabulary='Language',
                      required=True)

    title = TextLine(title=_('The translation file title.'),
                     required=True,
                     readonly=True)

    description = Text(title=_('The translation file description.'),
                       required=True)

    topcomment = Text(title=_('A comment about this translation file.'),
                      required=True)

    header = Text(
        title=_('Header'),
        description=_('The standard translation header in its native format.'),
        required=False)

    fuzzyheader = Bool(
        title=_('A flag indicating whether the header is fuzzy.'),
        required=True)

    lasttranslator = Object(title=_('Last person that translated a message.'),
                            schema=IPerson)

    date_changed = Datetime(title=_('When this file was last changed.'),
                            readonly=False,
                            required=True)

    lastparsed = Datetime(title=_('Last time this pofile was parsed.'))

    owner = Choice(title=_('Translation file owner'),
                   required=True,
                   description=_('''
            The owner of the translation file in Launchpad can edit its
            translations and upload new versions.
            '''),
                   vocabulary="ValidOwner")

    path = TextLine(title=_('The path to the file that was imported'),
                    required=True)

    datecreated = Datetime(title=_('When this translation file was created.'),
                           required=True)

    translators = List(title=_('Translators that have edit permissions.'),
                       description=_('''
            Translators designated as having permission to edit these files
            in this language.
            '''),
                       required=True,
                       readonly=True)

    contributors = List(
        title=_('Translators who have made any contribution to this file.'),
        required=True,
        readonly=True)

    translationpermission = Choice(title=_('Translation permission'),
                                   required=True,
                                   description=_('''
            The permission system which is used for this translation file.
            This is inherited from the product, project and/or distro in which
            the pofile is found.
            '''),
                                   vocabulary=TranslationPermission)

    from_sourcepackagename = Field(
        title=_('The source package this pofile comes from.'),
        description=_('''
            The source package this pofile comes from (set it only if it\'s
            different from IPOFile.potemplate.sourcepackagename).
            '''),
        required=False)

    translation_messages = Attribute(
        _("All `ITranslationMessage` objects related to this "
          "translation file."))

    plural_forms = Int(
        title=_('Number of plural forms for the language of this PO file.'),
        description=_('''
            Number of plural forms is a number of translations provided for
            each plural form message.  If `IPOFile.language` does not specify
            plural forms, it defaults to 2, which is the most common number
            of plural forms.
            '''),
        required=True,
        readonly=True)

    def getOtherSidePOFile():
        """Get the POFile for the same language on the other side.

        Follow the packaging link to find in the sharing template on the
        other side and get the POFile from there.
        Returns None if no link exists.
        """

    def translatedCount():
        """
        Returns the number of message sets which this PO file has current
        translations for.
        """

    def untranslatedCount():
        """
        Return the number of messages which this PO file has no translation
        for.
        """

    def hasPluralFormInformation():
        """Do we know the plural-forms information for this `POFile`?"""

    def getHeader():
        """Return an `ITranslationHeaderData` representing its header."""

    def findPOTMsgSetsContaining(text):
        """Get POTMsgSets where English text or translation contain `text`."""

    def getPOTMsgSetTranslated():
        """Get pot messages that are translated for this translation file."""

    def getPOTMsgSetUntranslated():
        """Get pot message sets that are untranslated for this file."""

    def getPOTMsgSetWithNewSuggestions():
        """Get pot message sets with suggestions submitted after last review.
        """

    def getPOTMsgSetDifferentTranslations():
        """Get pot message sets with different translations on both sides.
        """

    def getTranslationsFilteredBy(person):
        """Get TranslationMessages in this `IPOFile` contributed by `person`.

        Returned messages are ordered by the `POTMsgSet`, and then by
        `date_created` with newest first.
        """

    def getTranslationMessages(condition=None):
        """Get TranslationMessages in this `IPOFile`.

        If condition is None, return all messages, else narrow the result
        set down using the condition.
        """

    def export(ignore_obsolete=False, export_utf8=False):
        """Export this PO file as string.

        :param ignore_obsolete: Whether the exported PO file does not have
            obsolete entries.
        :param export_utf8: Whether the exported PO file should be exported as
            UTF-8.
        """

    def prepareTranslationCredits(potmsgset):
        """Add Launchpad contributors to translation credit strings.

        It adds to the translation for `potmsgset` if it exists, trying
        not to repeat same people who are already credited.
        """

    def canEditTranslations(person):
        """Whether the given person is able to add/edit translations."""

    def canAddSuggestions(person):
        """Whether the given person is able to add new suggestions."""

    def getStatistics():
        """Summarize this file's cached translation statistics.

        :return: tuple of (`currentcount`, `updatescount`, `rosettacount`,
            `unreviewed_count`), as collected by `updateStatistics`.
        """

    def updateStatistics():
        """Update the cached statistics fields.

        :return: a tuple (`currentcount`, `updatescount`, `rosettacount`,
            `unreviewed_count`), as for `getStatistics`."""

    def updateHeader(new_header):
        """Update the header information.

        new_header is a POHeader object.
        """

    def isTranslationRevisionDateOlder(header):
        """Whether given header revision date is newer then self one."""

    def setPathIfUnique(path):
        """Set path to given one, provided it's a valid, unique path.

        A `POFile`'s path must be unique within its context, i.e. for either
        the same `DistroSeries` and `SourcePackageName`, or for the same
        `ProductSeries`, depending on which the `POFile` is attached to.

        If the new path is not unique, the old path will be retained instead.
        """

    def importFromQueue(entry_to_import, logger=None, txn=None):
        """Import given queue entry.

        :param entry_to_import: `TranslationImportQueueEntry` specifying an
            approved import for this `POFile`
        :param logger: optional logger to report problems to.
        :param txn: optionally, a transaction manager.  It will not be
            used; it's here only for API symmetry with `IPOTemplate`.

        :return: a tuple of the subject line and body for a notification email
            to be sent to the uploader.
        """

    def getFullLanguageCode():
        """Return the language code."""

    def getFullLanguageName():
        """Return the language name."""

    def getTranslationRows():
        """Return exportable rows of translation data.

        :return: a list of `VPOExport` objects.
        """

    def getChangedRows():
        """Return exportable rows that differ from upstream translations.

        :return: a list of `VPOExport` objects.
        """

    def markChanged(translator=None, timestamp=None):
        """Note a change to this `POFile` or its contents.
Beispiel #16
0
class ITranslationGroup(IHasOwner):
    """A TranslationGroup."""

    export_as_webservice_entry(
        singular_name='translation_group', plural_name='translation_groups')

    id = Int(
            title=_('Translation Group ID'), required=True, readonly=True,
            )
    name = exported(
        TextLine(
            title=_('Name'), required=True,
            description=_("""Keep this name very short, unique, and
            descriptive, because it will be used in URLs. Examples:
            gnome-translation-project, ubuntu-translators."""),
            constraint=name_validator),
        as_of="devel"
        )
    title = exported(
        Title(
            title=_('Title'), required=True,
            description=_("""Title of this Translation Group.
            This title is displayed at the top of the Translation Group
            page and in lists or reports of translation groups.  Do not
            add "translation group" to this title, or it will be shown
            double.
            """),),
        as_of="devel"
        )
    summary = Summary(
            title=_('Summary'), required=True,
            description=_("""A single-paragraph description of the
            group. This will also be displayed in most
            translation group listings."""),
            )
    datecreated = Datetime(
            title=_('Date Created'), required=True, readonly=True,
            )
    owner = PublicPersonChoice(
            title=_('Owner'), required=True, vocabulary='ValidOwner',
            description=_("The owner's IPerson"))
    # joins
    translators = Attribute('The set of translators for this group.')
    projects = Attribute('The projects for which this group translates.')
    products = Attribute('The projects to which this group is directly '
        'appointed as a translator. There may be other projects that are '
        'part of project groups for which the group also translates.')
    distributions = Attribute('The distros for which this group translates.')

    translation_guide_url = URIField(
        title=_('Translation instructions'), required=False,
        allowed_schemes=['http', 'https', 'ftp'],
        allow_userinfo=False,
        description=_("The URL of the generic translation instructions "
                      "followed by this particular translation group. "
                      "This should include team policies and "
                      "recommendations, specific instructions for "
                      "any non-standard behaviour and other documentation."
                      "Can be any of http://, https://, or ftp://."))

    # accessing the translator list
    def query_translator(language):
        """Retrieve a translator, or None, based on a Language"""

    # adding and removing translators
    def remove_translator(language):
        """Remove the translator for this language from the group."""

    # used for the form machinery
    def add(content):
        """Add a new object."""

    top_projects = Attribute(
        "Most relevant projects using this translation group.")

    number_of_remaining_projects = Attribute(
        "Count of remaining projects not listed in the `top_projects`.")

    def __getitem__(language_code):
        """Retrieve the translator for the given language in this group.

        This is used for navigation through the group.
        """

    def fetchTranslatorData():
        """Fetch translators and related data.

        Prefetches display-related properties.

        :return: A result set of (`Translator`, `Language`, `Person`),
            ordered by language name in English.
        """

    def fetchProjectsForDisplay(user):
        """Fetch `Product`s using this group, for display purposes.

        Prefetches display-related properties.

        :return: A result set of `Product`, ordered by display name.
        """

    def fetchProjectGroupsForDisplay():
        """Fetch `Project`s using this group, for display purposes.

        Prefetches display-related properties.

        :return: A result set of `Project`, ordered by display name.
        """

    def fetchDistrosForDisplay():
        """Fetch `Distribution`s using this group, for display purposes.
Beispiel #17
0
class IWebhook(Interface):

    export_as_webservice_entry(as_of='beta')

    id = Int(title=_("ID"), readonly=True, required=True)

    target = exported(
        Reference(
            title=_("Target"),
            schema=Interface,  # Actually IWebhookTarget.
            required=True,
            readonly=True,
            description=_(
                "The object for which this webhook receives events.")))
    event_types = exported(
        List(Choice(vocabulary='ValidWebhookEventType'),
             title=_("Event types"),
             required=True,
             readonly=False))
    registrant = exported(
        Reference(title=_("Registrant"),
                  schema=IPerson,
                  required=True,
                  readonly=True,
                  description=_("The person who created this webhook.")))
    registrant_id = Int(title=_("Registrant ID"))
    date_created = exported(
        Datetime(title=_("Date created"), required=True, readonly=True))
    date_last_modified = exported(
        Datetime(title=_("Date last modified"), required=True, readonly=True))

    delivery_url = exported(
        URIField(title=_("Delivery URL"),
                 allowed_schemes=['http', 'https'],
                 required=True,
                 readonly=False))
    active = exported(
        Bool(title=_("Active"),
             description=_("Deliver details of subscribed events."),
             required=True,
             readonly=False))

    # Do not export this.
    secret = TextLine(
        title=_("Secret"),
        required=False,
        description=_(
            "An optional string used to sign delivery bodies with HMAC-SHA1 "
            "in the X-Hub-Signature header. This must be less than 200 bytes "
            "long."),
        max_length=200)

    deliveries = exported(
        doNotSnapshot(
            CollectionField(title=_("Recent deliveries for this webhook."),
                            value_type=Reference(schema=Interface),
                            readonly=True)))

    def getDelivery(id):
        """Retrieve a delivery by ID, or None if it doesn't exist."""

    @export_factory_operation(Interface, [])  # Actually IWebhookDelivery.
    @operation_for_version('devel')
    def ping():
        """Send a test event."""

    @export_destructor_operation()
    @operation_for_version('devel')
    def destroySelf():
        """Delete this webhook."""

    @export_write_operation()
    @operation_parameters(secret=copy_field(secret))
    @operation_for_version('devel')
    def setSecret(secret):
        """Set the secret used to sign deliveries."""
class IBugTracker(Interface):
    """A remote bug system.

    Launchpadlib example: What bug tracker is used for a distro source
    package?

    ::

        product = source_package.upstream_product
        if product:
            tracker = product.bug_tracker
            if not tracker:
                project = product.project_group
                if project:
                    tracker = project.bug_tracker
        if tracker:
            print "%s at %s" %(tracker.bug_tracker_type, tracker.base_url)

    """
    export_as_webservice_entry()

    id = Int(title=_('ID'))
    bugtrackertype = exported(
        Choice(title=_('Bug Tracker Type'),
               vocabulary=BugTrackerType,
               default=BugTrackerType.BUGZILLA),
        exported_as='bug_tracker_type')
    name = exported(
        BugTrackerNameField(
            title=_('Name'),
            constraint=name_validator,
            description=_('A URL-friendly name for the bug tracker, '
                          'such as "mozilla-bugs".')))
    title = exported(
        TextLine(
            title=_('Title'),
            description=_('A descriptive label for this tracker to show '
                          'in listings.')))
    summary = exported(
        Text(
            title=_('Summary'),
            description=_(
                'A brief introduction or overview of this bug '
                'tracker instance.'),
            required=False))
    baseurl = exported(
        BugTrackerURL(
            title=_('Location'),
            allowed_schemes=LOCATION_SCHEMES_ALLOWED,
            description=_(
                'The top-level URL for the bug tracker, or an upstream email '
                'address. This must be accurate so that Launchpad can link '
                'to external bug reports.')),
        exported_as='base_url')
    aliases = exported(
        List(
            title=_('Location aliases'),
            description=_(
                'A list of URLs or email addresses that all lead to the '
                'same bug tracker, or commonly seen typos, separated by '
                'whitespace.'),
            value_type=BugTrackerURL(
                allowed_schemes=LOCATION_SCHEMES_ALLOWED),
            required=False),
        exported_as='base_url_aliases')
    owner = exported(
        Reference(title=_('Owner'), schema=Interface),
        exported_as='registrant')
    contactdetails = exported(
        Text(
            title=_('Contact details'),
            description=_(
                'The contact details for the external bug tracker (so that, '
                'for example, its administrators can be contacted about a '
                'security breach).'),
            required=False),
        exported_as='contact_details')
    watches = doNotSnapshot(
        exported(
            CollectionField(
                title=_('The remote watches on this bug tracker.'),
                value_type=Reference(schema=IObject))))
    has_lp_plugin = exported(
        Bool(
            title=_('This bug tracker has a Launchpad plugin installed.'),
            required=False, default=False))
    products = Attribute('The products that use this bug tracker.')
    latestwatches = Attribute('The last 10 watches created.')
    imported_bug_messages = Attribute(
        'Bug messages that have been imported from this bug tracker.')
    multi_product = Attribute(
        "This bug tracker tracks multiple remote products.")
    active = exported(
        Bool(
            title=_('Updates for this bug tracker are enabled'),
            required=True, default=True))

    watches_ready_to_check = Attribute(
        "The set of bug watches that are scheduled to be checked.")
    watches_with_unpushed_comments = Attribute(
        "The set of bug watches that have unpushed comments.")
    watches_needing_update = Attribute(
        "The set of bug watches that need updating.")

    def getBugFilingAndSearchLinks(remote_product, summary=None,
                                   description=None, remote_component=None):
        """Return the bug filing and search links for the tracker.

        :param remote_product: The name of the product on which the bug
            is to be filed or searched for.
        :param summary: The string with which to pre-filly the summary
            field of the upstream bug tracker's search and bug filing forms.
        :param description: The string with which to pre-filly the description
            field of the upstream bug tracker's bug filing form.
        :param remote_component: The name of the component on which the bug
            is to be filed or search for.
        :return: A dict of the absolute URL of the bug filing form and
            the search form for `remote_product` on the remote tracker,
            in the form {'bug_filing_url': foo, 'search_url': bar}. If
            either or both of the URLs is unavailable for the current
            BugTrackerType the relevant values in the dict will be set
            to None. If the bug tracker requires a `remote_product` but
            None is passed, None will be returned for both values in the
            dict.
        """

    def getBugsWatching(remotebug):
        """Get the bugs watching the given remote bug in this bug tracker."""

    def getLinkedPersonByName(name):
        """Return the `IBugTrackerPerson` for a given name on a bugtracker.

        :param name: The name of the person on the bugtracker in
            `bugtracker`.
        :return: an `IBugTrackerPerson`.
        """

    def linkPersonToSelf(name, person):
        """Link a Person to the BugTracker using a given name.

        :param name: The name used for person on bugtracker.
        :param person: The `IPerson` to link to bugtracker.
        :raise BugTrackerPersonAlreadyExists: If `name` has already been
            used to link a person to `bugtracker`.
        :return: An `IBugTrackerPerson`.
        """

    def ensurePersonForSelf(
        display_name, email, rationale, creation_comment):
        """Return the correct `IPerson` for a given name on a bugtracker.

        :param bugtracker: The `IBugTracker` for which we should have a
            given Person.
        :param display_name: The name of the Person on `bugtracker`.
        :param email: The Person's email address if available. If `email`
            is supplied a Person will be created or retrieved using that
            email address and no `IBugTrackerPerson` records will be created.
        :param rationale: The `PersonCreationRationale` used to create a
            new `IPerson` for this `name` and `bugtracker`, if necessary.
        :param creation_comment: The creation comment for the `IPerson`
            if one is created.
         """

    def destroySelf():
        """Delete this bug tracker."""

    def resetWatches(new_next_check=None):
        """Reset the next_check times of this BugTracker's `BugWatch`es.

        :param new_next_check: If specified, contains the datetime to
            which to set the BugWatches' next_check times.  If not
            specified, the watches' next_check times will be set to a
            point between now and 24 hours hence.
        """

    @operation_parameters(
        component_group_name=TextLine(
            title=u"The name of the remote component group", required=True))
    @operation_returns_entry(Interface)
    @export_write_operation()
    def addRemoteComponentGroup(component_group_name):
        """Adds a new component group to the bug tracker"""

    @export_read_operation()
    @operation_returns_collection_of(Interface)
    def getAllRemoteComponentGroups():
        """Return collection of all component groups for this bug tracker"""

    @operation_parameters(
        component_group_name=TextLine(
            title=u"The name of the remote component group",
            required=True))
    @operation_returns_entry(Interface)
    @export_read_operation()
    def getRemoteComponentGroup(component_group_name):
        """Retrieve a given component group registered with the bug tracker.

        :param component_group_name: Name of the component group to retrieve.
        """

    @operation_parameters(
        distribution=TextLine(
            title=u"The distribution for the source package",
            required=True),
        sourcepackagename=TextLine(
            title=u"The source package name",
            required=True))
    @operation_returns_entry(Interface)
    @export_read_operation()
    @operation_for_version('devel')
    def getRemoteComponentForDistroSourcePackageName(
        distribution, sourcepackagename):
        """Returns the component linked to this source package, if any.

        If no components have been linked, returns value of None.
        """

    def getRelatedPillars(user=None):
        """Returns the `IProduct`s and `IProjectGroup`s that use this tracker.
Beispiel #19
0
class IDistroSeriesDifference(IDistroSeriesDifferencePublic,
                              IDistroSeriesDifferenceEdit,
                              IDistroSeriesDifferenceAdmin):
    """An interface for a package difference between two distroseries."""
    export_as_webservice_entry()
Beispiel #20
0
class IPOTemplate(IRosettaStats):
    """A translation template."""

    export_as_webservice_entry(singular_name='translation_template',
                               plural_name='translation_templates')

    id = exported(
        Int(title=u"The translation template id.",
            required=True,
            readonly=True))

    name = exported(
        TextLine(title=_("Template name"),
                 description=_(
                     "The name of this PO template, for example "
                     "'evolution-2.2'. Each translation template has a "
                     "unique name in its package. It's important to get this "
                     "correct, because Launchpad will recommend alternative "
                     "translations based on the name."),
                 constraint=valid_name,
                 required=True))

    translation_domain = exported(
        TextLine(
            title=_("Translation domain"),
            description=_(
                "The translation domain for a translation template. "
                "Used with PO file format when generating MO files for inclusion "
                "in language pack or MO tarball exports."),
            required=True))

    description = exported(
        Text(
            title=_("Description"),
            description=_(
                "Please provide a brief description of the content "
                "of this translation template, for example, telling translators "
                "if this template contains strings for end-users or other "
                "developers."),
            required=False))

    header = Text(
        title=_('Header'),
        description=_("The standard template header in its native format."),
        required=True)

    iscurrent = exported(Bool(
        title=_("Template is active"),
        description=_(
            "If unchecked, people can no longer change the template's "
            "translations."),
        required=True,
        default=True),
                         exported_as='active')

    owner = exported(
        PersonChoice(
            title=_("Owner"),
            required=True,
            description=_(
                "The owner of the template in Launchpad can edit the template "
                "and change it's status, and can also upload new versions "
                "of the template when a new release is made or when the "
                "translation strings have been changed during development."),
            vocabulary="ValidOwner"))

    productseries = Choice(title=_("Series"),
                           required=False,
                           vocabulary="ProductSeries")

    distroseries = Choice(title=_("Series"),
                          required=False,
                          vocabulary="DistroSeries")

    sourcepackagename = Choice(
        title=_("Source Package Name"),
        description=_("The source package that uses this template."),
        required=False,
        vocabulary="SourcePackageName")

    sourcepackagenameID = Int(
        title=_("Source Package Name ID"),
        description=_("The ID of the source package that uses this template."),
        required=False,
        readonly=True)

    sourcepackage = Reference(
        ISourcePackage,
        title=u"Source package this template is for, if any.",
        required=False,
        readonly=True)

    from_sourcepackagename = Choice(
        title=_("From Source Package Name"),
        description=_(
            "The source package this template comes from (set it only if it's"
            " different from the previous 'Source Package Name'."),
        required=False,
        vocabulary="SourcePackageName")

    sourcepackageversion = TextLine(title=_("Source Package Version"),
                                    required=False)

    binarypackagename = Choice(
        title=_("Binary Package"),
        description=_("The package in which this template's translations are "
                      "installed."),
        required=False,
        vocabulary="BinaryPackageName")

    languagepack = exported(Bool(
        title=_("Include translations for this template in language packs?"),
        description=_(
            "Check this box if this template is part of a language pack so "
            "its translations should be exported that way."),
        required=True,
        default=False),
                            exported_as='exported_in_languagepacks')

    path = exported(
        TextLine(title=_(
            "Path of the template in the source tree, including filename."),
                 required=True))

    source_file = Object(title=_('Source file for this translation template'),
                         readonly=True,
                         schema=ILibraryFileAlias)

    source_file_format = exported(Choice(
        title=_("File format for the source file"),
        required=False,
        vocabulary=TranslationFileFormat),
                                  exported_as='format')

    priority = exported(
        Int(title=_('Priority'),
            required=True,
            default=0,
            description=_(
                'A number that describes how important this template is. Often '
                'there are multiple templates, and you can use this as a way '
                'of indicating which are more important and should be '
                'translated first. Pick any number - higher priority '
                'templates will generally be listed first.')))

    datecreated = Datetime(
        title=_('When this translation template was created.'),
        required=True,
        readonly=True)

    translationgroups = Attribute(
        _('''
            The `ITranslationGroup` objects that handle translations for this
            template.
            There can be several because they can be inherited from project to
            product, for example.
            '''))

    translationpermission = Choice(title=_('Translation permission'),
                                   required=True,
                                   readonly=True,
                                   description=_('''
            The permission system which is used for this translation template.
            This is inherited from the product, project and/or distro in which
            the translation template is found.
            '''),
                                   vocabulary='TranslationPermission')

    pofiles = exported(
        CollectionField(
            title=_("All translation files that exist for this template."),
            # Really IPOFile, see _schema_circular_imports.py.
            value_type=Reference(schema=Interface)),
        exported_as='translation_files')

    relatives_by_source = Attribute(
        _('''All `IPOTemplate` objects that have the same source.
            For example those that came from the same productseries or the
            same source package.
            '''))

    displayname = TextLine(title=_('The translation template brief name.'),
                           required=True,
                           readonly=True)

    title = TextLine(title=_('The translation template title.'),
                     required=True,
                     readonly=True)

    product = Object(
        title=_('The `IProduct` to which this translation template belongs.'),
        required=False,
        readonly=True,
        # Really IProduct, see _schema_circular_imports.py.
        schema=Interface)

    distribution = Object(title=_(
        'The `IDistribution` to which this translation template '
        'belongs.'),
                          readonly=True,
                          schema=IDistribution)

    messagecount = exported(Int(
        title=_('The number of translation messages for this template.'),
        required=True,
        readonly=True),
                            exported_as='message_count')

    language_count = exported(
        Int(title=_('The number of languages for which we have translations.'),
            required=True,
            readonly=True))

    translationtarget = Attribute(
        _('''
            The direct object in which this template is attached.
            This will either be an `ISourcePackage` or an `IProductSeries`.
            '''))

    date_last_updated = exported(
        Datetime(title=_('Date for last update'), required=True))

    uses_english_msgids = Bool(title=_("Uses English strings as msgids"),
                               readonly=True,
                               description=_("""
            Some formats, such as Mozilla's XPI, use symbolic msgids where
            gettext uses the original English strings to identify messages.
            """))

    translation_side = Int(title=_("Translation side"),
                           required=True,
                           readonly=True)

    def __iter__():
        """Return an iterator over current `IPOTMsgSet` in this template."""

    def clearPOFileCache():
        """Clear `POFile`-related cached data.

        As you work with a `POTemplate`, some data about its `POFile`s
        gets cached.  But if you're iterating over the template's
        translations one `POFile` at a time, you can drop any cached
        data about a `POFile` as soon as you're done with it.  Use this
        method to do that.
        """

    def setActive(active):
        """Toggle the iscurrent flag.

        Takes care of updating the suggestive potempalte cache when the
        template is disabled.
        """

    def getHeader():
        """Return an `ITranslationHeaderData` representing its header."""

    def getPOTMsgSetByMsgIDText(singular_text,
                                plural_text=None,
                                only_current=False,
                                context=None):
        """Return `IPOTMsgSet` indexed by `singular_text` from this template.

        If the key is a string or a unicode object, returns the
        `IPOTMsgSet` in this template that has a primary message ID
        with the given text.

        If `only_current` is True, then get only current message sets.

        If `context` is not None, look for a message set with that context
        value.

        If `plural_text` is not None, also filter by that plural text.

        If no `IPOTMsgSet` is found, return None.
        """

    def getPOTMsgSetBySequence(sequence):
        """Return the `IPOTMsgSet` with the given sequence or None.

        :arg sequence: The sequence number when the `IPOTMsgSet` appears.

        The sequence number must be > 0.
        """

    def getPOTMsgSets(current=True, prefetch=True):
        """Return an iterator over `IPOTMsgSet` objects in this template.

        :param current: Whether to limit the search to current
            POTMsgSets.
        :param prefetch: Whether to prefetch the `POMsgID`s attached to
            the POTMsgSets.  This is for optimization only.
        :return: All current POTMsgSets for the template if `current` is
            True, or all POTMsgSets for the template otherwise.
        """

    def getTranslationCredits():
        """Return an iterator over translation credits.

        Return all `IPOTMsgSet` objects in this template that are translation
        credits.
        """

    def getPOTMsgSetsCount(current=True):
        """Return the number of POTMsgSet objects related to this object.

        The current argument is used to select only current POTMsgSets or all
        of them.
        """

    def __getitem__(key):
        """Same as getPOTMsgSetByMsgIDText(), with only_current=True
        """

    def sharingKey():
        """A key for determining the sharing precedence of a template.

        Active templates have precedence over inactive ones.
        Development foci have precendence over non-development foci.
        Product development foci have precedence over Package development
        foci.
        """

    def getPOTMsgSetByID(id):
        """Return the POTMsgSet object related to this POTemplate with the id.

        If there is no POTMsgSet with that id and for that POTemplate, return
        None.
        """

    def languages():
        """This Return the set of languages for which we have POFiles for
        this POTemplate.
        """

    def getPOFileByPath(path):
        """Get the PO file of the given path.

        Return None if there is no such `IPOFile`.
        """

    def getPOFileByLang(language_code):
        """Get the PO file of the given language.

        Return None if there is no such POFile.
        """

    def getOtherSidePOTemplate():
        """Get the POTemplate with the same name on the other side of a
        packaging link.
        """

    def hasPluralMessage():
        """Test whether this template has any message sets which are plural
        message sets."""

    def export():
        """Return a serialized version as a string using its native format."""

    def exportWithTranslations():
        """Return an ExportedTranslationFile using its native format.

        It include all translations available.
        """

    def expireAllMessages():
        """Mark all of our message sets as not current (sequence=0)"""

    def newPOFile(language_code, create_sharing=True):
        """Return a new `IPOFile` for the given language.

        Raise LanguageNotFound if the language does not exist in the
        database.

        We should not have already an `IPOFile` for the given language_code.

        :param language_code: The code of the language for which to create
            the IPOFile.
        :param requester: The requester person. If given and will have edit
            permissions on the IPOFile, it becomes the owner. Otherwise
            rosetta_experts own the file.
        :param create_sharing: Whether the IPOFile should be created in all
            sharing templates, too. Should only be set to False to avoid
            loops when creating a new IPOTemplate.
        """

    def getDummyPOFile(language, requester=None, check_for_existing=True):
        """Return a DummyPOFile if there isn't already a persistent `IPOFile`

        Raise `LanguageNotFound` if the language does not exist in the
        database.

        This method is designed to be used by read only actions. This way you
        only create a POFile when you actually need to store data.

        We should not have already a POFile for the given language:
        if check_for_existing is set to False, no check will be done for this.
        """

    def createPOTMsgSetFromMsgIDs(msgid_singular,
                                  msgid_plural=None,
                                  context=None,
                                  sequence=0):
        """Creates a new template message in the database.

        :param msgid_singular: A reference to a singular msgid.
        :param msgid_plural: A reference to a plural msgid.  Can be None
            if the message is not a plural message.
        :param context: A context for the template message differentiating
            it from other template messages with exactly the same `msgid`.
        :param sequence: The sequence number of this POTMsgSet within this
            POTemplate. If 0, it is considered obsolete.
        :return: The newly created message set.
        """

    def createMessageSetFromText(singular_text,
                                 plural_text,
                                 context=None,
                                 sequence=0):
        """Creates a new template message in the database using strings.

        Similar to createMessageSetFromMessageID, but takes text objects
        (unicode or string) along with textual context, rather than a
        message IDs.

        :param singular_text: The string for the singular msgid.
        :param msgid_plural: The string for the plural msgid.  Must be None
            if the message is not a plural message.
        :param context: A context for the template message differentiating
            it from other template messages with exactly the same `msgid`.
        :param sequence: The sequence number of this POTMsgSet within this
            POTemplate. If 0, it is considered obsolete.
        :return: The newly created message set.
        """

    def getOrCreateSharedPOTMsgSet(singular_text,
                                   plural_text,
                                   context=None,
                                   initial_file_references=None,
                                   initial_source_comment=None):
        """Finds an existing shared POTMsgSet to use or creates a new one.

        :param singular_text: string containing singular form.
        :param plural_text: string containing plural form.
        :param context: context to differentiate between two messages with
        same singular_text and plural_text.
        :param initial_file_references: Initializer for file_references if
            a new POTMsgSet needs to be created.  Will not be set on an
            existing POTMsgSet.
        :param initial_source_comment: Initializer for source_comment if
            a new POTMsgSet needs to be created.  Will not be set on an
            existing POTMsgSet.
        :return: existing or new shared POTMsgSet with a sequence of 0
        in this POTemplate.
        """

    def importFromQueue(entry_to_import, logger=None, txn=None):
        """Import given queue entry.

        :param entry_to_import: `TranslationImportQueueEntry` specifying an
            approved import for this `POTemplate`
        :param logger: optional logger to report problems to.
        :param txn: optional transaction manager for intermediate
            commits.  Used to prevent long-running transactions that can
            lead to deadlocks.

        :return: a tuple of the subject line and body for a notification email
            to be sent to the uploader.
        """

    def getTranslationRows():
        """Return the `IVPOTexport` objects for this template."""

    def awardKarma(person, action_name):
        """Award karma for a translation action on this template."""

    def getTranslationPolicy():
        """Return the applicable `ITranslationPolicy` object.
class IProductSeries(IProductSeriesEditRestricted, IProductSeriesPublic,
                     IProductSeriesView, IProductSeriesLimitedView,
                     IStructuralSubscriptionTarget, IBugTarget):
    """A series of releases. For example '2.0' or '1.3' or 'dev'."""
    export_as_webservice_entry('project_series')
Beispiel #22
0
class ILanguage(Interface):
    """A Language."""
    export_as_webservice_entry()

    id = Attribute("This Language ID.")

    code = exported(TextLine(title=u'The ISO 639 code', required=True))

    englishname = exported(TextLine(title=u'The English name', required=True),
                           exported_as='english_name')

    nativename = TextLine(
        title=u'Native name',
        description=u'The name of this language in the language itself.',
        required=False)

    pluralforms = exported(Int(
        title=u'Number of plural forms',
        description=u'The number of plural forms this language has.',
        required=False),
                           exported_as='plural_forms')

    guessed_pluralforms = Int(
        title=u"Number of plural forms, or a reasonable guess",
        required=False,
        readonly=True)

    pluralexpression = exported(TextLine(
        title=u'Plural form expression',
        description=(u'The expression that relates a number of items'
                     u' to the appropriate plural form.'),
        required=False),
                                exported_as='plural_expression')

    translators = doNotSnapshot(
        Field(title=u'List of Person/Team that translate into this language.',
              required=True))

    translators_count = exported(
        Int(title=u"Total number of translators for this language.",
            readonly=True))

    translation_teams = Field(
        title=u'List of Teams that translate into this language.',
        required=True)

    countries = Set(
        title=u'Spoken in',
        description=u'List of countries this language is spoken in.',
        required=True,
        value_type=Choice(vocabulary="CountryName"))

    def addCountry(country):
        """Add a country to a list of countries this language is spoken in.

        Provided by SQLObject.
        """

    def removeCountry(country):
        """Remove country from list of countries this language is spoken in.

        Provided by SQLObject.
        """

    visible = exported(
        Bool(title=u'Visible',
             description=(u'Whether this language is visible by default.'),
             required=True))

    direction = exported(Choice(
        title=u'Text direction',
        description=u'The direction of text in this language.',
        required=True,
        vocabulary=TextDirection),
                         exported_as='text_direction')

    displayname = TextLine(title=u'The displayname of the language',
                           required=True,
                           readonly=True)

    alt_suggestion_language = Attribute(
        "A language which can reasonably "
        "be expected to have good suggestions for translations in this "
        "language.")

    dashedcode = TextLine(
        title=(u'The language code in a form suitable for use in HTML and'
               u' XML files.'),
        required=True,
        readonly=True)

    abbreviated_text_dir = TextLine(
        title=(u'The abbreviated form of the text direction, suitable'
               u' for use in HTML files.'),
        required=True,
        readonly=True)

    @invariant
    def validatePluralData(form_language):
        pair = (form_language.pluralforms, form_language.pluralexpression)
        if None in pair and pair != (None, None):
            raise Invalid(
                'The number of plural forms and the plural form expression '
                'must be set together, or not at all.')
Beispiel #23
0
class ISharingService(IService):

    # XXX 2012-02-24 wallyworld bug 939910
    # Need to export for version 'beta' even though we only want to use it in
    # version 'devel'
    export_as_webservice_entry(publish_web_link=False, as_of='beta')

    def checkPillarAccess(pillars, information_type, person):
        """Check the person's access to the given pillars and information type.

        :return: True if the user has been granted SharingPermission.ALL on
            *any* of the specified pillars.
        """

    def getAccessPolicyGrantCounts(pillar):
        """Return the number of grantees who have policy grants of each type.

        Returns a resultset of (InformationType, count) tuples, where count is
        the number of grantees who have an access policy grant for the
        information type.
        """

    @export_read_operation()
    @call_with(user=REQUEST_USER)
    @operation_parameters(person=Reference(IPerson,
                                           title=_('Person'),
                                           required=True))
    @operation_returns_collection_of(IProduct)
    @operation_for_version('devel')
    def getSharedProjects(person, user):
        """Find projects for which person has one or more access policy grants.

        :param user: the user making the request. If the user is an admin, then
            all projects are returned, else only those for which the user is a
            maintainer or driver.
        :return: a collection of projects
        """

    @export_read_operation()
    @call_with(user=REQUEST_USER)
    @operation_parameters(person=Reference(IPerson,
                                           title=_('Person'),
                                           required=True))
    @operation_returns_collection_of(IDistribution)
    @operation_for_version('devel')
    def getSharedDistributions(person, user):
        """Find distributions for which person has one or more access policy
           grants.

        :param user: the user making the request. If the user is an admin, then
            all distributions are returned, else only those for which the user
            is a maintainer or driver.
        :return: a collection of distributions
        """

    @export_read_operation()
    @call_with(user=REQUEST_USER)
    @operation_parameters(pillar=Reference(IPillar,
                                           title=_('Pillar'),
                                           required=True),
                          person=Reference(IPerson,
                                           title=_('Person'),
                                           required=True))
    @operation_for_version('devel')
    def getSharedArtifacts(pillar, person, user):
        """Return the artifacts shared between the pillar and person.

        The result includes bugtasks rather than bugs since this is what the
        pillar filtering is applied to and is what the calling code uses.
        The shared bug can be obtained simply by reading the bugtask.bug
        attribute.

        :param user: the user making the request. Only artifacts visible to the
             user will be included in the result.
        :return: a (bugtasks, branches, specifications) tuple
        """

    def checkPillarArtifactAccess(pillar, user):
        """Return True if user has any grants on pillar else return False."""

    @export_read_operation()
    @call_with(user=REQUEST_USER)
    @operation_parameters(pillar=Reference(IPillar,
                                           title=_('Pillar'),
                                           required=True),
                          person=Reference(IPerson,
                                           title=_('Person'),
                                           required=True))
    @operation_returns_collection_of(IBug)
    @operation_for_version('devel')
    def getSharedBugs(pillar, person, user):
        """Return the bugs shared between the pillar and person.

        The result includes bugtasks rather than bugs since this is what the
        pillar filtering is applied to. The shared bug can be obtained simply
        by reading the bugtask.bug attribute.

        :param user: the user making the request. Only bugs visible to the
             user will be included in the result.
        :return: a collection of bug tasks.
        """

    @export_read_operation()
    @call_with(user=REQUEST_USER)
    @operation_parameters(pillar=Reference(IPillar,
                                           title=_('Pillar'),
                                           required=True),
                          person=Reference(IPerson,
                                           title=_('Person'),
                                           required=True))
    @operation_returns_collection_of(IBranch)
    @operation_for_version('devel')
    def getSharedBranches(pillar, person, user):
        """Return the branches shared between the pillar and person.

        :param user: the user making the request. Only branches visible to the
             user will be included in the result.
        :return: a collection of branches
        """

    @export_read_operation()
    @call_with(user=REQUEST_USER)
    @operation_parameters(pillar=Reference(IPillar,
                                           title=_('Pillar'),
                                           required=True),
                          person=Reference(IPerson,
                                           title=_('Person'),
                                           required=True))
    @operation_returns_collection_of(ISpecification)
    @operation_for_version('devel')
    def getSharedSpecifications(pillar, person, user):
        """Return the specifications shared between the pillar and person.

        :param user: the user making the request. Only branches visible to the
             user will be included in the result.
        :return: a collection of specifications.
        """

    def getVisibleArtifacts(person, branches=None, bugs=None):
        """Return the artifacts shared with person.

        Given lists of artifacts, return those a person has access to either
        via a policy grant or artifact grant.

        :param person: the person whose access is being checked.
        :param branches: the branches to check for which a person has access.
        :param bugs: the bugs to check for which a person has access.
        :return: a collection of artifacts the person can see.
        """

    def getInvisibleArtifacts(person, branches=None, bugs=None):
        """Return the artifacts which are not shared with person.

        Given lists of artifacts, return those a person does not have access to
        either via a policy grant or artifact grant.
        * Do not export this method to the API since it could be used to gain
          access to private information. Internal use only. *

        :param person: the person whose access is being checked.
        :param branches: the branches to check for which a person has access.
        :param bugs: the bugs to check for which a person has access.
        :return: a collection of artifacts the person can not see.
        """

    def getPeopleWithoutAccess(concrete_artifact, people):
        """Return the people who cannot access an artifact.

        Given a list of people, return those who do not have access to the
        specified bug or branch.

        :param concrete_artifact: the bug or branch whose access is being
            checked.
        :param people: the people whose access is being checked.
        :return: a collection of people without access to the artifact.
        """

    def getAllowedInformationTypes(pillar):
        """Return the allowed private information types for the given pillar.

        The allowed information types are those for which bugs and branches
        may be created. This does not mean that there will necessarily be bugs
        and branches of these types; the result is used to populate the allowed
        choices in the grantee sharing pillar and other similar things.

        The allowed information types are determined by the pillar's bug and
        branch sharing policies. It is possible that there are bugs or branches
        of a given information type which is now nominally not allowed with a
        change in policy. Such information types are also included in the
        result.
        """

    def getBugSharingPolicies(pillar):
        """Return the allowed bug sharing policies for the given pillar."""

    def getBranchSharingPolicies(pillar):
        """Return the allowed branch sharing policies for the given pillar."""

    def getSpecificationSharingPolicies(pillar):
        """Return specification sharing policies for a given pillar."""

    def getSharingPermissions():
        """Return the information sharing permissions."""

    def getPillarGrantees(pillar):
        """Return people/teams who can see pillar artifacts."""

    @export_read_operation()
    @operation_parameters(pillar=Reference(IPillar,
                                           title=_('Pillar'),
                                           required=True))
    @operation_for_version('devel')
    def getPillarGranteeData(pillar):
        """Return people/teams who can see pillar artifacts.

        The result records are json data which includes:
            - person name
            - permissions they have for each information type.
        """

    def jsonGranteeData(grant_permissions):
        """Return people/teams who can see pillar artifacts.

        :param grant_permissions: a list of (grantee, accesspolicy, permission)
            tuples.

        The result records are json data which includes:
            - person name
            - permissions they have for each information type.
        """

    @export_write_operation()
    @call_with(user=REQUEST_USER)
    @operation_parameters(pillar=Reference(IPillar,
                                           title=_('Pillar'),
                                           required=True),
                          grantee=Reference(IPerson,
                                            title=_('Grantee'),
                                            required=True),
                          permissions=Dict(
                              key_type=Choice(vocabulary=InformationType),
                              value_type=Choice(vocabulary=SharingPermission)))
    @operation_for_version('devel')
    def sharePillarInformation(pillar, grantee, user, permissions):
        """Ensure grantee has the grants for information types on a pillar.

        :param pillar: the pillar for which to grant access
        :param grantee: the person or team to grant
        :param user: the user making the request
        :param permissions: a dict of {InformationType: SharingPermission}
            if SharingPermission is ALL, then create an access policy grant
            if SharingPermission is SOME, then remove any access policy grants
            if SharingPermission is NONE, then remove all grants for the access
            policy
        """

    @export_write_operation()
    @call_with(user=REQUEST_USER)
    @operation_parameters(pillar=Reference(IPillar,
                                           title=_('Pillar'),
                                           required=True),
                          grantee=Reference(IPerson,
                                            title=_('Grantee'),
                                            required=True),
                          information_types=List(
                              Choice(vocabulary=InformationType),
                              required=False))
    @operation_for_version('devel')
    def deletePillarGrantee(pillar, grantee, user, information_types):
        """Remove a grantee from a pillar.

        :param pillar: the pillar from which to remove access
        :param grantee: the person or team to remove
        :param user: the user making the request
        :param information_types: if None, remove all access, otherwise just
                                   remove the specified access_policies
        """

    @export_write_operation()
    @call_with(user=REQUEST_USER)
    @operation_parameters(pillar=Reference(IPillar,
                                           title=_('Pillar'),
                                           required=True),
                          grantee=Reference(IPerson,
                                            title=_('Grantee'),
                                            required=True),
                          bugs=List(Reference(schema=IBug),
                                    title=_('Bugs'),
                                    required=False),
                          branches=List(Reference(schema=IBranch),
                                        title=_('Branches'),
                                        required=False),
                          specifications=List(Reference(schema=ISpecification),
                                              title=_('Specifications'),
                                              required=False))
    @operation_for_version('devel')
    def revokeAccessGrants(pillar,
                           grantee,
                           user,
                           branches=None,
                           bugs=None,
                           specifications=None):
        """Remove a grantee's access to the specified artifacts.

        :param pillar: the pillar from which to remove access
        :param grantee: the person or team for whom to revoke access
        :param user: the user making the request
        :param bugs: the bugs for which to revoke access
        :param branches: the branches for which to revoke access
        :param specifications: the specifications for which to revoke access
        """

    @export_write_operation()
    @call_with(user=REQUEST_USER)
    @operation_parameters(grantees=List(
        Reference(IPerson, title=_('Grantee'), required=True)),
                          bugs=List(Reference(schema=IBug),
                                    title=_('Bugs'),
                                    required=False),
                          branches=List(Reference(schema=IBranch),
                                        title=_('Branches'),
                                        required=False))
    @operation_for_version('devel')
    def ensureAccessGrants(grantees,
                           user,
                           branches=None,
                           bugs=None,
                           specifications=None):
        """Ensure a grantee has an access grant to the specified artifacts.

        :param grantees: the people or teams for whom to grant access
        :param user: the user making the request
        :param bugs: the bugs for which to grant access
        :param branches: the branches for which to grant access
        :param specifications: the specifications for which to grant access
        """

    @export_write_operation()
    @operation_parameters(
        pillar=Reference(IPillar, title=_('Pillar'), required=True),
        branch_sharing_policy=Choice(vocabulary=BranchSharingPolicy),
        bug_sharing_policy=Choice(vocabulary=BugSharingPolicy),
        specification_sharing_policy=Choice(
            vocabulary=SpecificationSharingPolicy))
    @operation_for_version('devel')
    def updatePillarSharingPolicies(pillar,
                                    branch_sharing_policy=None,
                                    bug_sharing_policy=None,
                                    specification_sharing_policy=None):
        """Update the sharing policies for a pillar.
Beispiel #24
0
class IQuestion(IHasOwner):
    """A single question, often a support request."""

    export_as_webservice_entry(as_of='beta')

    id = exported(Int(title=_('Question Number'),
                      required=True,
                      readonly=True,
                      description=_("The tracking number for this question.")),
                  as_of="devel")
    title = exported(TextLine(
        title=_('Summary'),
        required=True,
        description=_("A one-line summary of the issue or problem.")),
                     as_of="devel")
    description = exported(Text(
        title=_('Description'),
        required=True,
        description=_(
            "Include as much detail as possible: what "
            u"you\N{right single quotation mark}re trying to achieve, what steps "
            "you take, what happens, and what you think should happen instead."
        )),
                           as_of="devel")
    status = exported(Choice(title=_('Status'),
                             vocabulary=QuestionStatus,
                             default=QuestionStatus.OPEN,
                             readonly=True),
                      as_of="devel")
    priority = Choice(title=_('Priority'),
                      vocabulary=QuestionPriority,
                      default=QuestionPriority.NORMAL)
    # XXX flacoste 2006-10-28: It should be more precise to define a new
    # vocabulary that excludes the English variants.
    language = exported(ReferenceChoice(
        title=_('Language'),
        vocabulary='Language',
        schema=ILanguage,
        description=_('The language in which this question is written.')),
                        as_of="devel")
    owner = exported(PublicPersonChoice(title=_('Owner'),
                                        required=True,
                                        readonly=True,
                                        vocabulary='ValidPersonOrTeam'),
                     as_of="devel")
    assignee = exported(PublicPersonChoice(
        title=_('Assignee'),
        required=False,
        description=_("The person responsible for helping to resolve the "
                      "question."),
        vocabulary='ValidPersonOrTeam'),
                        as_of="devel")
    answerer = exported(PublicPersonChoice(
        title=_('Answered By'),
        required=False,
        description=_("The person who last provided a response intended to "
                      "resolve the question."),
        vocabulary='ValidPersonOrTeam'),
                        as_of="devel",
                        readonly=True)
    answer = exported(Reference(
        title=_('Answer'),
        required=False,
        description=_(
            "The IQuestionMessage that contains the answer "
            "confirmed by the owner as providing a solution to their "
            "problem."),
        schema=IQuestionMessage),
                      readonly=True,
                      as_of="devel")
    datecreated = exported(Datetime(title=_('Date Created'),
                                    required=True,
                                    readonly=True),
                           exported_as='date_created',
                           readonly=True,
                           as_of="devel")
    datedue = exported(Datetime(
        title=_('Date Due'),
        required=False,
        default=None,
        description=_(
            "The date by which we should have resolved this question.")),
                       exported_as='date_due',
                       readonly=True,
                       as_of="devel")
    datelastquery = exported(Datetime(
        title=_("Date Last Queried"),
        required=True,
        description=_("The date on which we last heard from the "
                      "customer (owner).")),
                             exported_as='date_last_query',
                             readonly=True,
                             as_of="devel")
    datelastresponse = exported(Datetime(
        title=_("Date last Responded"),
        required=False,
        description=_(
            "The date on which we last communicated "
            "with the customer. The combination of datelastquery and "
            "datelastresponse tells us in whose court the ball is.")),
                                exported_as='date_last_response',
                                readonly=True,
                                as_of="devel")
    date_solved = exported(Datetime(
        title=_("Date Answered"),
        required=False,
        description=_(
            "The date on which the question owner confirmed that the "
            "question is Solved.")),
                           exported_as='date_solved',
                           readonly=True,
                           as_of="devel")
    product = Choice(title=_('Upstream Project'),
                     required=False,
                     vocabulary='Product',
                     description=_(
                         'Select the upstream project with which you need '
                         'support.'))
    distribution = Choice(title=_('Distribution'),
                          required=False,
                          vocabulary='Distribution',
                          description=_(
                              'Select '
                              'the distribution for which you need support.'))
    sourcepackagename = Choice(
        title=_('Source Package'),
        required=False,
        vocabulary='SourcePackageName',
        description=_(
            'The source package '
            'in the distribution which contains the software with which you '
            'are experiencing difficulties.'))
    whiteboard = Text(
        title=_('Status Whiteboard'),
        required=False,
        description=_('Up-to-date notes on the status of the question.'))
    # other attributes
    target = exported(Reference(
        title=_('This question is about'),
        required=True,
        schema=IQuestionTarget,
        description=_('The distribution, source package, or project the '
                      'question pertains to.')),
                      as_of="devel")
    faq = Object(title=_('Linked FAQ'),
                 description=_(
                     'The FAQ document containing the long answer to this '
                     'question.'),
                 readonly=True,
                 required=False,
                 schema=IFAQ)

    # joins
    subscriptions = Attribute('The set of subscriptions to this question.')
    reopenings = Attribute("Records of times when this question was reopened.")
    messages = exported(CollectionField(
        title=_("Messages"),
        description=_(
            "The list of messages that were exchanged as part of this "
            "question , sorted from first to last."),
        value_type=Reference(schema=IQuestionMessage),
        required=True,
        default=[],
        readonly=True),
                        as_of='devel')

    # Workflow methods
    def setStatus(user, new_status, comment, datecreated=None):
        """Change the status of this question.

        Set the question's status to new_status and add an IQuestionMessage
        with action SETSTATUS.

        Only the question target owner or admin can change the status using
        this method.

        An InvalidQuestiontateError is raised when this method is called
        with new_status equals to the current question status.

        Return the created IQuestionMessage.

        This method should fire an IObjectCreatedEvent for the created
        IQuestionMessage and an IObjectModifiedEvent for the question.

        :user: The IPerson making the change.
        :new_status: The new QuestionStatus
        :comment: A string or IMessage containing an explanation for the
                  change.
        :datecreated: Date for the message. Defaults to the current time.
        """

    can_request_info = Attribute(
        'Whether the question is in a state where a user can request more '
        'information from the question owner.')

    def requestInfo(user, question, datecreated=None):
        """Request more information from the question owner.

        Add an IQuestionMessage with action REQUESTINFO containing the
        question. The question's status is changed to NEEDSINFO, and the
        datelastresponse attribute is updated to the message creation date.

        The user requesting more information cannot be the question's owner.
        This workflow method should only be called when the question status is
        OPEN or NEEDSINFO. An InvalidQuestionStateError is raised otherwise.

        It can also be called when the question is in the ANSWERED state, but
        in that case, the status will stay unchanged.

        Return the created IQuestionMessage.

        This method should fire an IObjectCreatedEvent for the created
        IQuestionMessage and an IObjectModifiedEvent for the question.

        :user: IPerson requesting for the information.
        :question: A string or IMessage containing the question.
        :datecreated: Date for the answer. Defaults to the current time.
        """

    can_give_info = Attribute(
        'Whether the question is in a state where the question owner can '
        'give more information on the question.')

    def giveInfo(reply, datecreated=None):
        """Reply to the information request.

        Add an IQuestionMessage with action GIVEINFO. The question status is
        changed to OPEN, the datelastquery attribute is updated to the
        message creation time.

        This method should only be called on behalf of the question owner when
        the question is in the OPEN or NEEDSINFO state. An
        InvalidQuestionStateError is raised otherwise.

        Return the created IQuestionMessage.

        This method should fire an IObjectCreatedEvent for the created
        IQuestionMessage and an IObjectModifiedEvent for the question.

        :reply: A string or IMessage containing the new information.
        :datecreated: Date for the message. Defaults to the current time.
        """

    can_give_answer = Attribute(
        'Whether the question is in a state a user can provide an answer on '
        'the question.')

    def giveAnswer(user, answer, datecreated=None):
        """Give an answer to this question.

        If the user is not the question's owner, add an IQuestionMessage with
        action ANSWER containing an answer for the question. This changes the
        question's status to ANSWERED and updates the datelastresponse
        attribute to the message's creation date.

        When the question owner answers the question, add an IQuestionMessage
        with action CONFIRM. The question status is changed to SOLVED, the
        answerer attribute is updated to contain the question owner, the
        answer attribute will be updated to point at the new message, the
        datelastresponse and date_solved attributes are updated to the
        message creation date.

        This workflow method should only be called when the question status is
        one of OPEN, ANSWERED or NEEDSINFO. An InvalidQuestionStateError is
        raised otherwise.

        Return the created IQuestionMessage.

        This method should fire an IObjectCreatedEvent for the created
        IQuestionMessage and an IObjectModifiedEvent for the question.

        :user: IPerson giving the answer.
        :answer: A string or IMessage containing the answer.
        :datecreated: Date for the message. Defaults to the current time.
        """

    def linkFAQ(user, faq, comment, datecreated=None):
        """Link a FAQ as an answer to this question.

        Exactly like giveAnswer() but also link the IFAQ faq object to this
        question.

        Return the created IQuestionMessage.

        This method should fire an IObjectCreatedEvent for the created
        IQuestionMessage and an IObjectModifiedEvent for the question.

        :param user: IPerson linking the faq.
        :param faq: The IFAQ containing the answer.
        :param comment: A string or IMessage explaining how the FAQ is
            relevant.
        :param datecreated: Date for the message. Defaults to the current
            time.
        """

    can_confirm_answer = Attribute(
        'Whether the question is in a state for the question owner to '
        'confirm that an answer solved their problem.')

    def confirmAnswer(comment, answer=None, datecreated=None):
        """Confirm that a solution to the question was found.

        Add an IQuestionMessage with action CONFIRM. The question status is
        changed to SOLVED. If the answer parameter is not None, it is recorded
        in the answer attribute and the answerer attribute is set to that
        message's owner. The datelastresponse and date_solved attributes are
        updated to the message creation date.

        This workflow method should only be called on behalf of the question
        owner, when the question status is ANSWERED, or when the status is
        OPEN or NEEDSINFO but an answer was already provided. An
        InvalidQuestionStateError is raised otherwise.

        Return the created IQuestionMessage.

        This method should fire an IObjectCreatedEvent for the created
        IQuestionMessage and an IObjectModifiedEvent for the question.

       :comment: A string or IMessage containing a comment.
        :answer: The IQuestionMessage that contain the answer to the question.
                 It must be one of the IQuestionMessage of this question.
        :datecreated: Date for the message. Defaults to the current time.
        """

    def canReject(user):
        """Test if a user can reject the question.

        Return true only if user is an answer contact for the question target,
        the question target owner or part of the administration team.
        """

    @operation_parameters(
        comment=Text(title=_("An explanation of the rejection")))
    @call_with(user=REQUEST_USER)
    @export_factory_operation(IQuestionMessage, [])
    @operation_for_version("devel")
    def reject(user, comment, datecreated=None):
        """Mark this question as INVALID.

        Add an IQuestionMessage with action REJECT. The question status is
        changed to INVALID. The created message is set as the question answer
        and its owner as the question answerer. The datelastresponse and
        date_solved are updated to the message creation.

        Only answer contacts for the question target, the target owner or a
        member of the admin team can reject a request. All questions can be
        rejected.

        Return the created IQuestionMessage.

        This method should fire an IObjectCreatedEvent for the created
        IQuestionMessage and an IObjectModifiedEvent for the question.

        :user: The user rejecting the request.
        :comment: A string or IMessage containing an explanation of the
                  rejection.
        :datecreated: Date for the message. Defaults to the current time.
        """

    def expireQuestion(user, comment, datecreated=None):
        """Mark a question as EXPIRED.

        Add an IQuestionMessage with action EXPIRE. This changes the question
        status to EXPIRED and update the datelastresponse attribute to the new
        message creation date.

        This workflow method should only be called when the question status is
        one of OPEN or NEEDSINFO. An InvalidQuestionStateError is raised
        otherwise.

        Return the created IQuestionMessage.

        (Note this method is named expireQuestion and not expire because of
        conflicts with SQLObject.)

        This method should fire an IObjectCreatedEvent for the created
        IQuestionMessage and an IObjectModifiedEvent for the question.

        :user: IPerson expiring the request.
        :comment: A string or IMessage containing an explanation for the
                  expiration.
        :datecreated: Date for the message. Defaults to the current time.
        """

    can_reopen = Attribute(
        'Whether the question state is a state where the question owner '
        'could reopen it.')

    def reopen(comment, datecreated=None):
        """Reopen a question that was ANSWERED, EXPIRED or SOLVED.

        Add an IQuestionMessage with action REOPEN. This changes the question
        status to OPEN and update the datelastquery attribute to the new
        message creation date. When the question was in the SOLVED state, this
        method should reset the date_solved, answerer and answer attributes.

        This workflow method should only be called on behalf of the question
        owner, when the question status is in one of ANSWERED, EXPIRED or
        SOLVED. An InvalidQuestionStateError is raised otherwise.

        Return the created IQuestionMessage.

        This method should fire an IObjectCreatedEvent for the created
        IQuestionMessage and an IObjectModifiedEvent for the question.

        :comment: A string or IMessage containing more information about the
                  request.
        :datecreated: Date for the message. Defaults to the current time.
        """

    def addComment(user, comment, datecreated=None):
        """Add a comment on the question.

        Create an IQuestionMessage with action COMMENT. It leaves the question
        status unchanged.

        This method should fire an IObjectCreatedEvent for the created
        IQuestionMessage and an IObjectModifiedEvent for the question.

        :user: The IPerson making the comment.
        :comment: A string or IMessage containing the comment.
        :datecreated: Date for the message. Defaults to the current time.
        """

    # subscription-related methods

    @operation_parameters(person=Reference(IPerson,
                                           title=_('Person'),
                                           required=True))
    @call_with(subscribed_by=REQUEST_USER)
    @export_write_operation()
    @operation_for_version("devel")
    def subscribe(person, subscribed_by=None):
        """Subscribe `person` to the question.

        :param person: the subscriber.
        :param subscribed_by: the person who created the subscription.
        :return: an `IQuestionSubscription`.
        """

    def isSubscribed(person):
        """Return a boolean indicating whether the person is subscribed."""

    @operation_parameters(person=Reference(IPerson,
                                           title=_('Person'),
                                           required=False))
    @call_with(unsubscribed_by=REQUEST_USER)
    @export_write_operation()
    @operation_for_version("devel")
    def unsubscribe(person, unsubscribed_by):
        """Unsubscribe `person` from the question.

        :param person: the subscriber.
        :param unsubscribed_by: the person who removed the subscription.
        """

    def getDirectSubscribers():
        """Return the persons who are subscribed to this question.

        :return: A list of persons sorted by displayname.
        """

    def getDirectSubscribersWithDetails():
        """Get direct subscribers and their subscriptions for the question.

        :returns: A ResultSet of tuples (Person, QuestionSubscription)
            representing a subscriber and their question subscription.
        """

    def getIndirectSubscribers():
        """Return the persons who are implicitly subscribed to this question.

        :return: A list of persons sorted by displayname.
        """

    def getRecipients():
        """Return the set of person to notify about changes in this question.

        That is the union of getDirectSubscribers() and
        getIndirectSubscribers().

        :return: An `INotificationRecipientSet` containing the persons to
            notify along the rationale for doing so.
        """

    direct_recipients = Attribute(
        "Return An `INotificationRecipientSet` containing the persons to "
        "notify along the rationale for doing so.")

    indirect_recipients = Attribute(
        "Return the INotificationRecipientSet of answer contacts for the "
        "question's target as well as the question's assignee.")

    @operation_parameters(comment_number=Int(
        title=_('The number of the comment in the list of messages.'),
        required=True),
                          visible=Bool(title=_('Show this comment?'),
                                       required=True))
    @call_with(user=REQUEST_USER)
    @export_write_operation()
    @operation_for_version('devel')
    def setCommentVisibility(user, comment_number, visible):
        """Set the visible attribute on a question message.
Beispiel #25
0
class ISourcePackagePublishingHistory(ISourcePackagePublishingHistoryPublic,
                                      ISourcePackagePublishingHistoryEdit):
    """A source package publishing history record."""
    export_as_webservice_entry(publish_web_link=False)
Beispiel #26
0
class IBranchSubscription(Interface):
    """The relationship between a person and a branch."""
    export_as_webservice_entry()

    id = Int(title=_('ID'), readonly=True, required=True)
    personID = Int(title=_('Person ID'), required=True, readonly=True)
    person = exported(
        PersonChoice(
            title=_('Person'),
            required=True,
            vocabulary='ValidPersonOrTeam',
            readonly=True,
            description=_(
                'Enter the launchpad id, or email '
                'address of the person you wish to subscribe to this branch. '
                'If you are unsure, use the "Choose..." option to find the '
                'person in Launchpad. You can only subscribe someone who is '
                'a registered user of the system.')))
    branch = exported(
        Reference(title=_('Branch ID'),
                  required=True,
                  readonly=True,
                  schema=IBranch))
    notification_level = exported(
        Choice(
            title=_('Notification Level'),
            required=True,
            vocabulary=BranchSubscriptionNotificationLevel,
            default=BranchSubscriptionNotificationLevel.ATTRIBUTEONLY,
            description=_(
                'Attribute notifications are sent when branch details are '
                'changed such as lifecycle status and name.  Revision '
                'notifications are generated when new branch revisions are '
                'found due to the branch being updated through either pushes '
                'to the hosted branches or the mirrored branches being '
                'updated.')))
    max_diff_lines = exported(
        Choice(
            title=_('Generated Diff Size Limit'),
            required=True,
            vocabulary=BranchSubscriptionDiffSize,
            default=BranchSubscriptionDiffSize.ONEKLINES,
            description=_(
                'Diffs greater than the specified number of lines will not '
                'be sent to the subscriber.  The subscriber will still '
                'receive an email with the new revision details even if the '
                'diff is larger than the specified number of lines.')))
    review_level = exported(
        Choice(title=_('Code review Level'),
               required=True,
               vocabulary=CodeReviewNotificationLevel,
               default=CodeReviewNotificationLevel.FULL,
               description=_(
                   'Control the kind of review activity that triggers '
                   'notifications.')))

    subscribed_by = exported(
        PersonChoice(
            title=_('Subscribed by'),
            required=True,
            vocabulary='ValidPersonOrTeam',
            readonly=True,
            description=_("The person who created this subscription.")))

    @call_with(user=REQUEST_USER)
    @export_read_operation()
    def canBeUnsubscribedByUser(user):
        """Can the user unsubscribe the subscriber from the branch?"""
Beispiel #27
0
class IPackageUpload(Interface):
    """A Queue item for the archive uploader."""

    export_as_webservice_entry(publish_web_link=False)

    id = exported(
        Int(
            title=_("ID"), required=True, readonly=True,
            ))

    status = exported(
        Choice(
            vocabulary=PackageUploadStatus,
            description=_("The status of this upload."),
            title=_("Queue status"), required=False, readonly=True,
            ))

    distroseries = exported(
        Reference(
            # Really IDistroSeries, patched in
            # _schema_circular_imports.py
            schema=Interface,
            description=_("The distroseries targeted by this upload."),
            title=_("Series"), required=True, readonly=False,
            ))

    pocket = exported(
        Choice(
            # Really PackagePublishingPocket, patched in
            # _schema_circular_imports.py
            vocabulary=DBEnumeratedType,
            description=_("The pocket targeted by this upload."),
            title=_("The pocket"), required=True, readonly=False,
            ))

    date_created = exported(
        Datetime(
            title=_('Date created'),
            description=_("The date this package upload was done.")))

    changesfile = Attribute("The librarian alias for the changes file "
                            "associated with this upload")
    changes_file_url = exported(
        TextLine(
            title=_("Changes file URL"),
            description=_("Librarian URL for the changes file associated with "
                          "this upload. Will be None if the upload was copied "
                          "from another series."),
            required=False, readonly=True),
        as_of="devel")

    signing_key = Attribute("Changesfile Signing Key.")

    package_copy_job = Reference(
        schema=IPackageCopyJob,
        description=_("The PackageCopyJob for this upload, if it has one."),
        title=_("Raw Package Copy Job"), required=False, readonly=True)

    concrete_package_copy_job = Reference(
        schema=IPackageCopyJob,
        description=_("Concrete IPackageCopyJob implementation, if any."),
        title=_("Package Copy Job"), required=False, readonly=True)

    archive = exported(
        Reference(
            # Really IArchive, patched in _schema_circular_imports.py
            schema=Interface,
            description=_("The archive for this upload."),
            title=_("Archive"), required=True, readonly=True))
    sources = Attribute("The queue sources associated with this queue item")
    builds = Attribute("The queue builds associated with the queue item")

    customfiles = Attribute("Custom upload files associated with this "
                            "queue item")
    custom_file_urls = exported(
        List(
            title=_("Custom file URLs"),
            description=_("Librarian URLs for all the custom files attached "
                          "to this upload."),
            value_type=TextLine(),
            required=False,
            readonly=True),
        ("devel", dict(exported=False)), exported=True)

    copy_source_archive = exported(
        Reference(
            # Really IArchive, patched in _schema_circular_imports.py
            schema=Interface,
            description=_("The archive from which this package was copied, if "
                          "any."),
            title=_("Copy source archive"), required=False, readonly=True))

    displayname = exported(
        TextLine(
            title=_("Generic displayname for a queue item"), readonly=True),
        exported_as="display_name")
    displayversion = exported(
        TextLine(
            title=_("This item's displayable source package version"),
            readonly=True),
        exported_as="display_version")
    displayarchs = exported(
        TextLine(
            title=_("Architectures related to this item"), readonly=True),
        exported_as="display_arches")

    sourcepackagerelease = Attribute(
        "The source package release for this item")

    searchable_names = TextLine(
        title=_("Searchable names for this item"), readonly=True)
    searchable_versions = List(
        title=_("Searchable versions for this item"), readonly=True)

    package_name = exported(
        TextLine(
            title=_("Name of the uploaded source package"), readonly=True),
        as_of="devel")

    package_version = exported(
        TextLine(title=_("Source package version"), readonly=True),
        as_of="devel")

    component_name = exported(
        TextLine(title=_("Source package component name"), readonly=True),
        as_of="devel")

    section_name = exported(
        TextLine(title=_("Source package section name"), readonly=True),
        as_of="devel")

    contains_source = exported(
        Bool(
            title=_("Whether or not this upload contains sources"),
            readonly=True),
        as_of="devel")
    contains_build = exported(
        Bool(
            title=_("Whether or not this upload contains binaries"),
            readonly=True),
        as_of="devel")
    contains_copy = exported(
        Bool(
            title=_("Whether or not this upload contains a copy from another "
                    "series."),
            readonly=True),
        as_of="devel")
    contains_installer = Attribute(
        "whether or not this upload contains installers images")
    contains_translation = Attribute(
        "whether or not this upload contains translations")
    contains_upgrader = Attribute(
        "whether or not this upload contains upgrader images")
    contains_ddtp = Attribute(
        "whether or not this upload contains DDTP images")
    contains_uefi = Attribute(
        "whether or not this upload contains a signed UEFI boot loader image")
    isPPA = Attribute(
        "Return True if this PackageUpload is a PPA upload.")

    components = Attribute(
        """The set of components used in this upload.

        For sources, this is the component on the associated
        sourcepackagerelease.  For binaries, this is all the components
        on all the binarypackagerelease records arising from the build.
        """)

    @export_read_operation()
    @operation_for_version("devel")
    def sourceFileUrls():
        """URLs for all the source files attached to this upload.

        :return: A collection of URLs for this upload.
        """

    @export_read_operation()
    @operation_for_version("devel")
    def binaryFileUrls():
        """URLs for all the binary files attached to this upload.

        :return: A collection of URLs for this upload.
        """

    @export_read_operation()
    @operation_for_version("devel")
    def customFileUrls():
        """URLs for all the custom files attached to this upload.

        :return: A collection of URLs for this upload.
        """

    @export_read_operation()
    @operation_for_version("devel")
    def getBinaryProperties():
        """The properties of the binaries associated with this queue item.

        :return: A list of dictionaries, each containing the properties of a
            single binary.
        """

    def getFileByName(filename):
        """Return the corresponding `ILibraryFileAlias` in this context.

        The following file types (and extension) can be looked up in the
        PackageUpload context:

         * Changes files: '.changes';
         * Source files: '.orig.tar.gz', 'tar.gz', '.diff.gz' and '.dsc'.
         * Custom files: '.tar.gz'.

        :param filename: the exact filename to be looked up.

        :raises NotFoundError if no file could be found.

        :return the corresponding `ILibraryFileAlias` if the file was found.
        """

    def setUnapproved():
        """Set queue state to UNAPPROVED."""

    def setAccepted():
        """Set queue state to ACCEPTED.

        Perform the required checks on its content, so we guarantee data
        integrity by code.
        """

    def setDone():
        """Set queue state to DONE."""

    def setRejected():
        """Set queue state to REJECTED."""

    def acceptFromUploader(changesfile_path, logger=None):
        """Perform upload acceptance during upload-time.

         * Move the upload to accepted queue in all cases.
         * Publish and close bugs for 'single-source' uploads.
         * Skip bug-closing for PPA uploads.
         * Grant karma to people involved with the upload.
        """

    @export_write_operation()
    @call_with(user=REQUEST_USER)
    @operation_for_version("devel")
    def acceptFromQueue(logger=None, dry_run=False, user=None):
        """Call setAccepted, do a syncUpdate, and send notification email.

         * Grant karma to people involved with the upload.
        """

    @export_write_operation()
    @operation_parameters(
        comment=TextLine(title=_("Rejection comment"), required=False))
    @call_with(user=REQUEST_USER)
    @operation_for_version("devel")
    def rejectFromQueue(user, logger=None, dry_run=False, comment=None):
        """Call setRejected, do a syncUpdate, and send notification email."""

    def realiseUpload(logger=None):
        """Take this ACCEPTED upload and create the publishing records for it
        as appropriate.

        When derivation is taken into account, this may result in queue items
        being created for derived distributions.

        If a logger is provided, messages will be written to it as the upload
        is entered into the publishing records.

        Return a list containing the publishing records created.
        """

    def addSource(spr):
        """Add the provided source package release to this queue entry."""

    def addBuild(build):
        """Add the provided build to this queue entry."""

    def addCustom(library_file, custom_type):
        """Add the provided library file alias as a custom queue entry of
        the given custom type.
        """

    def syncUpdate():
        """Write updates made on this object to the database.

        This should be used when you can't wait until the transaction is
        committed to have some updates actually written to the database.
        """

    def notify(summary_text=None, changes_file_object=None, logger=None):
        """Notify by email when there is a new distroseriesqueue entry.

        This will send new, accept, announce and rejection messages as
        appropriate.

        :param summary_text: Any additional text to append to the auto-
            generated summary.  This is also the only text used if there is
            a rejection message generated.

        :param changes_file_object: An open file object pointing at the
            changes file.  Current, only nascentupload need supply this
            as the transaction is not committed to the DB at that point so
            data needs to be obtained from the changes file.

        :param logger: Specify a logger object if required.  Mainly for tests.
        """

    @operation_parameters(
        new_component=TextLine(title=u"The new component name."),
        new_section=TextLine(title=u"The new section name."))
    @call_with(allowed_components=None, user=REQUEST_USER)
    @export_write_operation()
    @operation_for_version('devel')
    def overrideSource(new_component=None, new_section=None,
                       allowed_components=None, user=None):
        """Override the source package contained in this queue item.

        :param new_component: An IComponent to replace the existing one
            in the upload's source.
        :param new_section: An ISection to replace the existing one
            in the upload's source.
        :param allowed_components: A sequence of components that the
            callsite is allowed to override from and to.
        :param user: The user requesting the override change, used if
            allowed_components is None.

        :raises QueueInconsistentStateError: if either the existing
            or the new_component are not in the allowed_components
            sequence.

        The override values may be None, in which case they are not
        changed.

        :return: True if the source was overridden.
        """

    @operation_parameters(
        changes=List(
            title=u"A sequence of changes to apply.",
            description=(
                u"Each item may have a 'name' item which specifies the binary "
                "package name to override; otherwise, the change applies to "
                "all binaries in the upload. It may also have 'component', "
                "'section', and 'priority' items which replace the "
                "corresponding existing one in the upload's overridden "
                "binaries."),
            value_type=Dict(key_type=TextLine())))
    @call_with(allowed_components=None, user=REQUEST_USER)
    @export_write_operation()
    @operation_for_version('devel')
    def overrideBinaries(changes, allowed_components=None, user=None):
        """Override binary packages in a binary queue item.
Beispiel #28
0
class ITranslationImportQueueEntry(Interface):
    """An entry of the Translation Import Queue."""
    export_as_webservice_entry(
        singular_name='translation_import_queue_entry',
        plural_name='translation_import_queue_entries')

    id = exported(Int(title=_('The entry ID'), required=True, readonly=True))

    path = exported(
        TextLine(
            title=_("Path"),
            description=_(
                "The path to this file inside the source tree. Includes the"
                " filename."),
            required=True))

    importer = exported(
        PersonChoice(
            title=_("Uploader"),
            required=True,
            readonly=True,
            description=_(
                "The person that uploaded this file to Launchpad."),
            vocabulary="ValidOwner"),
        exported_as="uploader")

    dateimported = exported(
        Datetime(
            title=_("The timestamp when this queue entry was created."),
            required=True,
            readonly=True),
        exported_as="date_created")

    productseries = exported(
        Reference(
            title=_("Series"),
            required=False,
            readonly=True,
            schema=IProductSeries))

    distroseries = exported(
        Reference(
            title=_("Series"),
            required=False,
            readonly=True,
            schema=IDistroSeries))

    sourcepackagename = Choice(
        title=_("Source Package Name"),
        description=_(
            "The source package from where this entry comes."),
        required=False,
        vocabulary="SourcePackageName")

    by_maintainer = Bool(
        title=_(
            "This upload was done by the maintainer "
            "of the project or package."),
        description=_(
            "If checked, the translations in this import will be marked "
            "as is_current_upstream."),
        required=True,
        default=False)

    content = Attribute(
        "An ILibraryFileAlias reference with the file content. Must not be"
        " None.")

    format = exported(
        Choice(
            title=_('The file format of the import.'),
            vocabulary=TranslationFileFormat,
            required=True,
            readonly=True))

    status = exported(
        Choice(
            title=_("The status of the import."),
            vocabulary=RosettaImportStatus,
            required=True,
            readonly=True))

    date_status_changed = exported(
        Datetime(
            title=_("The timestamp when the status was changed."),
            required=True))

    is_targeted_to_ubuntu = Attribute(
        "True if this entry is to be imported into the Ubuntu distribution.")

    sourcepackage = exported(
        Reference(
            schema=ISourcePackage,
            title=_("The sourcepackage associated with this entry."),
            readonly=True))

    guessed_potemplate = Attribute(
        "The IPOTemplate that we can guess this entry could be imported into."
        " None if we cannot guess it.")

    import_into = Attribute("The Object where this entry will be imported. Is"
        " None if we don't know where to import it.")

    pofile = Field(
        title=_("The IPOfile where this entry should be imported."),
        required=False)

    potemplate = Field(
        title=_("The IPOTemplate associated with this entry."),
        description=_("The IPOTemplate associated with this entry. If path"
        " notes a .pot file, it should be used as the place where this entry"
        " will be imported, if it's a .po file, it indicates the template"
        " associated with tha translation."),
        required=False)

    error_output = exported(
        Text(
            title=_("Error output"),
            description=_("Output from most recent import attempt."),
            required=False,
            readonly=True))

    def canAdmin(roles):
        """Check if the user can administer this entry."""

    def canEdit(roles):
        """Check if the user can edit this entry."""

    def canSetStatus(new_status, user):
        """Check if the user can set this new status."""

    @call_with(user=REQUEST_USER)
    @operation_parameters(new_status=copy_field(status))
    @export_write_operation()
    def setStatus(new_status, user):
        """Transition to a new status if possible.

        :param new_status: Status to transition to.
        :param user: The user that is doing the transition.
        """

    def setErrorOutput(output):
        """Set `error_output` string."""

    def addWarningOutput(output):
        """Optionally add warning output to `error_output`.

        This may not do everything you expect of it.  Read the code if
        you need certainty.
        """

    def getGuessedPOFile():
        """Return an IPOFile that we think this entry should be imported into.

        Return None if we cannot guess it."""

    def getFileContent():
        """Return the imported file content as a stream."""

    def getTemplatesOnSameDirectory():
        """Return import queue entries stored on the same directory as self.

        The returned entries will be only .pot entries.
        """

    def getElapsedTimeText():
        """Return a string representing elapsed time since we got the file.
Beispiel #29
0
class IProjectGroupMilestone(IAbstractMilestone):
    """A marker interface for milestones related to a project"""
    export_as_webservice_entry(as_of="beta")
Beispiel #30
0
class IBugWatch(IHasBug):
    """A bug on a remote system."""
    export_as_webservice_entry()

    id = Int(title=_('ID'), required=True, readonly=True)

    # Actually refers to Bug; redefined in bug.py.
    bug = exported(
        Reference(title=_('Bug'),
                  schema=Interface,
                  required=True,
                  readonly=True))
    bugtracker = exported(ReferenceChoice(
        title=_('Bug System'),
        required=True,
        schema=IBugTracker,
        vocabulary='BugTracker',
        description=_("You can register new bug trackers from the Launchpad "
                      "Bugs home page.")),
                          exported_as='bug_tracker')
    remotebug = exported(StrippedTextLine(
        title=_('Remote Bug'),
        required=True,
        readonly=False,
        description=_(
            "The bug number of this bug in the remote bug tracker.")),
                         exported_as='remote_bug')
    remotestatus = exported(TextLine(title=_('Remote Status')),
                            exported_as='remote_status')
    remote_importance = exported(TextLine(title=_('Remote Importance')))
    lastchanged = exported(Datetime(title=_('Last Changed')),
                           exported_as='date_last_changed')
    lastchecked = exported(Datetime(title=_('Last Checked')),
                           exported_as='date_last_checked')
    last_error_type = exported(
        Choice(title=_('Last Error Type'), vocabulary=BugWatchActivityStatus))
    datecreated = exported(Datetime(title=_('Date Created'),
                                    required=True,
                                    readonly=True),
                           exported_as='date_created')
    owner = exported(
        Reference(title=_('Owner'),
                  required=True,
                  readonly=True,
                  schema=Interface))
    activity = Attribute('The activity history of this BugWatch.')
    next_check = exported(Datetime(title=_('Next Check')),
                          exported_as='date_next_checked')

    # Useful joins.
    bugtasks = exported(
        CollectionField(
            description=_(
                'The tasks which this watch will affect. '
                'In Launchpad, a bug watch can be linked to one or more '
                'tasks, and if it is linked and we notice a status change '
                'in the watched bug then we will try to update the '
                'Launchpad bug task accordingly.'),
            # value_type is redefined in bugtask.py, to use the right
            # interface.
            value_type=Reference(schema=Interface, )),
        exported_as='bug_tasks')

    # Properties.
    needscheck = Attribute(
        "A True or False indicator of whether or not "
        "this watch needs to be synchronised. The algorithm used considers "
        "the severity of the bug, as well as the activity on the bug, to "
        "ensure that we spend most effort on high-importance and "
        "high-activity bugs.")

    unpushed_comments = Attribute(
        "A set of comments on this BugWatch that need to be pushed to "
        "the remote bug tracker.")

    # Required for Launchpad pages.
    title = exported(Text(title=_('Bug watch title'), readonly=True))

    url = exported(
        Text(title=_('The URL at which to view the remote bug.'),
             readonly=True))

    can_be_rescheduled = Attribute(
        "A True or False indicator of whether or not this watch can be "
        "rescheduled.")

    def updateImportance(remote_importance, malone_importance):
        """Update the importance of the bug watch and any linked bug task.

        The lastchanged attribute gets set to the current time.
        """

    def updateStatus(remote_status, malone_status):
        """Update the status of the bug watch and any linked bug task.

        The lastchanged attribute gets set to the current time.
        """

    def destroySelf():
        """Delete this bug watch."""

    def hasComment(comment_id):
        """Return True if a comment has been imported for the BugWatch.

        If the comment has not been imported, return False.

        :param comment_id: The remote ID of the comment.
        """

    def addComment(comment_id, message):
        """Link and imported comment to the BugWatch.

        :param comment_id: The remote ID of the comment.

        :param message: The imported comment as a Launchpad Message object.
        """

    def getBugMessages(clauses):
        """Return all the `IBugMessage`s that reference this BugWatch.
        
        :param clauses: A iterable of Storm clauses to limit the messages.
        """

    def getImportedBugMessages():
        """Return all the `IBugMessage`s that have been imported."""

    def addActivity(result=None, message=None, oops_id=None):
        """Add an `IBugWatchActivity` record for this BugWatch."""

    def setNextCheck(next_check):
        """Set the next_check time of the watch.

        :raises: `BugWatchCannotBeRescheduled` if
                 `IBugWatch.can_be_rescheduled` is False.
        """

    def reset():
        """Completely reset the watch.