livivo-converter
Get a new metadata source into LIVIVO by writing a mapping, not code.
livivo-converter reads a JSONL export, one record per line, and writes the XML that the
LIVIVO import expects. What makes a given source work is not Python: it is a single YAML
mapping file saying which LIVIVO field gets which value, which records to leave out and
which count as withdrawn. The mapping format is a small DSL of its own, with paths into
the record, format strings and conditions, and it carries the whole translation. So
bringing a new source into LIVIVO is one YAML file, and the converter stays untouched.
$ livivo-converter export.jsonl livivo.xml --source biorxiv
INFO - Wrote livivo.xml.gz: 1284 documents, 3 delete entries, 0 skipped
The mappings that ship with the package cover preprint servers, and their input is the kind of file produced by pixls-preprints, which harvests bioRxiv, medRxiv and OSF. The two together form one pipeline: harvest to JSONL, convert to LIVIVO XML. Nothing in the converter itself is specific to preprints: any source you can export as JSONL can be mapped the same way.
Why use it
- New sources without new code. A source is a YAML mapping file. Adding one is a change to configuration, not to the converter.
- Filtering is part of the mapping. A
skipcondition decides what never gets imported. The OSF mapping uses it as a life-science relevance filter, keeping some preprint providers wholesale and others only for medical and life-science subjects. - Withdrawn preprints are handled, not dropped. Records matching a mapping's
withdrawalcondition become delete entries, which tell LIVIVO to remove a record it has already imported. - Mistakes surface early. Mappings are schema-checked when loaded, and a missing input file or an unknown source ends the run before any input is read, rather than part way through a large export.
- Output the importer accepts. Field text is cleaned on the way out, with control characters removed and line breaks replaced by the marker the import understands.
- Compressed by default. Output is gzipped unless you ask for plain XML.
Requirements
- Python 3.13 or newer (see
requires-pythoninpyproject.toml) - uv to install from source or to work on the converter
Installation
Run the package as a module from a source checkout:
git clone https://codeberg.org/pixls/livivo-converter.git
cd livivo-converter
uv sync
uv run python -m livivo_converter export.jsonl livivo.xml --source biorxiv
Installing the package instead provides a livivo-converter command, which takes the same
arguments and brings its mappings with it, so there is nothing else to set up:
uv pip install livivo-converter
livivo-converter export.jsonl livivo.xml --source biorxiv
uv tool install livivo-converter puts that command on your path without touching the
environment you are in, and for a single conversion uvx livivo-converter ... fetches and
runs it in one go, installing nothing at all.
Usage
Give the converter an input file, an output file and the source to convert:
livivo-converter export.jsonl livivo.xml --source biorxiv
The output is gzip-compressed and .gz is appended to the name you passed, so the command
above writes livivo.xml.gz. Pass --no-zip for plain XML:
livivo-converter export.jsonl livivo.xml --source osf --no-zip
# -> livivo.xml
From a source checkout the same run is uv run livivo-converter ..., which reaches the
same entry point as the module form above.
The source name is the name of its mapping file. Three ship with the package: biorxiv,
medrxiv and osf. An unknown source is rejected before any input is read.
Options
| Option | Description |
|---|---|
input_file |
Required. File to convert, one JSON record per line (JSONL) |
output_file |
Required. XML file to write, with .gz appended unless --no-zip is given |
--source |
Required. Source to convert, named after its mapping file |
--no-zip |
Write uncompressed XML instead of gzip-compressed XML |
-f, --force |
Overwrite the output file if it exists |
-v, --verbose |
Log at DEBUG level instead of INFO |
An existing output file is not overwritten. The run stops before any input is read unless
-f is given, and for a compressed run it is the .gz file that is checked, since that is
the one being written.
A run ends with one line counting what was written, deleted and skipped. Which records
those were is DEBUG detail, since a relevance filter is meant to drop the greater part of
an export. Pass -v and each skipped record is reported with the values its skip rule
tested, which is usually enough to see why it was left out, and each converted record with
the fields as they were built.
Mapping files
Each source is described by one YAML file in
livivo_converter/mappings/. A mapping says which LIVIVO
field gets which value, which records to leave out and which count as withdrawn:
source: biorxiv
settings:
delimiters: # split these input fields into lists first
authors: ";"
skip_values: # values that count as no value
- "NA"
fields:
DATABASE:
val: PIXLS # a constant
TITLE:
map: title # copied from the record
PUBLYEAR:
map: date
spec: T[:4] # sliced on the way through
DOCUMENTURL:
fstring: # built from a format string
- "url_oa|https://doi.org/{doi}"
Paths such as attributes.subjects.*.0.text are glom
expressions into the record, so nested and repeated values are reachable without code.
Conditions for skip and withdrawal are built from all, any and not around leaves
that test a field with equals, in, any_in or regex.
See docs/mapping-format.md for the full reference.
Output format
A root add element holding one doc per record, each a flat list of field elements
carrying the value and the name of the field it belongs to:
<add>
<doc>
<field name="DATABASE">PIXLS</field>
<field name="SUBDATABASE">biorxiv</field>
<field name="TITLE">Toy example study on synthetic cell behavior</field>
<field name="AUTHOR">Example, A.</field>
<field name="AUTHOR">Sample, B.</field>
</doc>
</add>
Withdrawn records travel in the same file as delete entries, which name only the record to remove:
<doc><entry><field name="id">PIXLS-biorxiv-10.1101/2025.07.15.664985</field></entry></doc>
Development
The Makefile wraps the common tasks:
make test # run the test suite
make reformat # black, rewriting files
make check-formatting # black, in check mode
make check-code # flake8
make check-coverage # pytest with coverage
make audit # formatting + lint + tests, the pre-merge gate
Project layout
livivo_converter/
├── cli.py # argument parsing, logging, checks before work starts
├── mappings.py # loading and schema validation of a mapping
├── convert.py # executes a mapping over the input records
├── formatter.py # resolves glom placeholders inside format strings
├── xml_builder.py # turns mapped fields into the LIVIVO XML tree
└── mappings/ # one YAML mapping per source
├── biorxiv.yaml
├── medrxiv.yaml
└── osf.yaml
Getting help
- Something broken, or a source converting wrongly? Open an issue at codeberg.org/pixls/livivo-converter/issues.
- Writing or changing a mapping:
docs/mapping-format.mdis the reference, and the three mappings that ship with the package are worked examples. - A record that did not come out as expected: run with
--verbose. Every skipped record is logged with the values its skip rule tested, and every converted one with the fields as they were built, which is usually enough to see which rule or path is at fault.
Contributing
Contributions are welcome. Please read CONTRIBUTING.md for the
workflow, coding conventions and what make audit expects before you open a pull request.
Maintainers
Developed and maintained by Benjamin Wolff (wolff@zbmed.de) at ZB MED – Information Centre for Life Sciences, as part of the PIXLS project.
Funding
Developed in PIXLS – Preprint Information eXtraction for Life Sciences, funded by the Deutsche Forschungsgemeinschaft (DFG, German Research Foundation).
License
Source code is licensed under the MIT License. Documentation and other content
are licensed under CC BY 4.0, and configuration and generated
files under CC0 1.0. See LICENSING.md for
details.
Metadata
Release files for livivo-converter 1.0.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| livivo_converter-1.0.0.tar.gz | 209.3 kB | Without provenance | Details for livivo_converter-1.0.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| livivo_converter-1.0.0-py3-none-any.whl | Python 3 | none | any | Details for livivo_converter-1.0.0-py3-none-any.whl |
Total release size: 245.9 kB
Release files / livivo_converter-1.0.0.tar.gz
| Download URL | livivo_converter-1.0.0.tar.gz |
|---|---|
| Size | 209.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
03129538ff2f41957db9bf42827de774240ce08773188579e6fc349a49c00137
|
|
BLAKE2b-256 checksum How to use checksums |
85d88f1c39f1629cf3c307f4bb55fc5df3e2a006f3221c03aa4f38dc521dcf09
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.10.2 {"installer":{"name":"uv","version":"0.10.2","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Arch Linux","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Release files / livivo_converter-1.0.0-py3-none-any.whl
| Download URL | livivo_converter-1.0.0-py3-none-any.whl |
|---|---|
| Size | 36.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
aba7b3fc1f5e64b15186bf2c506733dc80e7c672559978c1833cbbd9d74c8aa9
|
|
BLAKE2b-256 checksum How to use checksums |
cdc4d8d444e353afb14868c09e0d683dbbcb44956916844117dfa6bdc1f9181e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.10.2 {"installer":{"name":"uv","version":"0.10.2","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Arch Linux","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Security for livivo-converter 1.0.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.