changeset 27:9a75eb1b31b3

more work on projects.
author casties
date Mon, 22 Apr 2013 21:01:00 +0200
parents 8a99ad8713d6
children dd765003647b
files HashTree.py MPIWGDepartment.py MPIWGProjects.py MPIWGRoot.py __init__.py css/mpiwg.css zpt/project/manage_add_MPIWGProjectFolder.zpt
diffstat 7 files changed, 315 insertions(+), 19 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/HashTree.py	Mon Apr 22 21:01:00 2013 +0200
@@ -0,0 +1,147 @@
+import logging
+    
+class HashTreeNode:
+    """Node of a HashTree.
+    Has a value and keeps children in a dict indexed by key."""
+    key = None
+    value = None
+    children = None
+    
+    def __init__(self, key=None, value=None, children=None):
+        """create node"""
+        self.key = key
+        self.value = value
+        self.children = children
+
+
+    def getNode(self, key):
+        """return node under key"""
+        if self.children is None:
+            return None
+        else:
+            return self.children.get(key, None)
+
+
+    def addNode(self, node):
+        """add child node using key from node"""
+        if self.children is None:
+            self.children = {node.key : node}
+        else:
+            self.children[node.key] = node
+            
+
+    def getSubtreeList(self):
+        """returns the subtree as list sorted by key (depth first)"""
+        if self.children is None:
+            if self.value is None:
+                return []
+            else:
+                return [self.value]
+            
+        else:
+            if self.value is None:
+                sub = []
+            else:
+                sub = [self.value]
+                
+            keys = self.children.keys()
+            keys.sort()
+            for k in keys:
+                #logging.debug("getSubtreeList: k=%s sub=%s"%(k,sub))
+                sub.extend(self.children.get(k).getSubtreeList())
+                
+            return sub
+
+
+    def getSubtreeAsText(self):
+        """prints whole tree as text"""
+        if self.children is None:
+            return "(%s:%s)"%(self.key, self.value)
+        else:
+            sub = "(%s:%s):["%(self.key, self.value)
+            keys = self.children.keys()
+            keys.sort()
+            for k in keys:
+                sub += self.children.get(k).getSubtreeAsText()
+                sub += ", "
+                
+            return sub + "] "
+
+
+class HashTree:
+    """Tree using dictionaries"""
+    
+    root = None
+    keySeparator = '.'
+    keyFn = None
+    
+    def __init__(self, keySeparator='.', keyFn=None):
+        """creates a HashTree"""
+        self.root = HashTreeNode()
+        self.keySeparator = keySeparator
+        self.keyFn = keyFn
+    
+
+    def getNode(self, key):
+        """gets node under key from the tree.
+        key can be sequence of key string or a single string using keySeparator.
+        """
+        #logging.debug("getNode(key=%s)"%(key))
+        if isinstance(key, basestring):
+            keys = key.split(self.keySeparator)
+            if self.keyFn is not None:
+                keys = [self.keyFn(k) for k in keys]
+        elif isinstance(key, list):
+            keys = key
+        else:
+            keys = [key]
+            
+        node = self.root
+        for k in keys:
+            #logging.debug("getNode node=%s k=%s"%(node, repr(k)))
+            node = node.getNode(k)
+            #logging.debug("getNode got:%s"%(node))
+            if node is None:
+                return None
+            
+        return node
+
+
+    def get(self, key):
+        """gets value under key from the tree.
+        key can be sequence of key string or a single string using keySeparator.
+        """
+        node = self.getNode(key)
+        if node is None:
+            return None
+        
+        return node.value
+
+     
+    def add(self, key, value):
+        """adds value under key to the tree.
+        key can be sequence of key string or a single string using keySeparator.
+        """
+        #logging.debug("add(key=%s, value=%s)"%(repr(key), value))
+        if isinstance(key, basestring):
+            keys = key.split(self.keySeparator)
+            if self.keyFn is not None:
+                keys = [self.keyFn(k) for k in keys]
+        elif isinstance(key, list):
+            keys = key
+        else:
+            keys = [key]
+            
+        node = self.root
+        for k in keys:
+            nextnode = node.getNode(k)
+            if nextnode is None:
+                nextnode = HashTreeNode(key=k)
+                node.addNode(nextnode)
+            
+            node = nextnode
+            
+        node.value = value
+        
+    
+        
--- a/MPIWGDepartment.py	Thu Apr 18 20:39:01 2013 +0200
+++ b/MPIWGDepartment.py	Mon Apr 22 21:01:00 2013 +0200
@@ -7,6 +7,9 @@
 import logging
 from OFS.Folder import Folder
 from AccessControl import ClassSecurityInfo
+
+from SrvTxtUtils import getAt
+
 import MPIWGHelper
 
 class MPIWGDepartment(Folder):
@@ -136,7 +139,12 @@
         img = getattr(self, 'img-thumb.jpg')
         return img.absolute_url()
 
