# Copyright(C) 2011 Romain Bignon
#
# This file is part of woob.
#
# woob is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# woob is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with woob. If not, see <http://www.gnu.org/licenses/>.
from .base import Capability, BaseObject, Field, StringField,\
IntField, UserError, Enum
from .date import DateField, DeltaField
__all__ = ['IssueError', 'Project', 'User', 'Version', 'Status', 'Attachment',
'Change', 'Update', 'Issue', 'Query', 'CapBugTracker']
[docs]class IssueError(UserError):
"""
Raised when there is an error with an issue.
"""
[docs]class Project(BaseObject):
"""
Represents a project.
"""
name = StringField('Name of the project')
members = Field('Members of projects', list)
versions = Field('List of versions available for this project', list)
trackers = Field('All trackers', list)
categories = Field('All categories', list)
statuses = Field('Available statuses for issues', list)
priorities = Field('Available priorities for issues', list)
tags = Field('All tags', tuple, list)
fields = Field('Custom fields names', list)
def __init__(self, id, name, url=None):
super(Project, self).__init__(id, url)
self.name = str(name)
def __repr__(self):
return '<Project %r>' % self.name
[docs] def find_user(self, id, name):
"""
Find a user from its ID.
If not found, create a :class:`User` with the specified name.
:param id: ID of user
:type id: str
:param name: Name of user
:type name: str
:rtype: :class:`User`
"""
for user in self.members:
if user.id == id:
return user
if name is None:
return None
return User(id, name)
[docs] def find_version(self, id, name):
"""
Find a version from an ID.
If not found, create a :class:`Version` with the specified name.
:param id: ID of version
:type id: str
:param name: Name of version
:type name: str
:rtype: :class:`Version`
"""
for version in self.versions:
if version.id == id:
return version
if name is None:
return None
return Version(id, name)
[docs] def find_status(self, name):
"""
Find a status from a name.
:param name: Name of status
:type name: str
:rtype: :class:`Status`
"""
for status in self.statuses:
if status.name == name:
return status
if name is None:
return None
return None
[docs]class User(BaseObject):
"""
User.
"""
name = StringField('Name of user')
def __init__(self, id, name, url=None):
super(User, self).__init__(id, url)
self.name = str(name)
def __repr__(self):
return '<User %r>' % self.name
[docs]class Version(BaseObject):
"""
Version of a project.
"""
name = StringField('Name of version')
def __init__(self, id, name, url=None):
super(Version, self).__init__(id, url)
self.name = str(name)
def __repr__(self):
return '<Version %r>' % self.name
class StatusType(Enum):
NEW = 0
PROGRESS = 1
RESOLVED = 2
REJECTED = 3
[docs]class Status(BaseObject):
"""
Status of an issue.
**VALUE_** constants are the primary status
types.
"""
VALUE_NEW = StatusType.NEW
VALUE_PROGRESS = StatusType.PROGRESS
VALUE_RESOLVED = StatusType.RESOLVED
VALUE_REJECTED = StatusType.REJECTED
name = StringField('Name of status')
value = IntField('Value of status (constants VALUE_*)')
def __init__(self, id, name, value, url=None):
super(Status, self).__init__(id, url)
self.name = str(name)
self.value = value
def __repr__(self):
return '<Status %r>' % self.name
[docs]class Attachment(BaseObject):
"""
Attachment of an issue.
"""
filename = StringField('Filename')
def __repr__(self):
return '<Attachment %r>' % self.filename
[docs]class Change(BaseObject):
"""
A change of an update.
"""
field = StringField('What field has been changed')
last = StringField('Last value of field')
new = StringField('New value of field')
def __repr__(self):
return '<%s %r: %r (old: %r)>' % (type(self).__name__, self.field, self.new, self.last)
[docs]class Update(BaseObject):
"""
Represents an update of an issue.
"""
author = Field('Author of update', User)
date = DateField('Date of update')
hours = DeltaField('Time activity')
message = StringField('Log message')
attachments = Field('Files attached to update', list, tuple)
changes = Field('List of changes', list, tuple)
def __repr__(self):
return '<Update %r>' % self.id
[docs]class Issue(BaseObject):
"""
Represents an issue.
"""
project = Field('Project of this issue', Project)
title = StringField('Title of issue')
body = StringField('Text of issue')
creation = DateField('Date when this issue has been created')
updated = DateField('Date when this issue has been updated for the last time')
start = DateField('Date when this issue starts')
due = DateField('Date when this issue is due for')
attachments = Field('List of attached files', list, tuple)
history = Field('History of updates', list, tuple)
author = Field('Author of this issue', User)
assignee = Field('User assigned to this issue', User)
tracker = StringField('Name of the tracker')
category = StringField('Name of the category')
version = Field('Target version of this issue', Version)
status = Field('Status of this issue', Status)
fields = Field('Custom fields (key,value)', dict)
priority = StringField('Priority of the issue') #XXX
tags = Field('Categories/Tags of the issue', tuple, list)
related_issues = Field('Related issues', list)
[docs]class Query(BaseObject):
"""
Query to find an issue.
"""
project = Field('Filter on projects', str, str, Project)
title = StringField('Filter on titles')
author = Field('Filter on authors', str, User)
assignee = Field('Filter on assignees', str, User)
version = Field('Filter on versions', str, Version)
category = StringField('Filter on categories')
status = Field('Filter on statuses', str, Status)
tags = Field('Filter on tags', tuple, list)
fields = Field('Filter on custom fields', dict)
def __init__(self, id='', url=None):
super(Query, self).__init__(id, url)
[docs]class CapBugTracker(Capability):
"""
Bug trackers websites.
"""
(SORT_RELEVANCE,
SORT_RATING,
SORT_PRIORITY,
SORT_DATE) = range(4)
[docs] def iter_issues(self, query, sortby=SORT_RELEVANCE, ascending=False):
"""
Iter issues with optionnal patterns.
:param query: query
:type query: :class:`Query`
:rtype: iter[:class:`Issue`]
"""
raise NotImplementedError()
[docs] def get_issue(self, id):
"""
Get an issue from its ID.
:param id: ID of issue
:rtype: :class:`Issue`
"""
raise NotImplementedError()
[docs] def create_issue(self, project):
"""
Create an empty issue on the given project.
:param project: project
:type project: :class:`Project`
:returns: the created issue
:rtype: :class:`Issue`
"""
raise NotImplementedError()
[docs] def post_issue(self, issue):
"""
Post an issue to create or update it.
:param issue: issue to create or update
:type issue: :class:`Issue`
"""
raise NotImplementedError()
[docs] def update_issue(self, issue, update):
"""
Add an update to an issue.
:param issue: issue or id of issue
:type issue: :class:`Issue`
:param update: an Update object
:type update: :class:`Update`
"""
raise NotImplementedError()
[docs] def remove_issue(self, issue):
"""
Remove an issue.
:param issue: issue
:type issue: :class:`Issue`
"""
raise NotImplementedError()
[docs] def iter_projects(self):
"""
Iter projects.
:rtype: iter[:class:`Project`]
"""
raise NotImplementedError()
[docs] def get_project(self, id):
"""
Get a project from its ID.
:rtype: :class:`Project`
"""
raise NotImplementedError()