Metadata-Version: 2.4
Name: libopenschichtplaner5
Version: 1.4.0
Summary: Read and write the original Schichtplaner5 FoxPro .DBF database files, plus the SQLite/PostgreSQL ORM and sync layer used by OpenSchichtplaner5.
Author-email: Matthias Schabhüttl <matthias@matthiasschabhuettl.com>
License-Expression: MIT
Project-URL: Homepage, https://github.com/mschabhuettl/libopenschichtplaner5
Project-URL: Source, https://github.com/mschabhuettl/libopenschichtplaner5
Project-URL: Changelog, https://github.com/mschabhuettl/libopenschichtplaner5/blob/main/CHANGELOG.md
Project-URL: Issues, https://github.com/mschabhuettl/libopenschichtplaner5/issues
Project-URL: Main application, https://github.com/mschabhuettl/openschichtplaner5
Keywords: schichtplaner5,dbf,foxpro,shift-planning,dbase
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Database
Classifier: Topic :: Office/Business :: Scheduling
Classifier: Typing :: Typed
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: SQLAlchemy>=2.0.0
Requires-Dist: alembic>=1.13.0
Requires-Dist: bcrypt>=4.0.0
Requires-Dist: pyotp>=2.9.0
Requires-Dist: packaging>=21.0
Provides-Extra: postgres
Requires-Dist: psycopg2-binary>=2.9.0; extra == "postgres"
Provides-Extra: dev
Requires-Dist: pytest>=7.4.0; extra == "dev"
Requires-Dist: pytest-cov>=4.1.0; extra == "dev"
Requires-Dist: ruff>=0.1.0; extra == "dev"
Dynamic: license-file

# libopenschichtplaner5

[![CI](https://github.com/mschabhuettl/libopenschichtplaner5/actions/workflows/ci.yml/badge.svg)](https://github.com/mschabhuettl/libopenschichtplaner5/actions/workflows/ci.yml)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)

The core library behind [**OpenSchichtplaner5**](https://github.com/mschabhuettl/openschichtplaner5) —
a pip-installable package that reads **and writes** the original *Schichtplaner5*
FoxPro/dBASE `.DBF` database files, so an open replacement can run on the exact same
data as the proprietary Windows tool, with no migration.

> **Import name:** the distribution is `libopenschichtplaner5`, but the importable
> package keeps its historical name **`sp5lib`** (like `pip install PyYAML` → `import yaml`).
> This keeps OpenSchichtplaner5's imports unchanged after the extraction.

## What's inside

| Module | Purpose |
|---|---|
| `sp5lib.dbf_reader` | Pure-Python DBF reader (UTF-16-LE detection, date parsing, field decode) |
| `sp5lib.dbf_writer` | Safe DBF writer — exclusive `flock`, TOCTOU-safe record count, rollback, EOF marker preservation |
| `sp5lib.database` | High-level `SP5Database` facade over the DBF tables (employees, shifts, schedule, absences, auth, 2FA …) |
| `sp5lib.db_factory` / `sqlite_adapter` / `pg_database` | Optional SQLite / PostgreSQL backends |
| `sp5lib.orm` | SQLAlchemy models (`models.py` SQLite, `models_pg.py` Postgres), `repository`, `sync` |
| `sp5lib.auto_migrate` | Alembic-based automatic migrations |
| `sp5lib.email_service` | SMTP notification emails (HTML-escaped templates) |
| `sp5lib.color_utils` | FoxPro BGR ↔ hex/RGB color helpers |

## Installation

```bash
pip install "libopenschichtplaner5 @ git+https://github.com/mschabhuettl/libopenschichtplaner5.git"
# with the optional PostgreSQL backend:
pip install "libopenschichtplaner5[postgres] @ git+https://github.com/mschabhuettl/libopenschichtplaner5.git"
```

## Usage

```python
from sp5lib.database import SP5Database

db = SP5Database("/path/to/SP5/Daten")        # directory of .DBF files
for emp in db.get_employees():
    print(emp["ID"], emp["NAME"], emp["FIRSTNAME"])
```

Low-level DBF access:

```python
from sp5lib.dbf_reader import read_dbf
from sp5lib.dbf_writer import append_record, get_table_fields

rows   = read_dbf("/path/to/SP5/Daten/5EMPL.DBF")
fields = get_table_fields("/path/to/SP5/Daten/5EMPL.DBF")
append_record("/path/to/SP5/Daten/5NOTE.DBF", fields, {"ID": 1, "TEXT": "hello"})
```

## Dependencies

Runtime: `SQLAlchemy`, `alembic`, `bcrypt`, `pyotp`, `packaging`.
Optional: `psycopg2-binary` (via the `postgres` extra) for the PostgreSQL backend.

## Development

```bash
python -m venv .venv && . .venv/bin/activate
pip install -e ".[dev,postgres]"
pytest
ruff check .
```

## License

MIT — see [LICENSE](LICENSE). Extracted (with full git history) from
[OpenSchichtplaner5](https://github.com/mschabhuettl/openschichtplaner5)'s `backend/sp5lib/`.
