Browse Source

bug #52: make the status list more coherent+ add automatic status for items containing tasks

damien 11 years ago
parent
commit
dd1f820436

+ 10 - 0
doc/database/pod-upgrade-0.1.0_to_0.2.0.sql View File

@@ -0,0 +1,10 @@
1
+
2
+--
3
+-- Remove useless status
4
+--
5
+UPDATE pod_nodes SET node_status='information' WHERE node_status='immortal';
6
+UPDATE pod_nodes SET node_status='inprogress' WHERE node_status='actiontodo';
7
+UPDATE pod_nodes SET node_status='inprogress' WHERE node_status='hot';
8
+UPDATE pod_nodes SET node_status='actiontodo' WHERE node_status='actiontodo';
9
+UPDATE pod_nodes SET node_status='closed' WHERE node_status='archived';
10
+

+ 2 - 2
pboard/pboard/controllers/root.py View File

@@ -123,13 +123,13 @@ class RootController(BaseController):
123 123
         loApiController = pld.PODUserFilteredApiController(loCurrentUser.user_id)
124 124
 
125 125
         # loRootNodeList   = pbm.DBSession.query(pbmd.PBNode).filter(pbmd.PBNode.parent_id==None).order_by(pbmd.PBNode.node_order).all()
126
-        loRootNodeList = loApiController.buildTreeListForMenu()
126
+        loRootNodeList = loApiController.buildTreeListForMenu(pbmd.PBNodeStatus.getVisibleIdsList())
127 127
         liNodeId         = int(node)
128 128
         
129 129
         loCurrentNode    = None
130 130
         loNodeStatusList = None
131 131
         try:
132
-          loNodeStatusList = pbmd.PBNodeStatus.getList()
132
+          loNodeStatusList = pbmd.PBNodeStatus.getChoosableList()
133 133
           loCurrentNode    = loApiController.getNode(liNodeId)
134 134
         except Exception as e:
135 135
           flash(_('Document not found'), 'error')

+ 3 - 3
pboard/pboard/lib/dbapi.py View File

@@ -95,10 +95,10 @@ class PODUserFilteredApiController(object):
95 95
     return DBSession.query(pbmd.PBNode).options(joinedload_all("_lAllChildren")).filter(pbmd.PBNode.owner_id.in_(liOwnerIdList)).filter(pbmd.PBNode.node_status==psNodeStatus).order_by(pbmd.PBNode.updated_at).limit(piMaxNodeNb).all()
96 96
 
97 97
 
98
-  def buildTreeListForMenu(self):
98
+  def buildTreeListForMenu(self, plViewableStatusId):
99 99
     liOwnerIdList = self._getUserIdListForFiltering()
100 100
     
101
-    loNodeList = pbm.DBSession.query(pbmd.PBNode).filter(pbmd.PBNode.owner_id.in_(liOwnerIdList)).filter(pbmd.PBNode.node_type==pbmd.PBNodeType.Data).order_by(pbmd.PBNode.parent_tree_path).order_by(pbmd.PBNode.node_order).order_by(pbmd.PBNode.node_id).all()
101
+    loNodeList = pbm.DBSession.query(pbmd.PBNode).filter(pbmd.PBNode.owner_id.in_(liOwnerIdList)).filter(pbmd.PBNode.node_type==pbmd.PBNodeType.Data).filter(pbmd.PBNode.node_status.in_(plViewableStatusId)).order_by(pbmd.PBNode.parent_tree_path).order_by(pbmd.PBNode.node_order).order_by(pbmd.PBNode.node_id).all()
102 102
     loTreeList = []
103 103
     loTmpDict = {}
104 104
     for loNode in loNodeList:
@@ -112,7 +112,7 @@ class PODUserFilteredApiController(object):
112 112
         # The following line may raise an exception
113 113
         # We suppose that the parent node has already been added
114 114
         # this *should* be the case, but the code does not check it
115
-        if loNode.parent_id in loTmpDict.keys():
115
+        if loNode.parent_id not in loTmpDict.keys():
116 116
           loTmpDict[loNode.parent_id] = self.getNode(loNode.parent_id)
117 117
         loTmpDict[loNode.parent_id].appendStaticChild(loNode)
118 118
   

+ 24 - 0
pboard/pboard/lib/helpers.py View File

@@ -5,6 +5,7 @@
5 5
 #from webhelpers import date, feedgenerator, html, number, misc, text
6 6
 from markupsafe import Markup
7 7
 from datetime import datetime
8
+from tg.i18n import ugettext as _, lazy_ugettext as l_
8 9
 
9 10
 def current_year():
