From cbbbdc4bbfd6e4cdf4665cbc7579403793ded2f3 Mon Sep 17 00:00:00 2001 From: imperosol Date: Tue, 19 Nov 2024 00:41:49 +0100 Subject: [PATCH] start of work --- core/models.py | 14 ++- core/static/bundled/htmx-index.js | 1 + galaxy/models.py | 2 +- package-lock.json | 6 + package.json | 5 +- sas/widgets/select.py | 5 +- subscription/forms.py | 58 +++------- .../fragments/existing_user_form.html | 12 ++ .../fragments/new_user_form.jinja | 0 .../templates/subscription/subscription.jinja | 108 +++++++++--------- subscription/urls.py | 11 +- subscription/views.py | 102 +++++++++-------- vite.config.mts | 1 + 13 files changed, 173 insertions(+), 152 deletions(-) create mode 100644 subscription/templates/subscription/fragments/existing_user_form.html create mode 100644 subscription/templates/subscription/fragments/new_user_form.jinja diff --git a/core/models.py b/core/models.py index 20cbeb4b..7a9d3a46 100644 --- a/core/models.py +++ b/core/models.py @@ -529,13 +529,15 @@ class User(AbstractBaseUser): return False @cached_property - def can_create_subscription(self): - from club.models import Club + def can_create_subscription(self) -> bool: + from club.models import Membership - for club in Club.objects.filter(id__in=settings.SITH_CAN_CREATE_SUBSCRIPTIONS): - if club in self.clubs_with_rights: - return True - return False + return ( + Membership.objects.board() + .ongoing() + .filter(club_id__in=settings.SITH_CAN_CREATE_SUBSCRIPTIONS) + .exists() + ) @cached_property def is_launderette_manager(self): diff --git a/core/static/bundled/htmx-index.js b/core/static/bundled/htmx-index.js index 56edea4a..e2a56605 100644 --- a/core/static/bundled/htmx-index.js +++ b/core/static/bundled/htmx-index.js @@ -1,3 +1,4 @@ +import "htmx-ext-response-targets/response-targets"; import htmx from "htmx.org"; Object.assign(window, { htmx }); diff --git a/galaxy/models.py b/galaxy/models.py index 9316aacf..4d12f832 100644 --- a/galaxy/models.py +++ b/galaxy/models.py @@ -406,7 +406,7 @@ class Galaxy(models.Model): cls.logger.debug(f"\t\t> Scaled distance: {value}") return int(value) - def rule(self, picture_count_threshold=10) -> None: + def rule(self, picture_count_threshold=-1) -> None: """Main function of the Galaxy. Iterate over all the rulable users to promote them to citizens. diff --git a/package-lock.json b/package-lock.json index 05418a69..cb6483bf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -22,6 +22,7 @@ "d3-force-3d": "^3.0.5", "easymde": "^2.18.0", "glob": "^11.0.0", + "htmx-ext-response-targets": "^2.0.1", "htmx.org": "^2.0.3", "jquery": "^3.7.1", "jquery-ui": "^1.14.0", @@ -4140,6 +4141,11 @@ "node": ">= 0.4" } }, + "node_modules/htmx-ext-response-targets": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/htmx-ext-response-targets/-/htmx-ext-response-targets-2.0.1.tgz", + "integrity": "sha512-uCMw098+0xcrs7UW/s8l8hqj5wfOaVnVV7286cS+TNMNguo8fQpi/PEaZuT4VUysIiRcjj4pcTkuaP6Q9iJ3XA==" + }, "node_modules/htmx.org": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/htmx.org/-/htmx.org-2.0.3.tgz", diff --git a/package.json b/package.json index 2ca46967..bb7a4160 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,9 @@ "keywords": [], "author": "", "license": "GPL-3.0-only", - "sideEffects": [".css"], + "sideEffects": [ + ".css" + ], "imports": { "#openapi": "./staticfiles/generated/openapi/index.ts", "#core:*": "./core/static/bundled/*", @@ -47,6 +49,7 @@ "easymde": "^2.18.0", "glob": "^11.0.0", "htmx.org": "^2.0.3", + "htmx-ext-response-targets": "^2.0.1", "jquery": "^3.7.1", "jquery-ui": "^1.14.0", "jquery.shorten": "^1.0.0", diff --git a/sas/widgets/select.py b/sas/widgets/select.py index 9f8676d3..1d124a27 100644 --- a/sas/widgets/select.py +++ b/sas/widgets/select.py @@ -1,9 +1,6 @@ from pydantic import TypeAdapter -from core.views.widgets.select import ( - AutoCompleteSelect, - AutoCompleteSelectMultiple, -) +from core.views.widgets.select import AutoCompleteSelect, AutoCompleteSelectMultiple from sas.models import Album from sas.schemas import AlbumSchema diff --git a/subscription/forms.py b/subscription/forms.py index f627a6b7..f830ad3d 100644 --- a/subscription/forms.py +++ b/subscription/forms.py @@ -5,7 +5,7 @@ from django.core.exceptions import ValidationError from django.utils.translation import gettext_lazy as _ from core.models import User -from core.views.forms import SelectDate, SelectDateTime +from core.views.forms import SelectDateTime from core.views.widgets.select import AutoCompleteSelectUser from subscription.models import Subscription @@ -21,37 +21,15 @@ class SelectionDateForm(forms.Form): ) -class SubscriptionForm(forms.ModelForm): - class Meta: - model = Subscription - fields = ["member", "subscription_type", "payment_method", "location"] - widgets = {"member": AutoCompleteSelectUser} - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - self.fields["member"].required = False - self.fields |= forms.fields_for_model( - User, - fields=["first_name", "last_name", "email", "date_of_birth"], - widgets={"date_of_birth": SelectDate}, - ) - - def clean_member(self): - subscriber = self.cleaned_data.get("member") - if subscriber: - subscriber = User.objects.filter(id=subscriber.id).first() - return subscriber - +class SubscriptionNewUserForm(forms.Form): def clean(self): cleaned_data = super().clean() if ( - cleaned_data.get("member") is None - and "last_name" not in self.errors.as_data() + "last_name" not in self.errors.as_data() and "first_name" not in self.errors.as_data() and "email" not in self.errors.as_data() and "date_of_birth" not in self.errors.as_data() ): - self.errors.pop("member", None) if self.errors: return cleaned_data if User.objects.filter(email=cleaned_data.get("email")).first() is not None: @@ -70,19 +48,17 @@ class SubscriptionForm(forms.ModelForm): u.set_password(str(random.randrange(1000000, 10000000))) u.save() cleaned_data["member"] = u - elif cleaned_data.get("member") is not None: - self.errors.pop("last_name", None) - self.errors.pop("first_name", None) - self.errors.pop("email", None) - self.errors.pop("date_of_birth", None) - if cleaned_data.get("member") is None: - # This should be handled here, - # but it is done in the Subscription model's clean method - # TODO investigate why! - raise ValidationError( - _( - "You must either choose an existing " - "user or create a new one properly" - ) - ) - return cleaned_data + + +class SubscriptionForm(forms.ModelForm): + """Form to add a subscription to an existing user.""" + + template_name = "subscription/fragments/existing_user_form.html" + + class Meta: + model = Subscription + fields = ["member", "subscription_type", "payment_method", "location"] + widgets = {"member": AutoCompleteSelectUser} + + def clean_subscription_type(self): + raise ValidationError("ptdr non") diff --git a/subscription/templates/subscription/fragments/existing_user_form.html b/subscription/templates/subscription/fragments/existing_user_form.html new file mode 100644 index 00000000..fa64da9c --- /dev/null +++ b/subscription/templates/subscription/fragments/existing_user_form.html @@ -0,0 +1,12 @@ +{##} +{# {% csrf_token %}#} +{#
#} + {{ form.as_div }} +{#
#} +{# #} +{##} diff --git a/subscription/templates/subscription/fragments/new_user_form.jinja b/subscription/templates/subscription/fragments/new_user_form.jinja new file mode 100644 index 00000000..e69de29b diff --git a/subscription/templates/subscription/subscription.jinja b/subscription/templates/subscription/subscription.jinja index a26c7afc..3a47727f 100644 --- a/subscription/templates/subscription/subscription.jinja +++ b/subscription/templates/subscription/subscription.jinja @@ -4,59 +4,65 @@ {% trans %}New subscription{% endtrans %} {% endblock %} +{# The following statics are bundled with our autocomplete select. + However, if one tries to swap a form by another, then the urls in script-once + and link-once disappear. + So we give them here. + If the aforementioned bug is resolved, you can remove this. #} +{% block additional_js %} + +{% endblock %} +{% block additional_css %} + + +{% endblock %} + {% block content %}

{% trans %}New subscription{% endtrans %}

-
- {% csrf_token %} - {{ form.non_field_errors() }} -

{{ form.member.errors }} {{ form.member }}

-
-

{{ form.first_name.errors }} {{ form.first_name }}

-

{{ form.last_name.errors }} {{ form.last_name }}

-

{{ form.email.errors }} {{ form.email }}

-

{{ form.date_of_birth.errors }} {{ form.date_of_birth }}

-
-

{{ form.subscription_type.errors }} {{ form.subscription_type }}

-

{{ form.payment_method.errors }} {{ - form.payment_method }}

-

{% trans %}Eboutic is reserved to specific users. In doubt, don't use it.{% endtrans %}

-

{{ form.location.errors }} {{ form.location }}

-

-
+
+
+ {% csrf_token %} +
+ {{ existing_user_form }} +
+ +
+
+{# #} {% endblock %} -{% block script %} - {{ super() }} - -{% endblock %} +{#{% block script %}#} +{# {{ super() }}#} +{##} +{#{% endblock %}#} diff --git a/subscription/urls.py b/subscription/urls.py index 6bd1fa65..408d0d55 100644 --- a/subscription/urls.py +++ b/subscription/urls.py @@ -15,10 +15,19 @@ from django.urls import path -from subscription.views import NewSubscription, SubscriptionsStatsView +from subscription.views import ( + CreateSubscriptionExistingUserFragment, + NewSubscription, + SubscriptionsStatsView, +) urlpatterns = [ # Subscription views path("", NewSubscription.as_view(), name="subscription"), + path( + "existing-user/", + CreateSubscriptionExistingUserFragment.as_view(), + name="subscription-fragment-existing-user", + ), path("stats/", SubscriptionsStatsView.as_view(), name="stats"), ] diff --git a/subscription/views.py b/subscription/views.py index a6ecc294..83d38dec 100644 --- a/subscription/views.py +++ b/subscription/views.py @@ -13,85 +13,96 @@ # # -import secrets -from django import forms from django.conf import settings +from django.contrib.auth.mixins import UserPassesTestMixin from django.core.exceptions import PermissionDenied +from django.http import HttpResponse from django.urls import reverse_lazy -from django.views.generic.edit import CreateView, FormView +from django.utils.timezone import localdate +from django.views.generic import TemplateView +from django.views.generic.edit import FormView -from subscription.forms import SelectionDateForm, SubscriptionForm +from subscription.forms import ( + SelectionDateForm, + SubscriptionForm, + SubscriptionNewUserForm, +) from subscription.models import Subscription -class NewSubscription(CreateView): +class CanCreateSubscriptionMixin(UserPassesTestMixin): + def test_func(self): + return self.request.user.can_create_subscription + + +class NewSubscription(CanCreateSubscriptionMixin, TemplateView): template_name = "subscription/subscription.jinja" + + def get_context_data(self, **kwargs): + return super().get_context_data(**kwargs) | { + "existing_user_form": SubscriptionForm(), + "new_user_form": SubscriptionNewUserForm(), + } + + +class CreateSubscriptionExistingUserFragment(CanCreateSubscriptionMixin, FormView): + """Create a subscription for a user who already exists.""" + form_class = SubscriptionForm + success_url = reverse_lazy("subscription:subscription-fragment-existing-user") - def dispatch(self, request, *arg, **kwargs): - if request.user.can_create_subscription: - return super().dispatch(request, *arg, **kwargs) - raise PermissionDenied + def render_to_response(self, context, **response_kwargs): + print(context["form"]) + print(str(context["form"])) + print(context["form"].render()) + return HttpResponse(context["form"].render(), **response_kwargs) - def get_initial(self): - if "member" in self.request.GET: - return { - "member": self.request.GET["member"], - "subscription_type": "deux-semestres", - } - return {"subscription_type": "deux-semestres"} + # def dispatch(self, request, *args, **kwargs): + # s = SubscriptionForm() + # print(s) + # return super().dispatch(request, *args, **kwargs) - def form_valid(self, form): - form.instance.subscription_start = Subscription.compute_start( - duration=settings.SITH_SUBSCRIPTIONS[form.instance.subscription_type][ - "duration" - ], - user=form.instance.member, - ) - form.instance.subscription_end = Subscription.compute_end( - duration=settings.SITH_SUBSCRIPTIONS[form.instance.subscription_type][ - "duration" - ], - start=form.instance.subscription_start, - user=form.instance.member, - ) - return super().form_valid(form) + # def form_valid(self, form): + # duration = settings.SITH_SUBSCRIPTIONS[form.instance.subscription_type][ + # "duration" + # ] + # form.instance.subscription_start = Subscription.compute_start( + # duration=duration, user=form.instance.member + # ) + # form.instance.subscription_end = Subscription.compute_end( + # duration=duration, + # start=form.instance.subscription_start, + # user=form.instance.member, + # ) + # return super().form_valid(form) class SubscriptionsStatsView(FormView): template_name = "subscription/stats.jinja" form_class = SelectionDateForm + success_url = reverse_lazy("subscriptions:stats") def dispatch(self, request, *arg, **kwargs): - import datetime - - self.start_date = datetime.datetime.today() + self.start_date = localdate() self.end_date = self.start_date - res = super().dispatch(request, *arg, **kwargs) if request.user.is_root or request.user.is_board_member: - return res + return super().dispatch(request, *arg, **kwargs) raise PermissionDenied def post(self, request, *args, **kwargs): self.form = self.get_form() self.start_date = self.form["start_date"] self.end_date = self.form["end_date"] - res = super().post(request, *args, **kwargs) - if request.user.is_root or request.user.is_board_member: - return res - raise PermissionDenied + return super().post(request, *args, **kwargs) def get_initial(self): - init = { + return { "start_date": self.start_date.strftime("%Y-%m-%d %H:%M:%S"), "end_date": self.end_date.strftime("%Y-%m-%d %H:%M:%S"), } - return init def get_context_data(self, **kwargs): - from subscription.models import Subscription - kwargs = super().get_context_data(**kwargs) kwargs["subscriptions_total"] = Subscription.objects.filter( subscription_end__gte=self.end_date, subscription_start__lte=self.start_date @@ -100,6 +111,3 @@ class SubscriptionsStatsView(FormView): kwargs["payment_types"] = settings.SITH_COUNTER_PAYMENT_METHOD kwargs["locations"] = settings.SITH_SUBSCRIPTION_LOCATIONS return kwargs - - def get_success_url(self, **kwargs): - return reverse_lazy("subscriptions:stats") diff --git a/vite.config.mts b/vite.config.mts index fab81862..05405564 100644 --- a/vite.config.mts +++ b/vite.config.mts @@ -85,6 +85,7 @@ export default { inject({ // biome-ignore lint/style/useNamingConvention: that's how it's called Alpine: "alpinejs", + htmx: "htmx.org" }), viteStaticCopy({ targets: [