filemindr 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- filemindr-0.1.0/.github/workflows/publish.yml +46 -0
- filemindr-0.1.0/.gitignore +4 -0
- filemindr-0.1.0/.pre-commit-config.yaml +22 -0
- filemindr-0.1.0/.python-version +1 -0
- filemindr-0.1.0/PKG-INFO +193 -0
- filemindr-0.1.0/README.md +178 -0
- filemindr-0.1.0/main.py +6 -0
- filemindr-0.1.0/pyproject.toml +27 -0
- filemindr-0.1.0/src/filemindr/__init__.py +0 -0
- filemindr-0.1.0/src/filemindr/cli.py +32 -0
- filemindr-0.1.0/src/filemindr/core/__init__.py +0 -0
- filemindr-0.1.0/src/filemindr/core/config.py +16 -0
- filemindr-0.1.0/src/filemindr/core/runner.py +218 -0
- filemindr-0.1.0/src/filemindr/py.typed +0 -0
- filemindr-0.1.0/tests/test_conflicts.py +28 -0
- filemindr-0.1.0/tests/test_older_than.py +30 -0
- filemindr-0.1.0/tests/test_pipeline.py +34 -0
- filemindr-0.1.0/tests/test_rules.py +32 -0
- filemindr-0.1.0/tests/test_smoke.py +5 -0
- filemindr-0.1.0/uv.lock +395 -0
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
name: Publish Python Package
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- "v*.*.*"
|
|
7
|
+
|
|
8
|
+
permissions:
|
|
9
|
+
contents: read
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
test:
|
|
13
|
+
name: Test
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
container:
|
|
16
|
+
image: ghcr.io/astral-sh/uv:python3.12-bookworm
|
|
17
|
+
|
|
18
|
+
steps:
|
|
19
|
+
- name: Checkout
|
|
20
|
+
uses: actions/checkout@v4
|
|
21
|
+
|
|
22
|
+
- name: Create venv
|
|
23
|
+
run: uv venv
|
|
24
|
+
|
|
25
|
+
- name: Install project + dev deps
|
|
26
|
+
run: uv sync --extra dev
|
|
27
|
+
|
|
28
|
+
- name: Run tests
|
|
29
|
+
run: uv run pytest -q
|
|
30
|
+
|
|
31
|
+
publish:
|
|
32
|
+
name: Build & Publish (PyPI)
|
|
33
|
+
needs: test
|
|
34
|
+
runs-on: ubuntu-latest
|
|
35
|
+
container:
|
|
36
|
+
image: ghcr.io/astral-sh/uv:python3.12-bookworm
|
|
37
|
+
|
|
38
|
+
steps:
|
|
39
|
+
- name: Checkout
|
|
40
|
+
uses: actions/checkout@v4
|
|
41
|
+
|
|
42
|
+
- name: Build package
|
|
43
|
+
run: uv build
|
|
44
|
+
|
|
45
|
+
- name: Publish to PyPI
|
|
46
|
+
run: uv publish --token ${{ secrets.PYPI_API_TOKEN }}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
repos:
|
|
2
|
+
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
3
|
+
rev: v0.6.9
|
|
4
|
+
hooks:
|
|
5
|
+
- id: ruff
|
|
6
|
+
args: [--fix]
|
|
7
|
+
- id: ruff-format
|
|
8
|
+
|
|
9
|
+
- repo: https://github.com/pre-commit/pre-commit-hooks
|
|
10
|
+
rev: v4.6.0
|
|
11
|
+
hooks:
|
|
12
|
+
- id: check-merge-conflict
|
|
13
|
+
- id: end-of-file-fixer
|
|
14
|
+
- id: trailing-whitespace
|
|
15
|
+
- id: check-toml
|
|
16
|
+
- id: check-yaml
|
|
17
|
+
|
|
18
|
+
- repo: https://github.com/codespell-project/codespell
|
|
19
|
+
rev: v2.3.0
|
|
20
|
+
hooks:
|
|
21
|
+
- id: codespell
|
|
22
|
+
args: ["-L", "nd,teh"]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.12
|
filemindr-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: filemindr
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Declarative local file pipelines in Python
|
|
5
|
+
Author: Jeferson Peter
|
|
6
|
+
Requires-Python: >=3.12
|
|
7
|
+
Requires-Dist: loguru
|
|
8
|
+
Requires-Dist: pydantic-settings
|
|
9
|
+
Requires-Dist: pyyaml
|
|
10
|
+
Requires-Dist: typer
|
|
11
|
+
Provides-Extra: dev
|
|
12
|
+
Requires-Dist: pytest>=8; extra == 'dev'
|
|
13
|
+
Requires-Dist: ruff>=0.5; extra == 'dev'
|
|
14
|
+
Description-Content-Type: text/markdown
|
|
15
|
+
|
|
16
|
+
# Filemindr โ Rule-Driven Local File Automation
|
|
17
|
+
|
|
18
|
+
**Declarative local file pipelines for organizing your files.**
|
|
19
|
+
|
|
20
|
+
filemindr lets you describe *what should happen* to your files โ not *how*.
|
|
21
|
+
You define rules in YAML (extensions, regex, age, priority), and filemindr applies them safely with dryโrun support, conflict handling, and a clean CLI.
|
|
22
|
+
|
|
23
|
+
This project was built as a learning + portfolio tool focused on clarity, safety, and developer experience.
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## โจ Features
|
|
28
|
+
|
|
29
|
+
- Declarative YAML configuration
|
|
30
|
+
- Rule engine with priority (highest wins)
|
|
31
|
+
- Match by:
|
|
32
|
+
- File extensions
|
|
33
|
+
- Regex on filename
|
|
34
|
+
- File age (`older_than_days`)
|
|
35
|
+
- Conflict policies:
|
|
36
|
+
- `rename`
|
|
37
|
+
- `skip`
|
|
38
|
+
- `overwrite`
|
|
39
|
+
- Dry-run mode (preview changes before touching files)
|
|
40
|
+
- Summary report after each run
|
|
41
|
+
- Proper logging levels (`INFO` / `DEBUG`)
|
|
42
|
+
- Cross-platform (Windows, macOS, Linux)
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## ๐ฆ Installation
|
|
47
|
+
|
|
48
|
+
### From PyPI (planned / when published)
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
pip install Filemindr
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Or:
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
python -m pip install Filemindr
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### Local development
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
pip install -e .
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
With uv:
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
uv sync
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
---
|
|
73
|
+
|
|
74
|
+
## ๐ Quick Start
|
|
75
|
+
|
|
76
|
+
Create a `filemindr.yaml`:
|
|
77
|
+
|
|
78
|
+
```yaml
|
|
79
|
+
source: ~/Downloads
|
|
80
|
+
default_target: ~/Downloads/others
|
|
81
|
+
conflict_policy: rename
|
|
82
|
+
|
|
83
|
+
rules:
|
|
84
|
+
- name: invoices
|
|
85
|
+
priority: 100
|
|
86
|
+
match:
|
|
87
|
+
extensions: ["pdf"]
|
|
88
|
+
regex: "(?i)invoice|nota|nf"
|
|
89
|
+
action:
|
|
90
|
+
move_to: ~/Downloads/finance/invoices
|
|
91
|
+
|
|
92
|
+
- name: documents
|
|
93
|
+
priority: 50
|
|
94
|
+
match:
|
|
95
|
+
extensions: ["pdf", "docx", "xlsx"]
|
|
96
|
+
action:
|
|
97
|
+
move_to: ~/Downloads/documents
|
|
98
|
+
|
|
99
|
+
- name: images
|
|
100
|
+
priority: 40
|
|
101
|
+
match:
|
|
102
|
+
extensions: ["jpg", "jpeg", "png", "webp"]
|
|
103
|
+
action:
|
|
104
|
+
move_to: ~/Downloads/images
|
|
105
|
+
|
|
106
|
+
- name: old_installers
|
|
107
|
+
priority: 80
|
|
108
|
+
match:
|
|
109
|
+
extensions: ["exe", "msi"]
|
|
110
|
+
older_than_days: 14
|
|
111
|
+
action:
|
|
112
|
+
move_to: ~/Downloads/installers/old
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
Preview changes:
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
filemindr run --dry-run
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
Run for real:
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
filemindr run
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
Verbose mode (per-file logs):
|
|
128
|
+
|
|
129
|
+
```bash
|
|
130
|
+
filemindr run --log-level DEBUG
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
---
|
|
134
|
+
|
|
135
|
+
## ๐ง How it works
|
|
136
|
+
|
|
137
|
+
1. Filemind scans the source directory
|
|
138
|
+
2. Rules are evaluated by priority (highest first)
|
|
139
|
+
3. The first matching rule wins
|
|
140
|
+
4. Files are moved according to the selected conflict policy
|
|
141
|
+
5. A summary is printed at the end
|
|
142
|
+
|
|
143
|
+
---
|
|
144
|
+
|
|
145
|
+
## โ Conflict Policy
|
|
146
|
+
|
|
147
|
+
- `rename` (default): creates `file (1).ext`, `file (2).ext`, etc
|
|
148
|
+
- `skip`: ignores files that already exist
|
|
149
|
+
- `overwrite`: replaces existing files
|
|
150
|
+
|
|
151
|
+
---
|
|
152
|
+
|
|
153
|
+
## ๐งช Development
|
|
154
|
+
|
|
155
|
+
Run tests:
|
|
156
|
+
|
|
157
|
+
```bash
|
|
158
|
+
uv run pytest
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
Project layout:
|
|
162
|
+
|
|
163
|
+
```
|
|
164
|
+
filemindr/
|
|
165
|
+
โโโ src/filemindr/
|
|
166
|
+
โโโ tests/
|
|
167
|
+
โโโ pyproject.toml
|
|
168
|
+
โโโ README.md
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
---
|
|
172
|
+
|
|
173
|
+
## ๐ Status
|
|
174
|
+
|
|
175
|
+
Early alpha / experimental.
|
|
176
|
+
|
|
177
|
+
APIs may change.
|
|
178
|
+
|
|
179
|
+
---
|
|
180
|
+
|
|
181
|
+
## ๐ License
|
|
182
|
+
|
|
183
|
+
MIT
|
|
184
|
+
|
|
185
|
+
---
|
|
186
|
+
|
|
187
|
+
## Roadmap
|
|
188
|
+
|
|
189
|
+
- [ ] Watch mode (real-time folder monitoring)
|
|
190
|
+
- [ ] Per-rule conflict policies
|
|
191
|
+
- [ ] Copy instead of move
|
|
192
|
+
- [ ] Trash support
|
|
193
|
+
- [ ] Config validation
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
# Filemindr โ Rule-Driven Local File Automation
|
|
2
|
+
|
|
3
|
+
**Declarative local file pipelines for organizing your files.**
|
|
4
|
+
|
|
5
|
+
filemindr lets you describe *what should happen* to your files โ not *how*.
|
|
6
|
+
You define rules in YAML (extensions, regex, age, priority), and filemindr applies them safely with dryโrun support, conflict handling, and a clean CLI.
|
|
7
|
+
|
|
8
|
+
This project was built as a learning + portfolio tool focused on clarity, safety, and developer experience.
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## โจ Features
|
|
13
|
+
|
|
14
|
+
- Declarative YAML configuration
|
|
15
|
+
- Rule engine with priority (highest wins)
|
|
16
|
+
- Match by:
|
|
17
|
+
- File extensions
|
|
18
|
+
- Regex on filename
|
|
19
|
+
- File age (`older_than_days`)
|
|
20
|
+
- Conflict policies:
|
|
21
|
+
- `rename`
|
|
22
|
+
- `skip`
|
|
23
|
+
- `overwrite`
|
|
24
|
+
- Dry-run mode (preview changes before touching files)
|
|
25
|
+
- Summary report after each run
|
|
26
|
+
- Proper logging levels (`INFO` / `DEBUG`)
|
|
27
|
+
- Cross-platform (Windows, macOS, Linux)
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## ๐ฆ Installation
|
|
32
|
+
|
|
33
|
+
### From PyPI (planned / when published)
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
pip install Filemindr
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Or:
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
python -m pip install Filemindr
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### Local development
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
pip install -e .
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
With uv:
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
uv sync
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
## ๐ Quick Start
|
|
60
|
+
|
|
61
|
+
Create a `filemindr.yaml`:
|
|
62
|
+
|
|
63
|
+
```yaml
|
|
64
|
+
source: ~/Downloads
|
|
65
|
+
default_target: ~/Downloads/others
|
|
66
|
+
conflict_policy: rename
|
|
67
|
+
|
|
68
|
+
rules:
|
|
69
|
+
- name: invoices
|
|
70
|
+
priority: 100
|
|
71
|
+
match:
|
|
72
|
+
extensions: ["pdf"]
|
|
73
|
+
regex: "(?i)invoice|nota|nf"
|
|
74
|
+
action:
|
|
75
|
+
move_to: ~/Downloads/finance/invoices
|
|
76
|
+
|
|
77
|
+
- name: documents
|
|
78
|
+
priority: 50
|
|
79
|
+
match:
|
|
80
|
+
extensions: ["pdf", "docx", "xlsx"]
|
|
81
|
+
action:
|
|
82
|
+
move_to: ~/Downloads/documents
|
|
83
|
+
|
|
84
|
+
- name: images
|
|
85
|
+
priority: 40
|
|
86
|
+
match:
|
|
87
|
+
extensions: ["jpg", "jpeg", "png", "webp"]
|
|
88
|
+
action:
|
|
89
|
+
move_to: ~/Downloads/images
|
|
90
|
+
|
|
91
|
+
- name: old_installers
|
|
92
|
+
priority: 80
|
|
93
|
+
match:
|
|
94
|
+
extensions: ["exe", "msi"]
|
|
95
|
+
older_than_days: 14
|
|
96
|
+
action:
|
|
97
|
+
move_to: ~/Downloads/installers/old
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
Preview changes:
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
filemindr run --dry-run
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
Run for real:
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
filemindr run
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
Verbose mode (per-file logs):
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
filemindr run --log-level DEBUG
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
---
|
|
119
|
+
|
|
120
|
+
## ๐ง How it works
|
|
121
|
+
|
|
122
|
+
1. Filemind scans the source directory
|
|
123
|
+
2. Rules are evaluated by priority (highest first)
|
|
124
|
+
3. The first matching rule wins
|
|
125
|
+
4. Files are moved according to the selected conflict policy
|
|
126
|
+
5. A summary is printed at the end
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
|
|
130
|
+
## โ Conflict Policy
|
|
131
|
+
|
|
132
|
+
- `rename` (default): creates `file (1).ext`, `file (2).ext`, etc
|
|
133
|
+
- `skip`: ignores files that already exist
|
|
134
|
+
- `overwrite`: replaces existing files
|
|
135
|
+
|
|
136
|
+
---
|
|
137
|
+
|
|
138
|
+
## ๐งช Development
|
|
139
|
+
|
|
140
|
+
Run tests:
|
|
141
|
+
|
|
142
|
+
```bash
|
|
143
|
+
uv run pytest
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
Project layout:
|
|
147
|
+
|
|
148
|
+
```
|
|
149
|
+
filemindr/
|
|
150
|
+
โโโ src/filemindr/
|
|
151
|
+
โโโ tests/
|
|
152
|
+
โโโ pyproject.toml
|
|
153
|
+
โโโ README.md
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
---
|
|
157
|
+
|
|
158
|
+
## ๐ Status
|
|
159
|
+
|
|
160
|
+
Early alpha / experimental.
|
|
161
|
+
|
|
162
|
+
APIs may change.
|
|
163
|
+
|
|
164
|
+
---
|
|
165
|
+
|
|
166
|
+
## ๐ License
|
|
167
|
+
|
|
168
|
+
MIT
|
|
169
|
+
|
|
170
|
+
---
|
|
171
|
+
|
|
172
|
+
## Roadmap
|
|
173
|
+
|
|
174
|
+
- [ ] Watch mode (real-time folder monitoring)
|
|
175
|
+
- [ ] Per-rule conflict policies
|
|
176
|
+
- [ ] Copy instead of move
|
|
177
|
+
- [ ] Trash support
|
|
178
|
+
- [ ] Config validation
|
filemindr-0.1.0/main.py
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "filemindr"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "Declarative local file pipelines in Python"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.12"
|
|
7
|
+
authors = [{ name = "Jeferson Peter" }]
|
|
8
|
+
|
|
9
|
+
dependencies = [
|
|
10
|
+
"typer",
|
|
11
|
+
"pyyaml",
|
|
12
|
+
"loguru",
|
|
13
|
+
"pydantic-settings",
|
|
14
|
+
]
|
|
15
|
+
|
|
16
|
+
[project.scripts]
|
|
17
|
+
filemindr = "filemindr.cli:app"
|
|
18
|
+
|
|
19
|
+
[project.optional-dependencies]
|
|
20
|
+
dev = [
|
|
21
|
+
"pytest>=8",
|
|
22
|
+
"ruff>=0.5",
|
|
23
|
+
]
|
|
24
|
+
|
|
25
|
+
[build-system]
|
|
26
|
+
requires = ["hatchling"]
|
|
27
|
+
build-backend = "hatchling.build"
|
|
File without changes
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
import typer
|
|
3
|
+
from loguru import logger
|
|
4
|
+
|
|
5
|
+
from filemindr.core.config import resolve_config
|
|
6
|
+
from filemindr.core.runner import run_pipeline
|
|
7
|
+
|
|
8
|
+
app = typer.Typer(help="Declarative local file pipelines")
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@app.command()
|
|
12
|
+
def run(
|
|
13
|
+
config: str = "filemindr.yaml",
|
|
14
|
+
dry_run: bool = False,
|
|
15
|
+
log_level: str = typer.Option("INFO", help="Log level: INFO or DEBUG"),
|
|
16
|
+
):
|
|
17
|
+
"""
|
|
18
|
+
Run filemindr pipeline.
|
|
19
|
+
"""
|
|
20
|
+
# Configura logging
|
|
21
|
+
logger.remove()
|
|
22
|
+
logger.add(sys.stdout, level=log_level.upper())
|
|
23
|
+
|
|
24
|
+
config_path = resolve_config(config)
|
|
25
|
+
|
|
26
|
+
logger.info(f"Running pipeline with config={config_path} dry_run={dry_run}")
|
|
27
|
+
|
|
28
|
+
run_pipeline(str(config_path), dry_run)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
if __name__ == "__main__":
|
|
32
|
+
app()
|
|
File without changes
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def resolve_config(cli_path: str | None = None) -> Path:
|
|
5
|
+
if cli_path:
|
|
6
|
+
return Path(cli_path).expanduser()
|
|
7
|
+
|
|
8
|
+
local = Path.cwd() / "filemindr.yaml"
|
|
9
|
+
if local.exists():
|
|
10
|
+
return local
|
|
11
|
+
|
|
12
|
+
home = Path.home() / ".filemindr" / "config.yaml"
|
|
13
|
+
if home.exists():
|
|
14
|
+
return home
|
|
15
|
+
|
|
16
|
+
raise FileNotFoundError("No filemindr config found.")
|