import json import os import sys import time import datetime import logging from watchdog.observers import Observer from watchdog.events import FileSystemEventHandler from collections import OrderedDict class BuildPlanEventHandler(FileSystemEventHandler): """Monitors for file generation, and once some amount of time has passed, captures the results into a list of ``BuildPlan``s.""" def __init__(self, start_plans=[], delete=[], wait_period=1000): super(BuildPlanEventHandler, self).__init__() self.plans = start_plans self.delete = delete self.wait_period = wait_period self.last_ev = datetime.datetime.fromtimestamp(0) self.build_plan = OrderedDict() self.initial = True def is_probably_text(self, fn): return os.path.splitext(fn)[1] in [".v", ".srp", ".sort", ".naf", ".lpc", ".ipx", ".edn", ".tcl", ".log"] def is_probably_new_plan(self, curr): delta = curr - self.last_ev self.last_ev = curr return delta.total_seconds() > datetime.timedelta(milliseconds = self.wait_period).total_seconds() def start_new_plan(self, trigger): if self.initial: self.initial = False else: self.plans.append(self.build_plan) for f in self.delete: os.remove(f) self.build_plan = OrderedDict() def on_moved(self, event): super(BuildPlanEventHandler, self).on_moved(event) assert False def on_created(self, event): super(BuildPlanEventHandler, self).on_created(event) logging.debug("Created: %s", event.src_path) # Get timestamp now in case reading takes a while. now = datetime.datetime.now() if self.is_probably_new_plan(now): logging.info("New plan starting with creation of %s.", event.src_path) self.start_new_plan(event.src_path) if self.is_probably_text(event.src_path): with open(event.src_path, "rt") as fp: self.build_plan[event.src_path] = fp.read() def on_deleted(self, event): super(BuildPlanEventHandler, self).on_deleted(event) logging.debug("Deleted: %s", event.src_path) # Get timestamp now in case reading takes a while. now = datetime.datetime.now() try: del self.build_plan[event.src_path] except KeyError: logging.info("Tried deleting %s, which wasn't created as part of build plan initially. Continuing.", event.src_path) if self.is_probably_new_plan(now): logging.info("New plan starting with deletion of %s.", event.src_path) self.start_new_plan(event.src_path) def on_modified(self, event): super(BuildPlanEventHandler, self).on_modified(event) logging.debug("Modified: %s", event.src_path) # Get timestamp now in case reading takes a while. now = datetime.datetime.now() if self.is_probably_new_plan(now): logging.info("New plan starting with modification of %s.", event.src_path) self.start_new_plan(event.src_path) if self.is_probably_text(event.src_path): with open(event.src_path, "rt") as fp: self.build_plan[event.src_path] = fp.read() if __name__ == "__main__": fn = sys.argv[1] if len(sys.argv) > 1 else 'plans.json' logging.basicConfig(level=logging.DEBUG, datefmt='%Y-%m-%d %H:%M:%S') try: with open(sys.argv[1] if len(sys.argv) > 1 else 'plans.json', 'r') as fp: all_plans = json.load(fp) except (json.decoder.JSONDecodeError, FileNotFoundError): logging.info("No initial plans provided- will create %s later.", fn) all_plans = [] path = "IPE_out" event_handler = BuildPlanEventHandler() # delete=[os.path.join(path, "EFB.ipx")]) observer = Observer() observer.schedule(event_handler, path, recursive=True) observer.start() try: while True: time.sleep(1) finally: observer.stop() with open(fn, 'w') as fp: json.dump(event_handler.plans, fp, indent=2) observer.join()