UdasriHasindu commited on
Commit
6f5feff
·
1 Parent(s): 2a0dd1d

add cleanup on runs/ dir every 30min

Browse files
Files changed (3) hide show
  1. Dockerfile +1 -0
  2. README.md +28 -0
  3. scripts/cleanup_runs.py +102 -0
Dockerfile CHANGED
@@ -22,6 +22,7 @@ RUN pip install --no-cache-dir -r requirements.txt
22
 
23
  # Copy application files
24
  COPY app.py /app/app.py
 
25
 
26
  # Create output directory
27
  RUN mkdir -p /app/runs/outputs
 
22
 
23
  # Copy application files
24
  COPY app.py /app/app.py
25
+ COPY scripts /app/scripts
26
 
27
  # Create output directory
28
  RUN mkdir -p /app/runs/outputs
README.md CHANGED
@@ -29,3 +29,31 @@ Response includes:
29
  - full feature values
30
  - URL to annotated output video
31
  - URL to biomarker plot image
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
29
  - full feature values
30
  - URL to annotated output video
31
  - URL to biomarker plot image
32
+
33
+ ## Automatic cleanup for runs/
34
+
35
+ Generated files from `/analyze_files` are stored under `runs/outputs`.
36
+ To prevent storage growth, use the cleanup script every 30 minutes.
37
+
38
+ Script path:
39
+
40
+ - `scripts/cleanup_runs.py`
41
+
42
+ Behavior:
43
+
44
+ - Deletes files older than 30 minutes (default)
45
+ - Removes empty subdirectories
46
+
47
+ ### Local host cron example
48
+
49
+ ```bash
50
+ */30 * * * * /usr/bin/python3 /path/to/GAIT_API/scripts/cleanup_runs.py --path /path/to/GAIT_API/runs --max-age-minutes 30 >> /var/log/gait_cleanup.log 2>&1
51
+ ```
52
+
53
+ ### Docker container cron example (host cron running docker exec)
54
+
55
+ ```bash
56
+ */30 * * * * docker exec gait-api python /app/scripts/cleanup_runs.py --path /app/runs --max-age-minutes 30 >> /var/log/gait_cleanup.log 2>&1
57
+ ```
58
+
59
+ Replace `gait-api` with your running container name.
scripts/cleanup_runs.py ADDED
@@ -0,0 +1,102 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """Cleanup utility for GAIT_API generated files.
3
+
4
+ This script removes files under the runs directory that are older than a
5
+ configurable age (default: 30 minutes). It is designed to be triggered by cron.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import argparse
11
+ import os
12
+ import time
13
+ from pathlib import Path
14
+
15
+
16
+ def parse_args() -> argparse.Namespace:
17
+ parser = argparse.ArgumentParser(description="Cleanup old files from runs directory")
18
+ parser.add_argument(
19
+ "--path",
20
+ default="/app/runs",
21
+ help="Runs directory path (default: /app/runs)",
22
+ )
23
+ parser.add_argument(
24
+ "--max-age-minutes",
25
+ type=float,
26
+ default=30,
27
+ help="Delete files older than this many minutes (default: 30)",
28
+ )
29
+ parser.add_argument(
30
+ "--dry-run",
31
+ action="store_true",
32
+ help="Only print what would be deleted",
33
+ )
34
+ return parser.parse_args()
35
+
36
+
37
+ def cleanup_runs(root: Path, max_age_minutes: float, dry_run: bool = False) -> tuple[int, int]:
38
+ if not root.exists():
39
+ return 0, 0
40
+
41
+ now = time.time()
42
+ cutoff = now - (max_age_minutes * 60)
43
+
44
+ deleted_files = 0
45
+ deleted_dirs = 0
46
+
47
+ # Delete eligible files first
48
+ for file_path in root.rglob("*"):
49
+ if not file_path.is_file():
50
+ continue
51
+
52
+ try:
53
+ mtime = file_path.stat().st_mtime
54
+ if mtime <= cutoff:
55
+ if dry_run:
56
+ print(f"[DRY-RUN] file: {file_path}")
57
+ else:
58
+ file_path.unlink(missing_ok=True)
59
+ deleted_files += 1
60
+ except FileNotFoundError:
61
+ # Might have been removed by another process
62
+ continue
63
+ except PermissionError:
64
+ print(f"[WARN] Permission denied: {file_path}")
65
+
66
+ # Remove empty directories bottom-up (excluding root)
67
+ for dir_path in sorted((p for p in root.rglob("*") if p.is_dir()), key=lambda p: len(p.parts), reverse=True):
68
+ try:
69
+ if any(dir_path.iterdir()):
70
+ continue
71
+ if dry_run:
72
+ print(f"[DRY-RUN] dir: {dir_path}")
73
+ else:
74
+ dir_path.rmdir()
75
+ deleted_dirs += 1
76
+ except (FileNotFoundError, PermissionError, OSError):
77
+ # OSError when directory isn't empty anymore
78
+ continue
79
+
80
+ return deleted_files, deleted_dirs
81
+
82
+
83
+ def main() -> int:
84
+ args = parse_args()
85
+ root = Path(args.path)
86
+
87
+ deleted_files, deleted_dirs = cleanup_runs(
88
+ root=root,
89
+ max_age_minutes=args.max_age_minutes,
90
+ dry_run=args.dry_run,
91
+ )
92
+
93
+ print(
94
+ f"Cleanup complete for {root} | "
95
+ f"deleted_files={deleted_files} | deleted_dirs={deleted_dirs} | "
96
+ f"max_age_minutes={args.max_age_minutes}"
97
+ )
98
+ return 0
99
+
100
+
101
+ if __name__ == "__main__":
102
+ raise SystemExit(main())