improve queries: make configurable via query options
git-svn-id: svn://svn.cy55.de/Zope3/src/loops/trunk@3181 fd906abe-77d9-0310-91a1-e0d9ade77398
This commit is contained in:
		
							parent
							
								
									8ef18c8b59
								
							
						
					
					
						commit
						466f05e5a8
					
				
					 4 changed files with 113 additions and 51 deletions
				
			
		|  | @ -102,16 +102,15 @@ by calling the form controller's update method | ||||||
| Work items views | Work items views | ||||||
| ---------------- | ---------------- | ||||||
| 
 | 
 | ||||||
|   >>> from loops.organize.work.browser import WorkItemView, WorkItemsView |   >>> from loops.organize.work.browser import WorkItemView, TaskWorkItems | ||||||
|   >>> wi01 = workItems['0000001'] |   >>> wi01 = workItems['0000001'] | ||||||
|   >>> view = WorkItemView(wi01, TestRequest()) |   >>> view = WorkItemView(wi01, TestRequest()) | ||||||
|   >>> view.taskUrl |   >>> view.taskUrl | ||||||
|   'http://127.0.0.1/loops/concepts/loops_dev/@@SelectedManagementView.html' |   'http://127.0.0.1/loops/concepts/loops_dev/@@SelectedManagementView.html' | ||||||
| 
 | 
 | ||||||
|   >>> itemsView = WorkItemsView(home, request) |   >>> work = TaskWorkItems(task01, request) | ||||||
| 
 |  | ||||||
|   >>> from loops.organize.work.browser import WorkItemDetails |   >>> from loops.organize.work.browser import WorkItemDetails | ||||||
|   >>> view = WorkItemDetails(itemsView, wi01) |   >>> view = WorkItemDetails(work, wi01) | ||||||
|   >>> view.day, view.start, view.end |   >>> view.day, view.start, view.end | ||||||
|   (u'08/12/28', u'19:00', u'20:15') |   (u'08/12/28', u'19:00', u'20:15') | ||||||
| 
 | 
 | ||||||
|  | @ -144,6 +143,39 @@ work item, the form will be pre-filled with some of the item's data. | ||||||
|    {'name': 'modify', 'title': 'modify'}] |    {'name': 'modify', 'title': 'modify'}] | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | Work Item Queries | ||||||
|  | ================= | ||||||
|  | 
 | ||||||
|  |   >>> from loops.common import adapted | ||||||
|  |   >>> from loops.expert.concept import IQueryConcept | ||||||
|  |   >>> from loops.organize.work.browser import UserWorkItems, PersonWorkItems | ||||||
|  | 
 | ||||||
|  |   >>> tQuery = addAndConfigureObject(concepts, Concept, 'query', | ||||||
|  |   ...                   title=u'Query', conceptType=concepts.getTypeConcept(), | ||||||
|  |   ...                   typeInterface=IQueryConcept) | ||||||
|  | 
 | ||||||
|  |   >>> query = addAndConfigureObject(concepts, Concept, 'userworkitems', | ||||||
|  |   ...                               conceptType=tQuery) | ||||||
|  | 
 | ||||||
|  | The UserWorkItems view does not give any results because there is no current | ||||||
|  | user (principal) available in our test setting. | ||||||
|  | 
 | ||||||
|  |   >>> work = UserWorkItems(query, TestRequest()) | ||||||
|  |   >>> work.listWorkItems() | ||||||
|  | 
 | ||||||
|  | So we use the PersonWorkItems view, assigning john to the query. | ||||||
|  | 
 | ||||||
|  |   >>> query.assignParent(johnC) | ||||||
|  |   >>> adapted(query).options = ['wi_from:2009-01-01', 'wi_to:today'] | ||||||
|  | 
 | ||||||