10 11
   now = datetime.now()
@@ -15,3 +16,26 @@ def icon(icon_name, white=False):
15 16
         return Markup('<i class="icon-%s icon-white"></i>' % icon_name)
16 17
     else:
17 18
         return Markup('<i class="icon-%s"></i>' % icon_name)
19
+
20
+
21
+def getExplanationAboutStatus(psStatusId, psCurrentStatusId):
22
+  lsMsg = ""
23
+  if psStatusId==psCurrentStatusId:
24
+    return _("This is the current status.")
25
+  else:
26
+    if psStatusId=='information':
27
+      return _("The item is a normal document, like a howto or a text document.")
28
+    if psStatusId=='automatic':
29
+      return _("The item will be automatically computed as \"in progress\" or \"done\" according to its children status.")
30
+    if psStatusId=='new':
31
+      return _("No action done on the item.")
32
+    if psStatusId=='inprogress':
33
+      return _("The item is being worked on.")
34
+    if psStatusId=='standby':
35
+      return _("Waiting for some external actions.")
36
+    if psStatusId=='done':
37
+      return _("The work associated with the item is finished.")
38
+    if psStatusId=='closed':
39
+      return _("Close the item if you want not to see it anymore. The data won't be deleted")
40
+    if psStatusId=='deleted':
41
+      return _("This status tells that the item has been deleted.")

+ 56 - 19
pboard/pboard/model/data.py View File

@@ -5,14 +5,16 @@ import re
5 5
 import datetime as datetimeroot
6 6
 from datetime import datetime
7 7
 from hashlib import sha256
8
-__all__ = ['User', 'Group', 'Permission']
9 8
 
10 9
 from sqlalchemy import Table, ForeignKey, Column, Sequence
11 10
 from sqlalchemy.types import Unicode, Integer, DateTime, Text, LargeBinary
12 11
 from sqlalchemy.orm import relation, synonym, relationship
13 12
 from sqlalchemy.orm import backref
13
+import sqlalchemy.orm as sqlao
14 14
 from sqlalchemy import orm as sqlao
15 15
 
16
+from tg.i18n import ugettext as _, lazy_ugettext as l_
17
+
16 18
 import tg
17 19
 from pboard.model import DeclarativeBase, metadata, DBSession
18 20
 
@@ -92,29 +94,52 @@ class PBNodeStatusItem(object):
92 94
 class PBNodeStatus(object):
93 95
     
94 96
   StatusList = dict()
95
-  StatusList['immortal']   = PBNodeStatusItem('immortal',   'Information',         'normal',    'fa fa-info-circle',        'pod-status-grey-light')
96
-  StatusList['new']        = PBNodeStatusItem('new',        'New',                 'open',      'fa fa-lightbulb-o',        'btn-success')
97
-  StatusList['inprogress'] = PBNodeStatusItem('inprogress', 'In progress',         'open',      'fa fa-gears fa-inverse',   'btn-info')
98
-  StatusList['actiontodo'] = PBNodeStatusItem('actiontodo', 'Action to do',        'open',      'fa fa-spinner fa-inverse', 'btn-info')
99
-  StatusList['standby']    = PBNodeStatusItem('standby',    'Waiting for news',    'open',      'fa fa-spinner fa-inverse', 'btn-warning')
100
-  StatusList['hot']        = PBNodeStatusItem('hot',        'Hot',                 'open',      'fa fa-warning fa-inverse', 'btn-danger')
101
-  StatusList['done']       = PBNodeStatusItem('done',       'Done',                'closed',    'fa fa-check-square-o',     'pod-status-grey-light')
102
-  StatusList['closed']     = PBNodeStatusItem('closed',     'Closed',              'closed',    'fa fa-lightbulb-o',        'pod-status-grey-middle')
103
-  StatusList['archived']   = PBNodeStatusItem('archived',   'Archived',            'invisible', 'fa fa-archive',            'pod-status-grey-dark')
104
-  StatusList['deleted']    = PBNodeStatusItem('deleted',    'Deleted',             'invisible', 'fa fa-trash-o',            'pod-status-grey-dark')
97
+  StatusList['information'] = PBNodeStatusItem('information', 'Information',         'normal', 'fa fa-info-circle',            'pod-status-grey-light')
98
+  StatusList['automatic']   = PBNodeStatusItem('automatic',   'Automatic',           'open',   'fa fa-flash',                  'pod-status-grey-light')
99
+  StatusList['new']         = PBNodeStatusItem('new',         'New',                 'open',   'fa fa-lightbulb-o fa-inverse', 'btn-success')
100
+  StatusList['inprogress']  = PBNodeStatusItem('inprogress',  'In progress',         'open',   'fa fa-gears fa-inverse',       'btn-info')
101
+  StatusList['standby']     = PBNodeStatusItem('standby',     'In standby',          'open',   'fa fa-spinner fa-inverse',     'btn-warning')
102
+  StatusList['done']        = PBNodeStatusItem('done',        'Done',                'closed', 'fa fa-check-square-o',         'pod-status-grey-light')
103
+  StatusList['closed']      = PBNodeStatusItem('closed',      'Closed',              'closed', 'fa fa-lightbulb-o',            'pod-status-grey-middle')
104
+  StatusList['deleted']     = PBNodeStatusItem('deleted',     'Deleted',             'closed', 'fa fa-trash-o',                'pod-status-grey-dark')
105
+
106
+  @classmethod
107
+  def getChoosableList(cls):
108
+    return [
109
+      PBNodeStatus.StatusList['information'],
110
+      PBNodeStatus.StatusList['automatic'],
111
+      PBNodeStatus.StatusList['new'],
112
+      PBNodeStatus.StatusList['inprogress'],
113
+      PBNodeStatus.StatusList['standby'],
114
+      PBNodeStatus.StatusList['done'],
115
+      PBNodeStatus.StatusList['closed'],
116
+    ]
117
+
118
+  @classmethod
119
+  def getVisibleIdsList(cls):
120
+    return ['information', 'automatic', 'new', 'inprogress', 'standby', 'done' ]
121
+
122
+  @classmethod
123
+  def getVisibleList(cls):
124
+    return [
125
+      PBNodeStatus.StatusList['information'],
126
+      PBNodeStatus.StatusList['automatic'],
127
+      PBNodeStatus.StatusList['new'],
128
+      PBNodeStatus.StatusList['inprogress'],
129
+      PBNodeStatus.StatusList['standby'],
130
+      PBNodeStatus.StatusList['done'],
131
+    ]
105 132
 
