This repository has been archived on 2023-02-21. You can view files and clone it, but cannot push or open issues or pull requests.
tabellio.webviews/tabellio/webviews/deputy.py

419 lines
17 KiB
Python

# -*- coding: utf-8 -*-
import json
import csv
from cStringIO import StringIO
from Products.Five import BrowserView
from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile
from zope.interface import Interface
from zope import component
from zc.relation.interfaces import ICatalog
from zope.app.intid.interfaces import IIntIds
from Products.CMFCore.utils import getToolByName
from plone.registry.interfaces import IRegistry
from tabellio.config.interfaces import ITabellioSettings
import tabellio.config.utils
from misc import Cached
try:
from plone.app.caching.operations.utils import setCacheHeaders
except ImportError:
setCacheHeaders = None
import cairoplot
class View(BrowserView, Cached):
def birthline(self):
if not self.context.birthdate:
return
if self.context.sex == 'M':
word = u''
else:
word = u'Née'
if self.context.birthplace:
return u'%s à %s, le %s' % (word, self.context.birthplace,
self.context.birthdate.strftime('%d/%m/%Y'))
else:
return u'%s le %s' % (word, self.context.birthdate.strftime('%d/%m/%Y'))
def degrees(self):
degrees = self.context.degrees
return '<ul>' + '\n'.join(u'<li>%s</li>' % x for x in degrees.splitlines() if x.strip()) + '</ul>'
def mandates(self):
mandates = self.context.mandates
return '<ul>' + '\n'.join(u'<li>%s</li>' % x for x in mandates.splitlines() if x.strip()) + '</ul>'
def profession(self):
profession = self.context.profession
return '<ul>' + '\n'.join(u'<li>%s</li>' % x for x in profession.splitlines() if x.strip()) + '</ul>'
def get_commissions_and_roles(self):
intids = component.getUtility(IIntIds)
catalog = component.getUtility(ICatalog)
try:
author_intid = intids.getId(self.context)
except KeyError:
intids.register(self.context)
author_intid = intids.getId(self.context)
president = [x.from_object for x in catalog.findRelations({
'to_id': author_intid,
'from_attribute': 'president'})]
vicepresident = [x.from_object for x in catalog.findRelations({
'to_id': author_intid,
'from_attribute': 'vicepresidents'})]
member = [x.from_object for x in catalog.findRelations({
'to_id': author_intid,
'from_attribute': 'members'})]
substitute = [x.from_object for x in catalog.findRelations({
'to_id': author_intid,
'from_attribute': 'substitutes'})]
roles = []
if self.context.sex == 'M':
mapping = {'P': u'Président', 'VP': u'Vice-président', 'M': u'Membre', 'S': u'Suppléant'}
else:
mapping = {'P': u'Présidente', 'VP': u'Vice-présidente', 'M': u'Membre', 'S': u'Suppléante'}
# do not duplicate presidents and vice presidents as members
for commission in president + vicepresident:
if commission in member:
member.remove(commission)
for commission in president + vicepresident + member + substitute:
if commission.title.lower().startswith(u'commission'):
prefix = 'de la'
else:
prefix = 'du'
if commission in president:
title = mapping['P']
elif commission in vicepresident:
title = mapping['VP']
elif commission in member:
title = mapping['M']
elif commission in substitute:
title = mapping['S']
roles.append((title, prefix, commission))
return roles
_activity_as_author = None
def get_activity_as_author(self):
if self._activity_as_author is not None:
return self._activity_as_author
intids = component.getUtility(IIntIds)
catalog = component.getUtility(ICatalog)
try:
author_intid = intids.getId(self.context)
except KeyError:
intids.register(self.context)
author_intid = intids.getId(self.context)
activity = [x.from_object for x in catalog.findRelations({
'to_id': intids.getId(self.context),
'from_attribute': 'authors'})]
activity = [x for x in activity if hasattr(x, 'date')]
self._activity_as_author = activity
return activity
_activity_as_participant = None
def get_activity_as_participant(self):
if self._activity_as_participant is not None:
return self._activity_as_participant
intids = component.getUtility(IIntIds)
catalog = component.getUtility(ICatalog)
try:
author_intid = intids.getId(self.context)
except KeyError:
intids.register(self.context)
author_intid = intids.get(self.context)
activity = [x.from_object for x in catalog.findRelations({
'to_id': intids.getId(self.context),
'from_attribute': 'participants'})]
activity = [x for x in activity if hasattr(x, 'date')]
self._activity_as_participant = activity
return activity
_activity_as_reporter = None
def get_activity_as_reporter(self):
if self._activity_as_reporter is not None:
return self._activity_as_reporter
intids = component.getUtility(IIntIds)
catalog = component.getUtility(ICatalog)
try:
reporter_intid = intids.getId(self.context)
except KeyError:
intids.register(self.context)
reporter_intid = initds.get(self.context)
reports = [x.from_object for x in catalog.findRelations({
'to_id': reporter_intid,
'from_attribute': 'reporters'})]
reports = [x for x in reports if hasattr(x, 'date')]
self._activity_as_reporter = reports
return reports
_activity_as_speaker = None
def get_activity_as_speaker(self):
if self._activity_as_speaker is not None:
return self._activity_as_speaker
intids = component.getUtility(IIntIds)
catalog = component.getUtility(ICatalog)
try:
speaker_intid = intids.getId(self.context)
except KeyError:
intids.register(self.context)
speaker_intid = intids.get(self.context)
speeches = [x.from_object for x in catalog.findRelations({
'to_id': speaker_intid,
'from_attribute': 'speakers'})]
speeches = [x for x in speeches if hasattr(x, 'date')]
self._activity_as_speaker = speeches
return speeches
def sessions_with_activity(self):
sessions_dict = {}
for activity in self.get_activity_as_author():
sessions_dict[activity.session] = True
for activity in self.get_activity_as_participant():
sessions_dict[activity.session] = True
for activity in self.get_activity_as_speaker():
sessions_dict[activity.session] = True
for activity in self.get_activity_as_reporter():
sessions_dict[activity.session] = True
sessions = []
for session in tabellio.config.utils.get_sessions():
if session in sessions_dict:
sessions.append(session)
return sessions
def activity(self, session=None):
activity = [(x, 'author') for x in self.get_activity_as_author()] + \
[(x, 'participant') for x in self.get_activity_as_participant()] + \
[(x, 'reporter') for x in self.get_activity_as_reporter()] + \
[(x, 'speaker') for x in self.get_activity_as_speaker()]
if session:
activity = [x for x in activity if x[0].session == session]
def cmp_date(x, y):
x, y = x[0], y[0]
if x.date is None and y.date is None: return 0
if x.date is None: return -1
if y.date is None: return 1
return cmp(x.date, y.date)
activity.sort(cmp=cmp_date, reverse=True)
return activity
def contact_form_url(self):
brains = self.context.portal_catalog(portal_type='tabellio.contact.form')
if not brains:
return None
return brains[0].getURL()
class IFolderView(Interface):
pass
class FolderView(BrowserView):
deputy_listform = ViewPageTemplateFile('deputy_listform.pt')
def renderDeputyListForm(self):
return self.deputy_listform()
def sortedDeputies(self):
from plone.i18n.normalizer.fr import normalizer
def cmp_deputy(x, y):
# | replacement is an hack to get spaces to sort after letters
return cmp(normalizer.normalize('%s %s' % (x.lastname.replace(' ', '|'), x.firstname.replace(' ', '|'))).lower(),
normalizer.normalize('%s %s' % (y.lastname.replace(' ', '|'), y.firstname.replace(' ', '|'))).lower())
return sorted([x for x in self.context.values() if x.Type() == 'Deputy' and x.active], cmp=cmp_deputy)
def getPolGroups(self):
polgroups_path = tabellio.config.utils.get_polgroups_path()
current = getToolByName(self.context, 'portal_url').getPortalObject()
for part in polgroups_path.split('/'):
if not part:
continue
current = getattr(current, part)
return [x for x in current.objectValues() if x.portal_type == 'themis.datatypes.polgroup' and x.active]
def getQueryName(self):
return self.request.form.get('deputy.widgets.name')
def getSelectedPolgroup(self):
try:
return self.request.form.get('deputy.widgets.polgroup')[0]
except (IndexError, TypeError):
return None
def renderPolgroupTitle(self, item):
return item.Title().replace('-', ' -')
class IDeputiesAndOthersFolderView(Interface):
pass
class DeputiesAndOthersFolderView(FolderView, Cached):
pass
class IPfbDeputiesFolderView(Interface):
pass
class PfbDeputiesFolderView(FolderView, Cached):
pass
class GenderStatsView(BrowserView):
def __call__(self):
self.request.response.setHeader('Content-type', 'image/png')
if setCacheHeaders:
setCacheHeaders(self, self.request, self.request.response,
maxage=300, smaxage=300)
data = {u'Hommes': len([x for x in self.context.values() if
x.Type() == 'Deputy' and x.sex == 'M' and x.active]),
u'Femmes': len([x for x in self.context.values() if
x.Type() == 'Deputy' and x.sex == 'F' and x.active])}
settings = component.getUtility(IRegistry).forInterface(ITabellioSettings, False)
if settings.gender_colors:
series_colors = [ hexa_to_float(settings.gender_colors.split(',')[0]),
hexa_to_float(settings.gender_colors.split(',')[1])]
else:
series_colors = [ (0.77,0.92,0.94), (0.54,0.56,0.69) ]
surface = cairoplot.cairo.ImageSurface(cairoplot.cairo.FORMAT_ARGB32, 600, 320)
plot = cairoplot.DonutPlot(surface, data, 600, 320,
background=None,
gradient=False, shadow=True,
inner_radius=0.5, colors=series_colors)
plot.radius = 150
plot.center = (200, 160)
plot.render_shadow()
plot.render_plot()
for number, group in enumerate(plot.series):
if number == 0:
baseline = 320/2-30
else:
baseline = 320/2+30
percent = int(round(100.*sum(group.to_list())/plot.total))
plot.context.set_source_rgba(*plot.series_colors[number][:4])
plot.context.rectangle(410, baseline-25, 40, 40)
plot.context.fill()
plot.context.set_source_rgba(*plot.label_color)
plot.context.set_font_size( 1.2 * plot.font_size )
plot.context.move_to(415, baseline)
plot.context.show_text('%s %%' % percent)
plot.context.move_to(460, baseline)
plot.context.show_text(group.name)
plot.context.show_page()
fd = StringIO()
surface.write_to_png(fd)
surface.finish()
return fd.getvalue()
def hexa_to_float(code):
return (int(code[1:3], 16)/255., int(code[3:5], 16)/255., int(code[5:7], 16)/255.)
class AgeStatsView(BrowserView):
def __call__(self):
self.request.response.setHeader('Content-type', 'image/png')
if setCacheHeaders:
setCacheHeaders(self, self.request, self.request.response,
maxage=300, smaxage=300)
ageranges = [x.agerange for x in self.context.values() if
x.Type() == 'Deputy' and x.active]
x_labels = ('m30', 'd30-40', 'd40-50', 'd50-60', 'd60-70', 'p70')
data = []
for age in x_labels:
data.append(len([x for x in ageranges if x == age]))
x_labels = ('-30', '30-40', '40-50', '50-60', '60-70', '+70')
settings = component.getUtility(IRegistry).forInterface(ITabellioSettings, False)
if settings.ageranges_color:
series_colors = [ list(hexa_to_float(settings.ageranges_color) + ('solid',)) ] * len(x_labels)
else:
series_colors = [ [0.54,0.56,0.69, 'solid'] ] * len(x_labels)
increment = 5
y_bounds = [0, ((max(data)/increment)+1)*increment]
y_labels = [str(x) for x in range(0, y_bounds[1]+increment, increment)]
surface = cairoplot.cairo.ImageSurface(cairoplot.cairo.FORMAT_ARGB32, 500, 320)
plot = cairoplot.VerticalBarPlot(surface, data, 500, 320,
display_values=True,
grid=True,
series_colors=series_colors,
border=20,
x_labels=x_labels,
y_labels=y_labels,
y_bounds=y_bounds)
plot.pad = 15
plot.render()
plot.context.show_page()
fd = StringIO()
surface.write_to_png(fd)
surface.finish()
return fd.getvalue()
class JsonView(BrowserView):
def __call__(self):
l = []
for object in self.context.objectValues():
if object.portal_type != 'themis.datatypes.deputy':
continue
l.append(object.id)
self.request.response.setHeader('Content-type', 'application/json')
return json.dumps(l)
class CsvView(BrowserView):
def __call__(self):
output = StringIO()
csv_output = csv.writer(output)
def address_as_csv(address):
if address is None:
return [None]*8
row = []
for attr in ('title', 'street', 'zipcode', 'city',
'phone1', 'phone2', 'fax', 'email'):
if getattr(address, attr):
row.append(getattr(address, attr).encode('utf-8'))
else:
row.append(None)
return row
for object in self.context.objectValues():
if object.portal_type != 'themis.datatypes.deputy':
continue
if not object.active:
continue
row = []
for attr in ('firstname', 'lastname', 'sex', 'district',
'website', 'birthplace'):
if getattr(object, attr):
row.append(getattr(object, attr).encode('utf-8'))
else:
row.append(None)
if object.birthdate:
row.append(object.birthdate.strftime('%Y-%m-%d'))
else:
row.append(None)
if object.polgroup:
polgroup_name = object.polgroup.to_object.title
if type(polgroup_name) is unicode:
polgroup_name = polgroup_name.encode('utf-8')
row.append(polgroup_name)
else:
row.append(None)
row.extend(address_as_csv(object.private_address))
row.extend(address_as_csv(object.work_address))
row.extend(address_as_csv(object.work_address_2))
csv_output.writerow(row)
self.request.response.setHeader('Content-type', 'text/csv')
return output.getvalue()