|  |   >>> input = dict() | ||||||
|  |   >>> work = PersonWorkItems(query, TestRequest(form=input)) | ||||||
|  |   >>> work.listWorkItems() | ||||||
|  |   [<WorkItem ['36', 2, '33', '2009-01-19 08:00', 'planned']: | ||||||
|  |    {'start': 1232352000, 'created': ..., 'title': u'Install Zope', | ||||||
|  |     'creator': '33'}>] | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| Fin de partie | Fin de partie | ||||||
| ============= | ============= | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -22,6 +22,7 @@ View class(es) for work items. | ||||||
| $Id$ | $Id$ | ||||||
| """ | """ | ||||||
| 
 | 
 | ||||||
|  | from datetime import date | ||||||
| import time | import time | ||||||
| from zope import component | from zope import component | ||||||
| from zope.app.security.interfaces import IAuthentication, PrincipalLookupError | from zope.app.security.interfaces import IAuthentication, PrincipalLookupError | ||||||
|  | @ -165,6 +166,10 @@ class BaseWorkItemsView(object): | ||||||
|     def work_macros(self): |     def work_macros(self): | ||||||
|         return work_macros.macros |         return work_macros.macros | ||||||
| 
 | 
 | ||||||
|  |     @property | ||||||
|  |     def macro(self): | ||||||
|  |         return self.work_macros['workitems_query'] | ||||||
|  | 
 | ||||||
|     @Lazy |     @Lazy | ||||||
|     def workItems(self): |     def workItems(self): | ||||||
|         rm = self.loopsRoot.getRecordManager() |         rm = self.loopsRoot.getRecordManager() | ||||||
|  | @ -177,14 +182,20 @@ class BaseWorkItemsView(object): | ||||||
|     def baseCriteria(self): |     def baseCriteria(self): | ||||||
|         result = {} |         result = {} | ||||||
|         form = self.request.form |         form = self.request.form | ||||||
|         start = parseDate(form.get('wi_start')) |         tsFrom = parseDate(form.get('wi_from') or self.options.wi_from) | ||||||
|         end = parseDate(form.get('wi_end')) |         tsTo = parseDate(form.get('wi_to') or self.options.wi_to) | ||||||
|         if end: |         if tsTo: | ||||||
|             end += 3600 * 24    # include full end date |             tsTo += 3600 * 24    # include full end date | ||||||
|         if start or end: |         if tsFrom or tsTo: | ||||||
|             result['timeFromTo'] = (start, end) |             result['timeFromTo'] = (tsFrom, tsTo) | ||||||
|         state = form.get('wi_state') |         state = form.get('wi_state') or self.options.wi_state | ||||||
|         if state is not None: |         if not state: | ||||||
|  |             result['state'] = ['planned', 'accepted', 'running', 'done', 'done_x', | ||||||
|  |                              'finished', 'delegated'] | ||||||
|  |         else: | ||||||
|  |             if state == 'all': | ||||||
|  |                 del result['state'] | ||||||
|  |             else: | ||||||
|                 result['state'] = state |                 result['state'] = state | ||||||
|         return result |         return result | ||||||
| 
 | 
 | ||||||
|  | @ -195,17 +206,15 @@ class BaseWorkItemsView(object): | ||||||
|                 for wi in self.workItems.query(**criteria)] |                 for wi in self.workItems.query(**criteria)] | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class WorkItemsView(BaseWorkItemsView, NodeView): | class TaskWorkItems(BaseWorkItemsView, ConceptView): | ||||||
|     """ Standard view for showing work items for a node's target. |     """ Standard view for showing work items for a node's target. | ||||||
|     """ |     """ | ||||||
| 
 | 
 | ||||||
|     columns = set(['User', 'Title', 'Day', 'Start', 'End', 'Duration', 'Info']) |     columns = set(['User', 'Title', 'Day', 'Start', 'End', 'Duration', 'Info']) | ||||||
| 
 | 
 | ||||||
