Source code for intranet.apps.printing.forms
import logging
from django import forms
from django.conf import settings
from .models import PrintJob
logger = logging.getLogger(__name__)
[docs]class PrintJobForm(forms.ModelForm):
[docs] def __init__(self, *args, **kwargs):
printers = None
if "printers" in kwargs:
printers = kwargs["printers"]
del kwargs["printers"]
super().__init__(*args, **kwargs)
if printers:
self.fields["printer"].choices = [("", "Select a printer...")] + [(printer, desc) for printer, (desc, alerts) in printers.items()]
def validate_size(self):
filesize = self.file.__sizeof__()
if filesize > settings.FILES_MAX_UPLOAD_SIZE:
raise forms.ValidationError(f"The file uploaded is above the maximum upload size ({settings.FILES_MAX_UPLOAD_SIZE / 1024 / 1024}MB). ")
[docs] def validate_page_range(self):
"""
Expects string that only includes commas, digits, and dashes (already validated by regex)
Validates that the page_range field's ranges and numbers correctly selects any pages from 1 to infinity.
"""
# Return if user specifies all pages.
if self.strip() == "":
return
# Gets number with suffix for clean error handling, e.g 1st, 2nd, 3rd
def get_number_ordinal(n: int) -> str:
if 11 <= (n % 100) <= 20:
suffix = "th"
else:
# Codespell thinks 'nd' is misspelled
suffix = ["th", "st", "nd", "rd", "th"][min(n % 10, 4)] # codespell:ignore
return str(n) + suffix
range_list = [r.strip() for r in self.split(",")]
prev_max = 0
page_set = set()
for i, single_range in enumerate(range_list, start=1):
# Present a clean error message if there's multiple items or or not.
error_prefix = "Input is invalid, " if len(range_list) == 1 else f"The {get_number_ordinal(i)} item is invalid, "
if "-" in single_range: # Handling a range.
input_range = single_range.split("-")
if len(input_range) != 2:
raise forms.ValidationError(error_prefix + "make sure the range is properly formatted (x-y).")
try:
range_from, range_to = map(int, input_range)
except ValueError as e:
raise forms.ValidationError(error_prefix + "you must specify two numbers around a dash for ranges.") from e
if range_from <= 0 or range_to <= 0:
raise forms.ValidationError(error_prefix + "pages are numbered starting at 1, not 0.")
if range_from > range_to:
raise forms.ValidationError(error_prefix + "the lower bound should be smaller than the upper bound.")
if range_to < prev_max:
raise forms.ValidationError(
error_prefix + "please make sure your ranges and page numbers are in ascending order and not duplicating."
) # LPR won't accept non ascending ranges.
for page in range(range_from, range_to + 1):
if page in page_set:
raise forms.ValidationError(
error_prefix + "you may not specify duplicate page numbers."
) # CUPS will ignore duplicating page numbers, user should be notified.
else:
page_set.add(page)
prev_max = range_to
else: # Handling a single number.
try:
page_num = int(single_range)
except ValueError as e:
raise forms.ValidationError(error_prefix + "you may only specify numbers.") from e
if page_num == 0:
raise forms.ValidationError(error_prefix + "pages are numbered starting at 1, not 0.")
if page_num < prev_max:
raise forms.ValidationError(
error_prefix + "please make sure your ranges and page numbers are in ascending order and not duplicating."
) # LPR won't accept non ascending ranges.
if page_num in page_set:
raise forms.ValidationError(
error_prefix + "you may not specify duplicate page numbers."
) # CUPS will ignore duplicating page numbers, user should be notified.
else:
page_set.add(page_num)
prev_max = page_num
return
file = forms.FileField(validators=[validate_size])
printer = forms.ChoiceField()
page_range = forms.RegexField(
max_length=100,
required=False,
regex=r"^[0-9,\- ]*$",
error_messages={"invalid": "This field must contain only a list of comma separated numbers and/or ranges separated by dashes."},
help_text=(
"Specify pages to print as a comma-separated list of ascending numbers and ranges (inclusive), e.g. '1-3, 5, 7'."
"<br>You may not specify a page to print multiple times."
"<br>Leave blank for all pages."
),
validators=[validate_page_range],
widget=forms.TextInput(attrs={"placeholder": "All Pages"}),
)
class Meta:
model = PrintJob
fields = ["file", "printer", "page_range", "duplex", "fit"]