Skip to content

Commit

Permalink
feat(printing): add printer status table
Browse files Browse the repository at this point in the history
Closes #1728
  • Loading branch information
hstievat committed Nov 5, 2024
1 parent 5ff2d9e commit 32f2a96
Show file tree
Hide file tree
Showing 6 changed files with 109 additions and 25 deletions.
2 changes: 1 addition & 1 deletion intranet/apps/printing/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)

if printers:
self.fields["printer"].choices = [("", "Select a printer...")] + list(printers.items())
self.fields["printer"].choices = [("", "Select a printer...")] + [(printer, desc) for printer, (desc, alerts) in printers.items()]

def validate_size(self):
filesize = self.file.__sizeof__()
Expand Down
68 changes: 54 additions & 14 deletions intranet/apps/printing/views.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import datetime
import logging
import math
import os
import re
import subprocess
import tempfile
from io import BytesIO
from typing import Dict, Optional
from typing import Dict, Optional, Tuple

import magic
from django.conf import settings
Expand Down Expand Up @@ -62,6 +63,29 @@ def set_user_ratelimit_status(username: str) -> None:
cache.incr(cache_key)


def parse_alerts(alerts: str) -> Tuple[str, bool]:
known_alerts = {
"paused": "unavailable",
"media-empty-error": "out of paper",
"media-empty-warning": "out of paper",
"media-jam-error": "jammed",
"media-jam-warning": "jammed",
"none": "working",
}
alerts = alerts.split()
alerts_text = ", ".join(known_alerts.get(alert, "error") for alert in alerts)
error_alerts = ["paused"]
broken_alerts = ["media-empty-error", "media-empty-warning", "media-jam-error", "media-jam-warning"]
printer_class = "working"
for alert in alerts:
if alert in error_alerts or alert not in known_alerts:
printer_class = "error"
break
if alert in broken_alerts:
printer_class = "broken"
return alerts_text, printer_class


def get_printers() -> Dict[str, str]:
"""Returns a dictionary mapping name:description for available printers.
Expand All @@ -86,8 +110,9 @@ def get_printers() -> Dict[str, str]:
except (subprocess.CalledProcessError, subprocess.TimeoutExpired):
return []

PRINTER_LINE_RE = re.compile(r"^printer\s+(\w+)\s+(?!disabled)", re.ASCII)
PRINTER_LINE_RE = re.compile(r"^printer\s+(\w+)", re.ASCII)
DESCRIPTION_LINE_RE = re.compile(r"^\s+Description:\s+(.*)\s*$", re.ASCII)
ALERTS_LINE_RE = re.compile(r"^\s+Alerts:\s+(.*)\s*$", re.ASCII)

printers = {}
last_name = None
Expand All @@ -98,19 +123,23 @@ def get_printers() -> Dict[str, str]:
name = match.group(1)
if name != "Please_Select_a_Printer":
# By default, use the name of the printer instead of the description
printers[name] = name
printers[name] = [name]
# Record the name of the printer so when we parse the rest of the
# extended description we know which printer it's referring to.
last_name = name
elif last_name is not None:
match = DESCRIPTION_LINE_RE.match(line)
if match is not None:
# Pull out the description
description = match.group(1)
# And make sure we don't set an empty description
if description:
printers[last_name] = description
last_name = None
elif last_name is not None:
description_match = DESCRIPTION_LINE_RE.match(line)
if description_match is not None:
# Pull out the description
description = description_match.group(1)
# And make sure we don't set an empty description
if description:
printers[last_name] = [description]
alerts_match = ALERTS_LINE_RE.match(line)
if alerts_match is not None:
alerts = alerts_match.group(1)
printers[last_name].append(alerts)
last_name = None

cache.set(key, printers, timeout=settings.CACHE_AGE["printers_list"])
return printers
Expand Down Expand Up @@ -300,8 +329,11 @@ def html_to_pdf(template_src, filename, context=None):

def print_job(obj: PrintJob, do_print: bool = True):
printer = obj.printer
if printer not in get_printers().keys():
all_printers = get_printers()
if printer not in all_printers:
raise Exception("Printer not authorized.")
if parse_alerts(all_printers[printer][1])[1] == "error":
raise Exception("Printer unavailable.")

if not obj.file:
raise InvalidInputPrintingError("No file given to print.")
Expand Down Expand Up @@ -467,6 +499,9 @@ def print_view(request):
messages.error(request, "You don't have printer access outside of the TJ network.")
return redirect("index")

if request.method == "GET" and "refresh" in request.GET and request.user.is_printing_admin:
cache.delete("printing:printers")

printers = get_printers()
if request.method == "POST":
form = PrintJobForm(request.POST, request.FILES, printers=printers)
Expand Down Expand Up @@ -494,5 +529,10 @@ def print_view(request):
)
else:
form = PrintJobForm(printers=printers)
context = {"form": form}
alerts = {}
for printer in printers:
alerts[printer] = parse_alerts(printers[printer][1])
elapsed_seconds = settings.CACHE_AGE["printers_list"] - cache.ttl("printing:printers")
start_time = datetime.datetime.now() - datetime.timedelta(seconds=elapsed_seconds)
context = {"form": form, "alerts": alerts, "updated_time": start_time.strftime("%-I:%M:%S %p")}
return render(request, "printing/print.html", context)
1 change: 1 addition & 0 deletions intranet/settings/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -340,6 +340,7 @@
"schedule.widget",
"dashboard.widgets",
"profile",
"printing",
"polls",
"groups",
"board",
Expand Down
38 changes: 38 additions & 0 deletions intranet/static/css/printing.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
.primary-content .selectize-control {
height: 42px;
}

.primary-content tr th,
.primary-content tr td {
padding: 5px;
}

.primary-content form {
float: left;
width: 60%;
}

.print-status-container {
float: left;
padding: 10px;
}

.printer-status {
text-align: center;
min-width: 200px;
margin: 15px;
padding: 15px;
border: 3px solid #888;
}

.working {
background-color: #6BFF6B;
}

.broken {
background-color: yellow;
}

.error {
background-color: #FF9E9E;
}
15 changes: 5 additions & 10 deletions intranet/templates/printing/print.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,7 @@
{% block css %}
{{ block.super }}
<link rel="stylesheet" href="{% static 'vendor/selectize.js-0.12.4/dist/css/selectize.default.css' %}">
<style>
.primary-content .selectize-control {
height: 42px;
}

.primary-content tr th,
.primary-content tr td {
padding: 5px;
}
</style>
{% stylesheet 'printing' %}
{% endblock %}

{% block js %}
Expand All @@ -45,5 +36,9 @@ <h2>Printing</h2>
<br>
{% include "printing/print_form.html" %}

<div class="print-status-container">
{% include "printing/print_status.html" %}
</div>

</div>
{% endblock %}
10 changes: 10 additions & 0 deletions intranet/templates/printing/print_status.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<h2>Printer status:</h2>
{% for printer, alert in alerts.items %}
<div class="printer-status {{ alert.1 }}">
{{ printer }}: {{ alert.0 }}
</div>
{% endfor %}
Updated at: {{ updated_time }}
{% if user.is_printing_admin %}
(<a href="?refresh">Update</a>)
{% endif %}

0 comments on commit 32f2a96

Please sign in to comment.