Compare commits
No commits in common. "e38bd565499044b8c455c45867f542e7830a263b" and "cf99c3f40e9ef8d148c8eee2cde83051166521da" have entirely different histories.
e38bd56549
...
cf99c3f40e
|
@ -2,11 +2,11 @@ default_language_version:
|
|||
python: python3
|
||||
repos:
|
||||
- repo: https://github.com/psf/black
|
||||
rev: 22.12.0
|
||||
rev: 22.6.0
|
||||
hooks:
|
||||
- id: black
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: v4.4.0
|
||||
rev: v4.3.0
|
||||
hooks:
|
||||
- id: check-ast
|
||||
- id: check-merge-conflict
|
||||
|
@ -19,7 +19,7 @@ repos:
|
|||
- id: end-of-file-fixer
|
||||
- id: trailing-whitespace
|
||||
- repo: https://github.com/pycqa/flake8
|
||||
rev: 6.0.0
|
||||
rev: 5.0.4
|
||||
hooks:
|
||||
- id: flake8
|
||||
additional_dependencies:
|
||||
|
@ -28,7 +28,7 @@ repos:
|
|||
- flake8-tidy-imports
|
||||
args: [--max-line-length=88, --extend-exclude=src/project/settings/deployments/*]
|
||||
- repo: https://github.com/asottile/reorder_python_imports
|
||||
rev: v3.9.0
|
||||
rev: v3.8.2
|
||||
hooks:
|
||||
- id: reorder-python-imports
|
||||
args:
|
||||
|
@ -36,15 +36,15 @@ repos:
|
|||
- --application-directories=.:src
|
||||
exclude: migrations/
|
||||
- repo: https://github.com/asottile/pyupgrade
|
||||
rev: v3.3.1
|
||||
rev: v2.37.3
|
||||
hooks:
|
||||
- id: pyupgrade
|
||||
args:
|
||||
- --py310-plus
|
||||
- --py39-plus
|
||||
exclude: migrations/
|
||||
- repo: https://github.com/adamchainz/django-upgrade
|
||||
rev: 1.12.0
|
||||
rev: 1.7.0
|
||||
hooks:
|
||||
- id: django-upgrade
|
||||
args:
|
||||
- --target-version=4.1
|
||||
- --target-version=3.2
|
||||
|
|
|
@ -9,20 +9,23 @@ class OrderAdmin(admin.ModelAdmin):
|
|||
|
||||
list_display = ("who", "description", "created", "is_paid")
|
||||
|
||||
@admin.display(description=_("Customer"))
|
||||
def who(self, instance):
|
||||
return instance.user.get_full_name()
|
||||
|
||||
who.short_description = _("Customer")
|
||||
|
||||
|
||||
@admin.register(models.Payment)
|
||||
class PaymentAdmin(admin.ModelAdmin):
|
||||
|
||||
list_display = ("who", "description", "order_id", "created")
|
||||
|
||||
@admin.display(description=_("Customer"))
|
||||
def who(self, instance):
|
||||
return instance.order.user.get_full_name()
|
||||
|
||||
@admin.display(description=_("Order ID"))
|
||||
who.short_description = _("Customer")
|
||||
|
||||
def order_id(self, instance):
|
||||
return instance.order.id
|
||||
|
||||
order_id.short_description = _("Order ID")
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
from django.contrib.auth.models import User
|
||||
from django.contrib.postgres.constraints import ExclusionConstraint
|
||||
from django.contrib.postgres.fields import DateRangeField
|
||||
from django.contrib.postgres.fields import RangeOperators
|
||||
|
@ -9,26 +8,6 @@ from django.utils.translation import gettext as _
|
|||
from utils.mixins import CreatedModifiedAbstract
|
||||
|
||||
|
||||
class Member(User):
|
||||
class QuerySet(models.QuerySet):
|
||||
def annotate_membership(self):
|
||||
from membership.selectors import get_current_subscription_period
|
||||
|
||||
return self.annotate(
|
||||
active_membership=models.Exists(
|
||||
Membership.objects.filter(
|
||||
user=models.OuterRef("pk"),
|
||||
period=get_current_subscription_period().id,
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
objects = QuerySet.as_manager()
|
||||
|
||||
class Meta:
|
||||
proxy = True
|
||||
|
||||
|
||||
class SubscriptionPeriod(CreatedModifiedAbstract):
|
||||
"""
|
||||
Denotes a period for which members should pay their membership fee for.
|
||||
|
|
|
@ -1,9 +1,5 @@
|
|||
import contextlib
|
||||
|
||||
from django.contrib.auth.models import User
|
||||
from django.utils import timezone
|
||||
|
||||
from membership.models import Member
|
||||
from membership.models import Membership
|
||||
from membership.models import SubscriptionPeriod
|
||||
|
||||
|
@ -16,13 +12,6 @@ def get_subscription_periods() -> list[SubscriptionPeriod]:
|
|||
return list(subscription_periods)
|
||||
|
||||
|
||||
def get_current_subscription_period() -> SubscriptionPeriod | None:
|
||||
with contextlib.suppress(SubscriptionPeriod.DoesNotExist):
|
||||
return SubscriptionPeriod.objects.prefetch_related(
|
||||
"membership_set", "membership_set__user"
|
||||
).get(period__contains=timezone.now())
|
||||
|
||||
|
||||
def get_memberships(
|
||||
*, user: User | None = None, period: SubscriptionPeriod | None = None
|
||||
) -> Membership.QuerySet:
|
||||
|
@ -37,9 +26,5 @@ def get_memberships(
|
|||
return memberships
|
||||
|
||||
|
||||
def get_members():
|
||||
return Member.objects.all().annotate_membership()
|
||||
|
||||
|
||||
def get_member(*, member_id: int) -> Member:
|
||||
return get_members().get(id=member_id)
|
||||
def get_users():
|
||||
return User.objects.all()
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
{% extends "base.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
{{ member.username }}
|
||||
|
||||
{% endblock %}
|
|
@ -4,12 +4,10 @@ from django.utils.translation import gettext_lazy as _
|
|||
from zen_queries import render
|
||||
|
||||
from .permissions import ADMINISTRATE_MEMBERS
|
||||
from .selectors import get_member
|
||||
from .selectors import get_members
|
||||
from .selectors import get_memberships
|
||||
from .selectors import get_users
|
||||
from utils.view_utils import base_view_context
|
||||
from utils.view_utils import render_list
|
||||
from utils.view_utils import RowAction
|
||||
|
||||
|
||||
@login_required
|
||||
|
@ -36,7 +34,7 @@ def membership_overview(request):
|
|||
@login_required
|
||||
@permission_required(ADMINISTRATE_MEMBERS.path)
|
||||
def members_admin(request):
|
||||
users = get_members()
|
||||
users = get_users()
|
||||
|
||||
return render_list(
|
||||
request=request,
|
||||
|
@ -46,27 +44,5 @@ def members_admin(request):
|
|||
("first_name", _("First name")),
|
||||
("last_name", _("Last name")),
|
||||
("email", _("Email")),
|
||||
("active_membership", _("Active membership")),
|
||||
],
|
||||
row_actions=[
|
||||
RowAction(
|
||||
label=_("View"),
|
||||
url_name="admin-members-detail",
|
||||
url_kwargs={"member_id": "id"},
|
||||
)
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
@login_required
|
||||
@permission_required(ADMINISTRATE_MEMBERS.path)
|
||||
def members_admin_detail(request, member_id):
|
||||
member = get_member(member_id=member_id)
|
||||
|
||||
context = base_view_context(request) | {"member": member}
|
||||
|
||||
return render(
|
||||
request=request,
|
||||
template_name="membership/members_admin_detail.html",
|
||||
context=context,
|
||||
)
|
||||
|
|
|
@ -89,6 +89,7 @@ TIME_ZONE = "Europe/Copenhagen"
|
|||
|
||||
USE_I18N = True
|
||||
|
||||
USE_L10N = True
|
||||
|
||||
USE_TZ = True
|
||||
|
||||
|
|
|
@ -203,7 +203,7 @@
|
|||
</div>
|
||||
</nav>
|
||||
|
||||
<main class="col-md-9 ms-sm-auto col-lg-10 px-md-4 p-4">
|
||||
<main class="col-md-9 ms-sm-auto col-lg-10 px-md-4">
|
||||
{% block content %}
|
||||
{% endblock %}
|
||||
</main>
|
||||
|
|
|
@ -8,7 +8,6 @@ from django.urls import path
|
|||
from .views import index
|
||||
from .views import services_overview
|
||||
from membership.views import members_admin
|
||||
from membership.views import members_admin_detail
|
||||
from membership.views import membership_overview
|
||||
|
||||
urlpatterns = [
|
||||
|
@ -16,13 +15,8 @@ urlpatterns = [
|
|||
path("services/", login_required(services_overview), name="services-overview"),
|
||||
path("membership/", membership_overview, name="membership-overview"),
|
||||
path("admin/members/", members_admin, name="admin-members"),
|
||||
path(
|
||||
"admin/members/<int:member_id>/",
|
||||
members_admin_detail,
|
||||
name="admin-members-detail",
|
||||
),
|
||||
path("accounts/", include("allauth.urls")),
|
||||
path("_admin/", admin.site.urls),
|
||||
path("admin/", admin.site.urls),
|
||||
]
|
||||
|
||||
if settings.DEBUG:
|
||||
|
|
|
@ -11,20 +11,11 @@
|
|||
{% endfor %}
|
||||
</tr>
|
||||
</thead>
|
||||
{% for row in rows %}
|
||||
{% for row in object_rows %}
|
||||
<tr>
|
||||
{% for value in row.data.values %}
|
||||
{% for value in row %}
|
||||
<td>{{ value }}</td>
|
||||
{% endfor %}
|
||||
{% if row.actions %}
|
||||
<td>
|
||||
{% for action in row.actions %}
|
||||
<a href="{{ action.url }}">
|
||||
{{ action.label }}
|
||||
</a>
|
||||
{% endfor %}
|
||||
</td>
|
||||
{% endif %}
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
|
|
|
@ -1,10 +1,7 @@
|
|||
from dataclasses import dataclass
|
||||
|
||||
from django.contrib.sites.shortcuts import get_current_site as django_get_current_site
|
||||
from django.db.models import Model
|
||||
from django.http import HttpRequest
|
||||
from django.http import HttpResponse
|
||||
from django.urls import reverse
|
||||
from zen_queries import render
|
||||
|
||||
|
||||
|
@ -13,65 +10,21 @@ def base_view_context(request):
|
|||
return {"site": django_get_current_site(request)}
|
||||
|
||||
|
||||
@dataclass
|
||||
class Row:
|
||||
"""
|
||||
A row in a table.
|
||||
"""
|
||||
|
||||
data: dict[str, str]
|
||||
actions: list[dict[str, str]]
|
||||
|
||||
|
||||
@dataclass
|
||||
class RowAction:
|
||||
"""
|
||||
An action that can be performed on a row in a table.
|
||||
"""
|
||||
|
||||
label: str
|
||||
url_name: str
|
||||
url_kwargs: dict[str, str]
|
||||
|
||||
def render(self, obj) -> dict[str, str]:
|
||||
"""
|
||||
Render the action as a dictionary for the given object.
|
||||
"""
|
||||
url = reverse(
|
||||
self.url_name,
|
||||
kwargs={key: getattr(obj, value) for key, value in self.url_kwargs.items()},
|
||||
)
|
||||
return {"label": self.label, "url": url}
|
||||
|
||||
|
||||
def render_list(
|
||||
request: HttpRequest,
|
||||
objects: list["Model"],
|
||||
columns: list[tuple[str, str]],
|
||||
row_actions: list[RowAction] = None,
|
||||
list_actions: list[tuple[str, str]] = None,
|
||||
) -> HttpResponse:
|
||||
"""
|
||||
Render a list of objects with a table.
|
||||
"""
|
||||
# TODO: Actions per object
|
||||
# TODO: Listwide actions
|
||||
|
||||
# TODO: List actions
|
||||
|
||||
rows = []
|
||||
for obj in objects:
|
||||
row = Row(
|
||||
data={column: getattr(obj, column[0]) for column in columns},
|
||||
actions=[action.render(obj) for action in row_actions],
|
||||
)
|
||||
rows.append(row)
|
||||
object_rows = [[getattr(obj, column[0]) for column in columns] for obj in objects]
|
||||
|
||||
column_labels = [column[1] for column in columns]
|
||||
|
||||
context = base_view_context(request) | {
|
||||
"rows": rows,
|
||||
"object_rows": object_rows,
|
||||
"columns": column_labels,
|
||||
"row_actions": row_actions,
|
||||
"list_actions": list_actions,
|
||||
}
|
||||
|
||||
return render(
|
||||
|
|
Loading…
Reference in a new issue