|     @Lazy |  | ||||||
|     def listWorkItems(self): |     def listWorkItems(self): | ||||||
|         target = self.virtualTargetObject |  | ||||||
|         criteria = self.baseCriteria |         criteria = self.baseCriteria | ||||||
|         criteria['task'] = util.getUidForObject(target) |         criteria['task'] = util.getUidForObject(self.context) | ||||||
|         return sorted(self.query(**criteria), key=lambda x: x.track.timeStamp) |         return sorted(self.query(**criteria), key=lambda x: x.track.timeStamp) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | @ -215,22 +224,9 @@ class PersonWorkItems(BaseWorkItemsView, ConceptView): | ||||||
| 
 | 
 | ||||||
|     columns = set(['Task', 'Title', 'Day', 'Start', 'End', 'Duration', 'Info']) |     columns = set(['Task', 'Title', 'Day', 'Start', 'End', 'Duration', 'Info']) | ||||||
| 
 | 
 | ||||||
|     @property |  | ||||||
|     def macro(self): |  | ||||||
|         return self.work_macros['userworkitems'] |  | ||||||
| 
 |  | ||||||
|     def getCriteria(self): |     def getCriteria(self): | ||||||
|         crit = self.baseCriteria |         return self.baseCriteria | ||||||
|         tft = crit.get('timeFromTo') or (None, None) |  | ||||||
|         if not tft[0]: |  | ||||||
|             tft = (getTimeStamp() - (3 * 24 * 3600), tft[1]) |  | ||||||
|             crit['timeFromTo'] = tft |  | ||||||
|         if not crit.get('state'): |  | ||||||
|             crit['state'] = ['planned', 'accepted', 'running', 'done', 'done_x', |  | ||||||
|                              'finished', 'delegated'] |  | ||||||
|         return crit |  | ||||||
| 
 | 
 | ||||||
|     @Lazy |  | ||||||
|     def listWorkItems(self): |     def listWorkItems(self): | ||||||
|         criteria = self.getCriteria() |         criteria = self.getCriteria() | ||||||
|         for target in self.context.getParents([self.defaultPredicate]): |         for target in self.context.getParents([self.defaultPredicate]): | ||||||
|  | @ -239,6 +235,16 @@ class PersonWorkItems(BaseWorkItemsView, ConceptView): | ||||||
|         return sorted(self.query(**criteria), key=lambda x: x.track.timeStamp) |         return sorted(self.query(**criteria), key=lambda x: x.track.timeStamp) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | class UserWorkItems(PersonWorkItems): | ||||||
|  | 
 | ||||||
|  |     def listWorkItems(self): | ||||||
|  |         criteria = self.getCriteria() | ||||||
|  |         p = getPersonForUser(self.context, self.request) | ||||||
|  |         if p is not None: | ||||||
|  |             criteria['userName'] = util.getUidForObject(p) | ||||||
|  |             return sorted(self.query(**criteria), key=lambda x: x.track.timeStamp) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| # forms and form controllers | # forms and form controllers | ||||||
| 
 | 
 | ||||||
| class CreateWorkItemForm(ObjectForm, BaseTrackView): | class CreateWorkItemForm(ObjectForm, BaseTrackView): | ||||||
|  | @ -344,19 +350,15 @@ class CreateWorkItem(EditObject, BaseTrackView): | ||||||
|         for k in ('title', 'description', 'comment'): |         for k in ('title', 'description', 'comment'): | ||||||
|             setValue(k) |             setValue(k) | ||||||
|         startDate = form.get('start_date', '').strip() |         startDate = form.get('start_date', '').strip() | ||||||
|         startTime = form.get('start_time', '').strip().replace('T', '') |         startTime = form.get('start_time', '').strip().replace('T', '') or '00:00:00' | ||||||
|         endTime = form.get('end_time', '').strip().replace('T', '') |         endTime = form.get('end_time', '').strip().replace('T', '') or '00:00:00' | ||||||
|         #print '***', startDate, startTime, endTime |         #print '***', startDate, startTime, endTime | ||||||
|         if startDate and startTime: |         #if startDate and startTime: | ||||||
|  |         if startDate: | ||||||
|             result['start'] = parseDateTime('T'.join((startDate, startTime))) |             result['start'] = parseDateTime('T'.join((startDate, startTime))) | ||||||
|         if startDate and endTime: |  | ||||||
|             result['end'] = parseDateTime('T'.join((startDate, endTime))) |             result['end'] = parseDateTime('T'.join((startDate, endTime))) | ||||||
|         duration = form.get('duration') |         result['duration'] = parseTime(form.get('duration')) | ||||||
|         if duration: |         result['effort'] = parseTime(form.get('effort')) | ||||||
|             result['duration'] = parseTime(duration) |  | ||||||
|         effort = form.get('effort') |  | ||||||
|         if effort: |  | ||||||
|             result['effort'] = parseTime(effort) |  | ||||||
|         return action, result |         return action, result | ||||||
| 
 | 
 | ||||||
