Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
0378ab1
Add run_record_offset column to dataset
ghukill Jan 15, 2025
9c12b17
Add support for 'or' conditions to filtering method (#99)
jonavellecuerdo Feb 3, 2025
7c6777b
bump version to v0.9.0 (#100)
jonavellecuerdo Feb 3, 2025
edb190f
Surface pyarrow batch reading parameters and adjust defaults
ghukill Feb 5, 2025
639f839
Refactor to configuration object
ghukill Feb 6, 2025
dbbc356
bump version to v0.10.0
ghukill Feb 6, 2025
4de99a7
v1 release
ghukill Feb 21, 2025
811a870
Replace pipenv check with pip-audit
ghukill May 16, 2025
d6af75f
Additional logging configurations
ghukill May 19, 2025
c3b6662
TIMDEXRunManager for producing ETL run metadata
ghukill May 20, 2025
8c50aea
Reorder methods and expand docstrings
ghukill May 21, 2025
3d95848
Extend load to optionally limit to current parquet files
ghukill May 21, 2025
ba72b07
Bump library major version to 2.x
ghukill May 21, 2025
01b64e9
TIMDEXDataset capable of yielding current records only
ghukill May 21, 2025
f02fb75
Simplify TIMDEXRunManager interface
ghukill May 21, 2025
6d1c07d
Make current_records flag test more explicit
ghukill May 22, 2025
99fd37e
Reduce TIMDEXRunManager and TIMDEXDataset coupling
ghukill May 23, 2025
7940030
Support filtering for current_records
ghukill May 23, 2025
728c9b8
Update test for current and non-current counts
ghukill May 27, 2025
d460c8c
Rename variables around current record batches
ghukill May 28, 2025
41b3a33
Add run_timestamp column to dataset
ghukill May 29, 2025
0a735cd
Update test confirming minted run_timestamp
ghukill May 30, 2025
dcbd859
Add migrations folder and run_timestamp migration
ghukill May 30, 2025
1c28fee
Reorder functions in run_timestamp backfill migration
ghukill Jun 3, 2025
eadd7b9
Update migrations README and migration 001
ghukill Jun 3, 2025
b4b8f4e
DatasetRecord supports explicit run_timestamp
ghukill Jun 17, 2025
ac275da
Bump version to 2.1.0
ghukill Jun 17, 2025
db95a56
Update dependencies
ghukill Jun 24, 2025
0c88b9c
Create TIMDEXDatasetMetadata class
ghukill Jun 24, 2025
19a1966
Update README
ghukill Jun 24, 2025
c32cf0d
Bump TDA library version
ghukill Jun 25, 2025
aa23155
Addition of migration script 002
ghukill Jun 20, 2025
8c2d37f
Current records functionality uses TIMDEXDatasetMetadata
ghukill Jun 30, 2025
f620bde
Fully remove TIMDEXRunManager
ghukill Jun 30, 2025
b5a4e45
Bump library version
ghukill Jun 30, 2025
ae423e7
Add 003 row group size data migration
ghukill Jul 3, 2025
b36cbff
Add MinIO S3 support
ghukill Jul 11, 2025
c36d85c
Compose TIMDEXDatasetMetadata to .metadata
ghukill Jul 11, 2025
9c3df70
Use DuckDB secret vs PRAGMA for MinIO
ghukill Jul 14, 2025
d124f98
Dependencies, formatting, and logging tweaks
ghukill Aug 1, 2025
d28dfcb
Always prefix with source in test util
ghukill Aug 1, 2025
fa55a2e
Add S3Client for metadata management
ghukill Aug 1, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -159,3 +159,5 @@ cython_debug/

# VSCode
.vscode

output/
6 changes: 3 additions & 3 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ repos:
language: system
pass_filenames: true
types: ["python"]
- id: safety
name: safety
entry: pipenv check
- id: pip-audit
name: pip-audit
entry: pipenv run pip-audit
language: system
pass_filenames: false
20 changes: 18 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
-include .env

SHELL=/bin/bash
DATETIME:=$(shell date -u +%Y%m%dT%H%M%SZ)

Expand Down Expand Up @@ -44,7 +46,7 @@ ruff: # Run 'ruff' linter and print a preview of errors
pipenv run ruff check .

safety: # Check for security vulnerabilities and verify Pipfile.lock is up-to-date
pipenv check
pipenv run pip-audit
pipenv verify

lint-apply: black-apply ruff-apply # Apply changes with 'black' and resolve 'fixable errors' with 'ruff'
Expand All @@ -53,4 +55,18 @@ black-apply: # Apply changes with 'black'
pipenv run black .

ruff-apply: # Resolve 'fixable errors' with 'ruff'
pipenv run ruff check --fix .
pipenv run ruff check --fix .


######################
# Minio S3 Instance
######################
minio-start:
docker run \
-d \
-p 9000:9000 \
-p 9001:9001 \
-v $(MINIO_DATA):/data \
-e "MINIO_ROOT_USER=$(MINIO_USERNAME)" \
-e "MINIO_ROOT_PASSWORD=$(MINIO_PASSWORD)" \
quay.io/minio/minio server /data --console-address ":9001"
4 changes: 3 additions & 1 deletion Pipfile
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ pyarrow = "*"

[dev-packages]
black = "*"
boto3-stubs = {version = "*", extras = ["s3"]}
coveralls = "*"
ipython = "*"
moto = "*"
Expand All @@ -24,5 +23,8 @@ pyarrow-stubs = "*"
pytest = "*"
ruff = "*"
setuptools = "*"
pip-audit = "*"
boto3-stubs = {extras = ["essential"], version = "*"}

[requires]
python_version = "3.12"
1,779 changes: 1,046 additions & 733 deletions Pipfile.lock

Large diffs are not rendered by default.

141 changes: 139 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
from timdex_dataset_api import TIMDEXDataset

# timdex-dataset-api
Python library for interacting with a TIMDEX parquet dataset located remotely or in S3.
Python library for interacting with a TIMDEX parquet dataset located remotely or in S3. This library is often abbreviated as "TDA".

## Development

Expand All @@ -9,6 +11,13 @@ Python library for interacting with a TIMDEX parquet dataset located remotely or
- To run unit tests: `make test`
- To lint the repo: `make lint`

The library version number is set in [`timdex_dataset_api/__init__.py`](timdex_dataset_api/__init__.py), e.g.:
```python
__version__ = "2.1.0"
```

Updating the version number when making changes to the library will prompt applications that install it, when they have _their_ dependencies updated, to pickup the new version.

## Installation

This library is designed to be utilized by other projects, and can therefore be added as a dependency directly from the Github repository.
Expand All @@ -30,11 +39,139 @@ timdex_dataset_api = {git = "https://github.com/MITLibraries/timdex-dataset-api.

### Required

None at this time.

### Optional
```shell
TDA_LOG_LEVEL=# log level for timdex-dataset-api, accepts [DEBUG, INFO, WARNING, ERROR], default INFO
WARNING_ONLY_LOGGERS=# Comma-seperated list of logger names to set as WARNING only, e.g. 'botocore,charset_normalizer,smart_open'

MINIO_S3_ENDPOINT_URL=# If set, informs the library to use this Minio S3 instance. Requires the http(s):// protocol.
MINIO_USERNAME=# Username / AWS Key for Minio; required when MINIO_S3_ENDPOINT_URL is set
MINIO_PASSWORD=# Pasword / AWS Secret for Minio; required when MINIO_S3_ENDPOINT_URL is set
MINIO_DATA=# Path to persist MinIO data if started via Makefile command
```

## Local S3 via MinIO

Set env vars:
```shell
MINIO_S3_ENDPOINT_URL=http://localhost:9000
MINIO_USERNAME="admin"
MINIO_PASSWORD="password"
MINIO_DATA=<path to persist MinIO data, e.g. /tmp/minio>
```

Use a `Makefile` command that will start a MinIO instance:

```shell
make minio-start
```

With the env var `MINIO_S3_ENDPOINT_URL` set, this library will configure `pyarrow` and DuckDB connections to point at this local MinIO S3 instance.

## Usage

_TODO..._
Currently, the most common use cases are:
* **Transmogrifier**: uses TDA to **write** to the parquet dataset
* **TIMDEX-Index-Manager (TIM)**: uses TDA to **read** from the parquet dataset

Beyond those two ETL run use cases, others are emerging where this library proves helpful:

* yielding only the current version of all records in the dataset, useful for quickly re-indexing to Opensearch
* high throughput (time) + memory safe (space) access to the dataset for analysis

For both reading and writing, the following env vars are recommended:
```shell
TDA_LOG_LEVEL=INFO
WARNING_ONLY_LOGGERS=asyncio,botocore,urllib3,s3transfer,boto3
```

### Reading Data

First, import the library:
```python
from timdex_dataset_api import TIMDEXDataset
```

Load a dataset instance:
```python
# dataset in S3
timdex_dataset = TIMDEXDataset("s3://my-bucket/path/to/dataset")

# or, local dataset (e.g. testing or development)
timdex_dataset = TIMDEXDataset("/path/to/dataset")

# load the dataset, which discovers all parquet files
timdex_dataset.load()

# or, load the dataset but ensure that only current records are ever yielded
timdex_dataset.load(current_records=True)
```

All read methods for `TIMDEXDataset` allow for the same group of filters which are defined in `timdex_dataset_api.dataset.DatasetFilters`. Examples are shown below.

```python
# read a single row, no filtering
single_record_dict = next(timdex_dataset.read_dicts_iter())


# get batches of records, filtering to a particular run
for batch in timdex_dataset.read_batches_iter(
source="alma",
run_date="2025-06-01",
run_id="abc123"
):
# do thing with pyarrow batch...


# use convenience method to yield only transformed records
# NOTE: this is what TIM uses for indexing to Opensearch for a given ETL run
for transformed_record in timdex_dataset.read_transformed_records_iter(
source="aspace",
run_date="2025-06-01",
run_id="ghi789"
):
# do something with transformed record dictionary...


# load all records for a given run into a pandas dataframe
# NOTE: this can be potentially expensive memory-wise if the run is large
run_df = timdex_dataset.read_dataframe(
source="dspace",
run_date="2025-06-01",
run_id="def456"
)
```

### Writing Data

At this time, the only application that writes to the ETL parquet dataset is Transmogrifier.

To write records to the dataset, you must prepare an iterator of `timdex_dataset_api.record.DatasetRecord`. Here is some pseudocode for how a dataset write can work:

```python
from timdex_dataset_api import DatasetRecord, TIMDEXDataset

# different ways to achieve, just need some kind of iterator (e.g. list, generator, etc.)
# of DatasetRecords for writing
def records_to_write_iter() -> Iterator[DatasetRecord]:
records = [...]
for record in records:
yield DatasetRecord(
timdex_record_id=...,
source_record=...,
transformed_record=...,
source=...,
run_date=...,
run_type=...,
run_timestamp=...,
action=...,
run_record_offset=...
)
records_iter = records_to_write_iter()

# finally, perform the write, relying on the library to handle efficient batching
timdex_dataset = TIMDEXDataset("/path/to/dataset")
timdex_dataset.write(records_iter=records_iter)
```
Loading
Loading