work in progress: reporting; example 'work statement' report is operational
This commit is contained in:
		
							parent
							
								
									55fbb7eefc
								
							
						
					
					
						commit
						0a1d2ec262
					
				
					 5 changed files with 150 additions and 102 deletions
				
			
		|  | @ -15,7 +15,25 @@ | |||
| 
 | ||||
| 
 | ||||
| <div metal:define-macro="results"> | ||||
|   Default Results Listing | ||||
|   <h2 i18n:translate="">Work Items</h2> | ||||
|   <table tal:define="results view/results"> | ||||
|     <tr> | ||||
|         <th tal:repeat="col view/displayedColumns" | ||||
|             tal:content="col/title" | ||||
|             i18n:translate="" /> | ||||
|     </tr> | ||||
|     <tr tal:repeat="row results"> | ||||
|         <tal:column repeat="col view/displayedColumns"> | ||||
|             <metal:column use-macro="python:view.getColumnRenderer(col.renderer)" /> | ||||
|         </tal:column> | ||||
|     </tr> | ||||
|     <tr tal:define="row nocall:results/totals" | ||||
|         tal:condition="nocall:row"> | ||||
|         <tal:column repeat="col view/displayedColumns"> | ||||
|             <metal:column use-macro="python:view.getColumnRenderer(col.renderer)" /> | ||||
|         </tal:column> | ||||
|     </tr> | ||||
|   </table> | ||||
| </div> | ||||
| 
 | ||||
| 
 | ||||
|  | @ -24,11 +42,18 @@ | |||
| </metal:standard> | ||||
| 
 | ||||
| 
 | ||||
| <metal:standard define-macro="target"> | ||||
| <metal:right define-macro="right"> | ||||
|   <td style="text-align: right" | ||||
|       tal:content="python:col.getDisplayValue(row)" /> | ||||
| </metal:right> | ||||
| 
 | ||||
| 
 | ||||
| <metal:target define-macro="target"> | ||||
|   <td tal:define="value python:col.getDisplayValue(row)"> | ||||
|     <a tal:attributes="href value/url" | ||||
|     <a tal:omit-tag="not:value/url" | ||||
|        tal:attributes="href value/url" | ||||
|        tal:content="value/title" /></td> | ||||
| </metal:standard> | ||||
| </metal:target> | ||||
| 
 | ||||
| 
 | ||||
| </html> | ||||
|  |  | |||
							
								
								
									
										43
									
								
								expert/field.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								expert/field.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,43 @@ | |||
| # | ||||
| #  Copyright (c) 2011 Helmut Merz helmutm@cy55.de | ||||
| # | ||||
| #  This program is free software; you can redistribute it and/or modify | ||||
| #  it under the terms of the GNU General Public License as published by | ||||
| #  the Free Software Foundation; either version 2 of the License, or | ||||
| #  (at your option) any later version. | ||||
| # | ||||
| #  This program is distributed in the hope that it will be useful, | ||||
| #  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
| #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
| #  GNU General Public License for more details. | ||||
| # | ||||
| #  You should have received a copy of the GNU General Public License | ||||
| #  along with this program; if not, write to the Free Software | ||||
| #  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | ||||
| # | ||||
| 
 | ||||
| """ | ||||
| Field definitions for reports. | ||||
| """ | ||||
| 
 | ||||
| from cybertools.composer.report.field import Field | ||||
| from loops import util | ||||
| 
 | ||||
| 
 | ||||
| class TargetField(Field): | ||||
| 
 | ||||
|     renderer = 'target' | ||||
| 
 | ||||
|     def getValue(self, row): | ||||
|         value = self.getRawValue(row) | ||||
|         if value is None: | ||||
|             return None | ||||
|         return util.getObjectForUid(value) | ||||
| 
 | ||||
|     def getDisplayValue(self, row): | ||||
|         value = self.getValue(row) | ||||
|         if value is None: | ||||
|             return dict(title=u'', url=u'') | ||||
|         view = row.parent.context.view | ||||
|         return dict(title=value.title, url=view.getUrlForTarget(value)) | ||||
| 
 | ||||
|  | @ -220,16 +220,20 @@ in a results view. | |||
|   >>> from loops.expert.browser.report import ReportView, ResultsView | ||||
|   >>> resultsView = ResultsView(reportNode, TestRequest()) | ||||
| 
 | ||||
