Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
parkpow
GitHub Repository: parkpow/deep-license-plate-recognition
Path: blob/master/stream/remove_images.py
641 views
1
import argparse
2
import logging
3
import os
4
import shutil
5
import sys
6
from datetime import datetime, timedelta
7
from pathlib import Path
8
9
logging.basicConfig(
10
stream=sys.stdout,
11
format="%(asctime)s %(levelname)s %(name)s %(threadName)s : %(message)s",
12
)
13
14
15
def parse_arguments():
16
parser = argparse.ArgumentParser(
17
description="Image cleanup utility: delete old images based on disk usage or age."
18
)
19
mode_group = parser.add_mutually_exclusive_group(required=True)
20
mode_group.add_argument(
21
"--usage",
22
action="store_true",
23
help="Trigger disk space cleanup based on usage thresholds.",
24
)
25
mode_group.add_argument(
26
"--age",
27
action="store_true",
28
help="Trigger cleanup of images older than a threshold in hours.",
29
)
30
parser.add_argument(
31
"--target_free",
32
type=int,
33
choices=range(1, 100),
34
metavar="[1-99]",
35
help="(Usage mode) Target free space percentage to reach after cleanup.",
36
)
37
parser.add_argument(
38
"--trigger_usage",
39
type=int,
40
choices=range(1, 100),
41
metavar="[1-99]",
42
help="(Usage mode) Disk usage percentage that triggers cleanup.",
43
)
44
parser.add_argument(
45
"--threshold",
46
choices=range(1, 24),
47
metavar="[1-23]",
48
help="Time duration (in hours) to remove images older than xx hours",
49
type=int,
50
)
51
parser.add_argument(
52
"--directory",
53
type=str,
54
default=".",
55
help="Directory to clean (default: current directory).",
56
)
57
return parser.parse_args()
58
59
60
def get_disk_usage(path):
61
"""Returns disk usage stats: (total, used, free) in bytes."""
62
usage = shutil.disk_usage(path)
63
return usage.total, usage.used, usage.free
64
65
66
def get_percentages(directory):
67
"""Returns disk usage (used and free) stats in percentages: (used, free) in %."""
68
total, used, free = get_disk_usage(directory)
69
if total == 0:
70
raise ValueError(
71
f"Invalid disk usage: total space is zero for directory '{directory}'"
72
)
73
return (used / total) * 100, (free / total) * 100
74
75
76
def delete_image(file_path):
77
file_size = file_path.stat().st_size
78
file_path.unlink()
79
logging.info(f"Deleted: {file_path} ({file_size} bytes)")
80
81
82
def delete_images_by_percentage(directory, target_free_percent, batch_size=10):
83
directory_path = Path(directory)
84
all_files = sorted(directory_path.rglob("*.jpg"), key=lambda p: p.stat().st_mtime)
85
86
_, current_free_percent = get_percentages(directory)
87
logging.info(f"Current free space: {current_free_percent:.2f}%")
88
89
files_deleted = 0
90
91
for file_path in all_files:
92
if current_free_percent >= target_free_percent:
93
logging.info(f"Reached target free space: {current_free_percent:.2f}%")
94
break
95
try:
96
delete_image(file_path)
97
files_deleted += 1
98
except OSError as e:
99
logging.error(f"Error deleting file {file_path}: {e.strerror}")
100
101
if files_deleted % batch_size == 0:
102
_, current_free_percent = get_percentages(directory)
103
104
_, current_free_percent = get_percentages(directory)
105
logging.info(f"Final free space after deletion: {current_free_percent:.2f}%")
106
107
108
# Function to delete image files
109
def delete_images_by_age(directory, threshold_minutes):
110
now = datetime.now()
111
threshold_time = now - timedelta(minutes=threshold_minutes)
112
files_deleted = 0
113
114
for file_path in Path(directory).rglob("*.jpg"):
115
modified_time = datetime.fromtimestamp(file_path.stat().st_mtime)
116
if modified_time < threshold_time:
117
try:
118
delete_image(file_path)
119
files_deleted += 1
120
except OSError as e:
121
logging.error(f"Error deleting images: {e.filename} - {e.strerror}")
122
123
if files_deleted == 0:
124
logging.info("No file(s) to delete below the threshold time")
125
126
127
# Function to delete empty directories
128
def delete_empty_directories(directory):
129
for dir_path in reversed(list(Path(directory).rglob("*"))):
130
if dir_path.exists() and dir_path.is_dir():
131
try:
132
if not list(dir_path.iterdir()):
133
dir_path.rmdir()
134
except OSError as e:
135
logging.error(
136
f"Error deleting empty directories: {e.filename} - {e.strerror}"
137
)
138
139
140
def main():
141
args = parse_arguments()
142
directory = os.path.abspath(args.directory)
143
144
if args.usage:
145
if args.target_free is None or args.trigger_usage is None:
146
logging.error(
147
"Error: --target_free and --trigger_usage are required in --usage mode."
148
)
149
return
150
current_used_percent, _ = get_percentages(directory)
151
logging.info(f"Disk usage: {current_used_percent:.2f}%")
152
if current_used_percent >= args.trigger_usage:
153
logging.info(
154
f"Disk usage exceeded {args.trigger_usage}%. Starting cleanup..."
155
)
156
delete_images_by_percentage(directory, args.target_free)
157
delete_empty_directories(directory)
158
else:
159
logging.info(
160
f"Disk usage is below {args.trigger_usage}%. No cleanup needed."
161
)
162
163
elif args.age:
164
if args.threshold is None:
165
logging.error("Error: --threshold is required in --age mode.")
166
return
167
threshold_minutes = args.threshold * 60
168
delete_images_by_age(directory, threshold_minutes)
169
delete_empty_directories(directory)
170
171
172
if __name__ == "__main__":
173
main()
174
175