merge branch master - important: 'limits' parameter for reports

This commit is contained in:
Helmut Merz 2012-06-18 10:29:24 +02:00
commit 2fdf3a99b9
18 changed files with 422 additions and 51 deletions

View file

@ -75,9 +75,32 @@ class ResultsView(NodeView):
@Lazy @Lazy
def params(self): def params(self):
params = dict(self.request.form) params = dict(self.request.form)
params.pop('report_execute', None) params = self.parseParams(params)
return params return params
def parseParams(self, params):
params.pop('report_execute', None)
if 'limits' in params:
params['limits'] = self.parseLimitsParam(params['limits'])
return params
def parseLimitsParam(self, value):
if not value:
return None
if isinstance(value, basestring):
limits = value.split(',')
else:
limits = value
if len(limits) < 2:
limits.append(None)
result = []
for p in limits[:2]:
if not p:
result.append(None)
else:
result.append(int(p))
return result
@Lazy @Lazy
def report(self): def report(self):
return adapted(self.virtualTargetObject) return adapted(self.virtualTargetObject)
@ -147,7 +170,8 @@ class ResultsConceptView(ConceptView):
reportType = self.reportType or self.report.reportType reportType = self.reportType or self.report.reportType
ri = component.getAdapter(self.report, IReportInstance, ri = component.getAdapter(self.report, IReportInstance,
name=reportType) name=reportType)
ri.view = self.nodeView #ri.view = self.nodeView
ri.view = self
return ri return ri
def results(self): def results(self):

View file

@ -95,15 +95,21 @@ class ReportInstance(BaseReport):
crit = self.queryCriteria crit = self.queryCriteria
if crit is None: if crit is None:
return [] return []
limits = self.limits
if dynaParams is not None: if dynaParams is not None:
for k, v in dynaParams.items(): for k, v in dynaParams.items():
if k == 'limits':
limits = v
break
if k in crit.parts.keys(): if k in crit.parts.keys():
crit.parts[k].comparisonValue = v crit.parts[k].comparisonValue = v
parts = Jeep(crit.parts) parts = Jeep(crit.parts)
result = list(self.selectObjects(parts)) # may modify parts result = list(self.selectObjects(parts)) # may modify parts
qc = CompoundQueryCriteria(parts) qc = CompoundQueryCriteria(parts)
return ResultSet(self, result, rowFactory=self.rowFactory, return ResultSet(self, result, rowFactory=self.rowFactory,
sortCriteria=self.getSortCriteria(), queryCriteria=qc) sortCriteria=self.getSortCriteria(), queryCriteria=qc,
limits=limits)
def selectObjects(self, parts): def selectObjects(self, parts):
# to be implemented by subclass # to be implemented by subclass
return [] return []

View file

@ -32,6 +32,7 @@ from loops.browser.common import BaseView
from loops.browser.concept import ConceptView from loops.browser.concept import ConceptView
from loops.expert.browser.report import ResultsConceptView from loops.expert.browser.report import ResultsConceptView
from loops.knowledge.interfaces import IPerson, ITask from loops.knowledge.interfaces import IPerson, ITask
from loops.knowledge.qualification import QualificationRecord
from loops.organize.work.browser import CreateWorkItemForm, CreateWorkItem from loops.organize.work.browser import CreateWorkItemForm, CreateWorkItem
from loops.organize.party import getPersonForUser from loops.organize.party import getPersonForUser
from loops.util import _ from loops.util import _
@ -120,6 +121,8 @@ class PersonQualificationView(ResultsConceptView):
class CreateQualificationRecordForm(CreateWorkItemForm): class CreateQualificationRecordForm(CreateWorkItemForm):
macros = knowledge_macros macros = knowledge_macros
recordManagerName = 'qualification'
trackFactory = QualificationRecord
@Lazy @Lazy
def macro(self): def macro(self):

View file

@ -100,6 +100,19 @@
factory="loops.knowledge.browser.Candidates" factory="loops.knowledge.browser.Candidates"
permission="zope.View" /> permission="zope.View" />
<browser:page
name="create_qualification.html"
for="loops.interfaces.INode"
class="loops.knowledge.browser.CreateQualificationRecordForm"
permission="zope.View" />
<zope:adapter
name="create_qualification"
for="loops.browser.node.NodeView
zope.publisher.interfaces.browser.IBrowserRequest"
factory="loops.knowledge.browser.CreateQualificationRecord"
permission="zope.View" />
<!-- other adapters --> <!-- other adapters -->
<zope:adapter factory="loops.knowledge.schema.PersonSchemaFactory" /> <zope:adapter factory="loops.knowledge.schema.PersonSchemaFactory" />

