import argparse from datetime import date, datetime, timedelta from dateutil.relativedelta import relativedelta import os from pathlib import Path import re def get_quarter(p_date: date) -> int: return (p_date.month - 1) // 3 + 1 def get_day_minus_x(p_date: date, limitdays: int): # return list of dates list = [] while (limitdays >= 0): list.append(p_date + relativedelta(days=-limitdays)) limitdays = limitdays - 1 return list def get_month_minus_x(p_date: date, limitmonths: int): # return list of dates list = [] limitmonths = limitmonths - 1 while (limitmonths >= 0): list.append((datetime(p_date.year, p_date.month, 1) + relativedelta(months=-limitmonths) + timedelta(days=-1)).date()) limitmonths = limitmonths - 1 return list def get_quarter_minus_x(p_date: date, limitquarter: int): # return list of dates list = [] while (limitquarter > 0): last_quarter = get_quarter(p_date) - limitquarter list.append((datetime(p_date.year + 3 * last_quarter // 12, 3 * last_quarter % 12 + 1, 1) + timedelta(days=-1)).date()) limitquarter = limitquarter - 1 return list def get_year_minus_x(p_date: date, limityear: int): # return list of dates list = [] limityear = limityear - 1 while (limityear >= 0): list.append((datetime(p_date.year, 1, 1) + relativedelta(years=-limityear) + timedelta(days=-1)).date()) limityear = limityear - 1 return list def set_keep(keep, reason): if isinstance(keep, bool): return f"True - {reason}" else: return f"{keep} & {reason}" def process_file(child, daydates, monthdates, quarterdates, yeardates): date_reg_exp = re.compile('\d{4}\d{2}\d{2}') matches_list = date_reg_exp.findall(child.name) if(len(matches_list) < 1): print(f'{{"name": "{child.name}", "filedate": "", "keep": "True - no YYYYMMDD to parse", "action": "none"}}') else: filedate = datetime.strptime(matches_list[0], '%Y%m%d') keep = test_for_keep(filedate, daydates, monthdates, quarterdates, yeardates) if (keep): print( f'{{"name": "{child.name}", "filedate": "{filedate}", "keep": "{keep}", "action": "none"}}') else: print( f'{{"name": "{child.name}", "filedate": "{filedate}", "keep": "{keep}" , "action": "to remove"}}') os.remove(child) print( f'{{"name": "{child.name}", "filedate": "{filedate}", "keep": "{keep}" , "action": "removed"}}') def test_for_keep(filedate, daydates, monthdates, quarterdates, yeardates): keep = False # Keep last days of backups if(filedate.date() in daydates): keep = set_keep(keep, "last days") # Keep backup from last day of month if(filedate.date() in monthdates): keep = set_keep(keep, "last day of month") # Keep backup from last day of quarter if(filedate.date() in quarterdates): keep = set_keep(keep, "last day of quarter") # Keep backup from last day of year if(filedate.date() in yeardates): keep = set_keep(keep, "last day of year") # Keep backups from the future if(filedate.date() > date.today()): keep = set_keep(keep, "Future") return keep def main(path, days, months, quarters, years): print(f'Path is {path}') print( f'Keeping {years} end of year, {quarters} end of quarter, {months} end of month, {days} daily backups') daydates = get_day_minus_x(date.today(), days) monthdates = get_month_minus_x(date.today(), months) quarterdates = get_quarter_minus_x(date.today(), quarters) yeardates = get_year_minus_x(date.today(), years) for child in Path(path).iterdir(): if child.is_file(): process_file(child, daydates, monthdates, quarterdates, yeardates) if __name__ == "__main__": parser = argparse.ArgumentParser( description='Test a path for backup rotation') parser.add_argument('--path', type=str, required=True, help="The path to check") parser.add_argument('--days', type=int, required=True, help="Daily backups to keep") parser.add_argument('--months', type=int, required=True, help="Last day of months to keep") parser.add_argument('--quarters', type=int, required=True, help="Last day of quarter to keep") parser.add_argument('--years', type=int, required=True, help="Last day of years to keep") args = parser.parse_args() main(args.path, args.days, args.months, args.quarters, args.years)