membershipworks: Add upcoming events generator
This commit is contained in:
parent
7afcc1f9e0
commit
97bcc1df6d
@ -37,6 +37,7 @@ INSTALLED_APPS = [
|
|||||||
"rest_framework",
|
"rest_framework",
|
||||||
"rest_framework.authtoken",
|
"rest_framework.authtoken",
|
||||||
"django_q",
|
"django_q",
|
||||||
|
"django_bleach",
|
||||||
"tasks.apps.TasksConfig",
|
"tasks.apps.TasksConfig",
|
||||||
"rentals.apps.RentalsConfig",
|
"rentals.apps.RentalsConfig",
|
||||||
"membershipworks.apps.MembershipworksConfig",
|
"membershipworks.apps.MembershipworksConfig",
|
||||||
|
@ -0,0 +1,121 @@
|
|||||||
|
{% extends "base.dj.html" %}
|
||||||
|
|
||||||
|
{% load bleach_tags %}
|
||||||
|
|
||||||
|
{% block title %}Upcoming Events{% endblock %}
|
||||||
|
{% block content %}
|
||||||
|
<div id="preview">
|
||||||
|
<p>
|
||||||
|
<img class="aligncenter size-medium wp-image-2319"
|
||||||
|
src="https://claremontmakerspace.org/wp-content/uploads/2019/03/CMS-Logo-b-y-g-300x168.png"
|
||||||
|
alt=""
|
||||||
|
width="300"
|
||||||
|
height="168" />
|
||||||
|
</p>
|
||||||
|
<p>Greetings Upper Valley Makers:</p>
|
||||||
|
<p>We have an exciting list of upcoming classes at the Claremont MakerSpace that we think might interest you.</p>
|
||||||
|
<div>
|
||||||
|
<strong>For most classes and events, CMS MEMBERSHIP IS NOT REQUIRED.</strong> That said, members receive a discount
|
||||||
|
on registration and there are some classes/events that are for members only (this will be clearly noted in the event
|
||||||
|
description).
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<strong>Class policies</strong> (liability waiver, withdrawal, cancellation, etc.) can be found <a href="https://claremontmakerspace.org/class-policies/"
|
||||||
|
data-wpel-link="internal">here</a>.
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<strong>Instructors:</strong> Interested in teaching a class at CMS? Please fill out our <a href="https://claremontmakerspace.org/cms-class-proposal-form/"
|
||||||
|
data-wpel-link="internal">Class Proposal Form</a>.
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<strong>Tours:</strong> Want to see what the Claremont MakerSpace is all about? Tours are by appointment only.
|
||||||
|
</div>
|
||||||
|
<a href="https://tickets.claremontmakerspace.org/open.php"
|
||||||
|
target="_blank"
|
||||||
|
rel="noreferrer noopener external"
|
||||||
|
data-wpel-link="external">Contact Us</a> to schedule your tour where you can learn about all the awesome tools that
|
||||||
|
the CMS offers access to, as well as how membership, classes, and studio spaces work.
|
||||||
|
<hr />
|
||||||
|
{% for section in event_sections %}
|
||||||
|
{% if section.events %}
|
||||||
|
<h1>{{ section.title }}</h1>
|
||||||
|
<h4>
|
||||||
|
<i>{{ section.blurb }}</i>
|
||||||
|
</h4>
|
||||||
|
{% for event in section.events %}
|
||||||
|
{% with url="https://claremontmakerspace.org/events/#!event/register/"|add:event.url %}
|
||||||
|
{% spaceless %}
|
||||||
|
<h2 style="text-align: center;">
|
||||||
|
<a href="{{ url }}">
|
||||||
|
{# djlint:off H006,H013 #}
|
||||||
|
{% if "lgo" in event %}<img class="alignleft" width="400" src="{{ event.lgo.l }}">{% endif %}
|
||||||
|
{# djlint:on #}
|
||||||
|
<span>{{ event.ttl|bleach }}</span>
|
||||||
|
</a>
|
||||||
|
</h2>
|
||||||
|
{% endspaceless %}
|
||||||
|
{% spaceless %}
|
||||||
|
<div>
|
||||||
|
{# wordpress is very annoying with spacing here #}
|
||||||
|
{# djlint:off #}
|
||||||
|
<i>
|
||||||
|
{# TODO: different dates probably implies multiple instances. Should read RRULE or similar from the event notes #}
|
||||||
|
{{ event.sdp_dt|date }} {{ event.sdp_dt|time }} — {% if event.sdp_dt.date != event.edp_dt.date %}{{ event.edp_dt|date }}{% endif %} {{ event.edp_dt|time }}
|
||||||
|
</i>
|
||||||
|
{# djlint:on #}
|
||||||
|
</div>
|
||||||
|
{% endspaceless %}
|
||||||
|
{% if not section.truncate %}
|
||||||
|
<div>{{ event.dtl|bleach:"a,abbr,acronym,b,blockquote,code,em,i,li,ol,strong,ul,p,span,br,div" }}</div>
|
||||||
|
<div>
|
||||||
|
<a href="{{ url }}">Register for this class now!</a>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
<hr />
|
||||||
|
{% endwith %}
|
||||||
|
{% endfor %}
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
<div style="clear: both;">
|
||||||
|
<div>Happy Makin’!</div>
|
||||||
|
<div>
|
||||||
|
We are grateful for all of the public support that our 501(c)(3), non-profit organization receives. If you’d
|
||||||
|
like to make a donation,please visit the <a href="https://claremontmakerspace.org/support/"><strong>Support Us
|
||||||
|
page</strong></a> of our website.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="position-fixed end-0 bottom-0">
|
||||||
|
<button id="copy-button"
|
||||||
|
type="button"
|
||||||
|
class="btn btn-primary m-3"
|
||||||
|
data-bs-toggle="popover"
|
||||||
|
data-bs-content="Copied!">
|
||||||
|
<i class="bi bi-clipboard-fill"></i> Copy to clipboard
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
|
{% block script %}
|
||||||
|
<script>
|
||||||
|
// TODO: This should use the newer Clipboard API, but Firefox doesn't support it yet
|
||||||
|
// https://developer.mozilla.org/en-US/docs/Web/API/ClipboardItem
|
||||||
|
function copyToClipboard(event) {
|
||||||
|
const cb = e => {
|
||||||
|
e.clipboardData.setData("text/html", document.getElementById("preview").innerHTML);
|
||||||
|
e.clipboardData.setData("text/plain", document.getElementById("preview").innerHTML);
|
||||||
|
e.preventDefault();
|
||||||
|
};
|
||||||
|
document.addEventListener("copy", cb);
|
||||||
|
document.execCommand("copy");
|
||||||
|
document.removeEventListener("copy", cb);
|
||||||
|
|
||||||
|
bootstrap.Popover.getInstance(event.target).show();
|
||||||
|
setTimeout(() => bootstrap.Popover.getInstance(event.target).hide(), 1000);
|
||||||
|
}
|
||||||
|
const button = document.getElementById("copy-button");
|
||||||
|
const popover = new bootstrap.Popover(button, {
|
||||||
|
trigger: "manual"
|
||||||
|
})
|
||||||
|
button.addEventListener("click", copyToClipboard);
|
||||||
|
</script>
|
||||||
|
{% endblock %}
|
@ -1,6 +1,6 @@
|
|||||||
from django.urls import path
|
from django.urls import path
|
||||||
|
|
||||||
from .views import MemberAutocomplete
|
from .views import MemberAutocomplete, upcoming_events
|
||||||
|
|
||||||
app_name = "membershipworks"
|
app_name = "membershipworks"
|
||||||
|
|
||||||
@ -10,4 +10,9 @@ urlpatterns = [
|
|||||||
MemberAutocomplete.as_view(),
|
MemberAutocomplete.as_view(),
|
||||||
name="member-autocomplete",
|
name="member-autocomplete",
|
||||||
),
|
),
|
||||||
|
path(
|
||||||
|
"upcoming-events/",
|
||||||
|
upcoming_events,
|
||||||
|
name="upcoming-events",
|
||||||
|
),
|
||||||
]
|
]
|
||||||
|
@ -1,6 +1,15 @@
|
|||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
|
from django.contrib import messages
|
||||||
|
from django.contrib.auth.decorators import login_required
|
||||||
|
from django.http import HttpResponse
|
||||||
|
from django.shortcuts import render
|
||||||
|
|
||||||
from dal import autocomplete
|
from dal import autocomplete
|
||||||
|
|
||||||
from .models import Member
|
from .models import Member
|
||||||
|
from membershipworks import MembershipWorks
|
||||||
|
|
||||||
|
|
||||||
class MemberAutocomplete(autocomplete.Select2QuerySetView):
|
class MemberAutocomplete(autocomplete.Select2QuerySetView):
|
||||||
@ -12,3 +21,84 @@ class MemberAutocomplete(autocomplete.Select2QuerySetView):
|
|||||||
return Member.objects.none()
|
return Member.objects.none()
|
||||||
else:
|
else:
|
||||||
return super().get_queryset()
|
return super().get_queryset()
|
||||||
|
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
# TODO: permission required?
|
||||||
|
def upcoming_events(request):
|
||||||
|
now = datetime.now()
|
||||||
|
|
||||||
|
membershipworks = MembershipWorks()
|
||||||
|
membershipworks.login(
|
||||||
|
settings.MEMBERSHIPWORKS_USERNAME, settings.MEMBERSHIPWORKS_PASSWORD
|
||||||
|
)
|
||||||
|
|
||||||
|
events = membershipworks.get_events_list(now)
|
||||||
|
if "error" in events:
|
||||||
|
messages.add_message(
|
||||||
|
request,
|
||||||
|
messages.ERROR,
|
||||||
|
f"MembershipWorks Error: {events['error']}",
|
||||||
|
)
|
||||||
|
# TODO: this should probably be an HTTP 500 response
|
||||||
|
return render(request, "base.dj.html")
|
||||||
|
|
||||||
|
ongoing_events = []
|
||||||
|
full_events = []
|
||||||
|
upcoming_events = []
|
||||||
|
for event in events["evt"]:
|
||||||
|
try:
|
||||||
|
# ignore hidden events
|
||||||
|
if event["cal"] == 0:
|
||||||
|
continue
|
||||||
|
event_details = membershipworks.get_event_by_eid(event["eid"])
|
||||||
|
|
||||||
|
# Convert timestamps to datetime objects
|
||||||
|
event_details["sdp_dt"] = datetime.fromtimestamp(event_details["sdp"])
|
||||||
|
event_details["edp_dt"] = datetime.fromtimestamp(event_details["edp"])
|
||||||
|
print(event_details["ttl"])
|
||||||
|
|
||||||
|
# registration has already ended
|
||||||
|
if (
|
||||||
|
"erd" in event_details
|
||||||
|
and datetime.fromtimestamp(event_details["erd"]) < now
|
||||||
|
):
|
||||||
|
ongoing_events.append(event_details)
|
||||||
|
# class is full
|
||||||
|
elif event_details["cnt"] >= event_details["cap"]:
|
||||||
|
full_events.append(event_details)
|
||||||
|
else:
|
||||||
|
upcoming_events.append(event_details)
|
||||||
|
|
||||||
|
except KeyError as e:
|
||||||
|
messages.add_message(
|
||||||
|
request,
|
||||||
|
messages.ERROR,
|
||||||
|
f"Event '{event.get('ttl')}' missing required property: '{e.args[0]}'",
|
||||||
|
)
|
||||||
|
# TODO: this should probably be an HTTP 500 response
|
||||||
|
return render(request, "base.dj.html")
|
||||||
|
|
||||||
|
context = {
|
||||||
|
"event_sections": [
|
||||||
|
{
|
||||||
|
"title": "Upcoming Events",
|
||||||
|
"blurb": "Events that are currently open for registration.",
|
||||||
|
"events": upcoming_events,
|
||||||
|
"truncate": False,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Just Missed",
|
||||||
|
"blurb": "These classes are currently full at time of writing. If you are interested, please check the event's page; spots occasionally open up. Keep an eye on this newsletter to see when these classes are offered again.",
|
||||||
|
"events": full_events,
|
||||||
|
"truncate": True,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Ongoing Events",
|
||||||
|
"blurb": "These events are ongoing. Registration is currently closed, but these events may be offered again in the future.",
|
||||||
|
"events": ongoing_events,
|
||||||
|
"truncate": True,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
return render(request, "membershipworks/upcoming_events.dj.html", context)
|
||||||
|
15
pdm.lock
15
pdm.lock
@ -5,7 +5,7 @@
|
|||||||
groups = ["default", "debug", "lint", "server", "typing", "dev"]
|
groups = ["default", "debug", "lint", "server", "typing", "dev"]
|
||||||
strategy = ["cross_platform"]
|
strategy = ["cross_platform"]
|
||||||
lock_version = "4.4"
|
lock_version = "4.4"
|
||||||
content_hash = "sha256:8afb89517cfd55ec9138e679d7df47143d3dda10543096382656cb2028e97b50"
|
content_hash = "sha256:4b66341c252a0c283b65ee725342a18f2b71c34811cc7853c75e78fde80815df"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "aiohttp"
|
name = "aiohttp"
|
||||||
@ -443,6 +443,19 @@ files = [
|
|||||||
{file = "django-autocomplete-light-3.9.7.tar.gz", hash = "sha256:a34f192ac438c4df056dbfd399550799ddc631c4661960134ded924648770373"},
|
{file = "django-autocomplete-light-3.9.7.tar.gz", hash = "sha256:a34f192ac438c4df056dbfd399550799ddc631c4661960134ded924648770373"},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "django-bleach"
|
||||||
|
version = "1.0.0"
|
||||||
|
summary = "Easily use bleach with Django models and templates"
|
||||||
|
dependencies = [
|
||||||
|
"Django>=1.11",
|
||||||
|
"bleach>=1.5.0",
|
||||||
|
]
|
||||||
|
files = [
|
||||||
|
{file = "django-bleach-1.0.0.tar.gz", hash = "sha256:2586b90d641d4d7e70ee353570ad33d3625ed4b97036a3ea5b03ea1bb5bbeccd"},
|
||||||
|
{file = "django_bleach-1.0.0-py2.py3-none-any.whl", hash = "sha256:60074a4f4bc8d5200fdb2e03dce16fb4913427698b64570bc3e1a7ea1b8c3cf7"},
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "django-debug-toolbar"
|
name = "django-debug-toolbar"
|
||||||
version = "4.2.0"
|
version = "4.2.0"
|
||||||
|
@ -28,6 +28,7 @@ dependencies = [
|
|||||||
"django-object-actions~=4.2",
|
"django-object-actions~=4.2",
|
||||||
"udm-rest-client~=1.2",
|
"udm-rest-client~=1.2",
|
||||||
"openapi-client-udm~=1.0",
|
"openapi-client-udm~=1.0",
|
||||||
|
"django-bleach~=1.0",
|
||||||
]
|
]
|
||||||
requires-python = ">=3.11"
|
requires-python = ">=3.11"
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user