Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions printer/print_queue.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import subprocess
import time
import logging

class PrintQueue:
_queue = []

def add(self, file_name):
self._queue.append(file_name)

def in_queue(self, file_name):
return file_name in self._queue

def actual_queue_available(self):
proc = subprocess.Popen(
'lpstat -o',
shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True,
)

printer_job_count = str(proc.stdout.read()).count("\n")

return printer_job_count <= 2

def feed_into_printer(self):
while True:
time.sleep(1)

if self._queue.__len__() == 0:
continue

if self.actual_queue_available():
logging.info("FED REQUEST INTO PRINTER")
self._queue.pop(0)
72 changes: 47 additions & 25 deletions printer/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
import time
import uuid
import collector
import asyncio
from print_queue import PrintQueue

from fastapi import FastAPI, File, Form, HTTPException, UploadFile
from fastapi.middleware.cors import CORSMiddleware
Expand All @@ -17,6 +19,8 @@
from metrics import MetricsHandler


print_queue = PrintQueue()
printer_lock = asyncio.Lock()
metrics_handler = MetricsHandler.instance()
app = FastAPI()

Expand All @@ -35,7 +39,6 @@
logging.getLogger("uvicorn.access").setLevel(logging.WARNING)
logging.getLogger("uvicorn.error").setLevel(logging.WARNING)


def get_args() -> argparse.Namespace:
parser = argparse.ArgumentParser()
parser.add_argument(
Expand Down Expand Up @@ -113,6 +116,7 @@ def send_file_to_printer(
PRINTER_NAME = os.environ.get("RIGHT_PRINTER_NAME")
command = f"lp -n {num_copies} {maybe_page_range} -o sides={sides} -o media=na_letter_8.5x11in -d {PRINTER_NAME} {file_path}"
metrics_handler.print_jobs_recieved.inc()

if args.development:
logging.warning(
f"server is in development mode, command would've been `{command}`"
Expand All @@ -126,6 +130,7 @@ def send_file_to_printer(
stderr=subprocess.PIPE,
text=True,
)

print_job.wait()

if print_job.returncode != 0:
Expand All @@ -145,16 +150,14 @@ def send_file_to_printer(
# with code 0 but the output could not be parsed for a job id.
return ''


def maybe_delete_pdf(file_path):
if args.dont_delete_pdfs:
logging.info(
f"--dont-delete-pdfs is set, skipping deletion of file {file_path}"
f"--dont-delete-pdfs is set, skipping deletion of file {file_path}"
)
return
pathlib.Path(file_path).unlink()


@app.get("/healthcheck/printer")
def api():
metrics_handler.last_health_check_request.set(int(time.time()))
Expand All @@ -178,30 +181,42 @@ async def read_item(
"sides": string value from user input on clark frontend; we insert this into the lp command,
}
"""
try:
base = pathlib.Path("/tmp")
file_id = str(uuid.uuid4())
file_path = str(base / file_id)
with open(file_path, "wb") as f:
f.write(await file.read())
print_id = send_file_to_printer(
str(file_path),
copies,
sides=sides,
)
async with printer_lock:
if not args.development and not print_queue.actual_queue_available():
print_queue.add(file.filename)
timeout = 0
while print_queue.in_queue(file.filename):
timeout += 1

maybe_delete_pdf(file_path)
if timeout > 300:
raise Exception("/print TIMED OUT AFTER 300 SECONDS WHILE WAITING IN THE PRINTER QUEUE")

await asyncio.sleep(1)

if not args.development and print_id is None:
raise Exception("unable to extract print id from print request")
return {"print_id": print_id}
except Exception:
logging.exception("printing failed!")
return HTTPException(
status_code=500,
detail="printing failed, check logs",
)
try:
base = pathlib.Path("/tmp")
file_id = str(uuid.uuid4())
file_path = str(base / file_id)
with open(file_path, "wb") as f:
f.write(await file.read())
print_id = send_file_to_printer(
str(file_path),
copies,
sides=sides,
)

maybe_delete_pdf(file_path)

if not args.development and print_id is None:
raise Exception("unable to extract print id from print request")
return {"print_id": print_id}
except Exception:
logging.exception("printing failed!")
return HTTPException(
status_code=500,
detail="printing failed, check logs",
)


# we have a separate __name__ check here due to how FastAPI starts
# a server. the file is first ran (where __name__ == "__main__")
Expand All @@ -212,6 +227,13 @@ async def read_item(
# the thread interacts with an instance different than the one the
# server uses
if __name__ == "server":
if not args.development:
queue_thread = threading.Thread(
target=print_queue.feed_into_printer,
daemon=True
)
queue_thread.start()

if not args.development:
# set the last time we opened an ssh tunnel to now because
# when the script runs for the first time, we did so in what.sh
Expand Down
Loading