-    def getProjects(self, onlyActive=0, onlyArchived=0):
+    def getProject(self):
+        """returns the default project"""
+        proj = self.getProjects(count=1, onlyActive=1, onlyArchived=1)
+        return getAt(proj, 0, None) 
+
+    def getProjects(self, count=0, onlyActive=0, onlyArchived=0):
         """returns a list of projects of this department.
         
         onlyActive = 0 : all projects
@@ -147,12 +155,11 @@
         onlyArchived = 1 : current projects
         onlyArchived = 2 : archived projects
         """
-        # getTree: jeder Eintrag ist ein Tupel (Tiefe, ProjektNummer,Titel,ProjektObject)
-        tree = self.getTree(dep=self.getProjectId(), date=None, onlyActive=onlyActive, onlyArchived=onlyArchived)
-        # re-pack into simple list
-        projects = []
-        for item in tree:
-            projects.append(item[3])
+        pf = self.en.getProjectsFolder()
+        projects = pf.getProjectList(self.getProjectId(), active=onlyActive, archived=onlyArchived)
+        #logging.debug("getProjects projects=%s"%repr(projects))
+        if count > 0:
+            return projects[:count]
             
         return projects
     
--- a/MPIWGProjects.py	Thu Apr 18 20:39:01 2013 +0200
+++ b/MPIWGProjects.py	Mon Apr 22 21:01:00 2013 +0200
@@ -40,6 +40,7 @@
 
 #import MPIWGStaff
 
+from HashTree import HashTree
 from MPIWGHelper import *
 
 import MPIWGRoot
@@ -712,12 +713,26 @@
             if RESPONSE:
         
                 self.redirect(RESPONSE,'manageRelatedProjects')
+
+
+    def getNumber(self):
+        """returns sorting number"""
+        return getattr(self, 'xdata_05', None)
+    
               
-    def getThumbUrl(self):
+    def getUrl(self, baseUrl=None):
+        """returns URL to this Project"""
+        if baseUrl is None:
+            return self.absolute_url()
+
+        return '%s/%s'%(baseUrl, self.getId())
+                
+              
+    def getThumbUrl(self, default='http://defaultthumb.jpg'):
         """returns the URL of the project thumbnail image"""
         # TODO: improve this
         imgs = self.getImageList()
-        url = 'http://defaultthumb.jpg'
+        url = default
         if len(imgs) > 0:
             img = imgs[0]
             url = img.absolute_url()
@@ -963,20 +978,34 @@
         """check if the project is still active, default is true, set to false is the project is accomplished"""
         return getattr(self,'isActiveFlag',True)
  
+    def checkActive(self, active):
+        """returns if the project state matches the active state.
+        active = 0 : all projects
+        active = 1 : active projects
+        active = 2 : inactive projects
+        """
+        act = getattr(self,'isActiveFlag',True)
+        return (active == 1 and act) or (active == 0) or (active == 2 and not act)
+ 
     def isArchivedProject(self):
         """check if the project is archived"""
-        
         completed=self.getCompletedAt()
-       
-       #completed leer 
+        #completed leer 
         if completed=="" :
             return False;
         if completed == 0:
             return False;
         
-        
         return True
         
+    def checkArchived(self, archived):
+        """returns if the project state matches the archived state.
+        archived = 0 : all projects
+        archived = 1 : current projects
+        archived = 2 : archived projects
+        """
+        arch = self.isArchivedProject()
+        return (archived == 1 and not arch) or (archived == 0) or (archived == 2 and arch)        
         
     def setActiveFlag(self,status=True):
         """set the active flag"""
@@ -1808,3 +1837,75 @@
     
     if RESPONSE is not None:
         RESPONSE.redirect('manage_main')
