Browse Source

bug #15 - add dashboard/ root to the main controller, and associated template and methods in the dbapi

damien 11 years ago
parent
commit
575be7d06b

+ 12 - 0
pboard/pboard/controllers/root.py View File

90
         flash(_('We hope to see you soon!'))
90
         flash(_('We hope to see you soon!'))
91
         redirect(came_from)
91
         redirect(came_from)
92
         
92
         
93
+    @expose('pboard.templates.dashboard')
94
+    @require(predicates.in_group('user', msg=l_('Please login to access this page')))
95
+    def dashboard(self):
96
+      loCurrentUser   = pld.PODStaticController.getCurrentUser()
97
+      loApiController = pld.PODUserFilteredApiController(loCurrentUser.user_id)
98
+
99
+      loLastModifiedNodes = loApiController.getLastModifiedNodes(10)
100
+      loWhatsHotNodes     = loApiController.getNodesByStatus('hot', 5)
101
+      loActionToDoNodes   = loApiController.getNodesByStatus('actiontodo', 5)
102
+      return dict(last_modified_nodes=loLastModifiedNodes, whats_hot_nodes=loWhatsHotNodes, action_to_do_nodes = loActionToDoNodes)
103
+
104
+
93
     @expose('pboard.templates.document')
105
     @expose('pboard.templates.document')
94
     @require(predicates.in_group('user', msg=l_('Please login to access this page')))
106
     @require(predicates.in_group('user', msg=l_('Please login to access this page')))
95
     def document(self, node=0, came_from=lurl('/')):
107
     def document(self, node=0, came_from=lurl('/')):

+ 18 - 0
pboard/pboard/lib/dbapi.py View File

46
     self._iExtraUserIdList     = piExtraUserIdList
46
     self._iExtraUserIdList     = piExtraUserIdList
47
     self._iUserIdFilteringList = None
47
     self._iUserIdFilteringList = None
48
   
48
   
49
+
49
   def _getUserIdListForFiltering(self):
50
   def _getUserIdListForFiltering(self):
50
     if self._iUserIdFilteringList==None:
51
     if self._iUserIdFilteringList==None:
51
       self._iUserIdFilteringList = list()
52
       self._iUserIdFilteringList = list()
54
         self._iUserIdFilteringList.append(liUserId)
55
         self._iUserIdFilteringList.append(liUserId)
55
     return self._iUserIdFilteringList
56
     return self._iUserIdFilteringList
56
 
57
 
58
+
57
   def createNode(self):
59
   def createNode(self):
58
     loNode          = pbmd.PBNode()
60
     loNode          = pbmd.PBNode()
59
     loNode.owner_id = self._iCurrentUserId
61
     loNode.owner_id = self._iCurrentUserId
62
   
64
   
63
     query.filter(User.name.in_(['ed', 'wendy', 'jack']))
65
     query.filter(User.name.in_(['ed', 'wendy', 'jack']))
64
 
66
 
67
+
65
   def createDummyNode(self):
68
   def createDummyNode(self):
66
     loNewNode = self.createNode()
69
     loNewNode = self.createNode()
67
     loNewNode.data_label   = 'New document'
70
     loNewNode.data_label   = 'New document'
68
     loNewNode.data_content = 'insert content...'
71
     loNewNode.data_content = 'insert content...'
69
     return loNewNode
72
     return loNewNode
70
 
73
 
74
+
71
   def getNode(self, liNodeId):
75
   def getNode(self, liNodeId):
72
     liOwnerIdList = self._getUserIdListForFiltering()
76
     liOwnerIdList = self._getUserIdListForFiltering()
73
     if liNodeId==0:
77
     if liNodeId==0:
75
     else:
79
     else:
76
       return DBSession.query(pbmd.PBNode).options(joinedload_all("_lAllChildren")).filter(pbmd.PBNode.node_id==liNodeId).filter(pbmd.PBNode.owner_id.in_(liOwnerIdList)).one()
80
       return DBSession.query(pbmd.PBNode).options(joinedload_all("_lAllChildren")).filter(pbmd.PBNode.node_id==liNodeId).filter(pbmd.PBNode.owner_id.in_(liOwnerIdList)).one()
77
 
81
 