|   >>> results = resultsView.results().getResult() | ||||
|   >>> len(results) | ||||
|   >>> results = resultsView.results()#.getResult() | ||||
|   >>> len(list(results)) | ||||
|   1 | ||||
|   >>> for row in results: | ||||
|   ...     for col in resultsView.displayedColumns: | ||||
|   ...         print col.getDisplayValue(row), | ||||
|   ...     print | ||||
|   08/12/28 1230487200 1230491700 | ||||
|   08/12/28 19:00 20:15 | ||||
|     {'url': '.../home/report/.36', 'title': u'loops Development'} | ||||
|     {'url': '.../home/report/.33', 'title': u'john'}  4500 900 finished | ||||
|     {'url': '.../home/report/.33', 'title': u'john'}  01:15 00:15 finished | ||||
| 
 | ||||
|   >>> results.totals.data | ||||
|   {'effort': 900} | ||||
| 
 | ||||
| 
 | ||||
| Fin de partie | ||||
| ============= | ||||
|  |  | |||
|  | @ -33,41 +33,57 @@ from cybertools.util.date import timeStamp2Date | |||
| from cybertools.util.format import formatDate | ||||
| from cybertools.util.jeep import Jeep | ||||
| from loops.common import adapted, baseObject | ||||
| from loops.expert.field import TargetField | ||||
| from loops.expert.report import ReportInstance | ||||
| from loops import util | ||||
| 
 | ||||
| results_template = ViewPageTemplateFile('results.pt') | ||||
| 
 | ||||
| class DateField(Field): | ||||
| 
 | ||||
| class TargetField(Field): | ||||
| 
 | ||||
|     renderer = 'target' | ||||
|     part = 'date' | ||||
|     format = 'short' | ||||
|     renderer = 'right' | ||||
| 
 | ||||
|     def getValue(self, row): | ||||
|         value = self.getRawValue(row) | ||||
|         return util.getObjectForUid(value) | ||||
| 
 | ||||
|     def getDisplayValue(self, row): | ||||
|         value = self.getValue(row) | ||||
|         if value is None: | ||||
|             return dict(title=self.getRawValue(row), url=u'') | ||||
|         view = row.parent.context.view | ||||
|         return dict(title=value.title, url=view.getUrlForTarget(value)) | ||||
| 
 | ||||
| 
 | ||||
| class DayField(Field): | ||||
| 
 | ||||
|     def getValue(self, row): | ||||
|         return timeStamp2Date(self.getRawValue(row)) | ||||
|             return None | ||||
|         return timeStamp2Date(value) | ||||
| 
 | ||||
|     def getDisplayValue(self, row): | ||||
|         value = self.getValue(row) | ||||
|         if value: | ||||
|             view = row.parent.context.view | ||||
|             return formatDate(value, 'date', 'short', view.languageInfo.language) | ||||
|             return formatDate(value, self.part, self.format, | ||||
|                               view.languageInfo.language) | ||||
|         return u'' | ||||
| 
 | ||||
| 
 | ||||
| class TimeField(DateField): | ||||
| 
 | ||||
|     part = 'time' | ||||
| 
 | ||||
| 
 | ||||
| class DurationField(Field): | ||||
| 
 | ||||
|     renderer = 'right' | ||||
| 
 | ||||
|     def getValue(self, row): | ||||
|         value = self.getRawValue(row) | ||||
|         if value and 'totals' in self.executionSteps: | ||||
|             data = row.parent.totals.data | ||||
|             data[self.name] = data.get(self.name, 0) + value | ||||
|         if value: | ||||
|             value /= 3600.0 | ||||
|         return value | ||||
| 
 | ||||
|     def getDisplayValue(self, row): | ||||
|         value = self.getValue(row) | ||||
|         if not value: | ||||
|             return u'' | ||||
|         return u'%02i:%02i' % divmod(value * 60, 60) | ||||
| 
 | ||||
| 
 | ||||