106 133
   @classmethod
107 134
   def getList(cls):
108 135
     return [
109
-      PBNodeStatus.StatusList['immortal'],
136
+      PBNodeStatus.StatusList['information'],
137
+      PBNodeStatus.StatusList['automatic'],
110 138
       PBNodeStatus.StatusList['new'],
111
-      PBNodeStatus.StatusList['actiontodo'],
112 139
       PBNodeStatus.StatusList['inprogress'],
113 140
       PBNodeStatus.StatusList['standby'],
114
-      PBNodeStatus.StatusList['hot'],
115 141
       PBNodeStatus.StatusList['done'],
116 142
       PBNodeStatus.StatusList['closed'],
117
-      PBNodeStatus.StatusList['archived'],
118 143
       PBNodeStatus.StatusList['deleted']
119 144
     ]
120 145
     
@@ -162,8 +187,8 @@ class PBNode(DeclarativeBase):
162 187
   parent_tree_path = Column(Unicode(255), unique=False, nullable=False, default='')
163 188
   owner_id         = Column(Integer, ForeignKey('pod_user.user_id'), nullable=True, default=None)
164 189
 
165
-  node_order  = Column(Integer, nullable=True, default=1)
166
-  node_type   = Column(Unicode(16), unique=False, nullable=False, default='data')
190
+  node_order   = Column(Integer, nullable=True, default=1)
191
+  node_type    = Column(Unicode(16), unique=False, nullable=False, default='data')
167 192
   node_status = Column(Unicode(16), unique=False, nullable=False, default='new')
168 193
 
169 194
   created_at = Column(DateTime, unique=False, nullable=False)
@@ -177,7 +202,7 @@ class PBNode(DeclarativeBase):
177 202
   
178 203
   data_file_name      = Column(Unicode(255),  unique=False, nullable=False, default='')
179 204
   data_file_mime_type = Column(Unicode(255),  unique=False, nullable=False, default='')
180
-  data_file_content   = Column(LargeBinary(), unique=False, nullable=False, default=None)
205
+  data_file_content   = sqlao.deferred(Column(LargeBinary(), unique=False, nullable=False, default=None))
181 206
 
182 207
 
183 208
   _oParent = relationship('PBNode', remote_side=[node_id], backref='_lAllChildren')
@@ -280,7 +305,19 @@ class PBNode(DeclarativeBase):
280 305
     return poDateTime.strftime(psDateTimeFormat)
281 306
 
282 307
   def getStatus(self):
