A thread-safe manager for creating, updating, and retrieving information about background jobs. It stores job information in memory.
| 63 | return remaining_files * avg_time_per_file |
| 64 | |
| 65 | class JobManager: |
| 66 | """ |
| 67 | A thread-safe manager for creating, updating, and retrieving information |
| 68 | about background jobs. It stores job information in memory. |
| 69 | """ |
| 70 | def __init__(self): |
| 71 | self.jobs: Dict[str, JobInfo] = {} |
| 72 | self.lock = threading.Lock() # A lock to ensure thread-safe access to the jobs dictionary. |
| 73 | |
| 74 | def create_job(self, path: str, is_dependency: bool = False) -> str: |
| 75 | """Creates a new job, assigns it a unique ID, and stores it.""" |
| 76 | job_id = str(uuid.uuid4()) |
| 77 | with self.lock: |
| 78 | self.jobs[job_id] = JobInfo( |
| 79 | job_id=job_id, |
| 80 | status=JobStatus.PENDING, |
| 81 | start_time=datetime.now(), |
| 82 | path=path, |
| 83 | is_dependency=is_dependency |
| 84 | ) |
| 85 | return job_id |
| 86 | |
| 87 | def update_job(self, job_id: str, **kwargs): |
| 88 | """Updates the information for a specific job in a thread-safe manner.""" |
| 89 | with self.lock: |
| 90 | if job_id in self.jobs: |
| 91 | job = self.jobs[job_id] |
| 92 | for key, value in kwargs.items(): |
| 93 | if hasattr(job, key): |
| 94 | setattr(job, key, value) |
| 95 | |
| 96 | def get_job(self, job_id: str) -> Optional[JobInfo]: |
| 97 | """Retrieves the information for a single job.""" |
| 98 | with self.lock: |
| 99 | return self.jobs.get(job_id) |
| 100 | |
| 101 | def list_jobs(self) -> List[JobInfo]: |
| 102 | """Returns a list of all jobs currently in the manager.""" |
| 103 | with self.lock: |
| 104 | return list(self.jobs.values()) |
| 105 | |
| 106 | def find_active_job_by_path(self, path: str) -> Optional[JobInfo]: |
| 107 | """Finds the most recent, currently active (pending or running) job for a given path.""" |
| 108 | with self.lock: |
| 109 | path_obj = Path(path).resolve() |
| 110 | |
| 111 | matching_jobs = sorted( |
| 112 | [job for job in self.jobs.values() if job.path and Path(job.path).resolve() == path_obj], |
| 113 | key=lambda j: j.start_time, |
| 114 | reverse=True |
| 115 | ) |
| 116 | |
| 117 | for job in matching_jobs: |
| 118 | if job.status in [JobStatus.PENDING, JobStatus.RUNNING]: |
| 119 | return job |
| 120 | |
| 121 | return None |
| 122 |
no outgoing calls