View file

@ -1,17 +1,17 @@
type(u'competence', u'Competence', viewName=u'', type(u'competence', u'Kompetenz', viewName=u'',
typeInterface=u'', options=u'action.portlet:edit_concept') typeInterface=u'', options=u'action.portlet:create_subtype,edit_concept')
type(u'person', u'Person', viewName=u'', type(u'person', u'Person', viewName=u'',
typeInterface=u'loops.knowledge.interfaces.IPerson', typeInterface=u'loops.knowledge.interfaces.IPerson',
options=u'action.portlet:editPerson') options=u'action.portlet:createQualification,editPerson')
type(u'task', u'Aufgabe', viewName=u'', type(u'task', u'Aufgabe', viewName=u'',
typeInterface=u'loops.knowledge.interfaces.ITask', typeInterface=u'loops.knowledge.interfaces.ITask',
options=u'action.portlet:createTopic,editTopic') options=u'action.portlet:createTask,editTask')
type(u'topic', u'Thema', viewName=u'', type(u'topic', u'Thema', viewName=u'',
typeInterface=u'loops.knowledge.interfaces.ITopic', typeInterface=u'loops.knowledge.interfaces.ITopic',
options=u'action.portlet:createTopic,editTopic') options=u'action.portlet:createTask,createTopic,editTopic')
type(u'training', u'Schulung', viewName=u'', type(u'training', u'Schulung', viewName=u'',
typeInterface=u'loops.organize.interfaces.ITask', typeInterface=u'loops.organize.interfaces.ITask',
options=u'action.portlet:create_subtype,edit_concept') options=u'action.portlet:edit_concept')
concept(u'general', u'Allgemein', u'domain') concept(u'general', u'Allgemein', u'domain')
concept(u'system', u'System', u'domain') concept(u'system', u'System', u'domain')
@ -26,19 +26,19 @@ concept(u'issubtype', u'is Subtype', u'predicate', options=u'hide_children',
predicateInterface='loops.interfaces.IIsSubtype') predicateInterface='loops.interfaces.IIsSubtype')
# structure # structure
child(u'competence', u'general', u'standard') child(u'general', u'competence', u'standard')
child(u'depends', u'general', u'standard') child(u'general', u'depends', u'standard')
child(u'knows', u'general', u'standard') child(u'general', u'knows', u'standard')
child(u'person', u'general', u'standard') child(u'general', u'person', u'standard')
child(u'provides', u'general', u'standard') child(u'general', u'provides', u'standard')
child(u'requires', u'general', u'standard') child(u'general', u'requires', u'standard')
child(u'task', u'general', u'standard') child(u'general', u'task', u'standard')
child(u'topic', u'general', u'standard') child(u'general', u'topic', u'standard')
child(u'training', u'general', u'standard') child(u'general', u'training', u'standard')
child(u'issubtype', u'system', u'standard') child(u'system', u'issubtype', u'standard')
child(u'training', u'competence', u'issubtype', usePredicate=u'provides') child(u'competence', u'training', u'issubtype', usePredicate=u'provides')
# records # records
records(u'qualification', u'loops.knowledge.qualification.QualificationRecord') records(u'qualification', u'loops.knowledge.qualification.QualificationRecord')

View file

@ -0,0 +1,44 @@
type(u'competence', u'Kompetenz', viewName=u'',
typeInterface=u'', options=u'action.portlet:create_subtype,edit_concept')
# type(u'person', u'Person', viewName=u'',
# typeInterface=u'loops.knowledge.interfaces.IPerson',
# options=u'action.portlet:editPerson')
# type(u'task', u'Aufgabe', viewName=u'',
# typeInterface=u'loops.knowledge.interfaces.ITask',
# options=u'action.portlet:createTask,editTask')
# type(u'topic', u'Thema', viewName=u'',
# typeInterface=u'loops.knowledge.interfaces.ITopic',
# options=u'action.portlet:createTask,createTopic,editTopic')
type(u'training', u'Schulung', viewName=u'',
typeInterface=u'loops.organize.interfaces.ITask',
options=u'action.portlet:edit_concept')
concept(u'general', u'Allgemein', u'domain')
concept(u'system', u'System', u'domain')
# predicates
concept(u'depends', u'depends', u'predicate')
concept(u'knows', u'knows', u'predicate')
concept(u'provides', u'provides', u'predicate')
concept(u'requires', u'requires', u'predicate')
concept(u'issubtype', u'is Subtype', u'predicate', options=u'hide_children',
predicateInterface='loops.interfaces.IIsSubtype')
# structure
child(u'general', u'competence', u'standard')
child(u'general', u'depends', u'standard')
child(u'general', u'knows', u'standard')
#child(u'general', u'person', u'standard')
child(u'general', u'provides', u'standard')
child(u'general', u'requires', u'standard')
#child(u'general', u'task', u'standard')
#child(u'general', u'topic', u'standard')
child(u'general', u'training', u'standard')
child(u'system', u'issubtype', u'standard')
child(u'competence', u'training', u'issubtype', usePredicate=u'provides')
# records
records(u'qualification', u'loops.knowledge.qualification.QualificationRecord')

