selfprivacy-rest-api/selfprivacy_api/jobs/__init__.py

221 lines
6.1 KiB
Python
Raw Normal View History

2022-07-30 15:24:21 +00:00
"""
Jobs controller. It handles the jobs that are created by the user.
This is a singleton class holding the jobs list.
Jobs can be added and removed.
A single job can be updated.
A job is a dictionary with the following keys:
- id: unique identifier of the job
- name: name of the job
- description: description of the job
- status: status of the job
- created_at: date of creation of the job
- updated_at: date of last update of the job
- finished_at: date of finish of the job
- error: error message if the job failed
- result: result of the job
"""
import typing
import datetime
from uuid import UUID
2022-08-02 19:50:16 +00:00
import asyncio
2022-07-30 15:24:21 +00:00
import json
import os
import time
import uuid
from enum import Enum
from pydantic import BaseModel
from selfprivacy_api.utils import ReadUserData, UserDataFiles, WriteUserData
2022-07-30 15:24:21 +00:00
class JobStatus(Enum):
"""
Status of a job.
"""
2022-08-17 20:58:56 +00:00
2022-07-30 15:24:21 +00:00
CREATED = "CREATED"
RUNNING = "RUNNING"
FINISHED = "FINISHED"
ERROR = "ERROR"
class Job(BaseModel):
2022-07-30 15:24:21 +00:00
"""
Job class.
"""
2022-08-17 20:58:56 +00:00
2022-08-15 19:37:34 +00:00
uid: UUID = uuid.uuid4()
2022-08-17 20:58:56 +00:00
type_id: str
name: str
description: str
status: JobStatus
status_text: typing.Optional[str]
progress: typing.Optional[int]
created_at: datetime.datetime
updated_at: datetime.datetime
finished_at: typing.Optional[datetime.datetime]
error: typing.Optional[str]
result: typing.Optional[str]
2022-07-30 15:24:21 +00:00
class Jobs:
"""
Jobs class.
"""
__instance = None
@staticmethod
def get_instance():
"""
Singleton method.
"""
if Jobs.__instance is None:
Jobs()
2022-08-02 20:30:03 +00:00
if Jobs.__instance is None:
raise Exception("Couldn't init Jobs singleton!")
return Jobs.__instance
return Jobs.__instance
2022-07-30 15:24:21 +00:00
def __init__(self):
"""
Initialize the jobs list.
"""
if Jobs.__instance is not None:
raise Exception("This class is a singleton!")
else:
Jobs.__instance = self
2022-08-02 19:50:16 +00:00
2022-08-17 20:58:56 +00:00
@staticmethod
def reset() -> None:
2022-08-02 19:50:16 +00:00
"""
Reset the jobs list.
2022-08-02 19:50:16 +00:00
"""
with WriteUserData(UserDataFiles.JOBS) as user_data:
2022-08-15 21:31:24 +00:00
user_data["jobs"] = []
2022-07-30 15:24:21 +00:00
2022-08-17 20:58:56 +00:00
@staticmethod
2022-07-30 15:24:21 +00:00
def add(
2022-08-10 23:36:36 +00:00
name: str,
2022-08-17 20:58:56 +00:00
type_id: str,
2022-08-10 23:36:36 +00:00
description: str,
status: JobStatus = JobStatus.CREATED,
status_text: str = "",
progress: int = 0,
2022-07-30 15:24:21 +00:00
) -> Job:
"""
Add a job to the jobs list.
"""
job = Job(
name=name,
2022-08-17 20:58:56 +00:00
type_id=type_id,
2022-07-30 15:24:21 +00:00
description=description,
status=status,
2022-08-02 19:50:16 +00:00
status_text=status_text,
progress=progress,
2022-07-30 15:24:21 +00:00
created_at=datetime.datetime.now(),
updated_at=datetime.datetime.now(),
finished_at=None,
error=None,
result=None,
)
with WriteUserData(UserDataFiles.JOBS) as user_data:
try:
2022-08-15 21:31:24 +00:00
if "jobs" not in user_data:
user_data["jobs"] = []
user_data["jobs"].append(json.loads(job.json()))
except json.decoder.JSONDecodeError:
2022-08-15 21:31:24 +00:00
user_data["jobs"] = [json.loads(job.json())]
2022-07-30 15:24:21 +00:00
return job
def remove(self, job: Job) -> None:
"""
Remove a job from the jobs list.
"""
with WriteUserData(UserDataFiles.JOBS) as user_data:
2022-08-15 21:31:24 +00:00
if "jobs" not in user_data:
user_data["jobs"] = []
2022-08-15 21:44:22 +00:00
for i, j in enumerate(user_data["jobs"]):
if j["uid"] == str(job.uid):
del user_data["jobs"][i]
break
2022-07-30 15:24:21 +00:00
2022-08-17 20:58:56 +00:00
@staticmethod
2022-07-30 15:24:21 +00:00
def update(
job: Job,
status: JobStatus,
2022-08-02 19:50:16 +00:00
status_text: typing.Optional[str] = None,
progress: typing.Optional[int] = None,
name: typing.Optional[str] = None,
description: typing.Optional[str] = None,
error: typing.Optional[str] = None,
result: typing.Optional[str] = None,
2022-07-30 15:24:21 +00:00
) -> Job:
"""
Update a job in the jobs list.
"""
if name is not None:
job.name = name
if description is not None:
job.description = description
2022-08-02 19:50:16 +00:00
if status_text is not None:
job.status_text = status_text
if progress is not None:
job.progress = progress
2022-07-30 15:24:21 +00:00
job.status = status
job.updated_at = datetime.datetime.now()
job.error = error
job.result = result
if status in (JobStatus.FINISHED, JobStatus.ERROR):
2022-08-02 19:50:16 +00:00
job.finished_at = datetime.datetime.now()
with WriteUserData(UserDataFiles.JOBS) as user_data:
2022-08-15 21:31:24 +00:00
if "jobs" not in user_data:
user_data["jobs"] = []
2022-08-15 21:44:22 +00:00
for i, j in enumerate(user_data["jobs"]):
if j["uid"] == str(job.uid):
user_data["jobs"][i] = json.loads(job.json())
break
2022-08-02 19:50:16 +00:00
2022-07-30 15:24:21 +00:00
return job
2022-08-17 20:58:56 +00:00
@staticmethod
def get_job(uid: str) -> typing.Optional[Job]:
2022-07-30 15:24:21 +00:00
"""
Get a job from the jobs list.
"""
with ReadUserData(UserDataFiles.JOBS) as user_data:
2022-08-15 21:31:24 +00:00
if "jobs" not in user_data:
user_data["jobs"] = []
for job in user_data["jobs"]:
2022-08-17 20:58:56 +00:00
if job["uid"] == uid:
return Job(**job)
2022-07-30 15:24:21 +00:00
return None
2022-08-17 20:58:56 +00:00
@staticmethod
def get_jobs() -> typing.List[Job]:
2022-07-30 15:24:21 +00:00
"""
Get the jobs list.
"""
with ReadUserData(UserDataFiles.JOBS) as user_data:
try:
2022-08-15 21:31:24 +00:00
if "jobs" not in user_data:
user_data["jobs"] = []
return [Job(**job) for job in user_data["jobs"]]
except json.decoder.JSONDecodeError:
return []
2022-08-17 20:58:56 +00:00
@staticmethod
def is_busy() -> bool:
"""
Check if there is a job running.
"""
with ReadUserData(UserDataFiles.JOBS) as user_data:
if "jobs" not in user_data:
user_data["jobs"] = []
for job in user_data["jobs"]:
if job["status"] == JobStatus.RUNNING.value:
return True
return False