Group parts and copies but allow editors to have item access
authorArt Rhyno <art632000@yahoo.ca>
Sun, 13 Apr 2014 11:57:51 +0000 (07:57 -0400)
committerArt Rhyno <art632000@yahoo.ca>
Sun, 13 Apr 2014 11:57:51 +0000 (07:57 -0400)
It has been necessary to suppress items so that copies of parts
don't appear that are not on reserve. These changes only display
status information for items identified in syrup, grouping them
for anonymous and member access, but allowing individual editing
for anyone with permissions to change the course site.

Also, one teeny-tiny change to remove the word "Faculty" from the
"How to place materials on reserve" link,

Signed-off-by: Art Rhyno <art632000@yahoo.ca>
conifer/integration/evergreen_site.py
conifer/syrup/models.py
conifer/templates/components/site.xhtml
conifer/templates/item/item_metadata.xhtml
conifer/templates/site_detail.xhtml
conifer/templates/tabbar.xhtml
conifer/templates/tabbar_anonymous.xhtml

index 3936f1e..002cd90 100644 (file)
@@ -43,8 +43,6 @@ OPENSRF_FLESHEDCOPY_CALL  = "open-ils.search.asset.copy.fleshed.batch.retrieve.a
 def disable(func):
     return None
 
-
-
 class EvergreenIntegration(object):
 
     # Either specify EVERGREEN_SERVERin your local_settings, or override
@@ -85,7 +83,9 @@ class EvergreenIntegration(object):
     # end of variables dependent on EVERGREEN_SERVER
     # ----------------------------------------------------------------------
 
-
+    # BIB_PART_MERGE: display multiple parts for one bib title
+    # that have been scanned in separately in one section
+    BIB_PART_MERGE = bool(getattr(settings, 'BIB_PART_MERGE', True))
 
     # OPAC_LANG and OPAC_SKIN: localization skinning for your OPAC
 
@@ -113,6 +113,11 @@ class EvergreenIntegration(object):
     ATTACHMENT_EXPRESSION = getattr(settings, 'ATTACHMENT_REGEXP', '\w*DVD\s?|\w*CD\s?|\w[Gg]uide\s?|\w[Bb]ooklet\s?|\w*CD\-ROM\s?')
     IS_ATTACHMENT = re.compile(ATTACHMENT_EXPRESSION)
 
+    # regular expression to volume designations within a 
+    # call number
+    EMBEDDEDVOL_EXPRESSION = getattr(settings, 'EMBEDDEDVOL_REGEXP','\w*[Vv]\.\s?(\d+)')
+    IS_EMBEDDEDVOL = re.compile(EMBEDDEDVOL_EXPRESSION)
+
     # Used if you're doing updates to Evergreen from Syrup.
     UPDATE_CHOICES = [ 
         ('Cat', 'Catalogue'), 
@@ -120,11 +125,7 @@ class EvergreenIntegration(object):
         ('Zap', 'Remove from Syrup'), 
         ] 
 
-
     # ----------------------------------------------------------------------
-
-
-
     def __init__(self):
         # establish our OpenSRF connection.
         initialize(self)
@@ -137,11 +138,14 @@ class EvergreenIntegration(object):
         status_decode = [(str(x['id']), x['name'])
                          for x in E1('open-ils.search.config.copy_status.retrieve.all')]
 
+        #for x in E1('open-ils.search.config.copy_status.retrieve.all'):
+        #    print "x", x
         self.AVAILABLE  = [id for id, name in status_decode if name == 'Available'][0]
         self.RESHELVING = [id for id, name in status_decode if name == 'Reshelving'][0]
+        self.CHECKEDOUT = [id for id, name in status_decode if name == 'Checked out'][0]
 
 
-    def item_status(self, item):
+    def item_status(self, item, bcs=[], ids=[]):
         """
         Given an Item object, return three numbers: (library, desk,
         avail). Library is the total number of copies in the library
@@ -160,41 +164,71 @@ class EvergreenIntegration(object):
         """
         if not item.bib_id:
             return None
-        return self._item_status(item.bib_id, item.barcode)
 