|     def update(self): |     def update(self): | ||||||
|  | @ -403,6 +405,9 @@ class WorkItemStateAction(StateAction): | ||||||
| 
 | 
 | ||||||
| # auxiliary functions | # auxiliary functions | ||||||
| 
 | 
 | ||||||
|  | specialDays = ['yesterday', 'today', 'tomorrow'] | ||||||
|  | weekDays = ['monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday', 'sunday'] | ||||||
|  | 
 | ||||||
| def parseTime(s): | def parseTime(s): | ||||||
|     if not s: |     if not s: | ||||||
|         return None |         return None | ||||||
|  | @ -420,6 +425,20 @@ def parseDateTime(s): | ||||||
| def parseDate(s): | def parseDate(s): | ||||||
|     if not s: |     if not s: | ||||||
|         return None |         return None | ||||||
|  |     if isinstance(s, (list, tuple)): | ||||||
|  |         s = s[0] | ||||||
|  |     if s[0] in ('-', '+') or s.isdigit(): | ||||||
|  |         delta = int(s) * 3600 * 24 | ||||||
|  |         return int(time.mktime(date.today().timetuple())) + delta | ||||||
|  |     elif s in specialDays: | ||||||
|  |         delta = (specialDays.index(s) - 1) * 3600 * 24 | ||||||
|  |         return int(time.mktime(date.today().timetuple())) + delta | ||||||
|  |     elif s in weekDays: | ||||||
|  |         wd = weekDays.index(s) | ||||||
|  |         today = date.today() | ||||||
|  |         todayWd = today.weekday | ||||||
|  |         delta = (wd + 8 - todayWd) % 8 | ||||||
|  |         return int(time.mktime(today.timetuple())) + delta | ||||||
|     return int(time.mktime(time.strptime(s, '%Y-%m-%d'))) |     return int(time.mktime(time.strptime(s, '%Y-%m-%d'))) | ||||||
| 
 | 
 | ||||||
| def formatTimeDelta(value): | def formatTimeDelta(value): | ||||||
|  |  | ||||||
|  | @ -22,10 +22,12 @@ | ||||||
| 
 | 
 | ||||||
|   <!-- application views --> |   <!-- application views --> | ||||||
| 
 | 
 | ||||||
|   <browser:page |   <zope:adapter | ||||||
|       name="workitems.html" |         name="taskworkitems.html" | ||||||
|       for="loops.interfaces.INode" |         for="loops.interfaces.IConcept | ||||||
|       class="loops.organize.work.browser.WorkItemsView" |              zope.publisher.interfaces.browser.IBrowserRequest" | ||||||
|  |         provides="zope.interface.Interface" | ||||||
|  |         factory="loops.organize.work.browser.TaskWorkItems" | ||||||
|         permission="zope.View" /> |         permission="zope.View" /> | ||||||
| 
 | 
 | ||||||