+
+
+class MPIWGProjectFolder(Folder):
+    """Folder of project objects"""
+
+    meta_type="MPIWGProjectFolder"
+    security=ClassSecurityInfo()
+
+    # cached HashTree with project hierarchy
+    _v_projectTree = None
+
+
+    def __init__(self, id, title=None):
+        self.id = id
+        if title is None:
+            self.title = id
+        else:
+            self.title = title
+
+
+    def getTree(self):
+        """returns the hierarchy tree (and caches it)"""
+        tree = self._v_projectTree 
+        if tree is None:
+            tree = HashTree(keySeparator='.', keyFn=lambda x:getInt(x))
+            for p in self.objectValues(spec='MPIWGProject'):
+                tree.add(p.getNumber(), p)
+                
+            self._v_projectTree = tree
+            #logging.debug("getTree: tree=%s"%(tree.root.getSubtreeAsText()))
+
+        return tree
+    
+    
+    def getProjectList(self, start, active=1, archived=1):
+        """returns list of projects starting at start.
+        active = 0 : all projects
+        active = 1 : active projects
+        active = 2 : inactive projects
+        archived = 0 : all projects
+        archived = 1 : current projects
+        archived = 2 : archived projects
+        """
+        #logging.debug("getProjectList(start=%s,active=%s,archived=%s)"%(repr(start),active,archived))
+        tree = self.getTree()
+        node = tree.getNode(start)
+        if node is None:
+            return []
+
+        pl = node.getSubtreeList()
+        #logging.debug("getProjectList: node=(%s,%s) pl=%s"%(node.key,node.value,pl))
+        # return filtered list
+        return [p for p in pl if (p.checkActive(active) and p.checkArchived(archived))]     
+        
+        
+def manage_addMPIWGProjectFolderForm(self):
+    """form for adding the project"""
+    pt = PageTemplateFile('zpt/project/manage_add_MPIWGProjectFolder', globals()).__of__(self)
+    return pt()
+
+def manage_addMPIWGProjectFolder(self,id,title,RESPONSE=None):
+    """add it"""
+    newObj=MPIWGProjectFolder(id,title)
+
+    self._setObject(id,newObj)
+
+    if RESPONSE is not None:
+        RESPONSE.redirect('manage_main')
+
+
+        
+        
\ No newline at end of file
--- a/MPIWGRoot.py	Thu Apr 18 20:39:01 2013 +0200
+++ b/MPIWGRoot.py	Mon Apr 22 21:01:00 2013 +0200
@@ -273,6 +273,11 @@
         items.sort(key=lambda x:int(x.weight))
         return items
 
+    def getProjectsFolder(self):
+        """returns the MPIWGProjectFolder"""
+        dir = getattr(self, 'projects', None)
+        return dir
+
     def getMPIWGRoot(self):
         """returns the MPIWG root"""
         return self
@@ -923,6 +928,7 @@
         
         department fuer das Tree geholt werden soll
         """
+        logging.debug("MPIWGRoot.getTree()")
 
         returnListTmp=[]
         returnList=[]
--- a/__init__.py	Thu Apr 18 20:39:01 2013 +0200
+++ b/__init__.py	Mon Apr 22 21:01:00 2013 +0200
@@ -20,6 +20,14 @@
         )
 
     context.registerClass(
+        MPIWGProjects.MPIWGProjectFolder,
+        constructors = (
+          MPIWGProjects.manage_addMPIWGProjectFolderForm,
+          MPIWGProjects.manage_addMPIWGProjectFolder
+          )
+        )
+
+    context.registerClass(
         MPIWGLink.MPIWGLink,
         constructors = (
           MPIWGLink.manage_addMPIWGLinkForm,
--- a/css/mpiwg.css	Thu Apr 18 20:39:01 2013 +0200
+++ b/css/mpiwg.css	Mon Apr 22 21:01:00 2013 +0200
@@ -366,12 +366,12 @@
     padding-left: 21px;
 }
 
-div.main.home div.box h2 {
+div.box h2 {
     /* more-links in title are right */
     position: relative;
 }
 
-div.main.home div.box h2 a:link {
+div.box h2 a:link {
     /* more-links in title are right */
     position: absolute;
     right: 0;
@@ -620,8 +620,9 @@
     margin-bottom: 5px;
     border: 0;
 }
+
 /*
- * sonstige boxen
+ * other boxes
  */
 div.box {
     margin: 20px 0;
@@ -645,13 +646,21 @@
     color: #6a4d3c;
 }
 
-div.box h3+h2 {
+div.box h3 + h2 {
     /* directly over h2 */
     margin-top: 0;
 }
 
+/*
+ * small boxes with thumbnail
+ */
 div.mini {
     padding: 10px 0;
+    /* line-height: 1; */
+}
+
+div.row.quintuple div.mini {
+    font-size: 11px;
 }
 
 div.mini div.thumb {
@@ -659,13 +668,22 @@
     text-align: center;
 }
 
+div.row.quintuple div.mini div.thumb img {
+    width: 120px;
+    height: 75px;
+}
+
 div.mini div.type {
-    font-size: 11px;
     color: #9f917a;
 }
 
-div.box div.source div.author,div.box div.website div.title {
+div.mini div.title {
+    margin-top: 0.5em;
+}
+
+div.mini div.author {
     color: #3b4186;
+    margin-top: 0.5em;
 }
 
 div.box div.website div.description {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/zpt/project/manage_add_MPIWGProjectFolder.zpt	Mon Apr 22 21:01:00 2013 +0200
@@ -0,0 +1,9 @@
+  <div tal:replace="structure here/manage_page_header">Header</div>
+  <h2>Add a MPIWGProjectFolder</h2>
+  <form method="post" action="manage_addMPIWGProjectFolder">
+    <p class="form-label">ID</p>
+        <p class="form-element"><input size="80" name="id"/></p>
+    <p class="form-label">Title</p>
+        <p class="form-element"><input size="80" name="title"/></p>
+     <p><input type="submit" value="Add" /></p>
+  </form>