+        #really silly function to turn list into string for passing
+        def make_obj_string(objs):
+            objlist = ""
+            for obj in objs:
+                if len(objlist) > 0:
+                    objlist = objlist + ';'
+
+                for o in obj:
+                    if obj.index(o) > 0:
+                        objlist = objlist + '/'
+                    objlist = objlist + str(o)
+
+            return objlist
+
+        bclist = make_obj_string(bcs)
+        idlist = make_obj_string(ids)
+        return self._item_status(item.bib_id, item.barcode, bclist, idlist)
 
     CACHE_TIME = 300
 
     @memoize(timeout=CACHE_TIME)
-    def _item_status(self, bib_id, barcode):
+    def _item_status(self, bib_id, barcode, bclist, idlist):
+
+        #sanity variables for multipart titles
+        DUE = 0
+        READY = 1
+        LOCKED = 2 #lock in an available copy
 
         class copy_obj:
-           def __init__(self, circ_modifier, circs, part_label, part_sort):
+           def __init__(self, circ_modifier, circs, part_label, part_sort, syrup_id):
               self.circ_modifier = circ_modifier
               self.circs = circs
               self.part_label = part_label
               self.part_sort = part_sort
-
-        #if a reserve title is specific to a barcode, there will not be more than one
-        def limit_counts(avail,lib,desk):
-           limit_avail = 0
-           limit_lib = 0
-           limit_desk = 0
-           if avail >= 1:
-              limit_avail = 1
-           if lib >= 1:
-              limit_lib = 1
-           if desk >= 1:
-              limit_desk = 1
-           return limit_avail, limit_lib, limit_desk
-           
-           
-        def get_copydetails(barcode,copyids,reserves_loc):
+              self.syrup_id = syrup_id
+
+        def make_obj_list(objlist):
+           objset = []
+           objcoll = objlist.split(";")
+           for o in objcoll:
+               objset.append(o.split("/"))
+
+           return objset
+
+        def collect_set(barcode,bcs,ids):
+           bc_dups = []
+           id_dups = []
+           i=0
+           for bc in bcs:
+               if barcode in bc:
+                   return bc,ids[i]
+               i = i+1
+
+           return bc_dups, id_dups
+                            
+        def get_copydetails(barcode,copyids,reserves_loc,bcs,ids):
            copy_list = []
 
+           bcs_set, ids_set = collect_set(barcode,bcs,ids)
+
            for copyid in copyids:
               circinfo = E1(OPENSRF_FLESHED2_CALL, copyid)
               circbarcode = None
+
               if barcode is not None:
                   circbarcode = circinfo.get("barcode")
              
@@ -203,7 +237,8 @@ class EvergreenIntegration(object):
               if thisloc:
                   thisloc = thisloc.get("name")
 
-              if thisloc in reserves_loc and barcode==circbarcode:
+              #create copy object for supplied barcode - will be all barcodes if none supplied
+              if thisloc in reserves_loc and (barcode==circbarcode or circbarcode in bcs_set):
                   circ_modifier = circinfo.get("circ_modifier")
                   circs = circinfo.get("circulations")
                   parts = circinfo.get("parts")
@@ -213,14 +248,20 @@ class EvergreenIntegration(object):
                   if parts:
                       part = parts[0]
                   if part:
-                      part_label = ' ' + part.get("label")
+                      part_label = part.get("label")
                       part_sort = part.get("label_sortkey")
 
-                  copy_list.append(copy_obj(circ_modifier,circs,part_label,part_sort))
+                  id_ind = -1
+                  if circbarcode in bcs_set:
+                      id_ind = ids_set[bcs_set.index(circbarcode)]
+                  copy_list.append(copy_obj(circ_modifier,circs,part_label,part_sort,id_ind))
 
            return sorted(copy_list, key=lambda copy: copy.part_sort)
 
-        def get_dueinfo(callprefix,callsuffix,callno,earliestdue,attachtest,voltest,sort_callno,bringfw,dueinfo):
+        #deal with call numbers that have embedded parts - ugh!
+        def get_dueinfo(callprefix,callsuffix,callno,earliestdue,attachtest,voltest,sort_callno,
+                            bringfw,dueinfo):
+
             tmpinfo = ''
 
             _callprefix = callprefix
