membershipworks: Add download buttons for event reports
This commit is contained in:
parent
97e9a3c9d5
commit
bfe9fc7c2c
@ -0,0 +1,17 @@
|
|||||||
|
{% load export_url from django_tables2 %}
|
||||||
|
|
||||||
|
<div class="dropdown">
|
||||||
|
<button class="btn btn-sm btn-secondary dropdown-toggle"
|
||||||
|
type="button"
|
||||||
|
data-bs-toggle="dropdown"
|
||||||
|
aria-expanded="false">
|
||||||
|
<i class="bi bi-download"></i> Download
|
||||||
|
</button>
|
||||||
|
<ul class="dropdown-menu">
|
||||||
|
{% for format in view.export_formats %}
|
||||||
|
<li>
|
||||||
|
<a class="dropdown-item" href="{% export_url format %}">{{ format }}</a>
|
||||||
|
</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
</div>
|
@ -5,5 +5,6 @@
|
|||||||
{% block title %}Event Report Index{% endblock %}
|
{% block title %}Event Report Index{% endblock %}
|
||||||
{% block breadcrumbs %}<li class="breadcrumb-item active" aria-current="page">MW Event Reports</li>{% endblock %}
|
{% block breadcrumbs %}<li class="breadcrumb-item active" aria-current="page">MW Event Reports</li>{% endblock %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
{% include "membershipworks/components/download_table.dj.html" %}
|
||||||
{% render_table table %}
|
{% render_table table %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
<li class="breadcrumb-item active" aria-current="page">{{ month|date:"F" }}</li>
|
<li class="breadcrumb-item active" aria-current="page">{{ month|date:"F" }}</li>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
{% include "membershipworks/components/download_table.dj.html" %}
|
||||||
{% render_table table %}
|
{% render_table table %}
|
||||||
<nav aria-label="Page navigation">
|
<nav aria-label="Page navigation">
|
||||||
<ul class="pagination justify-content-center">
|
<ul class="pagination justify-content-center">
|
||||||
|
@ -10,6 +10,7 @@
|
|||||||
<li class="breadcrumb-item active" aria-current="page">{{ year|date:"Y" }}</li>
|
<li class="breadcrumb-item active" aria-current="page">{{ year|date:"Y" }}</li>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
{% include "membershipworks/components/download_table.dj.html" %}
|
||||||
{% render_table table %}
|
{% render_table table %}
|
||||||
<nav aria-label="Page navigation">
|
<nav aria-label="Page navigation">
|
||||||
<ul class="pagination justify-content-center">
|
<ul class="pagination justify-content-center">
|
||||||
|
@ -16,6 +16,7 @@ from django.views.generic.dates import (
|
|||||||
import django_tables2 as tables
|
import django_tables2 as tables
|
||||||
from dal import autocomplete
|
from dal import autocomplete
|
||||||
from django_tables2 import A, SingleTableMixin
|
from django_tables2 import A, SingleTableMixin
|
||||||
|
from django_tables2.export.views import ExportMixin
|
||||||
|
|
||||||
from membershipworks.membershipworks_api import MembershipWorks
|
from membershipworks.membershipworks_api import MembershipWorks
|
||||||
|
|
||||||
@ -118,11 +119,17 @@ class DurationColumn(tables.Column):
|
|||||||
return None
|
return None
|
||||||
return floatformat(value.total_seconds() / 60 / 60, -2)
|
return floatformat(value.total_seconds() / 60 / 60, -2)
|
||||||
|
|
||||||
|
def value(self, value: timedelta):
|
||||||
|
if value is None:
|
||||||
|
return None
|
||||||
|
return value.total_seconds() / 60 / 60
|
||||||
|
|
||||||
|
|
||||||
class EventTable(tables.Table):
|
class EventTable(tables.Table):
|
||||||
title = tables.Column(
|
title = tables.Column(
|
||||||
linkify=lambda record: f"https://membershipworks.com/admin/#!event/admin/{record.url}"
|
linkify=lambda record: f"https://membershipworks.com/admin/#!event/admin/{record.url}"
|
||||||
)
|
)
|
||||||
|
occurred = tables.BooleanColumn(visible=False)
|
||||||
start = tables.DateColumn("N d, Y")
|
start = tables.DateColumn("N d, Y")
|
||||||
duration = DurationColumn()
|
duration = DurationColumn()
|
||||||
person_hours = DurationColumn()
|
person_hours = DurationColumn()
|
||||||
@ -132,6 +139,7 @@ class EventTable(tables.Table):
|
|||||||
model = EventExt
|
model = EventExt
|
||||||
fields = (
|
fields = (
|
||||||
"title",
|
"title",
|
||||||
|
"occurred",
|
||||||
"start",
|
"start",
|
||||||
"instructor",
|
"instructor",
|
||||||
"category",
|
"category",
|
||||||
@ -158,13 +166,17 @@ class EventSummaryTable(tables.Table):
|
|||||||
person_hours__sum = DurationColumn("Person Hours")
|
person_hours__sum = DurationColumn("Person Hours")
|
||||||
|
|
||||||
|
|
||||||
class EventIndexReport(SingleTableMixin, PermissionRequiredMixin, ArchiveIndexView):
|
class EventIndexReport(
|
||||||
|
ExportMixin, SingleTableMixin, PermissionRequiredMixin, ArchiveIndexView
|
||||||
|
):
|
||||||
permission_required = "membershipworks.view_eventext"
|
permission_required = "membershipworks.view_eventext"
|
||||||
queryset = EventExt.objects.all()
|
queryset = EventExt.objects.all()
|
||||||
date_field = "start"
|
date_field = "start"
|
||||||
template_name = "membershipworks/event_index_report.dj.html"
|
template_name = "membershipworks/event_index_report.dj.html"
|
||||||
make_object_list = True
|
make_object_list = True
|
||||||
table_class = EventSummaryTable
|
table_class = EventSummaryTable
|
||||||
|
export_formats = ("csv", "xlsx", "ods")
|
||||||
|
export_name = "mw_events_index"
|
||||||
|
|
||||||
def get_table_kwargs(self):
|
def get_table_kwargs(self):
|
||||||
year_column = tables.DateColumn(
|
year_column = tables.DateColumn(
|
||||||
@ -189,13 +201,19 @@ class EventIndexReport(SingleTableMixin, PermissionRequiredMixin, ArchiveIndexVi
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class EventYearReport(SingleTableMixin, PermissionRequiredMixin, YearArchiveView):
|
class EventYearReport(
|
||||||
|
ExportMixin, SingleTableMixin, PermissionRequiredMixin, YearArchiveView
|
||||||
|
):
|
||||||
permission_required = "membershipworks.view_eventext"
|
permission_required = "membershipworks.view_eventext"
|
||||||
queryset = EventExt.objects.all()
|
queryset = EventExt.objects.all()
|
||||||
date_field = "start"
|
date_field = "start"
|
||||||
template_name = "membershipworks/event_year_report.dj.html"
|
template_name = "membershipworks/event_year_report.dj.html"
|
||||||
make_object_list = True
|
make_object_list = True
|
||||||
table_class = EventSummaryTable
|
table_class = EventSummaryTable
|
||||||
|
export_formats = ("csv", "xlsx", "ods")
|
||||||
|
|
||||||
|
def get_export_filename(self, export_format):
|
||||||
|
return f"mw_events_{self.get_year()}.{export_format}"
|
||||||
|
|
||||||
def get_table_kwargs(self):
|
def get_table_kwargs(self):
|
||||||
month_column = tables.DateColumn(
|
month_column = tables.DateColumn(
|
||||||
@ -220,9 +238,15 @@ class EventYearReport(SingleTableMixin, PermissionRequiredMixin, YearArchiveView
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class EventMonthReport(SingleTableMixin, PermissionRequiredMixin, MonthArchiveView):
|
class EventMonthReport(
|
||||||
|
ExportMixin, SingleTableMixin, PermissionRequiredMixin, MonthArchiveView
|
||||||
|
):
|
||||||
permission_required = "membershipworks.view_eventext"
|
permission_required = "membershipworks.view_eventext"
|
||||||
queryset = EventExt.objects.select_related("category", "instructor").all()
|
queryset = EventExt.objects.select_related("category", "instructor").all()
|
||||||
date_field = "start"
|
date_field = "start"
|
||||||
template_name = "membershipworks/event_month_report.dj.html"
|
template_name = "membershipworks/event_month_report.dj.html"
|
||||||
table_class = EventTable
|
table_class = EventTable
|
||||||
|
export_formats = ("csv", "xlsx", "ods")
|
||||||
|
|
||||||
|
def get_export_filename(self, export_format):
|
||||||
|
return f"mw_events_{self.get_year()}-{self.get_month():02}.{export_format}"
|
||||||
|
72
pdm.lock
72
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.1"
|
lock_version = "4.4.1"
|
||||||
content_hash = "sha256:b7ca762523a8ec97b04cc997ad7110cc9dcb77ffd1194d9d806a3a2784ccc62b"
|
content_hash = "sha256:c0e5c80b47118152c5b0167a588d3d1d078b0f2a710b8fc97869052ca04e7874"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "aiohttp"
|
name = "aiohttp"
|
||||||
@ -368,6 +368,16 @@ files = [
|
|||||||
{file = "decorator-5.1.1.tar.gz", hash = "sha256:637996211036b6385ef91435e4fae22989472f9d571faba8927ba8253acbc330"},
|
{file = "decorator-5.1.1.tar.gz", hash = "sha256:637996211036b6385ef91435e4fae22989472f9d571faba8927ba8253acbc330"},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "defusedxml"
|
||||||
|
version = "0.7.1"
|
||||||
|
requires_python = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
|
||||||
|
summary = "XML bomb protection for Python stdlib modules"
|
||||||
|
files = [
|
||||||
|
{file = "defusedxml-0.7.1-py2.py3-none-any.whl", hash = "sha256:a352e7e428770286cc899e2542b6cdaedb2b4953ff269a210103ec58f6198a61"},
|
||||||
|
{file = "defusedxml-0.7.1.tar.gz", hash = "sha256:1bb3032db185915b62d7c6209c5a8792be6a32ab2fedacc84e01b52c51aa3e69"},
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "django"
|
name = "django"
|
||||||
version = "5.0.1"
|
version = "5.0.1"
|
||||||
@ -691,6 +701,16 @@ files = [
|
|||||||
{file = "EditorConfig-0.12.3.tar.gz", hash = "sha256:57f8ce78afcba15c8b18d46b5170848c88d56fd38f05c2ec60dbbfcb8996e89e"},
|
{file = "EditorConfig-0.12.3.tar.gz", hash = "sha256:57f8ce78afcba15c8b18d46b5170848c88d56fd38f05c2ec60dbbfcb8996e89e"},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "et-xmlfile"
|
||||||
|
version = "1.1.0"
|
||||||
|
requires_python = ">=3.6"
|
||||||
|
summary = "An implementation of lxml.xmlfile for the standard library"
|
||||||
|
files = [
|
||||||
|
{file = "et_xmlfile-1.1.0-py3-none-any.whl", hash = "sha256:a2ba85d1d6a74ef63837eed693bcb89c3f752169b0e3e7ae5b16ca5e1b3deada"},
|
||||||
|
{file = "et_xmlfile-1.1.0.tar.gz", hash = "sha256:8eb9e2bc2f8c97e37a2dc85a09ecdcdec9d8a396530a6d5a33b30b9a92da0c5c"},
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "executing"
|
name = "executing"
|
||||||
version = "2.0.1"
|
version = "2.0.1"
|
||||||
@ -1172,6 +1192,17 @@ files = [
|
|||||||
{file = "nh3-0.2.15.tar.gz", hash = "sha256:d1e30ff2d8d58fb2a14961f7aac1bbb1c51f9bdd7da727be35c63826060b0bf3"},
|
{file = "nh3-0.2.15.tar.gz", hash = "sha256:d1e30ff2d8d58fb2a14961f7aac1bbb1c51f9bdd7da727be35c63826060b0bf3"},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "odfpy"
|
||||||
|
version = "1.4.1"
|
||||||
|
summary = "Python API and tools to manipulate OpenDocument files"
|
||||||
|
dependencies = [
|
||||||
|
"defusedxml",
|
||||||
|
]
|
||||||
|
files = [
|
||||||
|
{file = "odfpy-1.4.1.tar.gz", hash = "sha256:db766a6e59c5103212f3cc92ec8dd50a0f3a02790233ed0b52148b70d3c438ec"},
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "openapi-client-udm"
|
name = "openapi-client-udm"
|
||||||
version = "1.0.2"
|
version = "1.0.2"
|
||||||
@ -1187,6 +1218,19 @@ files = [
|
|||||||
{file = "openapi_client_udm-1.0.2-py3-none-any.whl", hash = "sha256:453d4fe405542729cd307005e4b9710ae475693c6795616b1568e045114e1be5"},
|
{file = "openapi_client_udm-1.0.2-py3-none-any.whl", hash = "sha256:453d4fe405542729cd307005e4b9710ae475693c6795616b1568e045114e1be5"},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "openpyxl"
|
||||||
|
version = "3.1.2"
|
||||||
|
requires_python = ">=3.6"
|
||||||
|
summary = "A Python library to read/write Excel 2010 xlsx/xlsm files"
|
||||||
|
dependencies = [
|
||||||
|
"et-xmlfile",
|
||||||
|
]
|
||||||
|
files = [
|
||||||
|
{file = "openpyxl-3.1.2-py2.py3-none-any.whl", hash = "sha256:f91456ead12ab3c6c2e9491cf33ba6d08357d802192379bb482f1033ade496f5"},
|
||||||
|
{file = "openpyxl-3.1.2.tar.gz", hash = "sha256:a6f5977418eff3b2d5500d54d9db50c8277a368436f4e4f8ddb1be3422870184"},
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "packaging"
|
name = "packaging"
|
||||||
version = "23.0"
|
version = "23.0"
|
||||||
@ -1630,6 +1674,32 @@ files = [
|
|||||||
{file = "stack_data-0.6.3.tar.gz", hash = "sha256:836a778de4fec4dcd1dcd89ed8abff8a221f58308462e1c4aa2a3cf30148f0b9"},
|
{file = "stack_data-0.6.3.tar.gz", hash = "sha256:836a778de4fec4dcd1dcd89ed8abff8a221f58308462e1c4aa2a3cf30148f0b9"},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tablib"
|
||||||
|
version = "3.5.0"
|
||||||
|
requires_python = ">=3.8"
|
||||||
|
summary = "Format agnostic tabular data library (XLS, JSON, YAML, CSV, etc.)"
|
||||||
|
files = [
|
||||||
|
{file = "tablib-3.5.0-py3-none-any.whl", hash = "sha256:9821caa9eca6062ff7299fa645e737aecff982e6b2b42046928a6413c8dabfd9"},
|
||||||
|
{file = "tablib-3.5.0.tar.gz", hash = "sha256:f6661dfc45e1d4f51fa8a6239f9c8349380859a5bfaa73280645f046d6c96e33"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tablib"
|
||||||
|
version = "3.5.0"
|
||||||
|
extras = ["ods", "xlsx"]
|
||||||
|
requires_python = ">=3.8"
|
||||||
|
summary = "Format agnostic tabular data library (XLS, JSON, YAML, CSV, etc.)"
|
||||||
|
dependencies = [
|
||||||
|
"odfpy",
|
||||||
|
"openpyxl>=2.6.0",
|
||||||
|
"tablib==3.5.0",
|
||||||
|
]
|
||||||
|
files = [
|
||||||
|
{file = "tablib-3.5.0-py3-none-any.whl", hash = "sha256:9821caa9eca6062ff7299fa645e737aecff982e6b2b42046928a6413c8dabfd9"},
|
||||||
|
{file = "tablib-3.5.0.tar.gz", hash = "sha256:f6661dfc45e1d4f51fa8a6239f9c8349380859a5bfaa73280645f046d6c96e33"},
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tinycss2"
|
name = "tinycss2"
|
||||||
version = "1.1.1"
|
version = "1.1.1"
|
||||||
|
@ -30,6 +30,7 @@ dependencies = [
|
|||||||
"django-nh3~=0.1",
|
"django-nh3~=0.1",
|
||||||
"nh3~=0.2",
|
"nh3~=0.2",
|
||||||
"django-tables2~=2.7",
|
"django-tables2~=2.7",
|
||||||
|
"tablib[ods,xlsx]~=3.5",
|
||||||
]
|
]
|
||||||
requires-python = ">=3.11"
|
requires-python = ">=3.11"
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user