add Issue.fields attribute to support custom fields
This commit is contained in:
parent
370c8ec0e6
commit
02f40ccf2c
4 changed files with 54 additions and 4 deletions
|
|
@ -189,6 +189,9 @@ class RedmineBackend(BaseBackend, ICapContent, ICapBugTracker, ICapCollection):
|
||||||
issue.body = params['body']
|
issue.body = params['body']
|
||||||
issue.creation = params['created_on']
|
issue.creation = params['created_on']
|
||||||
issue.updated = params['updated_on']
|
issue.updated = params['updated_on']
|
||||||
|
issue.fields = {}
|
||||||
|
for key, value in params['fields'].iteritems():
|
||||||
|
issue.fields[key] = value
|
||||||
issue.attachments = []
|
issue.attachments = []
|
||||||
for a in params['attachments']:
|
for a in params['attachments']:
|
||||||
attachment = Attachment(a['id'])
|
attachment = Attachment(a['id'])
|
||||||
|
|
@ -220,12 +223,14 @@ class RedmineBackend(BaseBackend, ICapContent, ICapBugTracker, ICapCollection):
|
||||||
def create_issue(self, project):
|
def create_issue(self, project):
|
||||||
try:
|
try:
|
||||||
with self.browser:
|
with self.browser:
|
||||||
r = self.browser.query_issues(project)
|
r = self.browser.get_project(project)
|
||||||
except BrowserHTTPNotFound:
|
except BrowserHTTPNotFound:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
issue = Issue(0)
|
issue = Issue(0)
|
||||||
issue.project = self._build_project(r['project'])
|
issue.project = self._build_project(r)
|
||||||
|
with self.browser:
|
||||||
|
issue.fields = self.browser.get_custom_fields(project)
|
||||||
return issue
|
return issue
|
||||||
|
|
||||||
def post_issue(self, issue):
|
def post_issue(self, issue):
|
||||||
|
|
@ -237,6 +242,7 @@ class RedmineBackend(BaseBackend, ICapContent, ICapBugTracker, ICapCollection):
|
||||||
'category': issue.category,
|
'category': issue.category,
|
||||||
'status': issue.status.id if issue.status else None,
|
'status': issue.status.id if issue.status else None,
|
||||||
'body': issue.body,
|
'body': issue.body,
|
||||||
|
'fields': issue.fields,
|
||||||
}
|
}
|
||||||
|
|
||||||
with self.browser:
|
with self.browser:
|
||||||
|
|
@ -291,7 +297,6 @@ class RedmineBackend(BaseBackend, ICapContent, ICapBugTracker, ICapCollection):
|
||||||
return self._build_project(params['project'])
|
return self._build_project(params['project'])
|
||||||
|
|
||||||
def fill_issue(self, issue, fields):
|
def fill_issue(self, issue, fields):
|
||||||
# currently there isn't cases where an Issue is uncompleted.
|
return self.get_issue(issue)
|
||||||
return issue
|
|
||||||
|
|
||||||
OBJECTS = {Issue: fill_issue}
|
OBJECTS = {Issue: fill_issue}
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ from urlparse import urlsplit
|
||||||
import urllib
|
import urllib
|
||||||
import lxml.html
|
import lxml.html
|
||||||
|
|
||||||
|
from weboob.capabilities.bugtracker import IssueError
|
||||||
from weboob.tools.browser import BaseBrowser, BrowserIncorrectPassword
|
from weboob.tools.browser import BaseBrowser, BrowserIncorrectPassword
|
||||||
|
|
||||||
from .pages.index import LoginPage, IndexPage, MyPage, ProjectsPage
|
from .pages.index import LoginPage, IndexPage, MyPage, ProjectsPage
|
||||||
|
|
@ -147,6 +148,12 @@ class RedmineBrowser(BaseBrowser):
|
||||||
'iter': self.page.iter_issues(),
|
'iter': self.page.iter_issues(),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def get_project(self, project):
|
||||||
|
self.location('/projects/%s/issues/new' % project)
|
||||||
|
assert self.is_on_page(NewIssuePage)
|
||||||
|
|
||||||
|
return self.page.get_project(project)
|
||||||
|
|
||||||
def get_issue(self, id):
|
def get_issue(self, id):
|
||||||
self.location('/issues/%s' % id)
|
self.location('/issues/%s' % id)
|
||||||
|
|
||||||
|
|
@ -165,12 +172,26 @@ class RedmineBrowser(BaseBrowser):
|
||||||
assert self.is_on_page(IssuePage)
|
assert self.is_on_page(IssuePage)
|
||||||
self.page.fill_form(note=message)
|
self.page.fill_form(note=message)
|
||||||
|
|
||||||
|
def get_custom_fields(self, project):
|
||||||
|
self.location('/projects/%s/issues/new' % project)
|
||||||
|
assert self.is_on_page(NewIssuePage)
|
||||||
|
|
||||||
|
fields = {}
|
||||||
|
for key, div in self.page.iter_custom_fields():
|
||||||
|
fields[key] = div.attrib['value']
|
||||||
|
|
||||||
|
return fields
|
||||||
|
|
||||||
def create_issue(self, project, **kwargs):
|
def create_issue(self, project, **kwargs):
|
||||||
self.location('/projects/%s/issues/new' % project)
|
self.location('/projects/%s/issues/new' % project)
|
||||||
|
|
||||||
assert self.is_on_page(NewIssuePage)
|
assert self.is_on_page(NewIssuePage)
|
||||||
self.page.fill_form(**kwargs)
|
self.page.fill_form(**kwargs)
|
||||||
|
|
||||||
|
error = self.page.get_errors()
|
||||||
|
if len(error) > 0:
|
||||||
|
raise IssueError(error)
|
||||||
|
|
||||||
assert self.is_on_page(IssuePage)
|
assert self.is_on_page(IssuePage)
|
||||||
return int(self.page.groups[0])
|
return int(self.page.groups[0])
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -81,6 +81,12 @@ class BaseIssuePage(BasePage):
|
||||||
token = tokens[0].attrib['value']
|
token = tokens[0].attrib['value']
|
||||||
return token
|
return token
|
||||||
|
|
||||||
|
def get_errors(self):
|
||||||
|
errors = []
|
||||||
|
for li in self.document.xpath('//div[@id="errorExplanation"]//li'):
|
||||||
|
errors.append(li.text.strip())
|
||||||
|
return ', '.join(errors)
|
||||||
|
|
||||||
|
|
||||||
class IssuesPage(BaseIssuePage):
|
class IssuesPage(BaseIssuePage):
|
||||||
PROJECT_FIELDS = {'members': 'values_assigned_to_id',
|
PROJECT_FIELDS = {'members': 'values_assigned_to_id',
|
||||||
|
|
@ -139,6 +145,11 @@ class NewIssuePage(BaseIssuePage):
|
||||||
'statuses': 'issue_status_id',
|
'statuses': 'issue_status_id',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def iter_custom_fields(self):
|
||||||
|
for div in self.document.xpath('//form//input[starts-with(@id, "issue_custom_field")]'):
|
||||||
|
label = self.document.xpath('//label[@for="%s"]' % div.attrib['id'])[0]
|
||||||
|
yield label.text.strip(), div
|
||||||
|
|
||||||
def set_title(self, title):
|
def set_title(self, title):
|
||||||
self.browser['issue[subject]'] = title.encode('utf-8')
|
self.browser['issue[subject]'] = title.encode('utf-8')
|
||||||
|
|
||||||
|
|
@ -178,6 +189,13 @@ class NewIssuePage(BaseIssuePage):
|
||||||
def set_note(self, message):
|
def set_note(self, message):
|
||||||
self.browser['notes'] = message.encode('utf-8')
|
self.browser['notes'] = message.encode('utf-8')
|
||||||
|
|
||||||
|
def set_fields(self, fields):
|
||||||
|
for key, div in self.iter_custom_fields():
|
||||||
|
try:
|
||||||
|
self.browser[div.attrib['name']] = fields[key]
|
||||||
|
except KeyError:
|
||||||
|
continue
|
||||||
|
|
||||||
def fill_form(self, **kwargs):
|
def fill_form(self, **kwargs):
|
||||||
self.browser.select_form(predicate=lambda form: form.attrs.get('id', '') == 'issue-form')
|
self.browser.select_form(predicate=lambda form: form.attrs.get('id', '') == 'issue-form')
|
||||||
for key, value in kwargs.iteritems():
|
for key, value in kwargs.iteritems():
|
||||||
|
|
@ -230,6 +248,11 @@ class IssuePage(NewIssuePage):
|
||||||
params['category'] = self._parse_selection('issue_category_id')
|
params['category'] = self._parse_selection('issue_category_id')
|
||||||
params['version'] = self._parse_selection('issue_fixed_version_id')
|
params['version'] = self._parse_selection('issue_fixed_version_id')
|
||||||
|
|
||||||
|
params['fields'] = {}
|
||||||
|
for key, div in self.iter_custom_fields():
|
||||||
|
value = div.attrib['value']
|
||||||
|
params['fields'][key] = value
|
||||||
|
|
||||||
params['attachments'] = []
|
params['attachments'] = []
|
||||||
try:
|
try:
|
||||||
for p in self.parser.select(content, 'div.attachments', 1).findall('p'):
|
for p in self.parser.select(content, 'div.attachments', 1).findall('p'):
|
||||||
|
|
|
||||||
|
|
@ -206,6 +206,7 @@ class Issue(CapBaseObject):
|
||||||
category = StringField('Name of the category')
|
category = StringField('Name of the category')
|
||||||
version = Field('Target version of this issue', Version)
|
version = Field('Target version of this issue', Version)
|
||||||
status = Field('Status of this issue', Status)
|
status = Field('Status of this issue', Status)
|
||||||
|
fields = Field('Custom fields (key,value)', dict)
|
||||||
|
|
||||||
|
|
||||||
class Query(CapBaseObject):
|
class Query(CapBaseObject):
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue