Metadata-Version: 2.5
Name: mfileskit
Version: 0.1.0
Summary: Read-only access to M-Files vault metadata and files over the M-Files Web Service (REST) API
Project-URL: Source, https://gt.pescoinc.biz/spencerm/MFilesKit
Author-email: Spencer McGuire <spencerm@pescoinc.biz>
License: Proprietary - PESCO internal use
Classifier: Private :: Do Not Upload
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Typing :: Typed
Requires-Python: >=3.11
Requires-Dist: httpx>=0.27
Requires-Dist: keyring>=25
Requires-Dist: pydantic-settings>=2.3
Requires-Dist: pydantic>=2.7
Provides-Extra: dev
Requires-Dist: build>=1.2; extra == 'dev'
Requires-Dist: pytest>=8; extra == 'dev'
Requires-Dist: respx>=0.21; extra == 'dev'
Provides-Extra: keyring
Description-Content-Type: text/markdown

# MFilesKit

Read-only Python access to PESCO's M-Files vault (metadata and files) over the M-Files Web Service REST API. See [PLAN.md](PLAN.md) for the design and roadmap.

## Install (development)

```bash
python -m venv .venv
.venv\Scripts\python -m pip install -e ".[dev,keyring]"
```

## Configure

Copy `.env.example` to `.env` and fill in `MFILES_URL`. Don't put the password in `.env`. Either:

- store it once in Windows Credential Manager: `.venv\Scripts\keyring set mfileskit mfileskitRO`, or
- set `MFILES_PASSWORD` in the environment of the process that runs the library.

The CLI prompts for it if neither is set.

## Use

```python
from mfileskit import Vault

with Vault.from_env() as vault:
    print(vault.info.name)
    drawing = vault.schema.cls("Drawing (DWG)")
    rev = vault.schema.property("Revision number", cls=drawing.id)
```

```bash
mfileskit info                         # sign in, show vault and schema counts
mfileskit schema                       # list object types
mfileskit schema --type Document       # classes of an object type
mfileskit schema --class "Drawing (DWG)"   # properties of a class
mfileskit schema --find drawing        # search class and property names
```

### Drawings (Packet Station's lookup)

```bash
mfileskit drawings status L073-76-2 K978-01-6H13         # released rev, draft rev, drafting status
mfileskit drawings status --in data\feed\want.json       # batch, JSON keyed by want.json item id
mfileskit drawings fetch L073-76-2 --rev 3 --out x.pdf   # download only if the released rev is 3
```

```python
from mfileskit.drawings import DrawingLookup

with Vault.from_env() as vault:
    lookup = DrawingLookup(vault)
    st = lookup.status("L073-76-2")
    if lookup.matches(st, visual_rev="3").attach:
        lookup.download(st, "L073762X r3.pdf")
```

See [docs/packet-station.md](docs/packet-station.md) for how drawings are laid out in the vault.

### Bulk export and sync

```bash
mfileskit count --class "Drawing Production (PDF)"                        # list only, no downloads
mfileskit export --class "Drawing Production (PDF)" --ext pdf --out D:\exports\drawings
mfileskit export --class "Drawing Production (PDF)" --ext pdf --out D:\exports\drawings              # again: only changes
mfileskit export --class "Drawing Production (PDF)" --ext pdf --out D:\exports\drawings --reconcile  # also record deletions
```

Current drawings only, kept as a mirror that drops replaced revs:

```bash
mfileskit export --class "Drawing Production (PDF)" --ext pdf --current --prune --out D:\exports\current-drawings
```

- `--current` is shorthand for `--where "Controlled Status=Effective"`. `--where "PROPERTY=VALUE|VALUE"` works with any property's display text, and can be repeated.
- The filter is applied after listing the whole class. That way, when a drawing becomes Superseded, the export notices: it writes a `deleted` line with the reason, and `--prune` deletes its files.
- `--prune` sweeps the whole folder on every run. It keeps only each object's latest exported version, and deletes the files of removed objects. Running it once on a folder that was exported without `--prune` tidies that folder too. It only ever deletes files inside the export folder, never anything in M-Files.

Output, one job per folder:

- `files/<type>/<id>/<version>/<file>`
- `manifest.jsonl`: one line per exported object version, with all properties (name, display text, and the raw value or lookup IDs) and its files (path, size, SHA-256). Deletions are added as `{"deleted": true, "reason": ...}` lines. When reading, keep the last line per `(type, id)`.
- `.mfileskit/state.sqlite`: what was exported, plus the checkpoint.
- `.mfileskit/runs.jsonl`: a summary of each run.

Searches stop at 500 results. The export gets around this by splitting the query into "Last modified" time windows, and it checks every result against the window it asked for. The checkpoint only advances after a complete run with no failures, so if a run fails, just run it again.

Names are matched case-insensitively. A few property names are reused in the vault (e.g. "Description"). Pass `cls=` to narrow them, or use the ID or GUID.

## Test

```bash
.venv\Scripts\python -m pytest
```
