history: report user in job history
When possible record the name of the user that requested the job. The klippy_api's component now takes an optional user argument in its "start_print" method. This user is broadcast in an event after a print request successfully returns. Signed-off-by: Eric Callahan <arksine.code@gmail.com>
This commit is contained in:
parent
1dfbffb422
commit
531028ef4f
|
@ -65,10 +65,11 @@ if TYPE_CHECKING:
|
|||
from io import BufferedReader
|
||||
from .authorization import Authorization
|
||||
from .template import TemplateFactory, JinjaTemplate
|
||||
MessageDelgate = Optional[tornado.httputil.HTTPMessageDelegate]
|
||||
MessageDelgate = Optional[HTTPMessageDelegate]
|
||||
AuthComp = Optional[Authorization]
|
||||
APICallback = Callable[[WebRequest], Coroutine]
|
||||
|
||||
# mypy: disable-error-code="attr-defined,name-defined"
|
||||
|
||||
# 50 MiB Max Standard Body Size
|
||||
MAX_BODY_SIZE = 50 * 1024 * 1024
|
||||
|
@ -1010,6 +1011,7 @@ class FileUploadHandler(AuthorizedRequestHandler):
|
|||
for name, value in form_args.items():
|
||||
debug_msg += f"\n{name}: {value}"
|
||||
debug_msg += f"\nChecksum: {calc_chksum}"
|
||||
form_args["current_user"] = self.current_user
|
||||
logging.debug(debug_msg)
|
||||
logging.info(f"Processing Uploaded File: {self._file.multipart_filename}")
|
||||
try:
|
||||
|
|
|
@ -880,7 +880,8 @@ class FileManager:
|
|||
'start_print': start_print,
|
||||
'unzip_ufp': unzip_ufp,
|
||||
'ext': f_ext,
|
||||
"is_link": os.path.islink(dest_path)
|
||||
"is_link": os.path.islink(dest_path),
|
||||
"user": upload_args.get("current_user")
|
||||
}
|
||||
|
||||
async def _finish_gcode_upload(
|
||||
|
@ -901,10 +902,11 @@ class FileManager:
|
|||
started: bool = False
|
||||
queued: bool = False
|
||||
if upload_info['start_print']:
|
||||
user: Optional[Dict[str, Any]] = upload_info.get("user")
|
||||
if can_start:
|
||||
kapis: APIComp = self.server.lookup_component('klippy_apis')
|
||||
try:
|
||||
await kapis.start_print(upload_info['filename'])
|
||||
await kapis.start_print(upload_info['filename'], user=user)
|
||||
except self.server.error:
|
||||
# Attempt to start print failed
|
||||
pass
|
||||
|
@ -913,7 +915,7 @@ class FileManager:
|
|||
if self.queue_gcodes and not started:
|
||||
job_queue: JobQueue = self.server.lookup_component('job_queue')
|
||||
await job_queue.queue_job(
|
||||
upload_info['filename'], check_exists=False)
|
||||
upload_info['filename'], check_exists=False, user=user)
|
||||
queued = True
|
||||
self.fs_observer.on_item_create("gcodes", upload_info["dest_path"])
|
||||
result = dict(self._sched_changed_event(
|
||||
|
|
|
@ -65,6 +65,8 @@ class History:
|
|||
"server:klippy_shutdown", self._handle_shutdown)
|
||||
self.server.register_event_handler(
|
||||
"job_state:state_changed", self._on_job_state_changed)
|
||||
self.server.register_event_handler(
|
||||
"klippy_apis:job_start_complete", self._on_job_requested)
|
||||
self.server.register_notification("history:history_changed")
|
||||
|
||||
self.server.register_endpoint(
|
||||
|
@ -88,6 +90,7 @@ class History:
|
|||
|
||||
self.current_job: Optional[PrinterJob] = None
|
||||
self.current_job_id: Optional[str] = None
|
||||
self.job_user: str = "No User"
|
||||
self.job_paused: bool = False
|
||||
self.next_job_id: int = 0
|
||||
self.cached_job_ids = self.history_ns.keys().result()
|
||||
|
@ -249,6 +252,12 @@ class History:
|
|||
# `CLEAR_PAUSE/SDCARD_RESET_FILE` workflow
|
||||
self.finish_job("cancelled", prev_stats)
|
||||
|
||||
def _on_job_requested(self, user: Optional[Dict[str, Any]]) -> None:
|
||||
username = (user or {}).get("username", "No User")
|
||||
self.job_user = username
|
||||
if self.current_job is not None:
|
||||
self.current_job.user = username
|
||||
|
||||
def _handle_shutdown(self) -> None:
|
||||
jstate: JobState = self.server.lookup_component("job_state")
|
||||
last_ps = jstate.get_last_stats()
|
||||
|
@ -265,6 +274,7 @@ class History:
|
|||
job_id = f"{self.next_job_id:06X}"
|
||||
self.current_job = job
|
||||
self.current_job_id = job_id
|
||||
self.current_job.user = self.job_user
|
||||
self.grab_job_metadata()
|
||||
for field in self.auxiliary_fields:
|
||||
field.tracker.reset()
|
||||
|
@ -296,6 +306,7 @@ class History:
|
|||
# Print stats have been reset, do not update this job with them
|
||||
pstats = {}
|
||||
|
||||
self.current_job.user = self.job_user
|
||||
self.current_job.finish(status, pstats)
|
||||
# Regrab metadata incase metadata wasn't parsed yet due to file upload
|
||||
self.grab_job_metadata()
|
||||
|
@ -310,6 +321,7 @@ class History:
|
|||
self.send_history_event("finished")
|
||||
self.current_job = None
|
||||
self.current_job_id = None
|
||||
self.job_user = "No User"
|
||||
|
||||
async def get_job(self,
|
||||
job_id: Union[int, str]
|
||||
|
@ -425,6 +437,7 @@ class PrinterJob:
|
|||
self.start_time = time.time()
|
||||
self.total_duration: float = 0.
|
||||
self.auxiliary_data: List[Dict[str, Any]] = []
|
||||
self.user: str = "No User"
|
||||
self.update_from_ps(data)
|
||||
|
||||
def finish(self,
|
||||
|
|
|
@ -135,7 +135,9 @@ class JobQueue:
|
|||
raise self.server.error(
|
||||
"Queue State Changed during Transition Gcode")
|
||||
self._set_queue_state("starting")
|
||||
await kapis.start_print(filename, wait_klippy_started=True)
|
||||
await kapis.start_print(
|
||||
filename, wait_klippy_started=True, user=job.user
|
||||
)
|
||||
except self.server.error:
|
||||
logging.exception(f"Error Loading print: {filename}")
|
||||
self._set_queue_state("paused")
|
||||
|
@ -165,7 +167,8 @@ class JobQueue:
|
|||
async def queue_job(self,
|
||||
filenames: Union[str, List[str]],
|
||||
check_exists: bool = True,
|
||||
reset: bool = False
|
||||
reset: bool = False,
|
||||
user: Optional[Dict[str, Any]] = None
|
||||
) -> None:
|
||||
async with self.lock:
|
||||
# Make sure that the file exists
|
||||
|
@ -178,7 +181,7 @@ class JobQueue:
|
|||
if reset:
|
||||
self.queued_jobs.clear()
|
||||
for fname in filenames:
|
||||
queued_job = QueuedJob(fname)
|
||||
queued_job = QueuedJob(fname, user)
|
||||
self.queued_jobs[queued_job.job_id] = queued_job
|
||||
self._send_queue_event(action="jobs_added")
|
||||
|
||||
|
@ -224,6 +227,7 @@ class JobQueue:
|
|||
else:
|
||||
qs = "ready" if self.automatic else "paused"
|
||||
self._set_queue_state(qs)
|
||||
|
||||
def _job_map_to_list(self) -> List[Dict[str, Any]]:
|
||||
cur_time = time.time()
|
||||
return [job.as_dict(cur_time) for
|
||||
|
@ -261,7 +265,8 @@ class JobQueue:
|
|||
files = web_request.get_list('filenames')
|
||||
reset = web_request.get_boolean("reset", False)
|
||||
# Validate that all files exist before queueing
|
||||
await self.queue_job(files, reset=reset)
|
||||
user = web_request.get_current_user()
|
||||
await self.queue_job(files, reset=reset, user=user)
|
||||
elif req_type == RequestType.DELETE:
|
||||
if web_request.get_boolean("all", False):
|
||||
await self.delete_job([], all=True)
|
||||
|
@ -319,14 +324,19 @@ class JobQueue:
|
|||
await self.pause_queue()
|
||||
|
||||
class QueuedJob:
|
||||
def __init__(self, filename: str) -> None:
|
||||
def __init__(self, filename: str, user: Optional[Dict[str, Any]] = None) -> None:
|
||||
self.filename = filename
|
||||
self.job_id = f"{id(self):016X}"
|
||||
self.time_added = time.time()
|
||||
self._user = user
|
||||
|
||||
def __str__(self) -> str:
|
||||
return self.filename
|
||||
|
||||
@property
|
||||
def user(self) -> Optional[Dict[str, Any]]:
|
||||
return self._user
|
||||
|
||||
def as_dict(self, cur_time: float) -> Dict[str, Any]:
|
||||
return {
|
||||
'filename': self.filename,
|
||||
|
|
|
@ -89,7 +89,8 @@ class KlippyAPI(APITransport):
|
|||
|
||||
async def _gcode_start_print(self, web_request: WebRequest) -> str:
|
||||
filename: str = web_request.get_str('filename')
|
||||
return await self.start_print(filename)
|
||||
user = web_request.get_current_user()
|
||||
return await self.start_print(filename, user=user)
|
||||
|
||||
async def _gcode_restart(self, web_request: WebRequest) -> str:
|
||||
return await self.do_restart("RESTART")
|
||||
|
@ -123,7 +124,10 @@ class KlippyAPI(APITransport):
|
|||
return result
|
||||
|
||||
async def start_print(
|
||||
self, filename: str, wait_klippy_started: bool = False
|
||||
self,
|
||||
filename: str,
|
||||
wait_klippy_started: bool = False,
|
||||
user: Optional[Dict[str, Any]] = None
|
||||
) -> str:
|
||||
# WARNING: Do not call this method from within the following
|
||||
# event handlers when "wait_klippy_started" is set to True:
|
||||
|
@ -139,7 +143,9 @@ class KlippyAPI(APITransport):
|
|||
if wait_klippy_started:
|
||||
await self.klippy.wait_started()
|
||||
logging.info(f"Requesting Job Start, filename = {filename}")
|
||||
return await self.run_gcode(script)
|
||||
ret = await self.run_gcode(script)
|
||||
self.server.send_event("klippy_apis:job_start_complete", user)
|
||||
return ret
|
||||
|
||||
async def pause_print(
|
||||
self, default: Union[Sentinel, _T] = Sentinel.MISSING
|
||||
|
|
|
@ -388,9 +388,10 @@ class OctoPrintCompat:
|
|||
except self.server.error:
|
||||
pstate = "not_avail"
|
||||
started: bool = False
|
||||
user = web_request.get_current_user()
|
||||
if pstate not in ["printing", "paused", "not_avail"]:
|
||||
try:
|
||||
await self.klippy_apis.start_print(filename)
|
||||
await self.klippy_apis.start_print(filename, user=user)
|
||||
except self.server.error:
|
||||
started = False
|
||||
else:
|
||||
|
@ -400,7 +401,7 @@ class OctoPrintCompat:
|
|||
if fmgr.upload_queue_enabled():
|
||||
job_queue: JobQueue = self.server.lookup_component(
|
||||
'job_queue')
|
||||
await job_queue.queue_job(filename, check_exists=False)
|
||||
await job_queue.queue_job(filename, check_exists=False, user=user)
|
||||
logging.debug(f"Job '{filename}' queued via OctoPrint API")
|
||||
else:
|
||||
raise self.server.error("Conflict", 409)
|
||||
|
|
|
@ -1598,7 +1598,7 @@ class PrintHandler:
|
|||
kapi: KlippyAPI = self.server.lookup_component("klippy_apis")
|
||||
data = {"state": "started"}
|
||||
try:
|
||||
await kapi.start_print(pending)
|
||||
await kapi.start_print(pending, user={"username": "SimplyPrint"})
|
||||
except Exception:
|
||||
logging.exception("Print Failed to start")
|
||||
data["state"] = "error"
|
||||
|
|
Loading…
Reference in New Issue