@@ -256,8 +297,90 @@ class EvergreenIntegration(object):
 
             return _dueinfo,_callno,_callprefix,_callsuffix
 
+        #get due information - lots of pieces passed on for embedded parts
+        def deal_with_dues(duetime,avail,bringfw,copy,callprefix,callsuffix,callno,earliestdue,
+                attachtest,voltest,sort_callno,dueinfo):
+
+            earlydue = earliestdue
+            due = dueinfo
+            due_callprefix = callprefix
+            due_callno = callno
+            due_callsuffix = callsuffix
+             
+            if (avail == 0 or bringfw) and copy.circs and len(copy.circs) > 0:
+                if len(dueinfo) == 0 or bringfw:
+                    earlydue = duetime
+                    due,due_callno,due_callprefix,due_callsuffix = get_dueinfo(callprefix,callsuffix,callno,
+                        earliestdue,attachtest,voltest,sort_callno,bringfw,dueinfo)
+
+                if duetime < earlydue and not bringfw:
+                   earlydue = duetime
+                   due = time.strftime(self.DUE_FORMAT,earliestdue)
+
+            return due, due_callprefix, due_callno,due_callsuffix
+
+        #create initial call no and counts
+        def initialVals(prefix,suffix,callno,lib):
+            initial_callno = callno
+            if prefix:
+                initial_callno = prefix + callno
+            if suffix:
+                initial_callno = callno + suffix
+            initial_avail = stats.get(self.AVAILABLE, 0)
+            initial_avail += stats.get(self.RESHELVING, 0)
+            anystatus_here = sum(stats.values())
+                    
+            return initial_callno, lib + anystatus_here
+
+        #sometimes part information is in the callno directly, try to combine for display by
+        #shifting to suffix - otherwise treat normally
+        def add_in_embedded_parts(prefix,suffix,callprefix,callsuffix,callno,voltest,attachtest,vol):
+            embed_prefix = callprefix
+            embed_callno = callno
+            embed_suffix = callsuffix
+
+            if (voltest and vol > 0 ):
+                if (int(voltest.group(1)) > vol):
+                    embed_suffix = "/" + callno
+                else:
+                    embed_prefix = callno + "/"
+            elif attachtest and callno.find(attachtest.group(0)) == -1:
+                if len(callno) > 0:
+                    embed_suffix = "/" + callno
+                else:
+                    embed_prefix = callno
+            else:
+                embed_callno = prefix + callno + suffix
+
+            return embed_prefix, embed_callno, embed_suffix
+
+        #probably not needed but final sanity check for embedded parts
+        def last_check_embed(callprefix,callno,callsuffix,voltest,vol):
+            last_call = callno
+            last_vol = vol 
+            if callno.find(callprefix) == -1:
+                last_call = callprefix + callno
+            if callno.find(callsuffix) == -1:
+                last_call = last_call + callsuffix
+            if voltest:
+                last_vol = int(voltest.group(1))
+
+            return last_call, last_vol
+
+        #use counts from system if not parts
+        def get_desk_counts(counts):
+            desk_count = 0
+            for i, j, k, l, m, n in counts:
+                if m in self.RESERVES_DESK_NAME:
+                      desk_count += n.get(self.AVAILABLE, 0)
+                      desk_count += n.get(self.RESHELVING, 0)
+                      desk_count += n.get(self.CHECKEDOUT, 0)
+            return desk_count
+                    
+        #pull together status information
         def sort_out_status(barcode, sort_vol, counts, version, sort_lib, sort_desk, sort_avail, 
-            sort_callno, sort_dueinfo, sort_circmod, sort_allcalls, sort_alldues, prefix, suffix):
+            sort_callno, sort_dueinfo, sort_circmod, sort_allcalls, sort_alldues, prefix, suffix, 
+            bcs,ids):
 
             vol = sort_vol
             lib = sort_lib