| tasks = Field('tasks', u'Tasks', | ||||
|                 description=u'The tasks from which work items should be selected.', | ||||
|                 executionSteps=['query']) | ||||
|  | @ -77,13 +93,13 @@ dayFrom = Field('dayFrom', u'Start Day', | |||
| dayTo = Field('dayTo', u'End Day', | ||||
|                 description=u'The last day until which to select work.', | ||||
|                 executionSteps=['query']) | ||||
| day = DayField('day', u'Day', | ||||
| day = DateField('day', u'Day', | ||||
|                 description=u'The day the work was done.', | ||||
|                 executionSteps=['sort', 'output']) | ||||
| timeStart = Field('start', u'Start Time', | ||||
| timeStart = TimeField('start', u'Start', | ||||
|                 description=u'The time the unit of work was started.', | ||||
|                 executionSteps=['sort', 'output']) | ||||
| timeEnd = Field('end', u'End Time', | ||||
| timeEnd = TimeField('end', u'End', | ||||
|                 description=u'The time the unit of work was finished.', | ||||
|                 executionSteps=['output']) | ||||
| task = TargetField('taskId', u'Task', | ||||
|  | @ -91,19 +107,19 @@ task = TargetField('taskId', u'Task', | |||
|                 executionSteps=['output']) | ||||
| party = TargetField('userName', u'Party', | ||||
|                 description=u'The party (usually a person) who did the work.', | ||||
|                 executionSteps=['sort', 'output']) | ||||
|                 executionSteps=['query', 'sort', 'output']) | ||||
| title = Field('title', u'Title', | ||||
|                 description=u'The short description of the work.', | ||||
|                 executionSteps=['output']) | ||||
| description = Field('description', u'Description', | ||||
|                 description=u'The long description of the work.', | ||||
|                 executionSteps=['x_output']) | ||||
| duration = Field('duration', u'Duration', | ||||
| duration = DurationField('duration', u'Duration', | ||||
|                 description=u'The duration of the work.', | ||||
|                 executionSteps=['output']) | ||||
| effort = Field('effort', u'Effort', | ||||
| effort = DurationField('effort', u'Effort', | ||||
|                 description=u'The effort of the work.', | ||||
|                 executionSteps=['output', 'sum']) | ||||
|                 executionSteps=['output', 'totals']) | ||||
| state = Field('state', u'State', | ||||
|                 description=u'The state of the work.', | ||||
|                 executionSteps=['query', 'output']) | ||||
|  | @ -122,7 +138,19 @@ class WorkRow(BaseRow): | |||
|     def getDay(self, attr): | ||||
|         return self.context.timeStamp | ||||
| 
 | ||||
|     attributeHandlers = dict(day=getDay) | ||||
|     def getDuration(self, attr): | ||||
|         value = self.context.data.get('duration') | ||||
|         if value is None: | ||||
|             value = self.getRawValue('end') - self.getRawValue('start') | ||||
|         return value | ||||
| 
 | ||||
|     def getEffort(self, attr): | ||||
|         value = self.context.data.get('effort') | ||||
|         if value is None: | ||||
|             value = self.getDuration(attr) | ||||
|         return value | ||||
| 
 | ||||
|     attributeHandlers = dict(day=getDay, duration=getDuration, effort=getEffort) | ||||
| 
 | ||||
| 
 | ||||
| class WorkReportInstance(ReportInstance): | ||||
|  | @ -131,37 +159,46 @@ class WorkReportInstance(ReportInstance): | |||
|     label = u'Work Report' | ||||
| 
 | ||||
|     rowFactory = WorkRow | ||||
| 
 | ||||
|     fields = Jeep((dayFrom, dayTo, tasks, | ||||
|                    day, timeStart, timeEnd, task, party, title, description, | ||||
|                    duration, effort, state)) | ||||
| 
 | ||||
|     defaultOutputFields = fields | ||||
|     defaultSortCriteria = (day, timeStart,) | ||||
| 
 | ||||
|     @property | ||||
|     def queryCriteria(self): | ||||
|         form = self.view.request.form | ||||
|         crit = self.context.queryCriteria | ||||
|         if crit is None: | ||||
|             f = self.fields['tasks'] | ||||
|             tasks = baseObject(self.context).getChildren([self.hasReportPredicate]) | ||||
|             tasks = [util.getUidForObject(task) for task in tasks] | ||||
|             crit = [LeafQueryCriteria(f.name, f.operator, tasks, f)] | ||||
|         for f in self.getAllQueryFields(): | ||||
|             if f.name in form: | ||||
|                 crit.append(LeafQueryCriteria(f.name, f.operator, form[f.name], f)) | ||||
|         return CompoundQueryCriteria(crit) | ||||
| 
 | ||||
|     def getResultsRenderer(self, name, defaultMacros): | ||||
|         return results_template.macros[name] | ||||
| 
 | ||||
|     def selectObjects(self, parts): | ||||
|         result = [] | ||||
|         tasks = parts.pop('tasks').comparisonValue | ||||
|         tasks = [util.getObjectForUid(t) for t in parts.pop('tasks').comparisonValue] | ||||
|         for t in list(tasks): | ||||
|             tasks.extend(self.getAllSubtasks(t)) | ||||
|         for t in tasks: | ||||
|             result.extend(self.selectWorkItems(t, parts)) | ||||
|         # TODO: remove parts already used for selection from parts list | ||||
|         # remove parts already used for selection from parts list: | ||||
|         parts.pop('userName', None) | ||||
|         return result | ||||
| 
 | ||||
|     def selectWorkItems(self, task, parts): | ||||
|         wi = self.workItems | ||||
|         states = ['done', 'done_x', 'finished'] | ||||
|         return wi.query(task=util.getUidForObject(task), state=states) | ||||
|         kw = dict(task=util.getUidForObject(task), state=states) | ||||
|         if 'userName' in parts: | ||||
|             kw['userName'] = parts['userName'].comparisonValue | ||||
|         wi = self.workItems | ||||
|         return wi.query(**kw) | ||||
| 
 | ||||
|     def getAllSubtasks(self, concept): | ||||
|         result = [] | ||||
|  |  | |||
|  | @ -1,61 +0,0 @@ | |||
| <html i18n:domain="loops"> | ||||
| 
 | ||||
| 
 | ||||
| <div metal:define-macro="results"> | ||||
|   <h2 i18n:translate="">Work Items</h2> | ||||
|   <table> | ||||
|     <tr> | ||||
|         <th tal:repeat="col view/displayedColumns" | ||||
|             tal:content="col/title" | ||||
|             i18n:translate="" /> | ||||
|     </tr> | ||||
|     <tr tal:repeat="row view/results"> | ||||
|         <tal:column repeat="col view/displayedColumns"> | ||||
|             <metal:column use-macro="python:view.getColumnRenderer(col.renderer)" /> | ||||
|         </tal:column> | ||||
|     </tr> | ||||
|   </table> | ||||
| </div> | ||||
| 
 | ||||
| 
 | ||||
| <div metal:define-macro="xx_results"> | ||||
|   Work Items Listing | ||||
|   <table class="listing"> | ||||
|       <tr> | ||||
|         <tal:colheader repeat="column work/allColumns"> | ||||
|           <th tal:condition="python: column in work.columns" | ||||
|               tal:content="column" | ||||
|               i18n:translate="">Task</th> | ||||
|         </tal:colheader> | ||||
|       </tr> | ||||
|       <tal:workitem tal:repeat="row work/listWorkItems"> | ||||
|         <tr tal:condition="row/monthChanged"> | ||||
|           <td class="headline" | ||||
|               tal:attributes="colspan python: len(work.columns)" | ||||
|               tal:content="row/month">2009-01</td></tr> | ||||
|         <tr tal:attributes="class python: | ||||
|                                 (repeat['row'].odd() and 'even' or 'odd')"> | ||||
|           <td class="nowrap center" | ||||
|               tal:define="today python: row.isToday and ' today' or ''" | ||||
|               tal:attributes="title row/weekDay; | ||||
|                               class string:nowrap center$today" | ||||
|               i18n:attributes="title" | ||||
|               tal:content="row/day">2007-03-30</td> | ||||
|           <td class="nowrap center" tal:content="row/start">17:30</td> | ||||
|           <td class="nowrap center" tal:content="row/end">20:00</td> | ||||
|           <td class="nowrap center" tal:content="row/duration">2:30</td> | ||||
|           <td tal:condition="python: 'Task' in work.columns"> | ||||
|             <a tal:attributes="href row/objectData/url" | ||||
|                tal:content="row/objectData/title">Task</a></td> | ||||
|           <td tal:condition="python: 'User' in work.columns"> | ||||
|             <a tal:attributes="href row/user/url" | ||||
|               tal:content="row/user/title">John</a></td> | ||||
|           <td tal:content="row/track/title" | ||||
|               tal:attributes="title row/descriptionPlain">Title</td> | ||||
|         </tr> | ||||
|       </tal:workitem> | ||||
|   </table> | ||||
| </div> | ||||
| 
 | ||||
| 
 | ||||
| </html> | ||||
		Loading…
	
	Add table
		
		Reference in a new issue