From 01ff48033f54e5c2b4d0fd59d204aaa23fdb108a Mon Sep 17 00:00:00 2001 From: Art Rhyno Date: Tue, 8 Apr 2014 20:03:59 -0400 Subject: [PATCH] Add Parts Support Syrup was developed before the parts mechanism in evergreen was available. I have left the munging option in place which tries to deal with call numbers that have parts information embedded in the call number string. Hopefully, such practices will not be common anymore since parts is wayyyy more elegant. Signed-off-by: Art Rhyno --- conifer/integration/evergreen_site.py | 263 ++++++++++++++++++----------- conifer/syrup/models.py | 10 +- conifer/syrup/urls.py | 1 + conifer/syrup/views/sites.py | 7 + conifer/templates/components/site.xhtml | 19 ++- conifer/templates/item/item_metadata.xhtml | 12 +- 6 files changed, 201 insertions(+), 111 deletions(-) diff --git a/conifer/integration/evergreen_site.py b/conifer/integration/evergreen_site.py index 0fa3aee..3936f1e 100644 --- a/conifer/integration/evergreen_site.py +++ b/conifer/integration/evergreen_site.py @@ -160,14 +160,104 @@ class EvergreenIntegration(object): """ if not item.bib_id: return None - return self._item_status(item.bib_id) + return self._item_status(item.bib_id, item.barcode) + CACHE_TIME = 300 @memoize(timeout=CACHE_TIME) - def _item_status(self, bib_id): - def sort_out_status(sort_vol, counts, version, sort_lib, sort_desk, sort_avail, - sort_callno, sort_dueinfo, sort_circmod, sort_alldues, prefix, suffix): + def _item_status(self, bib_id, barcode): + + class copy_obj: + def __init__(self, circ_modifier, circs, part_label, part_sort): + 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): + copy_list = [] + + for copyid in copyids: + circinfo = E1(OPENSRF_FLESHED2_CALL, copyid) + circbarcode = None + if barcode is not None: + circbarcode = circinfo.get("barcode") + + thisloc = circinfo.get("location") + + if thisloc: + thisloc = thisloc.get("name") + + if thisloc in reserves_loc and barcode==circbarcode: + circ_modifier = circinfo.get("circ_modifier") + circs = circinfo.get("circulations") + parts = circinfo.get("parts") + part_label = '' + part_sort = None + part = None + if parts: + part = parts[0] + if part: + part_label = ' ' + part.get("label") + part_sort = part.get("label_sortkey") + + copy_list.append(copy_obj(circ_modifier,circs,part_label,part_sort)) + + return sorted(copy_list, key=lambda copy: copy.part_sort) + + def get_dueinfo(callprefix,callsuffix,callno,earliestdue,attachtest,voltest,sort_callno,bringfw,dueinfo): + tmpinfo = '' + + _callprefix = callprefix + _callsuffix = callsuffix + _callno = callno + _dueinfo = dueinfo + + if voltest: + if (int(voltest.group(1)) > vol): + if len(_dueinfo) > 0: + _dueinfo = _dueinfo + "/" + _dueinfo = _dueinfo + voltest.group(0) + ': ' + time.strftime(self.DUE_FORMAT,earliestdue) + else: + tmpinfo = _dueinfo + _dueinfo = voltest.group(0) + ': ' + time.strftime(self.DUE_FORMAT,earliestdue) + if len(tmpinfo) > 0: + _dueinfo = _dueinfo + "/" + tmpinfo + _callprefix = _callsuffix = '' + elif attachtest: + tmpinfo = _dueinfo + _dueinfo = attachtest.group(0) + ': ' + time.strftime(self.DUE_FORMAT,earliestdue) + if len(_callno) > 0: + _callno = _callno + '/' + sort_callno + _callprefix = _callsuffix = '' + else: + _callno = sort_callno + if len(tmpinfo) > 0: + _dueinfo = _dueinfo + "/" + tmpinfo + + if not bringfw: + _dueinfo = time.strftime(self.DUE_FORMAT,earliestdue) + _callno = sort_callno + + return _dueinfo,_callno,_callprefix,_callsuffix + + 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): vol = sort_vol lib = sort_lib @@ -176,13 +266,13 @@ class EvergreenIntegration(object): callno = sort_callno dueinfo = sort_dueinfo circmod = sort_circmod + allcalls = sort_allcalls alldues = sort_alldues + try: if loc in self.RESERVES_DESK_NAME: callprefix = '' callsuffix = '' - if len(callno) == 0: - callno = callnum if prefix: callno = prefix + callno @@ -194,10 +284,10 @@ class EvergreenIntegration(object): lib += anystatus_here # volume check - based on v.1, etc. in call number - voltest = re.search(r'\w*v\.\s?(\d+)', callnum) + voltest = re.search(r'\w*v\.\s?(\d+)', callno) # attachment test - attachtest = re.search(self.IS_ATTACHMENT, callnum) + attachtest = re.search(self.IS_ATTACHMENT, callno) desk += anystatus_here avail += avail_here @@ -205,106 +295,84 @@ class EvergreenIntegration(object): if (voltest and vol > 0 ): if (int(voltest.group(1)) > vol): - callsuffix = "/" + callnum + callsuffix = "/" + callno else: - callprefix = callnum + "/" + callprefix = callno + "/" elif attachtest and callno.find(attachtest.group(0)) == -1: if len(callno) > 0: - callsuffix = "/" + callnum + callsuffix = "/" + callno else: - callprefix = callnum + callprefix = callno else: - callno = prefix + callnum + suffix + callno = prefix + callno + suffix if version >= 2.1: - copyids = E1(OPENSRF_CN_CALL, bib_id, [prefix,callnum,suffix], org) + copyids = E1(OPENSRF_CN_CALL, bib_id, [prefix,sort_callno,suffix], org) else: - copyids = E1(OPENSRF_CN_CALL, bib_id, callnum, org) + 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 # we want to return the resource that will be returned first if # already checked out - for copyid in copyids: - circinfo = E1(OPENSRF_FLESHED2_CALL, copyid) - - thisloc = circinfo.get("location") - if thisloc: - thisloc = thisloc.get("name") - - if thisloc in self.RESERVES_DESK_NAME: - bringfw = attachtest - - # multiple volumes - if voltest and callno.find(voltest.group(0)) == -1: - bringfw = True - - if len(circmod) == 0: - circmod = circinfo.get("circ_modifier") - circs = circinfo.get("circulations") - - if circs and isinstance(circs, list): - circ = circs[0] - rawdate = circ.get("due_date") - #remove offset info, %z is flakey for some reason - rawdate = rawdate[:-5] - duetime = time.strptime(rawdate, self.TIME_FORMAT) - - if (avail == 0 or bringfw) and circs and len(circs) > 0: - if len(dueinfo) == 0 or bringfw: - earliestdue = duetime - if voltest: - if (int(voltest.group(1)) > vol): - if len(dueinfo) > 0: - dueinfo = dueinfo + "/" - dueinfo = dueinfo + voltest.group(0) + ': ' + time.strftime(self.DUE_FORMAT,earliestdue) - else: - tmpinfo = dueinfo - dueinfo = voltest.group(0) + ': ' + time.strftime(self.DUE_FORMAT,earliestdue) - if len(tmpinfo) > 0: - dueinfo = dueinfo + "/" + tmpinfo - callprefix = callsuffix = '' - elif attachtest: - tmpinfo = dueinfo - dueinfo = attachtest.group(0) + ': ' + time.strftime(self.DUE_FORMAT,earliestdue) - if len(callno) > 0: - callno = callno + '/' + callnum - callprefix = callsuffix = '' - else: - callno = callnum - if len(tmpinfo) > 0: - dueinfo = dueinfo + "/" + tmpinfo - - if not bringfw: - dueinfo = time.strftime(self.DUE_FORMAT,earliestdue) - callno = callnum - - # way too wacky to sort out vols for this - if duetime < earliestdue and not bringfw: - earliestdue = duetime - dueinfo = time.strftime(self.DUE_FORMAT,earliestdue) - callno = callnum - - alldisplay = callnum + ' (Available)' - - if circs and isinstance(circs, list): - alldisplay = '%s (DUE: %s)' % (callnum, time.strftime(self.DUE_FORMAT,duetime)) - - alldues.append(alldisplay) + 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]) + + bringfw = attachtest + + # multiple volumes in identified in call number + if voltest and callno.find(voltest.group(0)) == -1: + bringfw = True + + if copy.circs and isinstance(copy.circs, list): + circ = copy.circs[0] + rawdate = circ.get("due_date") + #remove offset info, %z is flakey for some reason + 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) + + alldisplay = callno + ' (Available)' + + if copy.circs and isinstance(copy.circs, list): + 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] + + 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)) - - + if voltest: + vol = int(voltest.group(1)) except: print "due date/call problem: ", bib_id print "*** print_exc:" traceback.print_exc() - return (vol, lib, desk, avail, callno, dueinfo, circmod, alldues) + return (vol, lib, desk, avail, callno, dueinfo, circmod, allcalls, alldues) # At this point, status information does not require the opensrf # bindings, I am not sure there is a use case where an evergreen @@ -316,7 +384,9 @@ class EvergreenIntegration(object): dueinfo = '' callno = '' circmod = '' + allcalls = [] alldues = [] + cpname = 'copies' EVERGREEN_STATUS_ORG = getattr(settings, 'EVERGREEN_STATUS_ORG', 1) EVERGREEN_STATUS_DEPTH = getattr(settings, 'EVERGREEN_STATUS_DEPTH', 0) @@ -324,23 +394,28 @@ class EvergreenIntegration(object): counts = E1(OPENSRF_COPY_COUNTS, bib_id, EVERGREEN_STATUS_ORG, EVERGREEN_STATUS_DEPTH) version = getattr(settings, 'EVERGREEN_VERSION', - 2.0) + 2.4) #TODO: clean this up, a hackish workaround for now if version >= 2.1: - for org, prefix, callnum, suffix, loc, stats in counts: + 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, alldues = sort_out_status(vol, counts, - version, lib, desk, avail, callno, dueinfo, circmod, alldues, prefix, 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) else: - for org, callnum, loc, stats in counts: - vol, lib, desk, avail, callno, dueinfo, circmod, alldues = sort_out_status(vol, counts, - version, lib, desk, avail, callno, dueinfo, circmod, alldues) + 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) - return (lib, desk, avail, callno, dueinfo, circmod, alldues) + if len(allcalls) > 0: + cpname = 'parts' + if barcode is not None: + cpname = 'part' + + return (cpname, lib, desk, avail, callno, dueinfo, circmod, allcalls, alldues) diff --git a/conifer/syrup/models.py b/conifer/syrup/models.py index 19ee817..2f57fec 100644 --- a/conifer/syrup/models.py +++ b/conifer/syrup/models.py @@ -310,10 +310,6 @@ class Site(BaseModel): sitenotes = m.TextField('Course Site Notes', blank=True, null=True) - # temporary, uwindsor-only fields! For initial migration. FIXME: remove. - # uwindsor_bookbag = m.CharField(max_length=2048, blank=True, null=True) - # uwindsor_eres = m.CharField(max_length=2048, blank=True, null=True) - @property def term(self): """ @@ -875,11 +871,11 @@ class Item(BaseModel): if not stat: return (False, 'Status information not available.') else: - lib, desk, avail, callno, dueinfo, circmod, alldues = stat + cpname, lib, desk, avail, callno, dueinfo, circmod, allcalls, alldues = stat return (avail > 0, - '%d of %d copies available at reserves desk; ' + '%d of %d %s available at reserves desk; ' '%d total copies in library system' - % (avail, desk, lib)) + % (avail, desk, cpname, lib)) _video_type_re = re.compile(r'tag="007">v(.)') _video_types = {'c':'videocartridge', diff --git a/conifer/syrup/urls.py b/conifer/syrup/urls.py index 49d2b64..f78e138 100644 --- a/conifer/syrup/urls.py +++ b/conifer/syrup/urls.py @@ -8,6 +8,7 @@ GENERIC_REGEX = r'((?P\d+)/)?(?P.+)?$' urlpatterns = patterns('conifer.syrup.views', (r'^$', 'welcome'), + (r'^test/$', 'my_tests'), (r'^site/$', 'my_sites'), (r'^site/new/$', 'add_new_site'), (r'^site/invitation/$', 'site_invitation'), diff --git a/conifer/syrup/views/sites.py b/conifer/syrup/views/sites.py index 0a1736c..423453c 100644 --- a/conifer/syrup/views/sites.py +++ b/conifer/syrup/views/sites.py @@ -86,11 +86,16 @@ def _add_or_edit_site(request, instance=None, basis=None): else: form.save() site = form.instance + print "site", site assert site.id + test_site = models.Site.objects.get(pk=3) + print "TEST_SITE", test_site if 'basis' in POST: # We are duplicating a site. Copy all the items over into the new site. source_site = models.Site.objects.get(pk=POST['basis']) + print "SOURCE_SITE", source_site + print "POST", POST['basis'] _copy_contents(request, source_site, site) if is_add: # we need to configure permissions. @@ -193,6 +198,8 @@ def my_sites(request): time_query = models.Term.timeframe_query(timeframe) return g.render('my_sites.xhtml', **locals()) +def my_tests(request): + return g.render('my_test.xhtml', **locals()) def browse(request, browse_option='Department'): #the defaults should be moved into a config file or something... diff --git a/conifer/templates/components/site.xhtml b/conifer/templates/components/site.xhtml index 3b1e482..5d18086 100644 --- a/conifer/templates/components/site.xhtml +++ b/conifer/templates/components/site.xhtml @@ -39,16 +39,16 @@ searchtext = _('search this site...') stat = callhook('item_status', item) if (item.item_type == 'PHYS') else None valid = stat is not None ?> +
-
+
${_avail} copy available
-
${_avail} copies available
+
${_avail} ${_cpname} of ${_lib} available
2 Hour Loan 3 Day Loan @@ -63,7 +63,13 @@ searchtext = _('search this site...') 4 Hour Loan 7 Day Loan
-
Ask for: ${_callno}
+
+ Ask for: ${_callno} +
+
+ ${j} + Ask for: ${j} +
NEXT DUE: ${_dueinfo}
@@ -73,7 +79,7 @@ searchtext = _('search this site...')
-
+