@@ -274,55 +397,48 @@ class EvergreenIntegration(object):
                     callprefix = ''
                     callsuffix = ''
 
-                    if prefix:
-                       callno = prefix + callno
-                    if suffix:
-                       callno = callno + suffix
-                    avail_here = stats.get(self.AVAILABLE, 0)
-                    avail_here += stats.get(self.RESHELVING, 0)
-                    anystatus_here = sum(stats.values())
-                    lib += anystatus_here
+                    # get initial call number and total library count
+                    callno, lib = initialVals(prefix,suffix,callno,lib)
 
                     # volume check - based on v.1, etc. in call number
-                    voltest = re.search(r'\w*v\.\s?(\d+)', callno)
+                    voltest = re.search(self.IS_EMBEDDEDVOL, callno)
 
                     # attachment test
                     attachtest = re.search(self.IS_ATTACHMENT, callno)
 
-                    desk += anystatus_here
-                    avail += avail_here
                     dueinfo = ''
 
-                    if (voltest and vol > 0 ):
-                        if (int(voltest.group(1)) > vol):
-                            callsuffix = "/" + callno
-                        else:
-                            callprefix = callno + "/"
-                    elif attachtest and callno.find(attachtest.group(0)) == -1:
-                        if len(callno) > 0:
-                            callsuffix = "/" + callno
-                        else:
-                            callprefix = callno
-                    else:
-                           callno = prefix + callno + suffix
+                    # combine volume designations for embedded values
+                    callprefix,callno,callsuffix = add_in_embedded_parts(prefix,suffix,
+                        callprefix,callsuffix,callno,voltest,attachtest,vol)
 
                     if version >= 2.1:
                         copyids = E1(OPENSRF_CN_CALL, bib_id, [prefix,sort_callno,suffix], org)
                     else:
                         copyids = E1(OPENSRF_CN_CALL, bib_id, sort_callno, org)
 
-                    copies = get_copydetails(barcode,copyids,self.RESERVES_DESK_NAME)
-                    if barcode is not None:
-                        avail = lib = desk = 1
+                    #get copy information
+                    copies = get_copydetails(barcode,copyids,self.RESERVES_DESK_NAME,bcs,ids)
+
+                    desk = get_desk_counts(counts)
+                    avail = desk
+                       
+                    copy_parts = []
+                    duetime = None
+                    earliestdue = None
 
-                    # we want to return the resource that will be returned first if
-                    # already checked out
+                    # we want to identify the copy that will be returned first if
+                    # all are checked out
                     for copy in copies:
                         if copy.part_label:
                             #print "callno", callno
                             #print "sort_callno", sort_callno
                             callno = sort_callno + " " + copy.part_label
-                            allcalls.append([callno,1])
+                            if copy.part_sort in copy_parts and len(copy_parts) > 0:
+                                allcalls[len(allcalls) - 1] = [callno,READY,copy.syrup_id,copy.part_label]
+                            else:
+                                allcalls.append([callno,READY,copy.syrup_id,copy.part_label])
+                            copy_parts.append(copy.part_sort)
 
                         bringfw = attachtest
 
@@ -337,36 +453,31 @@ class EvergreenIntegration(object):
                             rawdate = rawdate[:-5]
                             duetime = time.strptime(rawdate, self.TIME_FORMAT)
 
-                        if (avail == 0 or bringfw) and copy.circs and len(copy.circs) > 0:
-                            if len(dueinfo) == 0 or bringfw:
-                                earliestdue = duetime
-                                dueinfo,callno,callprefix,callsuffix = get_dueinfo(callprefix,callsuffix,callno,
-                                   earliestdue,attachtest,voltest,sort_callno,bringfw,dueinfo)
-
-                            # way too wacky to sort out embedded vols for this
-                            if duetime < earliestdue and not bringfw:
-                                earliestdue = duetime
-                                dueinfo = time.strftime(self.DUE_FORMAT,earliestdue)
+                        #get due information - lots of extra pieces needed for embedded parts
+                        dueinfo,callprefix,callno,callsuffix = deal_with_dues(duetime,avail,bringfw,copy,
+                            callprefix,callsuffix,callno,earliestdue,attachtest,voltest,sort_callno,dueinfo)
 
                         alldisplay = callno + ' (Available)'
 
                         if copy.circs and isinstance(copy.circs, list):
