MARC Serials Toolkit v0.6.0
Proof of concept — under active development. Check your output before loading it into the catalog.
Three tools for MARC serials holdings. The Holdings Workbench finds the patterns in your 866 statements, asks you to confirm what each part of a pattern means, and converts using them. The Converter turns textual 866 holdings statements into structured 853 caption/pattern and 863 enumeration/chronology fields. The Pattern Detector groups free-text holdings by their structure and generates a matching regular expression for each group.
No license is granted at this time; the repository is published for reference only while institutional intellectual property rights are reviewed. See NOTICE.md.
Portions derive from extract.py by Phani Chaitanya Pendyala (MIT). See THIRD-PARTY-NOTICES.md.
Questions: Matthew Codey, codey@usc.edu
What has changed
v0.6.0
2026-08-25
A new Holdings Workbench: find a pattern, say what it means, convert with it.
Workbench A new tool joins the other two. It finds the patterns in your holdings the way the Pattern Detector does, then converts with them the way the Converter does — with a step in between where you say what each part of a pattern means. The Converter and the Pattern Detector are unchanged and still at their own addresses; nothing you do today has to change.
Workbench A pattern whose every captured value could be read is confirmed for you as soon as the patterns are found. What is left is the shorter list that genuinely needs you: patterns holding a value the tool cannot place, marked "needs a decision". Removing a confirmed pattern, or clearing the library, is remembered — nothing you have taken out comes back the next time you look for patterns.
Workbench Each pattern is shown against one holdings statement at a time, reading down the screen as the original 866, then the values taken out of it, then the 853 / 863 they produce. Use ‹ and › to step through the other statements the pattern covers; the table and the fields follow. Correct anything wrong and the MARC on screen changes as you do.
Workbench The fields shown for a pattern now carry the linking numbers they would really be given. $8 depends on the whole record — statements published to the same pattern share one 853 and run 1.1, 1.2, and a change of pattern starts 2.1 — so the screen shows the entire record the example belongs to, with that statement marked. It previously showed every statement as 1.1 whatever it would actually receive.
Converter Holdings that begin with a number carrying no caption of its own — "39 no 1 (Spring 1995)" — are now read, with that number taken as the volume. A number sitting a level above an issue is a volume, so "39 no 1" means the same as "v.39 no 1", which already worked. These statements were previously not converted at all.
Converter That reading is applied only where the statement supports it. "2016?" is still an uncertain year rather than volume 2016, and "34 no 3, 4 (Summer, Autumn 1990)" is still left alone: only its first issue can be read, and converting that much would remove the 866 and take the second issue and both seasons with it.
Workbench Where a level is worked out from cataloguing convention rather than stated in the holdings, the value is marked "likely, please confirm" and the pattern is not confirmed for you — it waits for you to look at it, even though the answer is already filled in.
Pattern detector A holdings statement that compresses its range — "v.1-5(1990-1994)", "no.1-6(1990)" — is now read correctly. The hyphen in "v.1-5" joins two volumes; it does not split the statement into a beginning and an end. Reading it as a split put every value after it on the wrong side, so both years in "v.1-5(1990-1994)" were labelled as the end of the range and the "5" was left as a number of no particular kind.
Pattern detector The parts of a generated expression are now named for what they hold — start_vol and end_vol, start_year and end_year — with each level getting at most one beginning and one end. Names such as end_year_2 no longer appear where a plain end_year was meant.
Pattern detector A pattern's heading shows a compressed range as one: "VOL-VOL(YEAR-YEAR)" rather than "VOL#(YEARYEAR)", which ran the two values together and gave no hint that a range was involved.
Workbench Because of those corrections, a statement such as "v.1-5(1990-1994)" now arrives with all four of its values already placed, and can be confirmed without correcting anything first. Statements where a value genuinely cannot be placed — the "5" in "v.1(1990)-5(1994)", which no caption reaches — still ask.
Workbench Holdings that were being left for review can now be converted. "?: 16" has a number in it but nothing to say whether it is a volume or an issue, so it was never converted and never could be. Tell the workbench which it is, once, and every statement of that shape converts.
Workbench Each converted statement says what read it — the name of one of your confirmed patterns, or "Standard parser". Converting a whole file reports the same breakdown, so you can see how much of it your patterns covered.
Workbench The patterns confirmed so far are listed under "Pattern library", folded away to a single line so it does not sit between you and the patterns you are working on. Open it to see what will read your holdings, to remove any that should not be used, or to Export them to a file and Import them again next time — so the work of confirming a collection's patterns is done once rather than every visit.
Workbench The MARC file is uploaded once and used both for finding patterns and for converting. Previously the same file had to be given to two separate tools.
Workbench The screen is one page of three numbered steps rather than a sidebar and a pair of tabs. At the start you see only Holdings; finding patterns appears once you have given it a file or pasted some statements, and converting appears once a file is loaded. Each step shows what the one before it produced, so the work reads top to bottom.
Workbench Each record under Convert says what it will produce before you open it — how many of its statements convert, and how many are held — so a file can be looked over as a list rather than a record at a time. Tick Reviewed as you work through it; the count beside the filters says how far you have got.
Workbench The record list can be narrowed to what you want to look at: only records with something held back, only those a confirmed pattern read, only those the standard parser read, or only those you have not reviewed yet. Open all and Close all work on whatever is showing.
Workbench Long files are read fifty records at a time, with Previous and Next to walk them.
Workbench Records are listed under Convert and open where they sit, showing what that record converts to without leaving the page. The record list previously lived in the sidebar underneath everything else, where on an ordinary screen it fell below the edge of the panel — and since choosing a record was the only way into the conversion view, that view looked permanently empty.
Workbench Holdings matching none of your confirmed patterns are converted exactly as the Converter converts them today, including the ones it holds back for review. Confirming no patterns at all gives output identical to the Converter's, so there is nothing to lose by working here instead.
Workbench That fallback can now be switched off, under Conversion settings. With it off only your own confirmed patterns convert anything, and a statement none of them matches is left exactly as it was, 866 and all. A statement only half covered by a pattern is left alone too rather than half converted — the 866 is removed once anything has been written from it, so converting one range of a two-range statement would take the other with it.
v0.5.2
2026-08-21
Holdings the converter cannot read are no longer deleted.
Converter A holdings statement the converter cannot read now keeps its original 866 field. Previously, converting a record could remove every 866 on it even when nothing was produced to replace them — so a statement in a format the converter did not recognise was deleted outright, and the file you downloaded was missing holdings with nothing on screen to say so. Please re-check any file you converted before this version: look for records that now have no holdings at all.
Converter Converting a single record from its own button follows the same rule. It previously removed every 866 on the record before it had worked anything out, so even statements it had set aside for review were lost.
Converter On a record where some statements convert and others do not, only the converted ones have their 866 removed. The rest stay exactly as they were. Previously the whole record was treated as one decision, so a single statement needing review kept every 866 — leaving the converted holdings recorded twice, once as an 866 and once as an 853/863.
v0.5.1
2026-08-21
Holdings that use slashes between their ranges now produce patterns.
Pattern detector A statement that separates its ranges with slashes — v.1(1990)-v.3(1992) / v.5(1994)-v.8(1997) / v.10(1999)- — is now broken into those separate ranges before patterns are worked out. Previously the whole statement was read as one long run, which was usually reported as too idiosyncratic to express as a pattern, so it produced no pattern at all. Statements separated by commas or semicolons already behaved this way.
Pattern detector Slashes that form part of a number are left alone. Combined issues such as v.1/2, split years such as 1990/91, and month spans such as Jan./Feb. are not split. Only a slash with a space on either side is treated as separating one range from the next.
Pattern detector The Options checkbox now reads "Split multi-range statements on top-level commas, semicolons or slashes". It has always handled semicolons as well as commas; the label mentioned only commas.
v0.5.0
2026-08-04
Corrected how 853s are numbered, and made previews appear on their own.
Converter A single 853 is now shared by every holdings statement that follows the same publication pattern, numbered $8 1.1, 1.2, 1.3 and so on. Previously each statement produced its own 853, so one record could end up with four identical patterns under four different linking numbers. A gap in your holdings no longer starts a new pattern — only an actual change in how the serial is published does.
Converter The 853/863 preview now appears as soon as you select a record. There is no longer a Preview button to press, and the 853 is shown once at the top of each group rather than repeated beside every statement.
Converter Each source 866 is shown side by side with the 863 it produced, so you can check them against each other directly.
Converter Conversion settings moved into their own window, opened with the Conversion settings button. They previously sat in the sidebar, where they overlapped the record list when expanded.
Converter Converting from a record's own button now converts every statement in that record. Doing them one at a time could silently discard the previous one.
Pattern detector Expanding "+ N more" under Example Statements now shows every statement in the group. It previously showed only two, however many the link promised.
Pattern detector Fixed a display fault where pattern cards collapsed to a few pixels and their contents were invisible.
Both The record list and results panel now scroll on their own instead of stretching the whole page.
v0.4.0
2026-08-04
Rebuilt both interfaces for readability.
Both Text is substantially larger throughout. The smallest text on screen went from around 8 pixels to 14, and body text to 18.
Both Added an A / A+ / A++ control in the top corner that enlarges everything, and a light/dark theme switch. Both remember your choice on the next visit.
Both The tools now respect whatever text size you have set in your own browser instead of overriding it. If you had already made text larger, these tools were previously shrinking it back down.
Both Colours were adjusted so labels and borders meet accessibility contrast guidance, buttons and boxes are large enough to click comfortably, and the layout reflows rather than clipping when text is enlarged.
Toolkit The MIT license was withdrawn pending a review of institutional intellectual property rights, and attribution to the original author was made complete.
v0.3.0
2026-08-03
The converter learned a second holdings format and stopped guessing.
Converter Year-first holdings such as "1993: (1 [Feb])" and "2019: (1-6 [Feb-Nov])2020: (7-12 [Jan-Dec])" are now understood. On one test file the share of statements the converter could read went from 6% to 94%; previously almost none of that format could be converted at all.
Converter When a record already has an 853 that describes its holdings, the converter now adds 863s under it rather than creating a second, competing 853.
Converter Statements where it cannot tell what a number means — whether it is a volume or an issue — are held for review and left unconverted, rather than being guessed at. They are listed with the reason.
Converter Running a conversion twice on the same file no longer duplicates fields.
Converter You can now choose which subfields the captions use, and whether chronology is written as MARC codes (03) or as text (Mar).
v0.2.0
2026-07-30
Pattern detector made usable on messy holdings.
Pattern detector Groups too irregular to express as a single pattern are now reported as a finding — "too idiosyncratic to express as a pattern, N statements affected" — instead of producing an unusable expression or an error about length.
Pattern detector Free text inside a holdings statement, such as "Library has:" or "[lacks v.3]", is now handled as a single unit. Generated expressions are shorter and match related statements rather than only the exact one they came from.
Pattern detector Fixed a fault where any group containing free text reported 0% matched, because the generated expression could not match the statement it was built from.
v0.1.0
2026-07-24
First working version.
Converter Converts 866 textual holdings into 853/863 fields, from pasted text or an uploaded MARC file, with month and season written as MARC chronology codes.
Pattern detector Groups 866 statements by structure and generates a named-group regular expression for each pattern found.
Done