This repo contains a docker-compose.yml file which configures the containers and their interaction.
To run the containers:
- users and passwords (adjust env variables as needed and set new passwords):
Optionally add the following env variables for postgres and/or OpenSearch (not needed for local dev):
cp env.template .env
POSTGRES_ADDRESS(default "postgres") andPOSTGRES_PORT(default 5432)OPENSEARCH_ADDRESS(default "opensearch") andOPENSEARCH_PORT(default 9200)FASTAPI_ADDRESS(default "127.0.0.1") andFASTAPI_PORT(default 8080)
- API keys for search API server:
cp keys.env.template keys.env
- Dev config for docker containers:
cp docker-compose.override.yml.template docker-compose.override.yml
-
docker compose up -d
- create postgreSQL table structure, see below.
- create OpenSearch index, see below.
- run transformation process, see below.
- when using pgAdmin, register a new server with
Host name"postgres" (container name in docker network) with port "5432". - provide credentials as defined in
.env.
-
cd scripts - Install uv and run
uv sync --frozen
In production, the DB is populated by running the crawler. In development, it may be more convenient to load pre-harvested static data:
-
create a folder per repository, e.g.,
scripts/postgres_data/data/dans_arch -
create an XML file containing records such as
<Records xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <record xmlns="http://www.openarchives.org/OAI/2.0/"> ... </record> ... </Records>
-
fetch additional metadata using
scripts/postgres_data/dataverse.py(Dataverse) orscripts/postgres_data/get_meta.py(HAL, Zenodo) Combine additional metadata files in one virtual structure usingDataverse:
import json, glob lookup = {} for f in glob.glob('*.json'): with open(f) as fh: obj = json.load(fh) key = obj["data"]["datasetPersistentId"] lookup[key] = obj # or just the fields you need # Optionally save it with open('lookup.json', 'w') as out: json.dump(lookup, out)
HAL:
import json, glob lookup = {} for f in glob.glob('*.json'): with open(f) as fh: obj = json.load(fh) if len(obj["response"]["docs"]) == 1: key = obj["response"]["docs"][0]["halId_s"] lookup[key] = obj # or just the fields you need else: print(f) print(obj) # Optionally save it with open('lookup.json', 'w') as out: json.dump(lookup, out)
-
check the settings in
scripts/postgres_data/import_data.py:HARVEST_ENDPOINTS = [ ('DANS', 'https://archaeology.datastations.nl/oai', Path('data/dans_arch/dans_arch.xml'), Path('doi_dataverse/lookup.json'), None)
where
data/dans_arch/dans_arch.xmlcontains the OAI-PMH records anddoi_dataverse/lookup.jsonthe additional metadata.
-
cd scripts/postgres_data -
create table structure and repo config as defined in
scripts/postgres_data/create_sql/$dbnameuv run create_db.py --db $dbname [--reset]This will create and init the specified DB if it does not exist yet. If it already exists and should be dropped and reinitialized, additionally provide the flag --reset.
-
load XML data from
scripts/postgres_data/data(populates tableharvest_events):uv run import_data.py
See Prepare Data For Local Import for further details about local data preparation.
-
transform data from
scripts/postgres_data/datato a local dir (to test transformation, alternative to using the Celery process):uv run transform.py -i harvests_{repo_suffix} -o {repo_suffix}_json -s JSON_schema_file [-n] [-v]If the -n flag is provided, the JSON data will be normalized (the raw JSON may look differently based on the input XML, see these specs). If the -v flag is set, the JSON will be validated against the JSON schema file
utils/schema.json
-
cd scripts/opensearch_data -
create
test_dataciteindex (deletes existingtest_dataciteindex):uv run create_index.py
-
for sample OpenSearch queries, see open_search_queries
-
to test queries requiring vector embeddings, run
uv run query_index.py
The transformer container provides an API to start the transformation and indexing process.
A transformation requires a harvest_run_id.
When running the script import_data.py (scripts/postgres_data/data),
for each endpoint a harves run is created, the single OAI-PMH records are registered as harvest events,
and the harvest run is then closed. Note that a transformation can only be performed for a closed harvest run.
-
check if transformer container is up and running:
http://127.0.0.1:8080/health
-
To obtain a harvest run id and status for a given endpoint (https://dabar.srce.hr/oai):
http://127.0.0.1:8080/harvest_run?harvest_url=https%3A%2F%2Fdabar.srce.hr%2Foai -
start transformation process:
http://127.0.0.1:8080/index?harvest_run_id=xyz -
see transformation task results in flower:
http://127.0.0.1:5555/tasks
After starting the stack with docker compose up, you can run the harvester for a given repository URL, e.g.:
docker compose run harvester https://lifesciences.datastations.nl/oaiThe scheduler automates the full ingestion workflow: harvesting → transformation → indexing. It is designed to be executed periodically via CRON.
Some repositories are configured as dependent endpoints in the endpoints table by setting
depends_on_endpoint_id. A dependent endpoint uses identifiers collected from its master
endpoint, so the scheduler always triggers independent endpoints first and moves dependent
endpoints to the end of the same harvesting batch. This ensures HAL is harvested before
Zenodo when both endpoints are due for harvesting.
The rest of the scheduler pipeline is unchanged: every selected endpoint is still crawled sequentially, the scheduler waits until harvest runs are closed, and then closed runs are sent to transformation/indexing.
uv run python -m scheduler.runOptionally add the following env variables (not needed for local dev):
WAREHOUSE_API_URL(default "http://transform:80")INDEX_NAME(default "test_datacite")
To format all files properly, run:
uv run ruff formatuv run ruff check --select I --fix
Before running the e2e tests locally, set the env vars POSTGRES_DB and FILE_DB
to testdatasetdb and testfiledb, respectively, since the e2e tests and the API
must use the same DBs.
Note that the e2e tests reset testdatasetdb and testfiledb on each run. Because
the test DB names are hardcoded in the e2e tests, your production DBs will not be
overwritten.
To run the e2e tests:
uv run pytest -s e2eKeep to this commit message style. For semantic versioning, see these release-please. Set up pre-commit hooks to check your messages before commiting them to the repo:
uv sync --frozen --all-extras --devuv run pre-commit install --hook-type commit-msg
See .pre-commit-config.yaml for further details.