+                            if len(allcalls) > 0 and (earliestdue is None or duetime < earliestdue):
+                                earliestdue = duetime
                             alldisplay = '%s (DUE: %s)' % (callno,time.strftime(self.DUE_FORMAT,duetime))
 
-                            if barcode is not None:
-                                avail = 0
-                            if copy.part_label:
-                                allcalls[len(allcalls) - 1] = [alldisplay,0]
+                            if len(allcalls) > 0:
+                                if allcalls[len(allcalls) - 1][1] != LOCKED:
+                                    allcalls[len(allcalls) - 1] = [alldisplay,DUE,copy.syrup_id,copy.part_label]
+                                    avail -= 1
+                            else:
+                                avail -= 1
+                               
+                        elif len(allcalls) > 0:
+                            allcalls[len(allcalls) - 1] = [callno,LOCKED,copy.syrup_id,copy.part_label]
 
                         alldues.append(alldisplay)
 
                         if voltest or attachtest:
-                            if callno.find(callprefix) == -1:
-                                callno = callprefix + callno
-                            if callno.find(callsuffix) == -1:
-                                callno = callno + callsuffix
-                            if voltest:
-                                vol = int(voltest.group(1))
+                            callno,vol = last_check_embed(callprefix,callno,callsuffix,voltest,vol)
             except:
                 print "due date/call problem: ", bib_id
                 print "*** print_exc:"
@@ -374,6 +485,10 @@ class EvergreenIntegration(object):
         
             return (vol, lib, desk, avail, callno, dueinfo, circmod, allcalls, alldues)
 
+        #get lists of barcodes and ids
+        bcs = make_obj_list(bclist)
+        ids = make_obj_list(idlist)
+
         # At this point, status information does not require the opensrf
         # bindings, I am not sure there is a use case where an evergreen
         # site would not have access to these but will leave for now
@@ -396,24 +511,22 @@ class EvergreenIntegration(object):
         version = getattr(settings, 'EVERGREEN_VERSION',
                       2.4)
 
-        #TODO: clean this up, a hackish workaround for now
         if version >= 2.1:
+            #this loop is needed in case there are multiple reserves locations
             for org, prefix, callno, suffix, loc, stats in counts:
                 if len(prefix) > 0:
                     prefix += ' '
                 if len(suffix) > 0:
                     suffix = ' ' + suffix
                 vol, lib, desk, avail, callno, dueinfo, circmod, allcalls, alldues = sort_out_status(barcode, vol, counts, 
-                    version, lib, desk, avail, callno, dueinfo, circmod, allcalls, alldues, prefix, suffix)
+                    version, lib, desk, avail, callno, dueinfo, circmod, allcalls, alldues, prefix, suffix, bcs,ids)
         else:
             for org, callno, loc, stats in counts:
                 vol, lib, desk, avail, callno, dueinfo, circmod, allcalls, alldues = sort_out_status(barcode, vol, counts, 
-                    version, lib, desk, avail, callno, dueinfo, circmod, allcalls, alldues)
+                    version, lib, desk, avail, callno, dueinfo, circmod, allcalls, alldues, bcs,ids)
             
         if len(allcalls) > 0:
-            cpname = 'parts'
-            if barcode is not None:
-                cpname = 'part'
+            cpname = 'volumes'
 
         return (cpname, lib, desk, avail, callno, dueinfo, circmod, allcalls, alldues)
 
index 2f57fec..680bcff 100644 (file)
@@ -26,6 +26,8 @@ if hasattr(settings, 'INTEGRATION_CLASS'):
     mod = __import__(modname, fromlist=[''])
     integration_class = getattr(mod, klassname)
 
+BIB_PART_MERGE = bool(getattr(settings, 'BIB_PART_MERGE', True))
+
 #----------------------------------------------------------------------
 
 class BaseModel(m.Model):
