diff --git a/chatmaild/src/chatmaild/expire.py b/chatmaild/src/chatmaild/expire.py index 2d3a4dd4..f12b21b3 100644 --- a/chatmaild/src/chatmaild/expire.py +++ b/chatmaild/src/chatmaild/expire.py @@ -4,6 +4,7 @@ Expire old messages and addresses. """ import os +import shutil import sys from argparse import ArgumentParser from datetime import datetime @@ -13,27 +14,19 @@ from chatmaild.config import read_config class FileEntry: - def __init__(self, basedir, relpath, mtime, size): - self.basedir = basedir + def __init__(self, relpath, mtime, size): self.relpath = relpath self.mtime = mtime self.size = size + def __hash__(self): + return hash(self.relpath) + def __repr__(self): return f"" - def __str__(self): - return self.get_path() - - def get_path(self): - return joinpath(self.basedir, self.relpath) - - def fmt_size(self): - return f"{int(self.size/1000):5.0f}K" - - def fmt_since(self, now): - diff_seconds = int(now) - int(self.mtime) - return f"{int(diff_seconds / 86400):2.0f}d" + def get_path(self, basedir): + return joinpath(basedir, self.relpath) def __eq__(self, other): return ( @@ -50,7 +43,6 @@ def joinpath(name, extra): class Stats: def __init__(self, basedir, maxnum=None): self.basedir = str(basedir) - self.mailboxes = [] self.maxnum = maxnum def iter_mailboxes(self, callback=None): @@ -58,7 +50,6 @@ class Stats: if "@" in name: basedir = joinpath(self.basedir, name) mailbox = MailboxStat(basedir) - self.mailboxes.append(mailbox) if callback is not None: callback(mailbox) @@ -86,17 +77,13 @@ class MailboxStat: st = os.stat(msg_path) relpath = joinpath(name, msg_name) self.messages.append( - FileEntry( - self.basedir, relpath, mtime=st.st_mtime, size=st.st_size - ) + FileEntry(relpath, mtime=st.st_mtime, size=st.st_size) ) self.totalsize += st.st_size else: st = os.stat(fpath) if S_ISREG(st.st_mode): - self.extrafiles.append( - FileEntry(self.basedir, name, st.st_mtime, st.st_size) - ) + self.extrafiles.append(FileEntry(name, st.st_mtime, st.st_size)) if name == "password": self.last_login = st.st_mtime self.totalsize += st.st_size @@ -108,8 +95,9 @@ def print_info(msg): class Expiry: - def __init__(self, config, stat, dry, now): + def __init__(self, config, stats, dry, now): self.config = config + self.stats = stats self.dry = dry self.now = now self.del_files = [] @@ -119,13 +107,14 @@ class Expiry: for mboxdir in self.del_mailboxes: print_info(f"removing {mboxdir}") if not self.dry: - self.rmtree(mboxdir) + shutil.rmtree(mboxdir) for path in self.del_files: print_info(f"removing {path}") if not self.dry: try: os.unlink(path) except FileNotFoundError: + print_info(f"delete failed, file vanished? {path}") pass # it's gone already, fine def process_mailbox_stat(self, mbox): @@ -143,9 +132,9 @@ class Expiry: for message in mbox.messages: if message.mtime < cutoff_mails: - self.del_files.append(message.get_path()) + self.del_files.append(joinpath(mbox.basedir, message.relpath)) elif message.size > 200000 and message.mtime < cutoff_large_mails: - self.del_files.append(message.get_path()) + self.del_files.append(joinpath(mbox.basedir, message.relpath)) else: continue changed = True @@ -187,9 +176,9 @@ def main(args): now = now - 86400 * int(args.days) maxnum = int(args.maxnum) if args.maxnum else None - stat = Stats(args.mailboxes_dir, maxnum=maxnum) - exp = Expiry(config, stat, dry=not args.remove, now=now) - stat.iter_mailboxes(exp.process_mailbox_stat) + stats = Stats(args.mailboxes_dir, maxnum=maxnum) + exp = Expiry(config, stats, dry=not args.remove, now=now) + stats.iter_mailboxes(exp.process_mailbox_stat) exp.perform_removes() diff --git a/chatmaild/src/chatmaild/fsreport.py b/chatmaild/src/chatmaild/fsreport.py index c5f33e38..e8cb7e6b 100644 --- a/chatmaild/src/chatmaild/fsreport.py +++ b/chatmaild/src/chatmaild/fsreport.py @@ -39,6 +39,7 @@ class Report: self.sum_extra = 0 self.sum_all_messages = 0 self.messages = [] + self.mailboxes = [] self.user_logins = [] self.ci_logins = [] self.stats = stats @@ -52,6 +53,7 @@ class Report: else: self.user_logins.append(last_login) self.messages.extend(mailbox.messages) + self.mailboxes.append(mailbox) self.sum_all_messages += sum(msg.size for msg in mailbox.messages) self.sum_extra += sum(entry.size for entry in mailbox.extrafiles) @@ -83,7 +85,7 @@ class Report: # list all 160K files of people who haven't logged in for a while messages = [] cutoff_date_login = self.now - 30 * DAYSECONDS - for mstat in self.stats.mailboxes: + for mstat in self.mailboxes: if mstat.last_login and mstat.last_login < cutoff_date_login: for msg in mstat.messages: if msg.size > 160000: @@ -132,7 +134,10 @@ def main(args=None): "mailboxes_dir", action="store", help="path to directory of mailboxes" ) parser.add_argument( - "--days", default=0, action="store", help="assume date to be days older than now" + "--days", + default=0, + action="store", + help="assume date to be days older than now", ) parser.add_argument( diff --git a/chatmaild/src/chatmaild/tests/test_delete_inactive_users.py b/chatmaild/src/chatmaild/tests/test_delete_inactive_users.py index a7c74a9e..7257fb85 100644 --- a/chatmaild/src/chatmaild/tests/test_delete_inactive_users.py +++ b/chatmaild/src/chatmaild/tests/test_delete_inactive_users.py @@ -1,7 +1,7 @@ import time from chatmaild.doveauth import AuthDictProxy -from chatmaild.expire import run_expire +from chatmaild.expire import main as main_expire def test_login_timestamps(example_config): @@ -45,7 +45,13 @@ def test_delete_inactive_users(example_config): for addr in to_remove: assert example_config.get_user(addr).maildir.exists() - run_expire(example_config, example_config.mailboxes_dir) + main_expire( + args=[ + "--remove", + str(example_config._inipath), + str(example_config.mailboxes_dir), + ] + ) for p in example_config.mailboxes_dir.iterdir(): assert not p.name.startswith("old") diff --git a/chatmaild/src/chatmaild/tests/test_expire.py b/chatmaild/src/chatmaild/tests/test_expire.py index ee6fb78b..9c760064 100644 --- a/chatmaild/src/chatmaild/tests/test_expire.py +++ b/chatmaild/src/chatmaild/tests/test_expire.py @@ -46,10 +46,7 @@ def mbox1(basedir1): def test_filentry_ordering(tmp_path): - l = [ - FileEntry(str(tmp_path), f"x{i}", size=i + 10, mtime=1000 - i) - for i in range(10) - ] + l = [FileEntry(f"x{i}", size=i + 10, mtime=1000 - i) for i in range(10)] sorted = list(l) random.shuffle(l) l.sort(key=lambda x: x.size) @@ -93,8 +90,6 @@ def test_expiry_cli_basic(example_config, mbox1): def test_expiry_cli_old_files(capsys, example_config, mbox1): - args = example_config._inipath, Path(mbox1.basedir).parent - relpaths_old = ["cur/msg_old1", "cur/msg_old1"] cutoff_days = int(example_config.delete_mails_after) + 1 create_new_messages(mbox1.basedir, relpaths_old, size=1000, days=cutoff_days) @@ -107,6 +102,7 @@ def test_expiry_cli_old_files(capsys, example_config, mbox1): create_new_messages(mbox1.basedir, ["cur/shouldstay"], size=1000 * 300, days=1) + args = example_config._inipath, Path(mbox1.basedir).parent, "--remove" expiry_main(args) out, err = capsys.readouterr()