|   <zope:adapter |   <zope:adapter | ||||||
|  | @ -36,6 +38,14 @@ | ||||||
|         factory="loops.organize.work.browser.PersonWorkItems" |         factory="loops.organize.work.browser.PersonWorkItems" | ||||||
|         permission="zope.View" /> |         permission="zope.View" /> | ||||||
| 
 | 
 | ||||||
|  |   <zope:adapter | ||||||
|  |         name="userworkitems.html" | ||||||
|  |         for="loops.interfaces.IConcept | ||||||
|  |              zope.publisher.interfaces.browser.IBrowserRequest" | ||||||
|  |         provides="zope.interface.Interface" | ||||||
|  |         factory="loops.organize.work.browser.UserWorkItems" | ||||||
|  |         permission="zope.View" /> | ||||||
|  | 
 | ||||||
|   <browser:page |   <browser:page | ||||||
|       name="create_workitem.html" |       name="create_workitem.html" | ||||||
|       for="loops.interfaces.INode" |       for="loops.interfaces.INode" | ||||||
|  |  | ||||||
|  | @ -6,7 +6,7 @@ | ||||||
| 
 | 
 | ||||||
| <metal:work define-macro="workitems" | <metal:work define-macro="workitems" | ||||||
|             tal:define="work nocall:view/workItems" |             tal:define="work nocall:view/workItems" | ||||||
|             tal:condition="work/listWorkItems"> |             tal:condition="work/listWorkItems|nothing"> | ||||||
|   <br /> |   <br /> | ||||||
|   <h2 i18n:translate="">Work Items</h2> |   <h2 i18n:translate="">Work Items</h2> | ||||||
|   <metal:workitems define-macro="workitems_listing" |   <metal:workitems define-macro="workitems_listing" | ||||||
|  | @ -49,10 +49,11 @@ | ||||||
|   </metal:workitems> |   </metal:workitems> | ||||||
| </metal:work> | </metal:work> | ||||||
| 
 | 
 | ||||||
| <metal:work define-macro="userworkitems" | 
 | ||||||
|  | <metal:work define-macro="workitems_query" | ||||||
|                 tal:define="work nocall:item"> |                 tal:define="work nocall:item"> | ||||||
|   <br /> |   <br /> | ||||||
|   <h2 i18n:translate="" tal:content="item/title">Work Items</h2> |   <metal:block use-macro="view/concept_macros/concepttitle" /> | ||||||
|   <metal:workitems use-macro="item/work_macros/workitems_listing" /> |   <metal:workitems use-macro="item/work_macros/workitems_listing" /> | ||||||
| </metal:work> | </metal:work> | ||||||
| 
 | 
 | ||||||
|  | @ -106,11 +107,11 @@ | ||||||
|             <div id="duration-effort"> |             <div id="duration-effort"> | ||||||
|               <input type="text" name="duration" style="width: 5em" |               <input type="text" name="duration" style="width: 5em" | ||||||
|                      dojoType="dijit.form.ValidationTextBox" |                      dojoType="dijit.form.ValidationTextBox" | ||||||
|                      regexp="[0-9]{1,2}(:[0-5][0-9]){0,1}" |                      regexp="-{0,1}[0-9]{1,2}(:[0-5][0-9]){0,1}" | ||||||
|                      tal:attributes="value view/duration" /> / |                      tal:attributes="value view/duration" /> / | ||||||
|               <input type="text" name="effort" style="width: 5em" |               <input type="text" name="effort" style="width: 5em" | ||||||
|                      dojoType="dijit.form.ValidationTextBox" |                      dojoType="dijit.form.ValidationTextBox" | ||||||
|                      regexp="[0-9]{1,2}(:[0-5][0-9]){0,1}" |                      regexp="-{0,1}[0-9]{1,2}(:[0-5][0-9]){0,1}" | ||||||
|                      tal:attributes="value view/effort" /></div> |                      tal:attributes="value view/effort" /></div> | ||||||
|       </div> |       </div> | ||||||
|       <div> |       <div> | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		
		Reference in a new issue
	
	 helmutm
						helmutm