Jul 28 - Update schedule page's referred time
This commit is contained in:
@@ -9,7 +9,9 @@ from logging.handlers import RotatingFileHandler
|
||||
|
||||
import bleach
|
||||
from bleach.css_sanitizer import CSSSanitizer
|
||||
from flask import Flask, flash, redirect, render_template, request, url_for
|
||||
from flask import (
|
||||
Flask, flash, jsonify, redirect, render_template, request, url_for,
|
||||
)
|
||||
from flask_sqlalchemy import SQLAlchemy
|
||||
from flask_wtf import CSRFProtect
|
||||
from markupsafe import Markup
|
||||
@@ -200,6 +202,40 @@ def demo_slots(config):
|
||||
return slots
|
||||
|
||||
|
||||
def taken_slots(day):
|
||||
"""Times already booked on `day`. Cancelled requests free their slot."""
|
||||
rows = (
|
||||
DemoRequest.query
|
||||
.with_entities(DemoRequest.preferred_time)
|
||||
.filter(DemoRequest.preferred_date == day,
|
||||
DemoRequest.status != "cancelled")
|
||||
.all()
|
||||
)
|
||||
return {row[0] for row in rows}
|
||||
|
||||
|
||||
def available_slots(config, day):
|
||||
"""Bookable times left on `day` — the full grid minus what's taken."""
|
||||
if day is None:
|
||||
return demo_slots(config)
|
||||
booked = taken_slots(day)
|
||||
return [slot for slot in demo_slots(config) if slot not in booked]
|
||||
|
||||
|
||||
def demo_day_status(config, day):
|
||||
"""Why a date can't be booked, or None if it's open. Shared by the form and
|
||||
the /demo/slots endpoint so both give the same answer."""
|
||||
today = date.today()
|
||||
if day < today:
|
||||
return "That date has already passed."
|
||||
if day > today + timedelta(days=config["DEMO_MAX_DAYS_AHEAD"]):
|
||||
return (f"Please pick a date within the next "
|
||||
f"{config['DEMO_MAX_DAYS_AHEAD']} days.")
|
||||
if day.weekday() >= 5:
|
||||
return "Demonstrations run Monday through Friday."
|
||||
return None
|
||||
|
||||
|
||||
def _rate_limited(ip, limit):
|
||||
"""True if `ip` has already submitted `limit` requests in the last hour."""
|
||||
if limit <= 0:
|
||||
@@ -350,15 +386,9 @@ def create_app():
|
||||
wanted = None
|
||||
errors.append("Please pick a date for the demonstration.")
|
||||
if wanted:
|
||||
if wanted < today:
|
||||
errors.append("Please pick a date that hasn't passed yet.")
|
||||
elif wanted > max_date:
|
||||
errors.append(
|
||||
f"Please pick a date within the next "
|
||||
f"{cfg['DEMO_MAX_DAYS_AHEAD']} days."
|
||||
)
|
||||
elif wanted.weekday() >= 5:
|
||||
errors.append("Demonstrations run Monday through Friday.")
|
||||
closed = demo_day_status(cfg, wanted)
|
||||
if closed:
|
||||
errors.append(closed)
|
||||
|
||||
if form["preferred_time"] not in slots:
|
||||
errors.append("Please pick an available time.")
|
||||
@@ -395,9 +425,19 @@ def create_app():
|
||||
_notify_demo(app, req)
|
||||
return redirect(url_for("demo_thanks"))
|
||||
|
||||
# Show only what's still free on the date in hand, so a re-render after
|
||||
# an error can't offer a slot that was taken in the meantime. With no
|
||||
# date chosen yet the full grid is listed and the JS narrows it as soon
|
||||
# as one is picked (and without JS the checks above are the backstop).
|
||||
chosen = None
|
||||
if form.get("preferred_date"):
|
||||
try:
|
||||
chosen = datetime.strptime(form["preferred_date"], "%Y-%m-%d").date()
|
||||
except ValueError:
|
||||
chosen = None
|
||||
return render_template(
|
||||
"demo.html",
|
||||
slots=slots,
|
||||
slots=available_slots(cfg, chosen) if chosen else slots,
|
||||
form=form,
|
||||
min_date=today.isoformat(),
|
||||
max_date=max_date.isoformat(),
|
||||
@@ -405,6 +445,23 @@ def create_app():
|
||||
demo_url=cfg["DEMO_CONTACT_URL"],
|
||||
)
|
||||
|
||||
@app.route("/demo/slots")
|
||||
def demo_slots_api():
|
||||
"""Times still bookable on ?date=YYYY-MM-DD, for the form's time picker.
|
||||
Public and read-only: it exposes which slots are free, which is exactly
|
||||
what the form shows anyway, and nothing about who booked them."""
|
||||
cfg = app.config
|
||||
raw = request.args.get("date", "")
|
||||
try:
|
||||
day = datetime.strptime(raw, "%Y-%m-%d").date()
|
||||
except ValueError:
|
||||
return jsonify(ok=False, error="Invalid date."), 400
|
||||
|
||||
closed = demo_day_status(cfg, day)
|
||||
if closed:
|
||||
return jsonify(ok=True, date=raw, open=False, reason=closed, slots=[])
|
||||
return jsonify(ok=True, date=raw, open=True, slots=available_slots(cfg, day))
|
||||
|
||||
@app.route("/demo/thanks")
|
||||
def demo_thanks():
|
||||
return render_template("demo_thanks.html", demo_url=app.config["DEMO_CONTACT_URL"])
|
||||
|
||||
Reference in New Issue
Block a user