add Issue.fields attribute to support custom fields

This commit is contained in:
Romain Bignon 2014-01-21 21:05:39 +01:00
commit 02f40ccf2c
4 changed files with 54 additions and 4 deletions

View file

@ -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}

View file

@ -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])

View file

@ -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'):

View file

@ -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):