82
+
83
+  def getLastModifiedNodes(self, piMaxNodeNb):
84
+    """
85
+    Returns a list of nodes order by modification time and limited to piMaxNodeNb nodes
86
+    """
87
+    liOwnerIdList = self._getUserIdListForFiltering()
88
+    return DBSession.query(pbmd.PBNode).options(joinedload_all("_lAllChildren")).filter(pbmd.PBNode.owner_id.in_(liOwnerIdList)).order_by(pbmd.PBNode.updated_at.desc()).limit(piMaxNodeNb).all()
89
+
90
+
91
+  def getNodesByStatus(self, psNodeStatus, piMaxNodeNb=5):
92
+    liOwnerIdList = self._getUserIdListForFiltering()
93
+    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()
94
+
95
+
78
   def buildTreeListForMenu(self):
96
   def buildTreeListForMenu(self):
79
     liOwnerIdList = self._getUserIdListForFiltering()
97
     liOwnerIdList = self._getUserIdListForFiltering()
80
     
98
     

+ 18 - 2
pboard/pboard/model/data.py View File

263
     else:
263
     else:
264
       return laIconClass[self.node_type]
264
       return laIconClass[self.node_type]
265
       
265
       
266
-      
267
-  def getFormattedDateTime(self, poDateTime, psDateTimeFormat = '%d/%m/%Y @ %H:%M'):
266
+  def getUserFriendlyNodeType(self):
267
+    laNodeTypesLng = dict()
268
+    laNodeTypesLng['node']   = 'Document' # FIXME - D.A. - 2013-11-14 - Make text translatable
269
+    laNodeTypesLng['folder'] = 'Document'
270
+    laNodeTypesLng['data']   = 'Document'
271
+
272
+    laNodeTypesLng['file']   = 'File'
273
+    laNodeTypesLng['event']  = 'Event'
274
+    laNodeTypesLng['contact'] = 'Contact'
275
+    laNodeTypesLng['comment'] = 'Comment'
276
+
277
+    if self.node_type==PBNodeType.Data and self.getStaticChildNb()>0:
278
+      return laNodeTypesLng['folder']
279
+    else:
280
+      return laNodeTypesLng[self.node_type]
281
+
282
+    
283
+  def getFormattedDateTime(self, poDateTime, psDateTimeFormat = '%d/%m/%Y ~ %H:%M'):
268
     return poDateTime.strftime(psDateTimeFormat)
284
     return poDateTime.strftime(psDateTimeFormat)
269
 
285
 
270
   def getFormattedDate(self, poDateTime, psDateTimeFormat = '%d/%m/%Y'):
286
   def getFormattedDate(self, poDateTime, psDateTimeFormat = '%d/%m/%Y'):

+ 109 - 0
pboard/pboard/templates/dashboard.mak View File