283
-    return PBNodeStatus.getStatusItem(self.node_status)
308
+    loStatus = PBNodeStatus.getStatusItem(self.node_status)
309
+    if loStatus.status_id!='automatic':
310
+      return loStatus
311
+    else:
312
+      # Compute the status:
313
+      # - if at least one child is 'new' or 'in progress' or 'in standby' => status is inprogress
314
+      # - else if all status are 'done', 'closed' or 'deleted' => 'done'
315
+      lsRealStatusId = 'done'
316
+      for loChild in self.getChildren():
317
+        if loChild.getStatus().status_id in ('new', 'inprogress', 'standby'):
318
+          lsRealStatusId = 'inprogress'
319
+          break
320
+      return PBNodeStatus.getStatusItem(lsRealStatusId)
284 321
 
285 322
   def getTruncatedLabel(self, piCharNb):
286 323
     lsTruncatedLabel = ''

+ 23 - 13
pboard/pboard/templates/document.mak View File

@@ -95,17 +95,24 @@ pod :: document ${current_node.getTruncatedLabel(40)} [#${current_node.node_id}
95 95
       </div>
96 96
     % endif
97 97
       <div class="btn-group">
98
-        <button class="btn">Status</button>
99
-        <a class="btn ${current_node.getStatus().css}" href="#"><i class="${current_node.getStatus().icon}"></i> ${current_node.getStatus().getLabel()}</a>
100
-        <a class="btn ${current_node.getStatus().css} dropdown-toggle" data-toggle="dropdown" href="#"><span class="caret"></span></a>
98
+        <button class="btn"  data-toggle="dropdown" href="#">${_("Change status")}</button>
99
+        <a class="btn dropdown-toggle" data-toggle="dropdown" href="#"><span class="caret"></span></a>
101 100
         <ul class="dropdown-menu">
102
-          % for node_status in node_status_list:
103
-            <li>
104
-              <a class="${node_status.css}" href="${tg.url('/api/edit_status?node_id=%i&node_status=%s'%(current_node.node_id, node_status.status_id))}">
105
-                <i class="${node_status.icon_id}"></i> ${node_status.label}
106
-              </a>
107
-            </li>
108
-          % endfor
101
+        % for node_status in node_status_list:
102
+          % if node_status.status_id==current_node.getStatus().status_id:
103
+          <li title="${h.getExplanationAboutStatus(node_status.status_id, current_node.getStatus().status_id)}">
104
+            <a class="${node_status.css}" href="#"  style="color: #999;">
105
+              <i class="${node_status.icon_id}"></i> ${node_status.label}
106
+            </a>
107
+          </li>
108
+          % else:
109
+          <li title="${h.getExplanationAboutStatus(node_status.status_id, current_node.getStatus().status_id)}">
110
+            <a class="${node_status.css}" href="${tg.url('/api/edit_status?node_id=%i&node_status=%s'%(current_node.node_id, node_status.status_id))}">
111
+              <i class="${node_status.icon_id}"></i> ${node_status.label}
112
+            </a>
113
+          </li>
114
+          % endif
115
+        % endfor
109 116
         </ul>
110 117
       </div>
111 118
       <div class="btn-group">
@@ -115,15 +122,18 @@ pod :: document ${current_node.getTruncatedLabel(40)} [#${current_node.node_id}
115 122
           ${node_treeview_for_set_parent_menu(current_node.node_id, root_node_list)}
116 123
         </ul>
117 124
 
118
-
119
-        <a href='${tg.url('/api/force_delete_node?node_id=%i'%(current_node.node_id))}' id='current-document-force-delete-button' class="btn" onclick="return confirm('${_('Delete current document?')}');"><i class="icon-g-remove"></i> ${_('Delete')}</a>
125
+        <a href='${tg.url('/api/edit_status?node_id=%i&node_status=%s'%(current_node.node_id, 'deleted'))}' id='current-document-force-delete-button' class="btn" onclick="return confirm('${_('Delete current document?')}');"><i class="fa fa-trash-o"></i> ${_('Delete')}</a>
126
+        
120 127
       </div>
121 128
 
122 129
       <div class="row">
123 130
         <div id='application-document-panel' class="span5">
124 131
           <p>
125 132
             <div id='current-document-content' class="">
126
-              <h3 id="current-document-title">#${current_node.node_id} - ${current_node.data_label}</h3>
133
+              <h3 id="current-document-title">#${current_node.node_id} - ${current_node.data_label}
134
+                <span class="label ${current_node.getStatus().css}" href="#">${current_node.getStatus().label}</a>
135
+              
136
+              </h3>
127 137
               ${current_node.getContentWithTags()|n}
128 138
             </div>
129 139
             <form style='display: none;' id="current-document-content-edit-form" method='post' action='${tg.url('/api/edit_label_and_content')}'>