Ken
Thanks for asking! The version of Django is 2:2.2.12-1ubuntu0.7 for Python 3 on
a Ubuntu Linux 20.04 LTS AWS VM.
Everything is working perfectly but I would still love to know why these error messages
still appear. At first I assumed they were happening in a try block somewhere but that
isn’t the case. These messages are actually a real problem because they flood my log files with noise which may prevent me noticing something else I need to see in there in the future.
Can you elaborate what you mean by “My guess would be that they’re caused by the template attempting to reference or render a variable not supplied in the context, which is valid within the Django template system.” ? I want to make sure I understand.
Here is my forms.py. The culprits are the few places the MULT_CH variable is used
which makes a MultipleChoiceField.
import bighelp.models
import django.forms
import django.core.validators
import django.contrib.auth.models
import captcha.fields
import datetime
import re
CUST = bighelp.models.Customer
APPT = bighelp.models.Appointment
INV = bighelp.models.Invoice
ACCT = bighelp.models.Account
PROV = bighelp.models.Provider
SERV = bighelp.models.Service
ASERV = bighelp.models.AccountService
USER = django.contrib.auth.models.User
MIN = django.core.validators.MinValueValidator
MAX = django.core.validators.MaxValueValidator
HIDD_INP = django.forms.HiddenInput
PASS_INP = django.forms.PasswordInput
CHECK_BOX = django.forms.CheckboxSelectMultiple
MULT_CH = django.forms.MultipleChoiceField
RADIO = django.forms.RadioSelect
TEXT_AREA = django.forms.Textarea
VAL_ERR = django.forms.ValidationError
AM_OR_PM = django.forms.Select(choices = [("AM", "AM"), ("PM", "PM")])
BILLING = ["monthly", "weekly", "irregular"]
BILLING = django.forms.Select(choices = [(e, e) for e in BILLING])
DAYS = [("6", "Sunday"),
("0", "Monday"),
("1", "Tuesday"),
("2", "Wednesday"),
("3", "Thursday"),
("4", "Friday"),
("5", "Saturday")]
INT_UNITS = ["day(s)", "week(s)", "month(s)", "year(s)"]
INT_UNITS = django.forms.Select(choices = [(e, e) for e in INT_UNITS])
STATES = ["AL", "AK", "AZ", "AR", "CA", "CO", "CT", "DE", "FL", "GA",
"HI", "ID", "IL", "IN", "IA", "KS", "KY", "LA", "ME", "MD",
"MA", "MI", "MN", "MS", "MO", "MT", "NE", "NV", "NH", "NJ",
"NM", "NY", "NC", "ND", "OH", "OK", "OR", "PA", "RI", "SC",
"SD", "TN", "TX", "UT", "VT", "VA", "WA", "WV", "WI", "WY"]
STATES = django.forms.Select(choices = [(e, e) for e in STATES])
RECURRING = ["none", "daily", "weekly", "weekdays", "monthly", "yearly",
"custom"]
RECURRING = django.forms.Select(choices = [(e, e) for e in RECURRING])
APPT_SET = [("1", "Modify this appointment only."),
("2", "Modify this and the following appointments in the set."),
("3", "Modify all the appointments in the set.")]
WINDOW = [("0.0", "none"),
("0.25", "15 minutes"),
("0.5", "30 minutes"),
("0.75", "45 minutes"),
("1.0", "1 hour")]
WINDOW += [(f"{n}.0", f"{n} hours") for n in range(2, 13)]
WINDOW = django.forms.Select(choices = WINDOW)
CUR_YEAR = datetime.date.today().year
INIT_FOOT = \
"""
Payment Methods: Zelle Venmo Check Cash
Thank you for your business!
""".strip()
def fix_phone(phone):
for e in "()- ":
phone = phone.replace(e, "")
if re.fullmatch(10 * "\d", phone):
phone = f"({phone[:3]}) {phone[3:6]}-{phone[6:]}"
else:
phone = None
return phone
def rec_end_checks(beg, earliest, latest, cleaned_data):
try:
end = datetime.date(cleaned_data["rec_end_y"],
cleaned_data["rec_end_m"],
cleaned_data["rec_end_d"])
except (ValueError, TypeError):
raise VAL_ERR("invalid recurring end date")
if (end <= beg) or not (earliest <= end <= latest):
raise VAL_ERR("invalid recurring end date")
def rec_cus_check(cleaned_data):
if (cleaned_data["rec_type"] == "custom") and \
(cleaned_data["rec_cus_u"] == "week(s)") and \
not cleaned_data["rec_cus_d"]:
raise VAL_ERR("missing recurring custom days")
def rec_type_check(cleaned_data):
if cleaned_data["rec_end_m"] and cleaned_data["rec_end_d"] and \
cleaned_data["rec_end_y"]:
raise VAL_ERR("missing recurring type")
class AppointmentNewForm(django.forms.Form):
def __init__(self, *args, **kwargs):
user = kwargs["user"]
del(kwargs["user"])
super().__init__(*args, **kwargs)
aservs = ASERV.objects.filter(account__user = user)
servs = [2 * (e.service.service,) for e in aservs]
servs = django.forms.Select(choices = servs)
self.fields["service"] = django.forms.CharField(widget = servs)
customer = django.forms.IntegerField()
provider = django.forms.IntegerField()
month = django.forms.IntegerField(validators = [MIN(1), MAX(12)])
day = django.forms.IntegerField()
year = django.forms.IntegerField()
hour = django.forms.IntegerField(validators = [MIN(1), MAX(12)])
minute = django.forms.IntegerField(validators = [MIN(0), MAX(59)])
am_or_pm = django.forms.CharField(widget = AM_OR_PM, initial = "PM")
window = django.forms.FloatField(widget = WINDOW, initial = 0)
price = django.forms.DecimalField()
remind = django.forms.BooleanField(required = False)
rem_text = django.forms.CharField(widget = TEXT_AREA,
required = False)
rec_type = django.forms.CharField(widget = RECURRING,
required = False)
rec_end_m = django.forms.IntegerField(validators = [MIN(1), MAX(12)],
required = False)
rec_end_d = django.forms.IntegerField(validators = [MIN(1), MAX(31)],
required = False)
rec_end_y = django.forms.IntegerField(validators = [MIN(CUR_YEAR)],
required = False)
rec_cus_n = django.forms.IntegerField(validators = [MIN(1)],
initial = 1,
required = False)
rec_cus_u = django.forms.CharField(widget = INT_UNITS,
initial = "week(s)",
required = False)
rec_cus_d = MULT_CH(choices = DAYS,
widget = CHECK_BOX,
required = False)
account = django.forms.IntegerField(widget = HIDD_INP)
original = django.forms.IntegerField(widget = HIDD_INP,
required = False)
back_to = django.forms.CharField(widget = HIDD_INP,
required = False)
appt_set = django.forms.ChoiceField(widget = RADIO,
choices = APPT_SET,
initial = "1",
required = False)
def clean(self):
for e in ["month", "day", "year"]:
if e not in self.cleaned_data:
raise VAL_ERR("invalid appointment date")
try:
beg = datetime.date(self.cleaned_data["year"],
self.cleaned_data["month"],
self.cleaned_data["day"])
except ValueError:
raise VAL_ERR("invalid appointment date")
today = datetime.date.today()
earliest = today - datetime.timedelta(days = 90)
latest = today + datetime.timedelta(days = 366)
if not (earliest <= beg <= latest):
raise VAL_ERR("invalid appointment date")
if not self.cleaned_data["original"]:
if self.cleaned_data["rec_type"] != "none":
rec_end_checks(beg,
earliest,
latest,
self.cleaned_data)
rec_cus_check(self.cleaned_data)
else:
rec_type_check(self.cleaned_data)
Here is my models.py:
import django.db.models
import django.conf
USER = django.conf.settings.AUTH_USER_MODEL
CASCADE = django.db.models.CASCADE
SET_NULL = django.db.models.SET_NULL
class Account(django.db.models.Model):
user = django.db.models.OneToOneField(USER, on_delete = CASCADE)
business = django.db.models.CharField(max_length = 10000)
phone = django.db.models.CharField(max_length = 10000)
add_top = django.db.models.CharField(max_length = 10000)
city = django.db.models.CharField(max_length = 10000)
state = django.db.models.CharField(max_length = 10000)
zip_code = django.db.models.CharField(max_length = 10000)
remind_0 = django.db.models.BooleanField()
update_0 = django.db.models.BooleanField()
send_inv_0 = django.db.models.BooleanField()
class AppointmentSet(django.db.models.Model):
account = django.db.models.ForeignKey(Account, on_delete = CASCADE)
customer = django.db.models.ForeignKey(Customer, on_delete = CASCADE)
class Appointment(django.db.models.Model):
account = django.db.models.ForeignKey(Account, on_delete = CASCADE)
customer = django.db.models.ForeignKey(Customer, on_delete = CASCADE)
provider = django.db.models.ForeignKey(Provider,
null = True,
on_delete = SET_NULL)
invoice = django.db.models.ForeignKey(Invoice,
null = True,
on_delete = SET_NULL)
appt_set = django.db.models.ForeignKey(AppointmentSet,
null = True,
on_delete = SET_NULL)
date = django.db.models.DateField()
time = django.db.models.TimeField()
window = django.db.models.FloatField()
service = django.db.models.CharField(max_length = 10000)
price = django.db.models.FloatField()
remind = django.db.models.BooleanField()
rem_text = django.db.models.CharField(max_length = 1000)
reminded = django.db.models.BooleanField()
completed = django.db.models.BooleanField()
updated = django.db.models.BooleanField()
class Service(django.db.models.Model):
service = django.db.models.CharField(max_length = 10000)
class AccountService(django.db.models.Model):
account = django.db.models.ForeignKey(Account, on_delete = CASCADE)
service = django.db.models.ForeignKey(Service, on_delete = CASCADE)
Here are my views. The problem is the one called “appts” for the appointments page…
import bighelp.forms
import bighelp.models
import bighelp.add_months
import invoices.handle_pdf
import django.forms
import django.contrib.auth
import django.contrib.auth.models
import django.contrib.auth.decorators
import django.shortcuts
import django.http
import calendar
import subprocess
import datetime
import secrets
import string
import os
CUST = bighelp.models.Customer
APPT = bighelp.models.Appointment
INV = bighelp.models.Invoice
ACCT = bighelp.models.Account
PROV = bighelp.models.Provider
SERV = bighelp.models.Service
ASERV = bighelp.models.AccountService
APPTSET = bighelp.models.AppointmentSet
USER = django.contrib.auth.models.User
AUTH = django.contrib.auth.authenticate
CLOCK = 12
SATURDAY = 5
SUNDAY = 6
SEND = "/home/cs/Ws/bighelp/send_email"
SEND_INV = "/home/cs/Ws/bighelp/invoices/send_invoice"
INV_DIR = "/home/var/www/bighelp/static/invoices"
PHONE = "({}) {}-{}"
UPDATE = \
"""
{}
{} Services
Date: {}
Time: {}
{}
""".strip()
UPDATE_ = "Appointment successfully completed."
USERNAME = \
"""
Your BigHelp username is: {}
You can use it to sign in at https://bighelp.business/sign_in .
"""
PASSWORD = \
"""
Your new BigHelp password is: {}
You can use it to sign in at https://bighelp.business/sign_in .
It is recommended that you go to the settings page and create a new secure
password after you have signed in.
"""
@django.contrib.auth.decorators.login_required
def appts(request):
acct = ACCT.objects.get(user = request.user)
custs = sorted(list(CUST.objects.filter(account = acct)),
key = lambda e : e.last)
custs = [(f"{e.first} {e.last}", e) for e in custs]
provs = [e for e in PROV.objects.filter(account = acct)]
provs = sorted(provs, key = lambda e : e.user.last_name)
provs = [(f"{e.user.first_name} {e.user.last_name}", e) for e in provs]
if request.method == "POST":
n_form = bighelp.forms.AppointmentNewForm(request.POST,
user = request.user)
c_form = bighelp.forms.AppointmentCompForm(request.POST,
user = request.user)
if ("n_form" in request.POST) and n_form.is_valid():
appts_proc_an_form(n_form, request.user)
reply = django.shortcuts.redirect("/appts")
elif ("c_form" in request.POST) and c_form.is_valid():
appts_proc_ac_form(c_form, request.user)
reply = django.shortcuts.redirect("/appts")
else:
appts_ = appts_get_list(c_form, request.user)
if "c_form" in request.POST:
n_form = appts_make_an_form(acct)
reply = django.shortcuts.render(request,
"appts.html",
{"n_form" : n_form,
"c_form" : c_form,
"custs" : custs,
"provs" : provs,
"appts" : appts_})
else:
initial = {"year" : datetime.date.today().year}
n_form = appts_make_an_form(acct, initial)
initial = {"update" : acct.update_0}
c_form = bighelp.forms.AppointmentCompForm(initial = initial,
user = \
request.user)
appts_ = appts_get_list(c_form, request.user)
reply = django.shortcuts.render(request,
"appts.html",
{"n_form" : n_form,
"c_form" : c_form,
"custs" : custs,
"provs" : provs,
"appts" : appts_})
return reply
@django.contrib.auth.decorators.login_required
def appts_caini(request):
appts_ = appts_caini_get_list(request.user)
return django.shortcuts.render(request,
"appts_caini.html",
{"appts" : appts_})
@django.contrib.auth.decorators.login_required
def appts_edit(request):
acct = ACCT.objects.get(user = request.user)
result = APPT.objects.filter(id = int(request.GET.get("appt")),
account = acct)
if not result:
raise django.http.Http404
return appts_edit_(request, acct, result[0])
def appts_edit_(request, acct, appt):
custs = CUST.objects.filter(account = acct)
custs = sorted(list(custs), key = lambda e : e.last)
custs = [(f"{e.first} {e.last}", e)
for e in custs if e != appt.customer]
custs = [(f"{appt.customer.first} {appt.customer.last}",
appt.customer)] + custs
provs = [e for e in PROV.objects.filter(account = acct)]
provs = sorted(provs, key = lambda e : e.user.last_name)
prov = appt.provider
provs = [(f"{e.user.first_name} {e.user.last_name}", e)
for e in provs if e != prov]
provs = [(f"{prov.user.first_name} {prov.user.last_name}", prov)] + \
provs
hour = appt.time.hour % CLOCK
hour = hour if hour else 12
initial = {"customer" : appt.customer,
"provider" : appt.provider,
"year" : appt.date.year,
"month" : f"{appt.date.month:02}",
"day" : f"{appt.date.day:02}",
"hour" : f"{hour:02}",
"minute" : f"{appt.time.minute:02}",
"am_or_pm" : appt.time.strftime("%p"),
"window" : appt.window,
"service" : appt.service,
"price" : f"{appt.price:.2f}",
"remind" : appt.remind,
"rem_text" : appt.rem_text,
"account" : acct.id,
"original" : appt.id,
"back_to" : request.GET.get("back_to")}
if request.method == "POST":
form = bighelp.forms.AppointmentNewForm(request.POST,
user = request.user)
if form.is_valid():
cust = form.cleaned_data["customer"]
cust = CUST.objects.get(id = cust,
account = acct)
prov = form.cleaned_data["provider"]
prov = PROV.objects.filter(account = acct,
id = prov)
prov = prov[0] if prov else None
year = int(form.cleaned_data["year"])
month = int(form.cleaned_data["month"])
day = int(form.cleaned_data["day"])
date = datetime.date(year, month, day)
hour = int(form.cleaned_data["hour"])
min_ = int(form.cleaned_data["minute"])
if form.cleaned_data["am_or_pm"] == "PM":
hour = 12 + (hour % CLOCK)
time = datetime.time(hour = hour,
minute = min_)
rem_text = form.cleaned_data["rem_text"].strip()
appts = [appt]
appt_set = appt.appt_set
if form.cleaned_data["appt_set"] == "2":
appts = APPT.objects.filter(appt_set = \
appt_set,
date__gte = \
appt.date)
elif form.cleaned_data["appt_set"] == "3":
appts = APPT.objects.filter(appt_set = appt_set)
n_days = date - appt.date
for e in appts:
e.customer = cust
e.provider = prov
e.date += n_days
e.time = time
e.window = form.cleaned_data["window"]
e.service = form.cleaned_data["service"]
e.price = form.cleaned_data["price"]
e.remind = form.cleaned_data["remind"]
e.rem_text = rem_text
e.save()
back_to = form.cleaned_data["back_to"]
reply = django.shortcuts.redirect(back_to)
else:
reply = django.shortcuts.render(request,
"appts_edit.html",
{"form" : form,
"custs" : custs,
"provs" : provs,
"appt" : appt})
else:
form = bighelp.forms.AppointmentNewForm(initial = initial,
user = request.user)
reply = django.shortcuts.render(request,
"appts_edit.html",
{"form" : form,
"custs" : custs,
"provs" : provs,
"appt" : appt})
return reply
@django.contrib.auth.decorators.login_required
def appts_delete(request):
acct = ACCT.objects.get(user = request.user)
appt = APPT.objects.get(id = int(request.GET.get("appt")),
account = acct)
appt.delete()
return django.shortcuts.redirect(request.GET.get("back_to"))
def add_units(date, n_units, units):
if units == "day(s)":
date_ = date + n_units * datetime.timedelta(days = 1)
elif units == "week(s)":
date_ = date + n_units * datetime.timedelta(days = 7)
elif units == "month(s)":
date_ = bighelp.add_months.add_months(date, n_units)
elif units == "year(s)":
year_ = date.year + n_units
if (date.month == 2) and (date.day == 29) and \
(calendar.monthrange(year_, 2)[1] == 28):
date_ = date.replace(year = year_, day = date.day - 1)
else:
date_ = date.replace(year = year_)
return date_
def add_dates_(dates, end, rec_cus_n, units):
n_units = rec_cus_n
date_ = add_units(dates[0], n_units, units)
while date_ <= end:
dates.append(date_)
n_units += rec_cus_n
date_ = add_units(dates[0], n_units, units)
def add_dates(dates, rec_type, data):
end = datetime.date(data["rec_end_y"],
data["rec_end_m"],
data["rec_end_d"])
if rec_type in ["daily", "weekly", "monthly", "yearly"]:
units = rec_type.replace("i", "y").replace("ly", "(s)")
add_dates_(dates, end, 1, units)
elif rec_type == "weekdays":
add_dates_(dates, end, 1, "day(s)")
for e in dates[:]:
if e.weekday() in [SATURDAY, SUNDAY]:
dates.remove(e)
elif rec_type == "custom":
if data["rec_cus_u"] != "week(s)":
add_dates_(dates,
end,
data["rec_cus_n"],
data["rec_cus_u"])
else:
add_dates_(dates, end, 1, "day(s)")
week = 0
for e in dates[:]:
if (str(e.weekday()) not in data["rec_cus_d"]) \
or (week % data["rec_cus_n"]):
dates.remove(e)
if e.weekday() == SATURDAY:
week += 1
def appts_proc_an_form(form, user):
acct = ACCT.objects.get(user = user)
cust = form.cleaned_data["customer"]
cust = CUST.objects.get(id = cust, account = acct)
prov = form.cleaned_data["provider"]
prov = PROV.objects.filter(account = acct, id = prov)
prov = prov[0] if prov else acct.user
year = int(form.cleaned_data["year"])
month = int(form.cleaned_data["month"])
day = int(form.cleaned_data["day"])
date = datetime.date(year, month, day)
hour = int(form.cleaned_data["hour"])
min_ = int(form.cleaned_data["minute"])
if form.cleaned_data["am_or_pm"] == "PM":
hour = 12 + (hour % CLOCK)
time = datetime.time(hour = hour, minute = min_)
dates = [date]
rec_type = form.cleaned_data["rec_type"]
appt_set = None
if rec_type != "none":
add_dates(dates, rec_type, form.cleaned_data)
appt_set = APPTSET.objects.create(account = acct,
customer = cust)
for e in dates:
APPT.objects.create(account = acct,
customer = cust,
provider = prov,
appt_set = appt_set,
date = e,
time = time,
window = form.cleaned_data["window"],
service = form.cleaned_data["service"],
price = form.cleaned_data["price"],
remind = form.cleaned_data["remind"],
rem_text = form.cleaned_data["rem_text"],
reminded = False,
completed = False,
updated = False)
def appts_proc_ac_form(form, user):
update = form.cleaned_data["update"]
upd_text = form.cleaned_data["upd_text"].strip()
for e in form.cleaned_data:
if (e not in ["update", "upd_text"]) and form.cleaned_data[e]:
appts_proc_ac_form_(e, update, upd_text, user)
def end(beg, window):
hour = beg.hour
minute = beg.minute
datetime_ = datetime.datetime(2000, 1, 1, hour, minute, 0, 0)
datetime_ += datetime.timedelta(hours = window)
return datetime_.time()
def appts_proc_ac_form_(appt, update, upd_text, user):
acct = ACCT.objects.get(user = user)
appt = APPT.objects.get(id = appt, account = acct)
appt.completed = True
if update:
appt.updated = True
time = appt.time.strftime("%I:%M %p")
if appt.window:
end_ = end(appt.time, appt.window)
time += " - " + end_.strftime("%I:%M %p")
update = UPDATE.format(appt.account.business,
appt.service.capitalize(),
appt.date.strftime("%a %b %d, %Y"),
time,
UPDATE_)
if upd_text:
update = upd_text
subprocess.Popen(["/home/send_text/send_text",
appt.customer.phone,
update,
appt.account.user.email])
if appt.customer.alt_phone:
subprocess.Popen(["/home/send_text/send_text",
appt.customer.alt_phone,
update,
appt.account.user.email])
appt.save()
def appts_make_an_form(account, initial = {}):
initial_ = {"account" : account.id, "remind" : account.remind_0}
initial_.update(initial)
return bighelp.forms.AppointmentNewForm(initial = initial_,
user = account.user)
def appts_get_list(form, user):
acct = ACCT.objects.get(user = user)
custs = sorted(list(CUST.objects.filter(account = acct)),
key = lambda e : e.last)
appts = [(f"{e.first} {e.last}",
sorted([(a, form[str(a.id)]) for a in APPT.objects.filter(
customer = e,
completed = False,
account = acct)],
key = lambda e : (e[0].date, e[0].time)))
for e in custs]
appts = [e for e in appts if e[1]]
return appts
def appts_caini_get_list(user):
acct = ACCT.objects.get(user = user)
custs = sorted(list(CUST.objects.filter(account = acct)),
key = lambda e : e.last)
appts = [(f"{e.first} {e.last}",
sorted([a for a in APPT.objects.filter(customer = e,
completed = True,
invoice = None,
account = acct)],
key = lambda e : (e.date, e.time)))
for e in custs]
appts = [e for e in appts if e[1]]
return appts