View file

@ -62,6 +62,48 @@
<metal:block define-macro="create_qualification"> <metal:block define-macro="create_qualification">
<form method="post" id="addQualification_form" class="dialog" <form method="post" id="addQualification_form" class="dialog"
dojoType="dijit.form.Form"> dojoType="dijit.form.Form">
<input type="hidden" name="form.action" value="create_qualification" />
<input type="hidden" name="id"
tal:attributes="value request/form/id|nothing" />
<div class="heading" i18n:translate="">Add Qualification Record</div>
<div>
<label i18n:translate="" for="title">Title</label>
<div>
<input name="title" id="title" style="width: 60em"
dojoType="dijit.form.ValidationTextBox" required
tal:attributes="value view/title" /></div>
</div>
<div>
<label i18n:translate="" for="action">Action</label>
<select name="workitem.action" id="action" style="display:none">
<option tal:repeat="action view/actions"
tal:attributes="value action/name"
tal:content="action/title"
i18n:translate="" />
</select>
<span id="target_competence">&nbsp;
<label i18n:translate="assign_to_competence" for="input_competence"
style="display: inline">Assign to Competence</label>
<span dojoType="dojox.data.QueryReadStore"
jsId="competence_search_store"
tal:define="types python:
view.getTypesParamsForFilteringSelect(['competence'])"
tal:attributes="url string:listConceptsForComboBox.js$types;" >
</span>
<input dojoType="dijit.form.FilteringSelect"
autoComplete="False" labelAttr="label" searchDelay="400"
name="competence" id="input_competence"
store="competence_search_store" />
</span>
</div>
<div>
<label i18n:translate="" for="comment">Comment</label>
<div>
<textarea name="comment" cols="80" rows="4" id="comment"
dojoType="dijit.form.SimpleTextarea"
style="width: 60em"
tal:content="view/comment"></textarea></div>
</div>
<div class="buttons"> <div class="buttons">
<input value="Save" type="submit" <input value="Save" type="submit"
i18n:attributes="value"> i18n:attributes="value">

View file

