Restrukturert trunk.
Lispkoden ligger nå i trunk/lisp Lagt til trunk/python, som inneholder interface.py, en sped begynnelse på et brukerinterfjas mot bibsys/databasen. Lagt til modifisert PyZ3950-bibliotek (\ /) (O.o) (> <) Bunny approves these changes.
This commit is contained in:
128
lisp/marc.lisp
Normal file
128
lisp/marc.lisp
Normal file
@@ -0,0 +1,128 @@
|
||||
(in-package #:z3950)
|
||||
|
||||
(defun marc-ascii (record start end)
|
||||
(babel:octets-to-string record :start start :end end))
|
||||
|
||||
(defun marc-number (record start end)
|
||||
(values (parse-integer (marc-ascii record start end))))
|
||||
|
||||
(defun marc21-base-address (record)
|
||||
(marc-number record 12 17))
|
||||
|
||||
(defun decode-subfield-code (octets)
|
||||
(intern (string-upcase (babel:octets-to-string octets :encoding :marc-8))
|
||||
"KEYWORD"))
|
||||
|
||||
(defun decode-marc21-subfields (field)
|
||||
(assert (equal #x1e (aref field (1- (length field)))))
|
||||
(loop
|
||||
with start = 0
|
||||
while (< start (1- (length field)))
|
||||
for end = (or (position #x1f field :start start)
|
||||
(1- (length field)))
|
||||
collect (list (decode-subfield-code (subseq field start (1+ start)))
|
||||
(babel:octets-to-string (subseq field (1+ start) end)
|
||||
:encoding :marc-8))
|
||||
do (setq start (1+ end))))
|
||||
|
||||
(defun decode-marc21-directory (record)
|
||||
"List of (TAG LENGTH START) lists. TAG is a string, the other two
|
||||
are integers. START is relative to the base address."
|
||||
(assert (equal "4500" (marc-ascii record 20 24)))
|
||||
(loop
|
||||
for s from 24 below (position #x1e record :start 24) by 12
|
||||
collect (list (marc-ascii record s (+ s 3))
|
||||
(marc-number record (+ s 3) (+ s 7))
|
||||
(marc-number record (+ s 7) (+ s 12)))))
|
||||
|
||||
(defun control-tag-p (tag)
|
||||
(equal "00" (subseq tag 0 2)))
|
||||
|
||||
(defun decode-marc21-field (record tag start end)
|
||||
(if (control-tag-p tag)
|
||||
(list tag
|
||||
(babel:octets-to-string (subseq record start end)
|
||||
:encoding :marc-8))
|
||||
(list tag
|
||||
(babel:octets-to-string (subseq record start
|
||||
(+ start 2))
|
||||
:encoding :marc-8)
|
||||
(decode-marc21-subfields (subseq record (+ start 3) end)))))
|
||||
|
||||
(defun decode-marc21 (record)
|
||||
(assert (equal "22" (marc-ascii record 10 12)))
|
||||
(let ((base (marc21-base-address record)))
|
||||
(loop
|
||||
for (tag length relative-start) in (decode-marc21-directory record)
|
||||
for start = (+ base relative-start)
|
||||
for end = (+ start length)
|
||||
collect (decode-marc21-field record tag start end))))
|
||||
|
||||
(defun fields (marc tag &rest more-tags)
|
||||
(let ((tags (cons tag more-tags)))
|
||||
(remove-if-not (lambda (x) (member x tags :test #'equal)) marc
|
||||
:key #'car)))
|
||||
|
||||
(defun subfield (contents subfield-code)
|
||||
(cadr (assoc subfield-code contents)))
|
||||
|
||||
(defmacro with-subfields ((&rest subfields) contents &body body)
|
||||
(alexandria:once-only (contents)
|
||||
`(let ,(loop for symbol in subfields
|
||||
collect `(,symbol
|
||||
(cadr (assoc ,(intern (string symbol)
|
||||
"KEYWORD")
|
||||
,contents))))
|
||||
,@body)))
|
||||
|
||||
;;; Mappings to Dublin Core, loosely based on
|
||||
;;; http://www.loc.gov/marc/marc2dc.html.
|
||||
|
||||
(defun personal-name (contents)
|
||||
(with-subfields (a b) contents
|
||||
(if b
|
||||
(format nil "~A ~A" a b)
|
||||
a)))
|
||||
|
||||
(defun marc21-contributor (marc)
|
||||
(loop
|
||||
for (tag indicators contents) in (fields marc "100" "110" "111"
|
||||
"700" "710" "711")
|
||||
collect (string-trim ".," (if (equal (char tag 2) #\1)
|
||||
(subfield contents :a)
|
||||
(personal-name contents)))))
|
||||
|
||||
(defun marc21-title (marc)
|
||||
(loop
|
||||
for (tag indicators contents) in (fields marc "245" "246")
|
||||
for title = (with-subfields (a b n p) contents
|
||||
(format nil "~{~A~^ ~}" (remove nil (list a b n p))))
|
||||
collect (let ((length (length title)))
|
||||
(if (and (>= length 2)
|
||||
(equal (subseq title (- length 2) length)
|
||||
" /"))
|
||||
(subseq title 0 (- (length title) 2))
|
||||
title))))
|
||||
|
||||
(defun marc21-isbn (marc)
|
||||
(loop
|
||||
for (tag indicators contents) in (fields marc "020")
|
||||
collect (subfield contents :a)))
|
||||
|
||||
(defun loc (isbn)
|
||||
(z3950:with-open-connection (c "z3950.loc.gov:7090/Voyager")
|
||||
(let ((rs (z3950:search-pqf c (format nil "@1=7 ~A" isbn))))
|
||||
(when (> (z3950:result-set-size rs) 0)
|
||||
(multiple-value-bind (octets syntax)
|
||||
(z3950:result-set-record rs 0)
|
||||
(assert (equal syntax "USmarc"))
|
||||
(let ((marc (decode-marc21 octets)))
|
||||
(values `((:contributor ,(marc21-contributor marc))
|
||||
(:title ,(marc21-title marc))
|
||||
(:identifier ,(marc21-isbn marc)))
|
||||
marc octets)))))))
|
||||
|
||||
#|
|
||||
(loc "9780201914658")
|
||||
|#
|
||||
|
Reference in New Issue
Block a user