@@ -355,7 +357,10 @@ class Site(BaseModel):
         """A list of all items which are headings."""
         return self.item_set.filter(item_type='HEADING').order_by('title')
 
-    def item_tree(self, subtree=None):
+    def item_tree_merge(self, edit_status=False):
+        return self.item_tree(None,edit_status)
+
+    def item_tree(self, subtree=None, edit_status=False):
         """
         Return a list, representing a tree of the site items, in
         display order.  Every element of the list is an (Item, [Item])
@@ -381,6 +386,7 @@ class Site(BaseModel):
             return  " ".join(words)
 
         items = self.items()
+
         # make a node-lookup table
         dct = {}
         for item in items:
@@ -389,14 +395,65 @@ class Site(BaseModel):
             # TODO: what's the sort order? - art weighing in on normalized title
             lst.sort(key=lambda item: (item.item_type=='HEADING',
                                        sort_title(item.title))) # sort in place
+
+        #has barcode already been dealt with
+        def is_collected(poss,barcodes):
+            for bc in barcodes:
+                if poss.barcode in bc:
+                    return True
+            return False
+
+        #only concerned about duplicate physical items
+        def is_dup_candidate(poss):
+            if len(poss.bib_id) > 0:
+                if poss.item_type == 'PHYS':
+                    if poss.barcode is not None:
+                        return True
+            return False
+
+        #collect barcodes for dups
+        def deal_with_dups(item,items,edit_status,barcodes):
+            dup_barcodes = []
+            dup_ids = []
+            push_thru = True
+
+            if not BIB_PART_MERGE or edit_status:
+                return push_thru, dup_barcodes, dup_ids
+            if not is_dup_candidate(item):
+                return push_thru, dup_barcodes, dup_ids
+            if is_collected(item,barcodes):
+                return False, dup_barcodes, dup_ids
+                
+            for display_item in items:
+                 if is_dup_candidate(display_item):
+                     if display_item.barcode != item.barcode:
+                         if display_item.bib_id == item.bib_id and display_item.barcode not in dup_barcodes:
+                             dup_barcodes.append(display_item.barcode)
+                             dup_ids.append(display_item.id)
+
+       
+            if len(dup_barcodes) > 0 and not item.barcode in dup_barcodes:
+                dup_barcodes.append(item.barcode)
+                dup_ids.append(item.id)
+
+            return push_thru, dup_barcodes, dup_ids
+
         # walk the tree
         out = []
+        out_barcodes = []
+        out_ids = []
         def walk(parent, accum):
             here = dct.get(parent, [])
             for item in here:
                 sub = []
                 walk(item, sub)
-                accum.append((item, sub))
+                push_thru, bib_barcodes, syrup_ids = deal_with_dups(item,items,edit_status,out_barcodes)
+                if len(bib_barcodes) > 0:
+                    out_barcodes.append(bib_barcodes)
+                    out_ids.append(syrup_ids)
+                if push_thru:
+                    accum.append((item, sub, out_barcodes, out_ids))
         walk(subtree, out)
         return out
 
@@ -864,18 +921,14 @@ class Item(BaseModel):
     def describe_physical_item_status(self):
         """Return a (bool,str) tuple: whether the item is available,
         and a friendly description of the physical item's status"""