@ -38,8 +38,12 @@ from loops.organize.work.base import WorkItem, WorkItems
@implementer(IStatesDefinition) @implementer(IStatesDefinition)
def qualificationStates(): def qualificationStates():
return StatesDefinition('qualification', return StatesDefinition('qualification',
State('new', 'new', ('assign',),
color='grey'),
State('open', 'open', State('open', 'open',
('register', 'pass', 'fail', 'cancel', 'modify'), ('register',
#'pass', 'fail',
'cancel', 'modify'),
color='red'), color='red'),
State('registered', 'registered', State('registered', 'registered',
('register', 'pass', 'fail', 'unregister', 'cancel', 'modify'), ('register', 'pass', 'fail', 'unregister', 'cancel', 'modify'),
@ -61,14 +65,17 @@ def qualificationStates():
State('open_x', 'open', ('modify',), color='red'), State('open_x', 'open', ('modify',), color='red'),
State('registered_x', 'registered', ('modify',), color='yellow'), State('registered_x', 'registered', ('modify',), color='yellow'),
# transitions: # transitions:
Transition('assign', 'assign', 'open'),
Transition('register', 'register', 'registered'), Transition('register', 'register', 'registered'),
Transition('pass', 'pass', 'passed'), Transition('pass', 'pass', 'passed'),
Transition('fail', 'fail', 'failed'), Transition('fail', 'fail', 'failed'),
Transition('unregister', 'unregister', 'open'), Transition('unregister', 'unregister', 'open'),
Transition('cancel', 'cancel', 'cancelled'), Transition('cancel', 'cancel', 'cancelled'),
Transition('modify', 'modify', 'open'),
Transition('close', 'close', 'closed'), Transition('close', 'close', 'closed'),
Transition('open', 'open', 'open'), Transition('open', 'open', 'open'),
initialState='open') #initialState='open')
initialState='new') # TODO: handle assignment to competence
class QualificationRecord(WorkItem): class QualificationRecord(WorkItem):

Binary file not shown.

View file

@ -3,7 +3,7 @@ msgstr ""
"Project-Id-Version: $Id$\n" "Project-Id-Version: $Id$\n"
"POT-Creation-Date: 2007-05-22 12:00 CET\n" "POT-Creation-Date: 2007-05-22 12:00 CET\n"
"PO-Revision-Date: 2012-05-30 12:00 CET\n" "PO-Revision-Date: 2012-06-10 12:00 CET\n"
"Last-Translator: Helmut Merz <helmutm@cy55.de>\n" "Last-Translator: Helmut Merz <helmutm@cy55.de>\n"
"Language-Team: loops developers <helmutm@cy55.de>\n" "Language-Team: loops developers <helmutm@cy55.de>\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
@ -242,6 +242,9 @@ msgstr "Folgetermin anlegen..."
msgid "Create an event that is linked to this one." msgid "Create an event that is linked to this one."
msgstr "Einen neuen Termin anlegen, der mit diesem verknüpft ist." msgstr "Einen neuen Termin anlegen, der mit diesem verknüpft ist."
msgid "Create Follow-up Event for: $event"
msgstr "Folgetermin anlegen für: $event"
msgid "Edit Event..." msgid "Edit Event..."
msgstr "Termin bearbeiten..." msgstr "Termin bearbeiten..."
@ -290,6 +293,9 @@ msgstr "Besprechungsprotokoll anzeigen..."
msgid "Show meeting minutes for this object." msgid "Show meeting minutes for this object."
msgstr "Besprechungsprotokoll für dieses Objekt anzeigen." msgstr "Besprechungsprotokoll für dieses Objekt anzeigen."
msgid "Download Meeting Minutes"
msgstr "Besprechungsprotokoll generieren"
msgid "Task/Action" msgid "Task/Action"
msgstr "Aufgabe" msgstr "Aufgabe"
@ -395,6 +401,9 @@ msgstr "Thema"
msgid "Task" msgid "Task"
msgstr "Aufgabe" msgstr "Aufgabe"
msgid "Tasks"
msgstr "Aufgaben"
msgid "Domain" msgid "Domain"
msgstr "Bereich" msgstr "Bereich"

View file

@ -260,7 +260,7 @@ Automatic security settings on persons
>>> from zope.traversing.api import getName >>> from zope.traversing.api import getName
>>> list(sorted(getName(c) for c in concepts['person'].getChildren())) >>> list(sorted(getName(c) for c in concepts['person'].getChildren()))
[u'general', u'jim', u'john', u'martha', u'person.newuser'] [u'jim', u'john', u'martha', u'person.newuser']
Person objects that have a user assigned to them receive this user Person objects that have a user assigned to them receive this user
(principal) as their owner. (principal) as their owner.
@ -390,6 +390,11 @@ Events listing
>>> list(listing.events()) >>> list(listing.events())
[<loops.browser.concept.ConceptRelationView ...>] [<loops.browser.concept.ConceptRelationView ...>]
Creation of follow-up event
---------------------------
>>> from loops.organize.browser.event import CreateFollowUpEvent
Send Email to Members Send Email to Members
===================== =====================

View file

@ -23,9 +23,11 @@ Definition of view classes and other browser related stuff for tasks.
import calendar import calendar
from datetime import date, datetime, timedelta from datetime import date, datetime, timedelta
from urllib import urlencode from urllib import urlencode
from zope import interface, component from zope.app.container.interfaces import INameChooser
from zope.app.pagetemplate import ViewPageTemplateFile from zope.app.pagetemplate import ViewPageTemplateFile
from zope.cachedescriptors.property import Lazy from zope.cachedescriptors.property import Lazy
from zope import interface, component
from zope.traversing.api import getName
from cybertools.browser.action import actions from cybertools.browser.action import actions
from cybertools.meta.interfaces import IOptions from cybertools.meta.interfaces import IOptions
@ -34,8 +36,12 @@ from loops.browser.concept import ConceptView
from loops.browser.form import CreateConceptPage, CreateConcept from loops.browser.form import CreateConceptPage, CreateConcept
from loops.browser.form import EditConceptPage, EditConcept from loops.browser.form import EditConceptPage, EditConcept
from loops.browser.node import NodeView from loops.browser.node import NodeView
from loops.common import adapted from loops.common import adapted, baseObject
from loops.concept import Concept
from loops.organize.work.meeting import MeetingMinutes
from loops.setup import addAndConfigureObject
from loops.util import _ from loops.util import _
from loops import util
organize_macros = ViewPageTemplateFile('view_macros.pt') organize_macros = ViewPageTemplateFile('view_macros.pt')
@ -247,12 +253,39 @@ class CalendarInfo(NodeView):
# special forms # special forms
class CreateFollowUpEventForm(CreateConceptPage): class CreateFollowUpEventForm(CreateConceptPage, MeetingMinutes):
fixedType = True fixedType = True
typeToken = '.loops/concepts/event' typeToken = '.loops/concepts/event'
form_action = 'create_followup_event' form_action = 'create_followup_event'
showAssignments = True showAssignments = False
@Lazy
def macro(self):
return organize_macros.macros['create_followup_event']
@Lazy
def baseEvent(self):
return adapted(self.virtualTargetObject)
@Lazy
def title(self):
event = self.baseEvent
evView = ConceptView(event, self.request)
eventTitle = u'%s, %s' % (event.title, evView.data['start'])
return _(u'Create Follow-up Event for: $event',
mapping=dict(event=eventTitle))
@Lazy
def data(self):
data = self.getData()
data['title'] = self.baseEvent.title
data['description'] = self.baseEvent.description
return data
def results(self):
return self.reportInstance.getResults(
dict(tasks=util.getUidForObject(self.virtualTargetObject)))
class EditFollowUpEventForm(EditConceptPage, CreateFollowUpEventForm): class EditFollowUpEventForm(EditConceptPage, CreateFollowUpEventForm):
@ -271,6 +304,55 @@ class CreateFollowUpEvent(CreateConcept, BaseFollowUpController):
defaultTypeToken = '.loops/concepts/event' defaultTypeToken = '.loops/concepts/event'
@Lazy
def followsPredicate(self):
return self.view.conceptManager['follows']
@Lazy
def baseEvent(self):
return adapted(self.view.virtualTargetObject)
def update(self):
result = super(CreateFollowUpEvent, self).update()
form = self.request.form
toBeAssigned = form.get('cb_select_tasks') or []
print '***', toBeAssigned
for uid in toBeAssigned:
task = util.getObjectForUid(uid)
self.createFollowUpTask(adapted(task))
return result
def createFollowUpTask(self, source):
cm = self.view.conceptManager
stask = baseObject(source)
bevt = baseObject(self.baseEvent)
taskType = stask.conceptType
taskName = getName(stask)
name = INameChooser(cm).chooseName(taskName, stask)
newTask = addAndConfigureObject(cm, Concept, name,
conceptType=taskType,
title=source.title,
description=source.description,
start=source.start,
end=source.end)
stask.assignChild(newTask, self.followsPredicate)
for rel in stask.getParentRelations():
if rel.predicate != self.view.typePredicate:
if rel.first == bevt:
parent = self.object
else:
parent = rel.first
newTask.assignParent(parent, rel.predicate,
order=rel.order, relevance=rel.relevance)
return newTask
def assignConcepts(self, obj):
bevt = baseObject(self.baseEvent)
bevt.assignChild(obj, self.followsPredicate)
for rel in bevt.getParentRelations():
if rel.predicate != self.view.typePredicate:
obj.assignParent(rel.first, rel.predicate)
class EditFollowUpEvent(EditConcept, BaseFollowUpController): class EditFollowUpEvent(EditConcept, BaseFollowUpController):

View file

@ -1,5 +1,4 @@
<!-- $Id$ --> <html i18n:domain="loops">
<metal:task define-macro="task"> <metal:task define-macro="task">
<metal:data use-macro="view/concept_macros/conceptdata"> <metal:data use-macro="view/concept_macros/conceptdata">
@ -56,6 +55,26 @@
</metal:block> </metal:block>
<form metal:define-macro="create_followup_event" method="post"
id="dialog_form" class="dialog"
dojoType="dijit.form.Form"
tal:define="item nocall:item|nocall:view">
<h2 tal:content="view/title"
i18n:translate="" />
<input type="hidden" name="form.action"
tal:attributes="value view/form_action" />
<metal:data use-macro="view/fieldRenderers/fields" />
<h3 i18n:translate="">Tasks</h3>
<tal:tasks define="report item/reportInstance;
reportView nocall:item;
results reportView/results;
cb_name string:cb_select_tasks;">
<metal:results use-macro="view/resultsRenderer" />
</tal:tasks>
<metal:buttons use-macro="view/form_macros/buttons" />
</form>
<form metal:define-macro="send_email" <form metal:define-macro="send_email"
method="post" id="sendEmail_form" name="sendEmail" class="dialog" method="post" id="sendEmail_form" name="sendEmail" class="dialog"
dojoType="dijit.form.Form"> dojoType="dijit.form.Form">
@ -113,3 +132,6 @@
</div> </div>
</div> </div>
</form> </form>
</html>

View file

@ -307,6 +307,8 @@ class UserWorkItems(PersonWorkItems):
class CreateWorkItemForm(ObjectForm, BaseTrackView): class CreateWorkItemForm(ObjectForm, BaseTrackView):
template = work_macros template = work_macros
recordManagerName = 'work'
trackFactory = WorkItem
def checkPermissions(self): def checkPermissions(self):
return canAccessObject(self.task or self.target) return canAccessObject(self.task or self.target)
@ -325,9 +327,10 @@ class CreateWorkItemForm(ObjectForm, BaseTrackView):
def track(self): def track(self):
id = self.request.form.get('id') id = self.request.form.get('id')
if id is not None: if id is not None:
workItems = self.loopsRoot.getRecordManager()['work'] workItems = self.loopsRoot.getRecordManager()[
self.recordManagerName]
return workItems.get(id) return workItems.get(id)
return WorkItem(None, 0, None, {}) return self.trackFactory(None, 0, None, {})
@Lazy @Lazy
def title(self): def title(self):

View file

@ -103,6 +103,12 @@
class="loops.organize.work.meeting.MeetingMinutes" class="loops.organize.work.meeting.MeetingMinutes"
permission="zope.View" /> permission="zope.View" />
<browser:page
name="meeting_minutes.doc"
for="loops.interfaces.IConceptSchema"
class="loops.organize.work.meeting.MeetingMinutesDocument"
permission="zope.View" />
<!-- setup --> <!-- setup -->
<zope:adapter factory="loops.organize.work.setup.SetupManager" <zope:adapter factory="loops.organize.work.setup.SetupManager"

View file

@ -3,19 +3,58 @@
<div metal:define-macro="content" <div metal:define-macro="content"
tal:define="report item/reportInstance; tal:define="report item/reportInstance;
reportView nocall:item"> reportView nocall:item;
results reportView/results">
<div tal:attributes="class string:content-$level;"> <div tal:attributes="class string:content-$level;">
<metal:block use-macro="view/concept_macros/concepttitle" /> <metal:block use-macro="view/concept_macros/concepttitle" />
<metal:block use-macro="view/concept_macros/conceptfields" /> <metal:block use-macro="view/concept_macros/conceptfields" />
</div><br /> </div><br />
<span class="button"
tal:condition="item/reportDownload">
<a target="_blank"
tal:attributes="href string:${item/targetUrl}/${item/reportDownload}"
i18n:translate="">Download Meeting Minutes</a></span>
<br />&nbsp;
<div metal:use-macro="reportView/resultsRenderer" /> <div metal:use-macro="reportView/resultsRenderer" />
</div> </div>
<div metal:define-macro="document"
tal:define="item nocall:view;
report item/reportInstance;
reportView nocall:item;
results reportView/results;
fields results/context/fields">
<metal:header use-macro="item/macros/header" />
<metal:results use-macro="reportView/resultsRenderer" />
</div>
<div metal:define-macro="header"
tal:define="row results/first">
<h1 i18n:translate="">Meeting Minutes</h1>
<h2 tal:define="col fields/eventTitle">
<metal:col use-macro="python:item.getColumnRenderer(col)" /></h2>
<div>
<span tal:define="col fields/eventStart">
<metal:col use-macro="python:item.getColumnRenderer(col)" /></span> -
<span tal:define="col fields/eventEnd">
<metal:col use-macro="python:item.getColumnRenderer(col)" /></span>
</div>
<div tal:define="col fields/eventDescription">
<metal:col use-macro="python:item.getColumnRenderer(col)" /></div>
</div>
<div metal:define-macro="results"> <div metal:define-macro="results">
<table class="report" <table class="report"
tal:define="results reportView/results"> tal:define="showCheckboxes cb_name|nothing">
<tr> <tr>
<th tal:condition="showCheckboxes"
style="border: 1px solid grey">
<input type="checkbox" checked
tal:attributes="onclick
string:toggleCheckBoxes(this, '$cb_name:list')" /></th>
<th i18n:translate="" <th i18n:translate=""
style="border: 1px solid grey">Task/Action</th> style="border: 1px solid grey">Task/Action</th>
<th style="border: 1px solid grey" <th style="border: 1px solid grey"
@ -27,6 +66,12 @@
</tr> </tr>
<tal:task repeat="row results"> <tal:task repeat="row results">
<tr tal:repeat="colname python: ('title', 'description',)"> <tr tal:repeat="colname python: ('title', 'description',)">
<td tal:condition="showCheckboxes"
style="border: 1px solid grey">
<input type="checkbox" checked
tal:condition="python:colname == 'title'"
tal:attributes="name string:$cb_name:list;
value row/context/uid" /></td>
<td style="border: 1px solid grey" <td style="border: 1px solid grey"
tal:define="col report/fields/?colname" tal:define="col report/fields/?colname"
tal:attributes="class col/cssClass"> tal:attributes="class col/cssClass">
@ -49,6 +94,8 @@
<div metal:define-macro="subreport" <div metal:define-macro="subreport"
tal:define="results python:col.getValue(row)"> tal:define="results python:col.getValue(row)">
<tr class="listing" tal:repeat="row results"> <tr class="listing" tal:repeat="row results">
<td tal:condition="showCheckboxes"
style="border: 1px solid grey" />
<td tal:repeat="col results/displayedColumns" <td tal:repeat="col results/displayedColumns"
tal:attributes="class col/cssClass" tal:attributes="class col/cssClass"
style="border: 1px solid grey"> style="border: 1px solid grey">

View file

@ -24,6 +24,7 @@ from zope.app.pagetemplate import ViewPageTemplateFile
from zope.cachedescriptors.property import Lazy from zope.cachedescriptors.property import Lazy
from cybertools.browser.action import actions from cybertools.browser.action import actions
from cybertools.docgen.base import WordDocument
from loops.browser.action import TargetAction from loops.browser.action import TargetAction
from loops.expert.browser.report import ResultsConceptView from loops.expert.browser.report import ResultsConceptView
from loops.util import _ from loops.util import _
@ -41,6 +42,7 @@ actions.register('meeting_minutes', 'portlet', TargetAction,
class MeetingMinutes(ResultsConceptView): class MeetingMinutes(ResultsConceptView):
reportName = 'meeting_minutes' reportName = 'meeting_minutes'
reportDownload = 'meeting_minutes.doc'
@Lazy @Lazy
def meeting_macros(self): def meeting_macros(self):
@ -59,3 +61,25 @@ class MeetingMinutes(ResultsConceptView):
if renderer == 'subreport': if renderer == 'subreport':
return self.meeting_macros[renderer] return self.meeting_macros[renderer]
return self.result_macros[renderer] return self.result_macros[renderer]
class MeetingMinutesDocument(WordDocument, MeetingMinutes):
isToplevel = True
def __init__(self, context, request):
MeetingMinutes.__init__(self, context, request)
@Lazy
def macros(self):
return meeting_template.macros
@Lazy
def reportRenderer(self):
return self.macros['document']
@Lazy
def content(self):
return self.reportRenderer

View file

@ -26,14 +26,14 @@ from zope.component import adapter
from cybertools.composer.report.base import Report from cybertools.composer.report.base import Report
from cybertools.composer.report.base import LeafQueryCriteria, CompoundQueryCriteria from cybertools.composer.report.base import LeafQueryCriteria, CompoundQueryCriteria
from cybertools.composer.report.field import Field from cybertools.composer.report.field import Field, CalculatedField
from cybertools.composer.report.result import ResultSet, Row as BaseRow from cybertools.composer.report.result import ResultSet, Row as BaseRow
from cybertools.organize.interfaces import IWorkItems from cybertools.organize.interfaces import IWorkItems
from cybertools.util.date import timeStamp2Date from cybertools.util.date import timeStamp2Date
from cybertools.util.format import formatDate from cybertools.util.format import formatDate
from cybertools.util.jeep import Jeep from cybertools.util.jeep import Jeep
from loops.common import adapted, baseObject from loops.common import adapted, baseObject
from loops.expert.field import TargetField, TextField, UrlField from loops.expert.field import TargetField, DateField, TextField, UrlField
from loops.expert.field import SubReport, SubReportField from loops.expert.field import SubReport, SubReportField
from loops.expert.report import ReportInstance from loops.expert.report import ReportInstance
from loops import util from loops import util
@ -44,7 +44,8 @@ class StateField(Field):
value = self.getValue(row) value = self.getValue(row)
return util._(value) return util._(value)
class DateField(Field):
class TrackDateField(Field):
part = 'date' part = 'date'
format = 'short' format = 'short'
@ -65,7 +66,7 @@ class DateField(Field):
return u'' return u''
class TimeField(DateField): class TrackTimeField(TrackDateField):
part = 'time' part = 'time'
@ -105,14 +106,14 @@ dayFrom = Field('dayFrom', u'Start Day',
dayTo = Field('dayTo', u'End Day', dayTo = Field('dayTo', u'End Day',
description=u'The last day until which to select work.', description=u'The last day until which to select work.',
executionSteps=['query']) executionSteps=['query'])
day = DateField('day', u'Day', day = TrackDateField('day', u'Day',
description=u'The day the work was done.', description=u'The day the work was done.',
cssClass='center', cssClass='center',
executionSteps=['sort', 'output']) executionSteps=['sort', 'output'])
timeStart = TimeField('start', u'Start', timeStart = TrackTimeField('start', u'Start',
description=u'The time the unit of work was started.', description=u'The time the unit of work was started.',
executionSteps=['sort', 'output']) executionSteps=['sort', 'output'])
timeEnd = TimeField('end', u'End', timeEnd = TrackTimeField('end', u'End',
description=u'The time the unit of work was finished.', description=u'The time the unit of work was finished.',
executionSteps=['output']) executionSteps=['output'])
task = TargetField('taskId', u'Task', task = TargetField('taskId', u'Task',
@ -272,7 +273,21 @@ class MeetingMinutesWork(WorkReportInstance, SubReport):
return [] return []
taskTitle = UrlField('title', u'Title', eventTitle = CalculatedField('eventTitle', u'Event Title',
description=u'',
executionSteps=(['header']))
eventDescription = CalculatedField('eventDescription', u'Event Description',
description=u'',
executionSteps=(['header']))
eventStart = DateField('eventStart', u'Event Start',
description=u'',
format=('dateTime', 'short'),
executionSteps=(['header']))
eventEnd = DateField('eventEnd', u'Event End',
description=u'',
format=('dateTime', 'short'),
executionSteps=(['header']))
taskTitle = UrlField('title', u'Task Title',
description=u'The short description of the task.', description=u'The short description of the task.',
cssClass='header-1', cssClass='header-1',
executionSteps=['output']) executionSteps=['output'])
@ -288,27 +303,46 @@ workItems = SubReportField('workItems', u'Work Items',
class TaskRow(BaseRow): class TaskRow(BaseRow):
pass @Lazy
def event(self):
return self.parent.context.view.adapted
@Lazy
def eventTitle(self):
return self.event.title
@Lazy
def eventDescription(self):
return self.event.description
@Lazy
def eventStart(self):
return self.event.start
@Lazy
def eventEnd(self):
return self.event.end
useRowProperty = BaseRow.useRowProperty
attributeHandlers = dict(
eventStart=useRowProperty,
eventEnd=useRowProperty,
)
class MeetingMinutes(WorkReportInstance): class MeetingMinutes(WorkReportInstance):
# TODO:
# header (event) fields: title, description, from/to,
# location, participants (or put in description?)
# result set field for work items
# work item fields: title, description, party, deadline, state
type = "meeting_minutes" type = "meeting_minutes"
label = u'Meeting Minutes' label = u'Meeting Minutes'
rowFactory = TaskRow rowFactory = TaskRow
fields = Jeep((tasks, taskTitle, taskDescription, workItems)) fields = Jeep((eventTitle, eventStart, eventEnd, eventDescription,
tasks, taskTitle, taskDescription, workItems))
defaultOutputFields = fields defaultOutputFields = fields
states = ('planned', 'accepted', 'done', 'done_x', 'finished') states = ('planned', 'accepted', 'done', 'done_x', 'finished')
def selectObjects(self, parts): def selectObjects(self, parts):
return self.getTasks(parts)[1:] return [adapted(t) for t in self.getTasks(parts)[1:]]