Source code for intranet.apps.schedule.views

import calendar
import logging
from datetime import datetime, timedelta

from dateutil.relativedelta import relativedelta
from django.conf import settings
from django.contrib import messages
from django.contrib.auth.decorators import login_required, user_passes_test
from django.core.cache import cache
from django.http import HttpResponse
from django.shortcuts import get_object_or_404, redirect, render
from django.urls import reverse
from django.utils import timezone
from django.views.decorators.clickjacking import xframe_options_exempt

from ..auth.decorators import deny_restricted
from .forms import DayForm, DayTypeForm
from .models import Block, Day, DayType, Time

logger = logging.getLogger(__name__)
schedule_admin_required = user_passes_test(lambda u: not u.is_anonymous and u.has_admin_permission("schedule"))


[docs]def date_format(date): try: d = date.strftime("%Y-%m-%d") except ValueError: return None return d
[docs]def decode_date(date): try: d = datetime.strptime(date, "%Y-%m-%d") except ValueError: return None return d
[docs]def is_weekday(date): return date.isoweekday() in range(1, 6)
[docs]def schedule_context(request=None, date=None, use_cache=True, show_tomorrow=True): monday = 1 friday = 5 if request and "date" in request.GET: date = decode_date(request.GET["date"]) if date is None: date = timezone.localtime() if is_weekday(date) and date.hour > 17 and show_tomorrow: delta = 3 if date.isoweekday() == friday else 1 date += timedelta(days=delta) else: while not is_weekday(date): date += timedelta(days=1) date_fmt = date_format(date) key = f"bell_schedule:{date_fmt}" cached = cache.get(key) if cached and use_cache: return cached else: try: dayobj = Day.objects.select_related("day_type").get(date=date) comment = dayobj.comment except Day.DoesNotExist: dayobj = None comment = None if dayobj is not None: blocks = dayobj.day_type.blocks.select_related("start", "end").order_by("start__hour", "start__minute") else: blocks = [] try: delta = 3 if date.isoweekday() == friday else 1 date_tomorrow = date_format(date + timedelta(days=delta)) date_today = date_format(date) delta = -3 if date.isoweekday() == monday else -1 date_yesterday = date_format(date + timedelta(days=delta)) except OverflowError: date_tomorrow = None date_yesterday = None date_today = None schedule_tomorrow = None if request and request.user.is_authenticated and request.user.is_eighth_admin: try: schedule_tomorrow = Day.objects.select_related("day_type").get(date=date_tomorrow) if not schedule_tomorrow.day_type: schedule_tomorrow = False except Day.DoesNotExist: schedule_tomorrow = False else: schedule_tomorrow = None data = { "sched_ctx": { "dayobj": dayobj, "blocks": blocks, "date": date, "is_weekday": is_weekday(date), "date_tomorrow": date_tomorrow, "date_today": date_today, "date_yesterday": date_yesterday, "schedule_tomorrow": schedule_tomorrow, "comment": comment, } } cache.set(key, data, timeout=settings.CACHE_AGE["bell_schedule"]) return data
# does NOT require login
[docs]def schedule_view(request): data = schedule_context(request) return render(request, "schedule/view.html", data)
# does NOT require login
[docs]def week_data(request, date=None): if request: given_date = schedule_context(request)["sched_ctx"]["date"] if given_date.isoweekday() not in range(1, 6): start_date = given_date + timedelta(days=-given_date.weekday(), weeks=1) else: start_date = given_date - timedelta(days=given_date.weekday()) else: given_date = date if given_date.isoweekday() not in range(1, 6): start_date = given_date + timedelta(days=-given_date.weekday(), weeks=1) else: start_date = given_date - timedelta(days=given_date.weekday()) days = [] for i in range(5): new_date = start_date + timedelta(days=i) days.append(schedule_context(date=new_date)) next_week = days[-1]["sched_ctx"]["date_tomorrow"] last_week = date_format(days[0]["sched_ctx"]["date"] - timedelta(days=7)) today = date_format(timezone.localtime()) data = {"days": days, "next_week": next_week, "last_week": last_week, "today": today} return data
[docs]def month_data(request): if request and "date" in request.GET: first_date = decode_date(request.GET["date"]) + relativedelta(day=1) else: first_date = timezone.now() + relativedelta(day=1) week1 = week_data(None, first_date) week2 = week_data(None, decode_date(week1["next_week"])) week3 = week_data(None, decode_date(week2["next_week"])) week4 = week_data(None, decode_date(week3["next_week"])) week5 = week_data(None, decode_date(week4["next_week"])) month = first_date.strftime("%B") one_month = relativedelta(months=1) next_month = date_format(first_date + one_month) last_month = date_format(first_date - one_month) data = {"weeks": [week1, week2, week3, week4, week5], "next_month": next_month, "last_month": last_month, "current_month": month} return data
[docs]@login_required def calendar_view(request): data = {} data["week_data"] = week_data(request) data["month_data"] = month_data(request) if "view" in request.GET and request.GET["view"] == "month": data["view"] = "month" else: data["view"] = "week" return render(request, "schedule/calendar.html", data)
# does NOT require login
[docs]@xframe_options_exempt def schedule_embed(request): data = schedule_context(request) return render(request, "schedule/embed.html", data)
# DOES require login
[docs]@login_required def schedule_widget_view(request): data = schedule_context(request) return render(request, "schedule/widget.html", data)
[docs]def get_day_data(firstday, daynum): if daynum == 0: return {"empty": True} date = firstday + timedelta(days=daynum - 1) data = {"day": daynum, "formatted_date": date_format(date), "date": date} try: dayobj = Day.objects.get(date=date) data["schedule"] = dayobj.day_type data["dayobj"] = dayobj except Day.DoesNotExist: data["schedule"] = None data["dayobj"] = None return data
@schedule_admin_required @deny_restricted def do_default_fill(request): """Change all Mondays to 'Anchor Day' Change all Tuesday/Thursdays to 'Blue Day' Change all Wednesday/Fridays to 'Red Day'.""" monday = 0 tuesday = 1 wednesday = 2 thursday = 3 friday = 4 try: anchor_day = DayType.objects.get(name="Anchor Day") blue_day = DayType.objects.get(name="Blue Day") red_day = DayType.objects.get(name="Red Day") except DayType.DoesNotExist: return render( request, "schedule/fill.html", {"msgs": ["Failed to insert any schedules.", "Make sure you have DayTypes defined for Anchor Days, Blue Days, and Red Days."]}, ) daymap = {monday: anchor_day, tuesday: blue_day, wednesday: red_day, thursday: blue_day, friday: red_day} msgs = [] month = request.POST.get("month") firstday = datetime.strptime(month, "%Y-%m") yr, mn = month.split("-") cal = calendar.monthcalendar(int(yr), int(mn)) for w in cal: for d in w: day = get_day_data(firstday, d) if "empty" in day: continue if "schedule" not in day or day["schedule"] is None: day_of_week = day["date"].weekday() if day_of_week in daymap: type_obj = daymap[day_of_week] day_obj = Day.objects.create(date=day["formatted_date"], day_type=type_obj) msg = "{} is now a {}".format(day["formatted_date"], day_obj.day_type) msgs.append(msg) return render(request, "schedule/fill.html", {"msgs": msgs})
[docs]def delete_cache(): cache.delete_pattern("bell_schedule:*") logger.debug("Deleted bell schedule cache.")
@schedule_admin_required @deny_restricted def admin_home_view(request): if "default_fill" in request.POST: return do_default_fill(request) if "delete_cache" in request.POST: delete_cache() messages.success(request, "Deleted schedule cache manually") return redirect("schedule_admin") if "month" in request.GET: month = request.GET.get("month") elif "schedule_month" in request.session: month = request.session["schedule_month"] else: month = timezone.localtime().strftime("%Y-%m") request.session["schedule_month"] = month firstday = datetime.strptime(month, "%Y-%m") month_name = firstday.strftime("%B") year_name = firstday.strftime("%Y") yr, mn = month.split("-") cal = calendar.monthcalendar(int(yr), int(mn)) sch = [] for w in cal: week = [] for d in w: week.append(get_day_data(firstday, d)) sch.append(week) add_form = DayForm() this_month = firstday.strftime("%Y-%m") next_month = (firstday + timedelta(days=31)).strftime("%Y-%m") last_month = (firstday + timedelta(days=-31)).strftime("%Y-%m") daytypes = DayType.objects.all() data = { "month_name": month_name, "year_name": year_name, "sch": sch, "add_form": add_form, "this_month": this_month, "next_month": next_month, "last_month": last_month, "daytypes": daytypes, } return render(request, "schedule/admin_home.html", data) @schedule_admin_required @deny_restricted def admin_add_view(request): if request.method == "POST": delete_cache() date = request.POST.get("date") day = Day.objects.filter(date=date) if len(day) <= 1: day.delete() form = DayForm(request.POST) if form.is_valid(): form.save() messages.success(request, "{} is now a {}".format(date, form.cleaned_data["day_type"])) return redirect("schedule_admin") else: messages.success(request, f"{date} has no schedule assigned") return redirect("schedule_admin") else: form = DayForm() context = {"form": form} return render(request, "schedule/admin_add.html", context) @schedule_admin_required @deny_restricted def admin_comment_view(request): date = request.GET.get("date") if request.method == "POST" and "comment" in request.POST: delete_cache() date = request.POST.get("date") comment = request.POST.get("comment") day, _ = Day.objects.get_or_create(date=date) day.comment = comment day.save() return redirect("schedule_admin") else: try: day = Day.objects.get(date=date) comment = day.comment except Day.DoesNotExist: day = None comment = None context = {"day": day, "date": date, "comment": comment} return render(request, "schedule/admin_comment.html", context) @schedule_admin_required @deny_restricted def admin_daytype_view(request, daytype_id=None): if request.method == "POST": delete_cache() if "id" in request.POST: daytype_id = request.POST["id"] if "make_copy" in request.POST: daytype = get_object_or_404(DayType, id=daytype_id) blocks = daytype.blocks.all() daytype.pk = None daytype.name += " (Copy)" daytype.save() for blk in blocks: daytype.blocks.add(blk) daytype.save() if "return_url" in request.POST: return HttpResponse(reverse("schedule_daytype", args=[daytype.id])) return redirect("schedule_daytype", daytype.id) if "delete" in request.POST: daytype = get_object_or_404(DayType, id=daytype_id) name = str(daytype) daytype.delete() messages.success(request, f"Deleted {name}") return redirect("schedule_admin") if daytype_id: daytype = get_object_or_404(DayType, id=daytype_id) form = DayTypeForm(request.POST, instance=daytype) else: daytype = None form = DayTypeForm(request.POST) if form.is_valid(): model = form.save() """Add blocks""" blocks = zip( request.POST.getlist("block_order"), request.POST.getlist("block_name"), [[int(j) if j else 0 for j in i.split(":")] if ":" in i else [9, 0] for i in request.POST.getlist("block_start")], [[int(j) if j else 0 for j in i.split(":")] if ":" in i else [10, 0] for i in request.POST.getlist("block_end")], ) model.blocks.all().delete() for blk in blocks: start, _ = Time.objects.get_or_create(hour=blk[2][0], minute=blk[2][1]) end, _ = Time.objects.get_or_create(hour=blk[3][0], minute=blk[3][1]) bobj, _ = Block.objects.get_or_create(order=blk[0], name=blk[1], start=start, end=end) model.blocks.add(bobj) model.save() if "assign_date" in request.POST: assign_date = request.POST.get("assign_date") try: dayobj = Day.objects.get(date=assign_date) except Day.DoesNotExist: dayobj = Day.objects.create(date=assign_date, day_type=model) else: dayobj.day_type = model dayobj.save() messages.success(request, f"{dayobj.date} is now a {dayobj.day_type}") messages.success(request, "Successfully {} Day Type.".format("modified" if daytype_id else "added")) return redirect("schedule_daytype", model.id) else: messages.error(request, "Error adding Day Type") elif daytype_id: daytype = get_object_or_404(DayType, id=daytype_id) form = DayTypeForm(instance=daytype) else: daytype = None form = DayTypeForm() context = {"form": form, "action": "add", "daytype": daytype} if "assign_date" in request.GET: context["assign_date"] = request.GET.get("assign_date") return render(request, "schedule/admin_daytype.html", context)