1
+<%inherit file="local:templates.master"/>
2
+<%namespace name="POD" file="pboard.templates.pod"/>
3
+
4
+<%def name="title()">
5
+pod > dashobard
6
+</%def>
7
+
8
+  <div class="row">
9
+    <div class="container-fluid">
10
+      <div class="row-fluid">
11
+        <div class="span6">
12
+          ## LEFT PANEL OF THE DASHBOARD
13
+          <div id='whats-hot-panel' class="well">
14
+            ## WHAT'S HOT PANEL
15
+            <h3><i class="fa fa-warning pod-red"></i> ${_("What's hot!")}</h3>
16
+            % if len(whats_hot_nodes)<=0:
17
+              <p>${_("No hot stuff for today.")}</p>
18
+            % else:
19
+              <ul>
20
+              % for node in whats_hot_nodes:
21
+                <li title="${node.data_label}">
22
+                  <i class="${node.getIconClass()}" title="${node.getUserFriendlyNodeType()}"></i> 
23
+                % if node.node_type=='data' or node.parent_id==None:
24
+                  <a href="${tg.url('/document/%i'%node.node_id)}">
25
+                % else:
26
+                  <a href="${tg.url('/document/%i#tab-%ss'%(node.parent_id, node.node_type))}">
27
+                % endif
28
+                    ${node.getTruncatedLabel(70)}
29
+                  </a>
30
+                    <span title="${_('last modification')}" class="pull-right label">
31
+                     ${node.getFormattedDateTime(node.updated_at)}
32
+                    </span>
33
+                </li>
34
+              % endfor
35
+              </ul>
36
+            % endif
37
+            ## WHAT'S HOT PANEL [END]
38
+          </div>
39
+
40
+          <div id='action-to-do-documents-panel' class="well">
41
+            ## DOCUMENTS REQUIRING ACTIONS PANEL
42
+            <h3><i class="pod-blue fa fa-gears"></i> ${_("Actions to do")}</h3>
43
+            % if len(action_to_do_nodes)<=0:
44
+              <p>${_("No document requiring action.")}</p>
45
+            % else:
46
+              <ul>
47
+              % for node in action_to_do_nodes:
48
+                <li title="${node.data_label}">
49
+                  <i class="${node.getIconClass()}" title="${node.getUserFriendlyNodeType()}"></i> 
50
+                % if node.node_type=='data' or node.parent_id==None:
51
+                  <a href="${tg.url('/document/%i'%node.node_id)}">
52
+                % else:
53
+                  <a href="${tg.url('/document/%i#tab-%ss'%(node.parent_id, node.node_type))}">
54
+                % endif
55
+                    ${node.getTruncatedLabel(70)}
56
+                  </a>
57
+                    <span title="${_('last modification')}" class="pull-right label">
58
+                     ${node.getFormattedDateTime(node.updated_at)}
59
+                    </span>
60
+                </li>
61
+              % endfor
62
+              </ul>
63
+            % endif
64
+            ## DOCUMENTS REQUIRING ACTIONS PANEL [END]
65
+          </div>
66
+
67
+          ## LEFT PANEL OF THE DASHBOARD [END]
68
+        </div>
69
+        <div class="span6">
70
+          ## RIGHT PANEL OF THE DASHBOARD
71
+          <div id='last-modified-documents-panel' class="well">
72
+            <h3><i style="color: #999;" class="fa fa-clock-o"></i> ${_("Latest operations")}</h3>
73
+            % if len(last_modified_nodes)<=0:
74
+              <p>${_("No activity found")}</p>
75
+            % else:
76
+              <table class="table table-condensed table-hover">
77
+              % for node in last_modified_nodes:
78
+                <tr title="${node.data_label}">
79
+                  <td>${node.getFormattedDateTime(node.updated_at)}</td>
80
+                  <td title="${node.getUserFriendlyNodeType()}">
81
+                    <i class="${node.getIconClass()}"></i> 
82
+                  </td>
83
+                  <td>
84
+                  % if node.node_type=='data' or node.parent_id==None:
85
+                    <a href="${tg.url('/document/%i'%node.node_id)}">
86
+                  % else:
87
+                    <a href="${tg.url('/document/%i#tab-%ss'%(node.parent_id, node.node_type))}">
88
+                  % endif
89
+                      ${node.getTruncatedLabel(35)}
90
+                    </a>
91
+                  </td>
92
+                  <td>
93
+                  % if node.updated_at==node.created_at:
94
+                    <span class="label label-success">${_("created")}</span>
95
+                  % else:
96
+                    <span class="label label-info">${_("updated")}</span>
97
+                  % endif
98
+                  </td>
99
+                </tr>
100
+              % endfor
101
+              </table>
102
+            % endif
103
+          </div>
104
+          ## RIGHT PANEL OF THE DASHBOARD [END]
105
+        </div>
106
+      </div>
107
+    </div>
108
+  </div>
109
+

+ 6 - 3
pboard/pboard/templates/master.mak View File

78
   visibility: visible;
78
   visibility: visible;
79
 }
79
 }
80
 
80
 
81
+.pod-blue {color: #3a87ad !important; }
82
+.pod-red {color: #F00 !important; }
83
+
81
 body { padding-top: 60px; }
84
 body { padding-top: 60px; }
82
 @media screen and (max-width: 768px) {
85
 @media screen and (max-width: 768px) {
83
     body { padding-top: 0px; }
86
     body { padding-top: 0px; }
84
 }
87
 }
85
 
88
 
86
-ul.nav li.dropdown:hover > ul.dropdown-menu {
87
-    display: block;
88
-}
89
+## ul.nav li.dropdown:hover > ul.dropdown-menu {
90
+##     display: block;
91
+## }
89
 
92
 
90
     </style>
93
     </style>
91
 </head>
94
 </head>