-        # TODO: this needs to be reimplemented, based on copy detail
-        # lookup in the ILS. It also may not belong here!
-        #return (True, 'NOT-IMPLEMENTED')
         stat = callhook('item_status', self)
         if not stat:
             return (False, 'Status information not available.')
         else:
             cpname, lib, desk, avail, callno, dueinfo, circmod, allcalls, alldues = stat
             return (avail > 0,
-                    '%d of %d %s available at reserves desk; '
-                    '%d total copies in library system'
-                    % (avail, desk, cpname, lib))
+                    '%d of %d %s available at reserves desk; %d total copies in library system' % 
+                    (avail, desk, cpname, lib))
 
     _video_type_re = re.compile(r'tag="007">v(.)')
     _video_types = {'c':'videocartridge',
index 5d18086..f52bfa8 100644 (file)
@@ -30,25 +30,25 @@ searchtext = _('search this site...')
   <ul py:def="show_tree(tree, edit=False)"
       py:if="tree"
       class="itemtree">
-    <span py:for="item, subs in tree" py:with="forbidden=not item.copyright_status_ok()">
+    <span py:for="item, subs, bcs, ids in tree" py:with="forbidden=not item.copyright_status_ok()">
     <li py:if="not item.suppress_item or user.is_staff" class="item_${item.item_type} an_item ${forbidden and 'forbidden' or ''}"
        id="item_${item.id}">
       <abbr py:if="not item.item_type=='HEADING'" class="unapi-id" title="${item.id}"/>
       <?python
        downloads_allowed = site.allows_downloads_to(user)
-       stat = callhook('item_status', item) if (item.item_type == 'PHYS') else None
+       stat = callhook('item_status', item, bcs, ids) if (item.item_type == 'PHYS') else None
        valid = stat is not None
       ?>
       <span py:if="valid" py:with="(_cpname, _lib, _desk, _avail, _callno, _dueinfo, _circmod, _allcalls, _alldues) = stat">
       <div class="availability" py:if="item.item_type == 'PHYS'">
        <a href="${item.item_url()}">
        <div class="${_avail &gt; 0 and 'available' or 'unavailable'}"
-            title="${_avail} of ${_desk} ${_cpname} available at reserves desk; ${_lib} total copies in library system">
+            title="${_avail} of ${_desk} copies available at reserves desk; ${_lib} total copies in library system">
           <!--
           <div>${_avail and 'Available' or 'Unavailable'} (${_avail}/${_desk})</div>
           -->
           <div py:if="_avail == 1">${_avail} copy available</div>
-          <div py:if="_avail &gt; 1 or _avail == 0">${_avail} ${_cpname} of ${_lib} available</div>
+          <div py:if="_avail &gt; 1 or _avail == 0">${_avail} copies of ${_desk} available</div>
           <div py:if="_circmod">
                <span py:if="_circmod == 'RSV2'">2 Hour Loan</span>
                <span py:if="_circmod == 'RSV3'">3 Day Loan</span>
@@ -68,7 +68,7 @@ searchtext = _('search this site...')
           </div>
          <div py:for="j,k in _allcalls" class="callnumber">
                 <span py:if="k==0" class="unavailable">${j}</span>
-                <span py:if="k==1">Ask for: <span class="standout">${j}</span></span>
+                <span py:if="k &gt; 0">Ask for: <span class="standout">${j}</span></span>
          </div>
           <div py:if="_dueinfo">NEXT DUE: ${_dueinfo}</div>
          </div>
@@ -105,8 +105,6 @@ searchtext = _('search this site...')
                </div>
          </div>
       </div>
-      </span>
-         
 
       <div class="availability avail_nonphys" py:if="item.item_type != 'PHYS'">
       </div>
@@ -114,10 +112,35 @@ searchtext = _('search this site...')
        <span py:if="item.item_type=='ELEC' and not downloads_allowed">
          <img title="Sorry, this document is not available to you."
               src="${ROOT}/static/tango/lock.png"/></span>
-         <a href="${item.item_url()}" class="mainlink"
+         <a py:if="len(_allcalls) == 0 or (edit and user.is_staff)" href="${item.item_url()}" class="mainlink"
             py:strip="item.item_type=='ELEC' and not downloads_allowed">${item}</a> 
        <span py:if="item.suppress_item">[additional copy - not displayed]</span>
+        <span py:if="len(_allcalls) &gt; 0 and not (edit and user.is_staff)">
+        <?python
+            site_url = item.item_url()
+            #get base of url without item number
+            site_url = site_url[:-1]
+            last_slash = site_url.rfind('/')
+            site_url = site_url[:last_slash+1]
+
+            display_calls = []
+            #parts are worth displaying as separate titles but otherwise limit 
+            for j,k,l,m in _allcalls:
+                part = ''
+                if len(m) > 0:
+                    part = ' - ' + m
+                #link to item if provided, otherwise use provided url
+                if l == -1: 
+                    display_calls.append([item.item_url(),item,part])
+                elif len(m) > 0 or len(display_calls) == 0:
+                    display_calls.append([site_url + str(l),item,part])
+        ?>
+         <div py:for="i,j,k in display_calls"> 
+               <a href="${i}" class="mainlink">${j}${k}</a>
+         </div>
+        </span>
       </div>
+      </span>
       <div class="author_pub" py:if="item.author or item.publisher or item.published">
        <span py:with="vt=item.video_type()"><span class="videotype" py:if="vt">[${vt}]</span></span>
                <span py:if="item.author">${item.author}<span py:if="item.author[-1]!='.'">.</span></span>
index 1c6469c..b8e7eac 100644 (file)
@@ -64,14 +64,14 @@ callnum = item.call_number()
         valid = stat is not None
       ?>
       <tr py:if="valid" py:with="(_cpname, _lib, _desk, _avail, _callno, _dueinfo, _circmod, _allcalls, _alldues) = stat"
-             title="${_avail} of ${_desk} ${_cpname} available at reserves desk; ${_lib} total copies in library system">
-         <th>Call Number (take this to the counter)</th>
+             title="${_avail} of ${_desk} copies available at reserves desk; ${_lib} total copies in library system">
+         <th>Call Number(s) (take to the counter)</th>
           <td>
           <div class="${_avail &gt; 0 and 'available' or 'unavailable'}" py:if="_dueinfo and len(_allcalls) == 0">DUE: ${_dueinfo}</div>
           <div class="${_avail &gt; 0 and 'available' or 'unavailable'}"><span py:if="len(_allcalls) == 0">${_callno}</span></div>
           <div py:for="j,k in _allcalls"> 
                 <span py:if="k==0" class="unavailable">${j}</span>
-                <span py:if="k==1" class="available">${j}</span>
+                <span py:if="k &gt; 0" class="available">${j}</span>
           </div>
           <div py:if="_circmod">
                 <span py:if="_circmod == 'RSV2'">2 Hour Loan</span>
index f402dc4..cac44e6 100644 (file)
@@ -1,7 +1,7 @@
 <?python
 title = '%s: %s (%s)' % (site.course.code, site.course.name, site.term)
-item_tree = site.item_tree()
 is_editor = site.can_edit(request.user)
+item_tree = site.item_tree(None,is_editor)
 is_joinable = site.is_joinable_by(request.user)
 ?>
 <html xmlns="http://www.w3.org/1999/xhtml"
@@ -31,6 +31,7 @@ is_joinable = site.is_joinable_by(request.user)
       <div py:if="is_editor" id="edit_site" class="little_action_panel">
        <div><a href="${site.site_url()}edit/">Setup</a></div>
        <div><a href="${site.site_url()}edit/permission/">Permissions</a></div>
+        <!-- an item must be added by barcode to have revert parameters -->
        <div><a href="${site.site_url()}revert_parms/">Revert Parameters</a></div>
       </div>
       <div py:if="is_joinable">
index ce0733b..22c1ad0 100644 (file)
@@ -18,7 +18,7 @@
     <li><a href="${ROOT}/admin/">Admin Options</a></li>
   </div>
   <li class="faculty_note"><a href="http://cronus.uwindsor.ca/units/leddy/leddy.nsf/PlacingMaterialInTheCourseReservesCollection?OpenForm">
-       Faculty, How to place materials on Course Reserve</a>
+       How to place materials on Course Reserve</a>
   </li>
 </ul>
 </html>
index 0ef7ca5..8091ddd 100644 (file)
@@ -10,7 +10,7 @@
   <!-- !<li><a href="${ROOT}/">Home</a></li> -->
   <li><a href="${ROOT}/browse/">Browse</a></li>
   <li class="faculty_note"><a href="http://cronus.uwindsor.ca/units/leddy/leddy.nsf/PlacingMaterialInTheCourseReservesCollection?OpenForm">
-        Faculty, How to place materials on Course Reserve</a>
+        How to place materials on Course Reserve</a>
   </li>
 </ul>
 </html>