Test coverage for vnccollab.theme.portlets.redmine_tickets
1: import logging
1: import textile
1: from pyactiveresource.activeresource import ActiveResource
1: from zope.component import getMultiAdapter, getUtility
1: from zope.formlib import form
1: from zope.interface import implements
1: from zope import schema
1: from Products.Five.browser.pagetemplatefile import ZopeTwoPageTemplateFile
1: from Products.CMFCore.utils import getToolByName
1: from Products.CMFPlone.utils import safe_unicode
1: from plone.registry.interfaces import IRegistry
1: from plone.memoize.instance import memoize
1: from plone.portlets.interfaces import IPortletDataProvider
1: from plone.app.portlets.portlets import base
1: from vnccollab.common.portlets import deferred
1: from vnccollab.theme.portlets.zimbra_mail import logException
1: from vnccollab.theme import messageFactory as _
1: logger = logging.getLogger('vnccollab.theme.RedmineTicketsPortlet')
2: class IRedmineTicketsPortlet(IPortletDataProvider):
1: header = schema.TextLine(
1: title=_(u"Header"),
1: description=_(u"Header of the portlet."),
1: required=True,
1: default=u'Redmine Tickets')
1: count = schema.Int(
1: title=_(u"Number of items to display"),
1: description=_(u"How many items to list."),
1: required=True,
1: default=5)
1: request_timeout = schema.Int(
1: title=_(u"Request timeout"),
1: description=_(u"How many seconds to wait for hanging Redmine request."),
1: required=True,
1: default=15)
2: class Assignment(base.Assignment):
1: implements(IRedmineTicketsPortlet)
1: header = u''
1: count = 5
1: request_timeout = 15
1: @property
def title(self):
"""Return portlet header"""
>>>>>> return self.header
1: def __init__(self, header=u'', count=5, request_timeout=15):
1: self.header = header
1: self.count = count
1: self.request_timeout = request_timeout
2: class Renderer(deferred.DeferredRenderer):
1: render_preload = render_full = ZopeTwoPageTemplateFile(
1: 'templates/redmine_tickets.pt')
1: def refresh(self):
'''Calculates the data needed for deferred_update.'''
>>>>>> self.getTickets()
1: def getTicketsURL(self):
"""Returns tickets root url"""
>>>>>> return '%s/issues' % self._url()
1: def getTickets(self):
"""Returns list of opened issues for authenticated user"""
1: username, password = self.getAuthCredentials()
1: if not username or not password:
1: return ()
>>>>>> return self._tickets(self._url(), username, password)
1: @memoize
def _tickets(self, url, username, password):
"""Requests redmine for list of opened issues for current user"""
# create ActiveResource classes to fetch data from Redmine over REST API
>>>>>> attrs = {'_site': url, '_user': username, '_password': password}
>>>>>> if self.data.request_timeout:
>>>>>> attrs['_timeout'] = self.data.request_timeout
>>>>>> Issue = type("Issue", (ActiveResource,), attrs.copy())
>>>>>> User = type("User", (ActiveResource,), attrs.copy())
# do actual calls to redmine
>>>>>> try:
# fetch opened issues belonging to authenticated user
>>>>>> data = Issue.find(assigned_to_id=User.find('current').id,
>>>>>> status_id='o',
>>>>>> sort='updated_on:desc')
>>>>>> except:
>>>>>> logException(_(u"Error during fetching redmine tickets %s" % url),
>>>>>> context=self.context, logger=logger)
>>>>>> return ()
>>>>>> plone_view = getMultiAdapter((self.context, self.request),
>>>>>> name=u'plone')
# process retrieved data
>>>>>> tickets = []
>>>>>> limit = self.data.count
>>>>>> counter = 0
>>>>>> for item in data:
# we've got enough tickets
>>>>>> if counter >= limit:
>>>>>> break
>>>>>> info = item.to_dict()
# skip invalid entries
>>>>>> if not info.get('id') or not info.get('subject'):
>>>>>> continue
# prepare date
>>>>>> date = info.get('updated_on', '')
>>>>>> if date:
>>>>>> date = plone_view.toLocalizedTime(date, long_format=1)
# prepare ticket body
>>>>>> body = safe_unicode(info.get('description', ''))
>>>>>> if body:
# convert textile to html and do not cut down ticket
# description anymore
>>>>>> try:
>>>>>> body = textile.textile(body)
>>>>>> except:
>>>>>> pass
# crop length to 160 characters
# body = plone_view.cropText(body, 160, ellipsis=u'...')
>>>>>> tickets.append({
>>>>>> 'id': info['id'],
>>>>>> 'title': safe_unicode(info['subject']),
>>>>>> 'body': body,
>>>>>> 'date': date,
>>>>>> 'url': '%s/issues/%s' % (url, info['id'])
})
>>>>>> counter += 1
>>>>>> return tuple(tickets)
1: @memoize
def getAuthCredentials(self):
"""Returns username and password for redmine user."""
# take username and password from authenticated user Zimbra creds
1: mtool = getToolByName(self.context, 'portal_membership')
1: member = mtool.getAuthenticatedMember()
1: username, password = member.getProperty('redmine_username', ''), \
1: member.getProperty('redmine_password', '')
# password could contain non-ascii chars, ensure it's properly encoded
1: return username, safe_unicode(password).encode('utf-8')
1: @memoize
def _url(self):
"""Redmine root url"""
>>>>>> registry = getUtility(IRegistry)
>>>>>> return registry.get('vnccollab.theme.redmine.url')
1: @property
def title(self):
"""return title of feed for portlet"""
>>>>>> return self.data.header
2: class AddForm(base.AddForm):
1: form_fields = form.Fields(IRedmineTicketsPortlet)
1: label = _(u"Add Redmine Tickets Portlet")
1: description = _(u"Renders list of opened Redmine Tickets for authenticated "
"user.")
1: def create(self, data):
>>>>>> return Assignment(**data)
2: class EditForm(base.EditForm):
1: form_fields = form.Fields(IRedmineTicketsPortlet)
1: label = _(u"Edit Redmine Tickets Portlet")
1: description = _(u"Renders list of opened Redmine Tickets for authenticated "
"user.")