Catmandu::AlephX(3pm)
π Catmandu::AlephX π
okay so bestie, welcome to the Catmandu::AlephX page π
β it's one of those User Contributed Perl Documentation pages, filed under Catmandu::AlephX(3pm), and it is literally your low-level client for Aleph X-Services. lowkey iconic, highkey useful. let's get into it π¦
β¨ NAME β¨
Catmandu::AlephX β a Low level client for Aleph X-Services. that's the whole vibe. π
πΈ SYNOPSIS πΈ
here's the group-chat summary of how you actually use this thing:
my $aleph = Catmandu::AlephX->new(url => "http://localhost/X");
my $item_data = $aleph->item_data(base => "rug01",doc_number => "001484477");
#all public methods return a Catmandu::AlephX::Response
# 'is_success' means that the xml-response did not contain the element 'error'
# most methods return only one 'error', but some (like update_doc) multiple.
# other errors are thrown (xml parse error, no connection ..)
if($item_data->is_success){
say "valid response from aleph x server";
}else{
say "aleph x server returned error-response: ".join("\n",@{$item_data->errors});
}
so the tea is π: every public method hands you back a Catmandu::AlephX::Response. and is_success? it just means the xml-response did not contain the element error β that's it, that's the whole check. most methods only give you one error, but some (looking at you, update_doc π) can give you several. and then there's a whole other category of chaos that just gets thrown at you β xml parse errors, no connection, that kind of thing. no is_success for those, they just yeet. π
π METHODS π
π item-data π
straight from the AlephX docs, bestie:
this service takes a document number from you, the user. then for every single item of that document it goes and grabs:
- Item information (From Z30). π
- Loan information (from Z36). π
- An indication whether the request is on-hold π
example:
my $item_data = $aleph->item_data(base => "rug01",doc_number => "001484477");
if($item_data->is_success){
for my $item(@{ $item_data->items() }){
print Dumper($item);
};
}else{
print STDERR join("\n",@{$item_data->errors})."\n";
}
remarks:
This method is equivalent to'op'='item-data'β periodt. π
π¦ item-data-multi π¦
documentation from AlephX, and it's giving "item-data but upgraded":
this service takes a document number from you, and for each of the document's items it retrieves the following:
- Item information (from Z30) β¨
- Loan information (from Z36) An indication of whether or not the item is on hold, has hold requests, or is expected (that is, has not arrived yet but is expected) β¨
It is similar to the item_data X-service, except for the parameter START_POINT, which enables the retrieval of information for documents with more than 1000 items. π
example:
my $item_data_m = $aleph->item_data_multi(base => "rug01",doc_number => "001484477",start_point => '000000990');
if($item_data_m->is_success){
for my $item(@{ $item_data_m->items() }){
print Dumper($item);
};
}else{
print STDERR join("\n",@{$item_data_m->errors})."\n";
}
say "items retrieved, starting at ".$item_data_m->start_point() if $item_data_m->start_point();
remarks:
This method is equivalent to'op'='item-data-multi'. The attribute'start_point'only supplies a value, if the document has over 990 items. (yes, 990. not 1000. we don't make the rules, bestie π)
π read_item π
documentation from AlephX:
this service retrieves a requested item's record from a given ADM library β but only in case such an item does actually exist in that ADM library. no item, no record, that's the deal. π«
example:
my $readitem = $aleph->read_item(library=>"usm50",item_barcode=>293);
if($readitem->is_success){
for my $z30(@{ $readitem->z30 }){
print Dumper($z30);
}
}else{
say STDERR join("\n",@{$readitem->errors});
}
remarks:
This method is equivalent to'op'='read-item'. π«Ά
β find β
documentation from Aleph X:
this service retrieves a set number and the number of records answering a search request that you, the user, inserted. so you ask, it tells you what set you got and how much is in it. π―
example:
my $find = $aleph->find(request => 'wrd=(art)',base=>'rug01');
if($find->is_success){
say "set_number: ".$find->set_number;
say "no_records: ".$find->no_records;
say "no_entries: ".$find->no_entries;
}else{
say STDERR join("\n",@{$find->errors});
}
remarks:
This method is equivalent to'op'='find'.
arguments:
request- search request πadjacent- if'Y'then the documents should contain all the search words adjacent to each other, otherwise'N'π§©
π find_doc π
documentation from AlephX:
this service retrieves the OAI XML format of an expanded document as given by the user. you hand it the deets, it hands you the XML. β¨
example:
my $find = $aleph->find_doc(base=>'rug01',doc_num=>'000000444',format=>'marc');
if($find->is_success){
say Dumper($find->record);
}else{
say STDERR join("\n",@{$find->errors});
}
remarks:
This method is equivalent to'op'='find-doc'.
π present π
documentation from Aleph X:
this service retrieves OAI XML format of expanded documents. and plot twist β you can view documents according to the locations within a specific set number. π
example:
my $set_number = $aleph->find(request => "wrd=(BIB.AFF)",base => "rug01")->set_number;
my $present = $aleph->present(
set_number => $set_number,
set_entry => "000000001-000000003"
);
if($present->is_success){
for my $record(@{ $present->records() }){
say "doc_number: ".$record->doc_number;
say "\tmetadata: ".$record->metadata->type;
}
}else{
say STDERR join("\n",@{$present->errors});
}
remarks:
This method is equivalent to'op'='present'. π
π¦ ill_get_doc_short π¦
documentation from Aleph X:
this service retrieves the doc number and the XML of the short document (Z13). short and sweet, just like a good group chat reply. π
example:
my $result = $aleph->ill_get_doc_short(doc_number => "000000001",library=>"usm01");
if($result->is_success){
for my $z30(@{ $result->z13 }){
print Dumper($z30);
}
}else{
say STDERR join("\n",@{$result->errors});
}
remarks:
This method is equivalent to'op'='ill-get-doc-short'.
π bor_auth π
documentation from Aleph X:
this service retrieves the Global record (Z303), Local record (Z305) and the Data record (Z304) for a given Patron β if the given ID and verification code match. π
Otherwise, an error message is returned. no match, no data, bestie. that's just how it is. π
example:
my %args = (
library => $library,
bor_id => $bor_id,
verification => $verification
);
my $auth = $aleph->bor_auth(%args);
if($auth->is_success){
for my $type(qw(z303 z304 z305)){
say "$type:";
my $data = $auth->$type();
for my $key(keys %$data){
say "\t$key : $data->{$key}->[0]";
}
}
}else{
say STDERR "error: ".join("\n",@{$auth->errors});
exit 1;
}
π bor_info π
documentation from Aleph X:
this service retrieves all information related to a given Patron: Global and Local records, Loan records, Loaned items records, Short doc record, Cash record, and so on β if the ID and verification code provided match. π
If not, an error message is returned. and here's the thing: since the bor-info X-Service retrieves a very large amount of data, and not all of it may be relevant, you can choose to receive a part of the data, based on your needs. main character energy: you decide what you want. π
example:
my %args = (
library => $library,
bor_id => $bor_id,
verification => $verification,
loans => 'P'
);
my $info = $aleph->bor_info(%args);
if($info->is_success){
for my $type(qw(z303 z304 z305)){
say "$type:";
my $data = $info->$type();
for my $key(keys %$data){
say "\t$key : $data->{$key}->[0]";
}
}
say "fine:";
for my $fine(@{ $info->fine() }){
for my $type(qw(z13 z30 z31)){
say "\t$type:";
my $data = $fine->{$type}->[0];
for my $key(keys %$data){
say "\t\t$key : $data->{$key}->[0]";
}
}
}
}else{
say STDERR "error: ".join("\n",@{$info->errors});
exit 1;
}
π¦ ill_bor_info π¦
documentation from Aleph X:
this service retrieves Z303, Z304, Z305 and Z308 records for a given borrower ID / barcode. four whole record types, slay. β¨
example:
the original page leaves this one blank, bestie β no code here, just vibes. π
π circ_status π
documentation from Aleph X:
The service retrieves the circulation status for each document number entered by the user. and it's giving:
- Item information (From Z30). π
- Loan information (from Z36). π
- Loan Status (Tab15), Due Date, Due Hour etc. π
example:
again, the original page is just... empty here. no code. iconic of it honestly. π
β circ_stat_m β
documentation from Aleph X:
The service retrieves the circulation status for each document number entered by the user β and this one is suitable for documents with more than 1000 items. π
- Item information (From Z30). β¨
- Loan information (from Z36). β¨
- Loan Status (Tab15), Due Date, Due Hour etc. β¨
This service is similar to circ-status X-service, except for the parameter START_POINT which enables to retrieve information for documents with more than 1000 items.
example:
yep. blank again. the original page said nothing and meant it. π
π publish_avail π
documentation from Aleph X:
This service supplies the current availability status of a document.
heads up: The X-Server does not change any data. it's read-only energy, bestie. π«Ά
example:
my $publish = $aleph->publish_avail(doc_num =>
'000196220,001313162,001484478,001484538,001317121,000000000',library=>'rug01');
if($publish->is_success){
#format for $publish->list() : [ [<id>,<marc-array>], .. ]
for my $item(@{ $publish->list }){
say "id: $item->[0]";
if($item->[1]){
say "marc array:";
say Dumper($item->[1]);
}else{
say "nothing for $item->[0]";
}
say "\n---";
}
}else{
say STDERR join("\n",@{$publish->errors});
}
remarks:
The parameter'doc_num'supports multiple values, separated by','. Compare this toill_get_doc, that does not support this. π
π ill_get_doc π
documentation from Aleph X:
This service takes a document number and the library where the corresponding document is located, and generates the XML of the requested document as it appears in the library given. π¦
example:
my $illgetdoc = $aleph->ill_get_doc(doc_number =>
'001317121',library=>'rug01'); if($illgetdoc->is_success){
if($illgetdoc->record){
say "data: ".to_json($illgetdoc->record,{ pretty => 1 });
}
else{
say "nothing found";
}
}else{
say STDERR join("\n",@{$illgetdoc->errors}); }
π renew π
documentation from Aleph X:
This service renews the loan of a given item for a given patron. π
The X-Service renews the loan only if it can be done. If, for example, there is a delinquency on the patron, the service does not renew the loan. no exceptions, no mercy. π
example:
blank in the original, bestie. nothing to show here. π
π¦ hold_req π¦
documentation from Aleph X:
The service creates a hold-request record (Z37) for a given item after performing initial checks. π
example:
also empty on the original page. just vibes, no code. π
π update_doc π
documentation from Aleph X:
This service performs actions (Update / Insert / Delete) related to the update of a document.
(The service uses pc_cat_c0203 which updates a document via the GUI). π
notes:
When executing an update request, most of the'errors'will be warnings instead of real errors. This happens because AlephX performs an'UPDATE-CHK'before trying to execute an'UPDATE', and stores all warnings during that check in the xml attribute'error'. Therefore the method'is_success'of theCatmandu::AlephX::Responseis not very useful in this case. π¨ Search for the last'error', and check weather it contains'updated successfully'. (yes, "weather". the original said weather. we are faithfully reporting the weather. π¦οΈ)
β οΈ bestie. sit down. this next bit is the "please do not break your library" part and i need you FOCUSED β οΈ
- An update replaces the WHOLE record. like, the entire thing. so if you fail to supply
xml_full_req(ormarc, which sneaks in and builds it for you), the record will be deleted! π not a vibe. not a drill. - To be sure, use the full xml response of
find-doc, change the fields insideoai_marc, and supply this xml asxml_full_req. that's the safe girl routine π«Ά - Every update adds a CAT-field to the record. your updates can be recognized by
CAT$$a == "WWW-X". so when you update a record you need to include the old CAT fields (that's the default behaviour), otherwise these fields will be deleted β and all the history is lost. gone. no receipts. π - straight from Aleph X, quoted exactly:
"Unlike other X-Services, the parameters can include XML up to 20,000 characters in length"
- When you update often (and therefore add a lot of CAT fields), this can lead to the error
'Server closed connection'. this is due to the maximum of characters allowed in an XML request. it's giving ceiling energy π - Possible solution:
- retrieve record by
find_doc - get marc record:
- retrieve record by
my $marc = $res->record->metadata->data
- filter out your CAT fields (
$a == "WWW-X") to shorten the XML:
$marc = [grep { !( $_->[0] eq "CAT" && $_->[4] eq "WWW-X" ) } @$marc];
- update
$marc - send:
$aleph->update_doc(library => 'usm01',doc_action => 'UPDATE',doc_number => $doc_number,marc => $marc);
=> your xml will now contain one CAT field with subfield 'a' equal to 'WWW-X'. slay, you trimmed it πΏ
β¨ example β¨
okay here's the whole flow, copy it, live it, love it:
my $aleph = Catmandu::AlephX->new(url => "http://localhost/X");
my $doc_number = '000000444';
my $find_doc = $aleph->find_doc(
doc_num => $doc_number,
base => "usm01"
);
my $marc = $find_doc->record->metadata->data;
my $content_ref = $find_doc->content_ref;
my %args = (
'library' => 'usm01',
'doc_action' => 'UPDATE',
'doc_number' => $doc_number,
xml_full_req => $$content_ref
);
my $u = $aleph->update_doc(%args);
if($u->is_success){
say "all ok";
}else{
say STDERR join("\n",@{$u->errors});
}
β¨ special support for catmandu marc records β¨
when you supply the argument 'marc', an xml document will be created for you, and stored in the argument 'xml_full_req'. 'marc' must be an array of arrays. and heads up π«£ β when you already supplied 'xml_full_req', it will be overwritten. so don't be out here thinking you can have both.
πΈ update_item πΈ
documentation from Aleph X
The service updates an existing item in the required ADM library after performing all relevant initial checks prior to that action.
notes
- AlephX stores not only errors in
'errors', but also the success message. yes really. the success is sitting in the errors bucket like it pays rent there π - Therefore the method
'is_success'of theCatmandu::AlephX::Responseis not very useful in this case. Search for the last'error', and check weather it contains'updated successfully'. (iykyk β this is THE trick) - The result of
'read_item'often contains translations, instead of the real values. But these translation cannot be used when updating items. - e.g.
z30-item-statuscontains'Regular loan'instead of'001'. so pretty, so useless for writing back π
example
my $alephx = Catmandu::AlephX->new(url => "http://localhost/X");
my $item_barcode = '32044044980076';
my %args = (
'library' => 'usm50',
'item_barcode' => $item_barcode,
);
my $z30 = $alephx->read_item(%args)->z30();
my $xml = XMLout($z30,,RootName=>"z30",NoAttr => 1);
$xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n".$xml;
$args{xml_full_req} = $xml;
my $u = alephx->update_item(%args);
if($u->is_success){
say "all ok";
}else{
say STDERR join("\n",@{$u->errors});
}
π¦ create_item π¦
documentation from Aleph X
The service creates a new item in the required ADM library after performing all relevant initial checks prior to that action. The item can be created for a bib record when no ADM record is linked to it yet, or it can be created to an ADM record with existing items.
notes
<npm>β¦</npm>
okay ngl the notes here are literally empty in the original. nothing to report, bestie, the notes section is just standing there being mysterious π
example
my $alephx = Catmandu::AlephX->new(url => "http://localhost/X");
my $item_barcode = '32044044980076';
my %args = (
'adm_library' => 'rug50',
'bib_library' => 'rug01',
'bib_doc_number' => '231843137',
);
my $xml = <<EOF;
<?xml version="1.0" encoding="UTF-8" ?>
<z30>
<z30-doc-number>15</z30-doc-number>
<z30-item-sequence>10</z30-item-sequence>
<z30-barcode>32044003924339</z30-barcode>
<z30-sub-library>WID</z30-sub-library>
<z30-material>BOOK</z30-material>
<z30-item-status>01</z30-item-status>
<z30-open-date>19980804</z30-open-date>
<z30-update-date>20020708</z30-update-date>
<z30-cataloger>EXLIBRIS</z30-cataloger>
<z30-date-last-return>20080607</z30-date-last-return>
<z30-hour-last-return>1631</z30-hour-last-return>
<z30-ip-last-return>CONV</z30-ip-last-return>
<z30-no-loans>011</z30-no-loans>
<z30-alpha>L</z30-alpha>
<z30-collection>GEN</z30-collection>
<z30-call-no-type>7</z30-call-no-type>
<z30-call-no>Heb 2106.385.5</z30-call-no>
<z30-call-no-key>7 selected</z30-call-no-key>
<z30-call-no-2-type />
<z30-call-no-2 />
<z30-call-no-2-key />
<z30-description>v.1</z30-description>
<z30-note-opac />
<z30-note-circulation />
<z30-note-internal />
<z30-order-number />
<z30-inventory-number />
<z30-inventory-number-date />
<z30-last-shelf-report-date>00000000</z30-last-shelf-report-date>
<z30-price />
<z30-shelf-report-number />
<z30-on-shelf-date>00000000</z30-on-shelf-date>
<z30-on-shelf-seq>000000</z30-on-shelf-seq>
<z30-doc-number-2>000000015</z30-doc-number-2>
<z30-schedule-sequence-2>00000</z30-schedule-sequence-2>
<z30-copy-sequence-2>00000</z30-copy-sequence-2>
<z30-vendor-code />
<z30-invoice-number />
<z30-line-number>00000</z30-line-number>
<z30-pages />
<z30-issue-date />
<z30-expected-arrival-date />
<z30-arrival-date />
<z30-item-statistic />
<z30-item-process-status>XX</z30-item-process-status>
<z30-copy-id>1</z30-copy-id>
<z30-hol-doc-number>000000046</z30-hol-doc-number>
<z30-temp-location>No</z30-temp-location>
<z30-enumeration-a />
<z30-enumeration-b />
<z30-enumeration-c />
<z30-enumeration-d />
<z30-enumeration-e />
<z30-enumeration-f />
<z30-enumeration-g />
<z30-enumeration-h />
<z30-chronological-i />
<z30-chronological-j />
<z30-chronological-k />
<z30-chronological-l />
<z30-chronological-m />
<z30-supp-index-o />
<z30-85x-type />
<z30-depository-id />
<z30-linking-number>000000000</z30-linking-number>
<z30-gap-indicator />
<z30-maintenance-count>007</z30-maintenance-count>
<z30-process-status-date>20080408</z30-process-status-date>
</z30>
EOF
$args{xml_full_req} = $xml;
my $u = alephx->create_item(%args);
if($u->is_success){
say "all ok";
}else{
say STDERR join("\n",@{$u->errors});
}
π AUTHOR π
Nicolas Franck, "<nicolas.franck at ugent.be>" β ty for your service, king π«‘
π LICENSE AND COPYRIGHT π
This program is free software; you can redistribute it and/or modify it under the terms of either: the GNU General Public License as published by the Free Software Foundation; or the Artistic License.
See <http://dev.perl.org/licenses/> for more information.
and that's the whole page, bestie π
β¨ this one shipped as perl v5.36.0, dated 2023-02-02 β go update some records responsibly, and for the love of everything, do not forget your xml_full_req ππ«ΆπΈ
π¦ where this page comes from
- debian bookworm: libcatmandu-alephx-perl 1.073-2 Β· Artistic-1.0-Perl OR GPL-1.0-or-later
- debian trixie: libcatmandu-alephx-perl 1.073-2 Β· Artistic-1.0-Perl OR GPL-1.0-or-later
- ubuntu noble: libcatmandu-alephx-perl 1.073-2 Β· Artistic-1.0-Perl OR GPL-1.0-or-later
- ubuntu resolute: libcatmandu-alephx-perl 1.073-2 Β· Artistic-1.0-Perl OR GPL-1.0-or-later
- ubuntu stonking: libcatmandu-alephx-perl 1.073-2 Β· Artistic-1.0-Perl OR GPL-1.0-or-later
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms