├── README.md ├── tests ├── test-runner.xq └── test-search.xqm ├── index.xhtml ├── user-stories └── user-stories.txt └── modules └── search.xqm /README.md: -------------------------------------------------------------------------------- 1 | # catalogers-workbench 2 | Currently in development, this will be an open source productivity tool and prototype BIBFRAME editor for library catalogers. Designed to be an eXist-db app built using the XRX approach (with XForms, RESTXQ, and XQuery). 3 | -------------------------------------------------------------------------------- /tests/test-runner.xq: -------------------------------------------------------------------------------- 1 | xquery version "3.0"; 2 | 3 | (: This main module fires off the tests stored in tests.xql :) 4 | 5 | import module namespace test = "http://exist-db.org/xquery/xqsuite" at "resource:org/exist/xquery/lib/xqsuite/xqsuite.xql"; 6 | import module namespace inspect = "http://exist-db.org/xquery/inspection"; 7 | import module namespace wb-test-search = "http://libserv6.princeton.edu/exist/apps/workbench/test/search" at "test-search.xqm"; 8 | 9 | let $test-search-modules := ( 10 | xs:anyURI("/db/apps/workbench/tests/test-search.xqm") 11 | ) 12 | let $test-search-functions := $test-search-modules ! inspect:module-functions(.) 13 | return 14 | test:suite($test-search-functions) -------------------------------------------------------------------------------- /index.xhtml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | 11 | 12 | Cataloger's Workbench 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | Test 26 | 27 | 28 | Success. 29 | 30 | Submission error () 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | Title 40 | 41 | 42 | Search 43 | 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /user-stories/user-stories.txt: -------------------------------------------------------------------------------- 1 | Cataloger's Workbench User Stories 2 | Revised: 2015-02-13 3 | 4 | Data Entry Stories 5 | 6 | General Cataloging Stories 7 | 1. As a user cataloging books, journals, etc., I want to search WorldCat for catalog records so that I can save time by finding LC and member copy. 8 | Search by defined fields 9 | ISBN, ISSN, standard number, title, personal name, and corporate/conference name 10 | Sort results for quality and completeness 11 | LC copy, PCC, full-level member copy, incomplete member copy, non-English records, etc. 12 | 2. As a user cataloging books, journals, etc., I want to select results from WorldCat search. 13 | For LC/PCC records or full-level member copy, pre-select the record 14 | For "bad" member copy, attempt to normalize it (ensuring that data fields are consistent with control fields, etc. 15 | For non-English records, attempt to extract enough relevant info to create a well-structured brief record in English 16 | 3. As a user cataloging books, journals, etc., I want to save results from WorldCat search so that I can edit or accept a catalog record. 17 | Save as MARCXML, convert to binary MARC for import into Voyager 18 | To edit records, convert data to BF using LC conversion scripts, then load data into XForm 19 | 4. As a user cataloging books, journals, etc., I want to save data from the Web form. 20 | Save to triplestore as BF 21 | Convert BF to MARCXML and save to eXist 22 | Convert MARCXML to binary MARC for import into Voyager 23 | 5. As a user cataloging books, journals, etc., if no records in WorldCat, I want to query public APIs like Open Library for available metadata. 24 | If available, load imported metadata into Web form to create a brief record 25 | 6. As a user cataloging books, journals, etc., if no external data available, I want to produce a blank Web form so that I can create a new bibliographic description. 26 | 27 | DVD Cataloging Stories 28 | 1. As a user cataloging DVDs, if no records in WorldCat, I want to query DBpedia and/or other APIs (like OMDb) for available metadata. 29 | If available, load imported metadata into Web form to create a brief record 30 | 2. As a user cataloging DVDs, if no external data available, I want to produce a blank Web form so that I can create a new bibliographic description. 31 | 32 | -------------------------------------------------------------------------------- /modules/search.xqm: -------------------------------------------------------------------------------- 1 | xquery version "3.0"; 2 | 3 | (:~ 4 | : This module defines the RESTXQ functions and endpoints used when executing a search 5 | : against the WorldCat Search API. It accepts submissions from an XForms search interface 6 | : and queries the API to retrieve MARCXML records. 7 | : 8 | : @see http://www.oclc.org/developer/develop/web-services/worldcat-search-api.en.html 9 | : @author tat2 10 | : @version 0.1 11 | :) 12 | 13 | module namespace wb-search = "http://libserv6.princeton.edu/exist/apps/workbench/search"; 14 | 15 | import module namespace functx="http://www.functx.com"; 16 | 17 | declare namespace rest = "http://exquery.org/ns/restxq"; 18 | declare namespace httpclient = "http://exist-db.org/xquery/httpclient"; 19 | declare namespace http = "http://expath.org/ns/http-client"; 20 | declare namespace oclc-sru = "http://www.loc.gov/zing/srw/"; 21 | declare namespace wb = "http://libserv6.princeton.edu/exist/apps/workbench"; 22 | 23 | declare variable $wb-search:oclc-uri := "http://www.worldcat.org/webservices/catalog/search/sru?query="; 24 | declare variable $wb-search:oclc-key := "&wskey=YOUR KEY HERE"; 25 | 26 | (:~ 27 | : Accepts an XML document submitted from the XForms interface and uses it to query the WorldCat Search API. 28 | : Possible fields to query are title, personal name, corporate/conference name, ISBN, ISSN, or standard number (e.g., EAN). 29 | : Title and name can be searched in combination with each other. 30 | : 31 | : @param $query An XML document POSTed from XForms submission 32 | : @return Either MARCXML, a notification that there were no results, or an appropriate error message (server or client) 33 | :) 34 | 35 | declare 36 | %rest:path("/query") 37 | %rest:POST("{$query}") 38 | %rest:consumes("application/xml") 39 | %rest:produces("application/xml") 40 | function wb-search:oclc-search($query as document-node()) as element()* { 41 | 42 | let $uri := $wb-search:oclc-uri 43 | let $key := $wb-search:oclc-key 44 | 45 | (: Assign POSTed values to variables. :) 46 | let $title := $query//wb:title 47 | let $persname := $query//wb:persName 48 | let $corpname := $query//wb:corpName 49 | let $isbn := $query//wb:isbn 50 | let $issn := $query//wb:issn 51 | let $sn := $query//wb:sn 52 | 53 | (: Encode $title string for URI and concat with SRU syntax. :) 54 | let $encoded-title := encode-for-uri(concat("srw.ti = "", $title, """)) 55 | let $search-uri := concat($uri, $encoded-title, $key) 56 | let $request := 57 | let $response := http:send-request($request) 58 | let $head := $response[1] 59 | 60 | return 61 | if ($head/@status = "200") then 62 | { 63 | if ($response//oclc-sru:record) 64 | then for $r in $response 65 | return $r//oclc-sru:record/functx:add-attributes(., xs:QName("wb-search:test"), "false") 66 | else Sorry, there were no results for your search. 67 | } 68 | else 69 | Oops, something went wrong!{ $head } 70 | 71 | }; 72 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /tests/test-search.xqm: -------------------------------------------------------------------------------- 1 | xquery version "3.0"; 2 | 3 | (:~ 4 | : This library module contains XQSuite tests for the wb-search module stored in modules/search.xqm. 5 | : 6 | : @author tat2 7 | : @version 0.1 8 | :) 9 | 10 | module namespace wb-test-search = "http://libserv6.princeton.edu/exist/apps/workbench/test/search"; 11 | 12 | import module namespace wb-search = "http://libserv6.princeton.edu/exist/apps/workbench/search" at "../modules/search.xqm"; 13 | 14 | declare namespace test = "http://exist-db.org/xquery/xqsuite"; 15 | declare namespace wb = "http://libserv6.princeton.edu/exist/apps/workbench"; 16 | 17 | (: Set up test data. :) 18 | 19 | declare 20 | %test:setUp 21 | function wb-test-search:_test-setup() { 22 | xmldb:create-collection("/db/apps/workbench/tests", "test-records"), 23 | xmldb:store("/db/apps/workbench/tests/test-records", "search-test-1.xml", 24 | 25 | Pérez, Henock 26 | Província Camiliana Brasileira 27 | Land of Shant 28 | 9786124165139 29 | 19968663 30 | 7898114691107 31 | ), 32 | xmldb:store("/db/apps/workbench/tests/test-records", "search-test-2.xml", 33 | 34 | Pérez, Henock 35 | Província Camiliana Brasileira 36 | adfadfadsf 37 | 9786124165139 38 | 19968663 39 | 7898114691107 40 | ) 41 | }; 42 | 43 | (: Remove test data. :) 44 | 45 | declare 46 | %test:tearDown 47 | function wb-test-search:_test-teardown() { 48 | xmldb:remove("/db/apps/workbench/tests/test-records") 49 | }; 50 | 51 | (:~ 52 | : Given an XML document with elements containing search strings, 53 | : run a search against the World Search API (using SRU query params). 54 | : Test to see whether one or more MARCXML records has been returned. 55 | :::: If a MARCXML record has been returned, 56 | :::: the result should have a child element. 57 | :) 58 | 59 | declare 60 | %test:args("/db/apps/workbench/tests/test-records/search-test-1.xml") 61 | %test:assertXPath("count($result/*[local-name() = 'record']) >= 1") 62 | function wb-test-search:oclc-test-for-results($query-path as xs:string) { 63 | let $arg := doc($query-path) 64 | return 65 | wb-search:oclc-search($arg) 66 | }; 67 | 68 | (:~ 69 | : Given an XML document with elements containing search strings, 70 | : run a search against the World Search API (using SRU query params). 71 | : Test for a server response code of 200. 72 | :::: If the server response code is not 200, 73 | :::: an element with an error message should be produced. 74 | :) 75 | 76 | declare 77 | %test:args("/db/apps/workbench/tests/test-records/search-test-1.xml") 78 | %test:assertXPath("not($result/error)") 79 | function wb-test-search:oclc-test-for-server($query-path as xs:string) { 80 | let $arg := doc($query-path) 81 | return 82 | wb-search:oclc-search($arg) 83 | }; 84 | 85 | (:~ 86 | : Given an XML document with elements containing search strings, 87 | : run a search against the World Search API (using SRU query params). 88 | : Test for a search with no results. 89 | :::: If no MARCXML records are returned, 90 | :::: a element should be returned, notifying the user that there were no results. 91 | :) 92 | 93 | declare 94 | %test:args("/db/apps/workbench/tests/test-records/search-test-2.xml") 95 | %test:assertXPath("exists($result/message)") 96 | function wb-test-search:oclc-test-for-no-results($query-path as xs:string) { 97 | let $arg := doc($query-path) 98 | return 99 | wb-search:oclc-search($arg) 100 | }; 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | --------------------------------------------------------------------------------