mirror of
https://github.com/ae-utbm/sith.git
synced 2026-10-07 19:34:15 +00:00
Merge pull request #898 from ae-utbm/taiste
Complete webpack migration, introduction of tom select, better SAS moderation workflow, more ruff and bugfixes
This commit is contained in:
132 files changed
+3710
-2434
No files matched your search
+10
-1
@@ -15,7 +15,16 @@
|
||||
|
||||
from django.contrib import admin
|
||||
|
||||
from accounting.models import *
|
||||
from accounting.models import (
|
||||
AccountingType,
|
||||
BankAccount,
|
||||
ClubAccount,
|
||||
Company,
|
||||
GeneralJournal,
|
||||
Label,
|
||||
Operation,
|
||||
SimplifiedAccountingType,
|
||||
)
|
||||
|
||||
admin.site.register(BankAccount)
|
||||
admin.site.register(ClubAccount)
|
||||
|
||||
+11
-34
@@ -82,9 +82,7 @@ class Company(models.Model):
|
||||
|
||||
def is_owned_by(self, user):
|
||||
"""Check if that object can be edited by the given user."""
|
||||
if user.is_in_group(pk=settings.SITH_GROUP_ACCOUNTING_ADMIN_ID):
|
||||
return True
|
||||
return False
|
||||
return user.is_in_group(pk=settings.SITH_GROUP_ACCOUNTING_ADMIN_ID)
|
||||
|
||||
def can_be_edited_by(self, user):
|
||||
"""Check if that object can be edited by the given user."""
|
||||
@@ -127,9 +125,7 @@ class BankAccount(models.Model):
|
||||
if user.is_in_group(pk=settings.SITH_GROUP_ACCOUNTING_ADMIN_ID):
|
||||
return True
|
||||
m = self.club.get_membership_for(user)
|
||||
if m is not None and m.role >= settings.SITH_CLUB_ROLES_ID["Treasurer"]:
|
||||
return True
|
||||
return False
|
||||
return m is not None and m.role >= settings.SITH_CLUB_ROLES_ID["Treasurer"]
|
||||
|
||||
|
||||
class ClubAccount(models.Model):
|
||||
@@ -161,29 +157,20 @@ class ClubAccount(models.Model):
|
||||
"""Check if that object can be edited by the given user."""
|
||||
if user.is_anonymous:
|
||||
return False
|
||||
if user.is_in_group(pk=settings.SITH_GROUP_ACCOUNTING_ADMIN_ID):
|
||||
return True
|
||||
return False
|
||||
return user.is_in_group(pk=settings.SITH_GROUP_ACCOUNTING_ADMIN_ID)
|
||||
|
||||
def can_be_edited_by(self, user):
|
||||
"""Check if that object can be edited by the given user."""
|
||||
m = self.club.get_membership_for(user)
|
||||
if m and m.role == settings.SITH_CLUB_ROLES_ID["Treasurer"]:
|
||||
return True
|
||||
return False
|
||||
return m and m.role == settings.SITH_CLUB_ROLES_ID["Treasurer"]
|
||||
|
||||
def can_be_viewed_by(self, user):
|
||||
"""Check if that object can be viewed by the given user."""
|
||||
m = self.club.get_membership_for(user)
|
||||
if m and m.role >= settings.SITH_CLUB_ROLES_ID["Treasurer"]:
|
||||
return True
|
||||
return False
|
||||
return m and m.role >= settings.SITH_CLUB_ROLES_ID["Treasurer"]
|
||||
|
||||
def has_open_journal(self):
|
||||
for j in self.journals.all():
|
||||
if not j.closed:
|
||||
return True
|
||||
return False
|
||||
return self.journals.filter(closed=False).exists()
|
||||
|
||||
def get_open_journal(self):
|
||||
return self.journals.filter(closed=False).first()
|
||||
@@ -228,17 +215,13 @@ class GeneralJournal(models.Model):
|
||||
return False
|
||||
if user.is_in_group(pk=settings.SITH_GROUP_ACCOUNTING_ADMIN_ID):
|
||||
return True
|
||||
if self.club_account.can_be_edited_by(user):
|
||||
return True
|
||||
return False
|
||||
return self.club_account.can_be_edited_by(user)
|
||||
|
||||
def can_be_edited_by(self, user):
|
||||
"""Check if that object can be edited by the given user."""
|
||||
if user.is_in_group(pk=settings.SITH_GROUP_ACCOUNTING_ADMIN_ID):
|
||||
return True
|
||||
if self.club_account.can_be_edited_by(user):
|
||||
return True
|
||||
return False
|
||||
return self.club_account.can_be_edited_by(user)
|
||||
|
||||
def can_be_viewed_by(self, user):
|
||||
return self.club_account.can_be_viewed_by(user)
|
||||
@@ -416,9 +399,7 @@ class Operation(models.Model):
|
||||
if self.journal.closed:
|
||||
return False
|
||||
m = self.journal.club_account.club.get_membership_for(user)
|
||||
if m is not None and m.role >= settings.SITH_CLUB_ROLES_ID["Treasurer"]:
|
||||
return True
|
||||
return False
|
||||
return m is not None and m.role >= settings.SITH_CLUB_ROLES_ID["Treasurer"]
|
||||
|
||||
def can_be_edited_by(self, user):
|
||||
"""Check if that object can be edited by the given user."""
|
||||
@@ -427,9 +408,7 @@ class Operation(models.Model):
|
||||
if self.journal.closed:
|
||||
return False
|
||||
m = self.journal.club_account.club.get_membership_for(user)
|
||||
if m is not None and m.role == settings.SITH_CLUB_ROLES_ID["Treasurer"]:
|
||||
return True
|
||||
return False
|
||||
return m is not None and m.role == settings.SITH_CLUB_ROLES_ID["Treasurer"]
|
||||
|
||||
|
||||
class AccountingType(models.Model):
|
||||
@@ -472,9 +451,7 @@ class AccountingType(models.Model):
|
||||
"""Check if that object can be edited by the given user."""
|
||||
if user.is_anonymous:
|
||||
return False
|
||||
if user.is_in_group(pk=settings.SITH_GROUP_ACCOUNTING_ADMIN_ID):
|
||||
return True
|
||||
return False
|
||||
return user.is_in_group(pk=settings.SITH_GROUP_ACCOUNTING_ADMIN_ID)
|
||||
|
||||
|
||||
class SimplifiedAccountingType(models.Model):
|
||||
|
||||
+3
-3
@@ -102,7 +102,7 @@ class TestOperation(TestCase):
|
||||
code="443", label="Ce code n'existe pas", movement_type="CREDIT"
|
||||
)
|
||||
at.save()
|
||||
l = Label.objects.create(club_account=self.journal.club_account, name="bob")
|
||||
label = Label.objects.create(club_account=self.journal.club_account, name="bob")
|
||||
self.client.force_login(User.objects.get(username="comptable"))
|
||||
self.op1 = Operation(
|
||||
journal=self.journal,
|
||||
@@ -111,7 +111,7 @@ class TestOperation(TestCase):
|
||||
remark="Test bilan",
|
||||
mode="CASH",
|
||||
done=True,
|
||||
label=l,
|
||||
label=label,
|
||||
accounting_type=at,
|
||||
target_type="USER",
|
||||
target_id=self.skia.id,
|
||||
@@ -124,7 +124,7 @@ class TestOperation(TestCase):
|
||||
remark="Test bilan",
|
||||
mode="CASH",
|
||||
done=True,
|
||||
label=l,
|
||||
label=label,
|
||||
accounting_type=at,
|
||||
target_type="USER",
|
||||
target_id=self.skia.id,
|
||||
|
||||
+35
-1
@@ -15,7 +15,41 @@
|
||||
|
||||
from django.urls import path
|
||||
|
||||
from accounting.views import *
|
||||
from accounting.views import (
|
||||
AccountingTypeCreateView,
|
||||
AccountingTypeEditView,
|
||||
AccountingTypeListView,
|
||||
BankAccountCreateView,
|
||||
BankAccountDeleteView,
|
||||
BankAccountDetailView,
|
||||
BankAccountEditView,
|
||||
BankAccountListView,
|
||||
ClubAccountCreateView,
|
||||
ClubAccountDeleteView,
|
||||
ClubAccountDetailView,
|
||||
ClubAccountEditView,
|
||||
CompanyCreateView,
|
||||
CompanyEditView,
|
||||
CompanyListView,
|
||||
JournalAccountingStatementView,
|
||||
JournalCreateView,
|
||||
JournalDeleteView,
|
||||
JournalDetailView,
|
||||
JournalEditView,
|
||||
JournalNatureStatementView,
|
||||
JournalPersonStatementView,
|
||||
LabelCreateView,
|
||||
LabelDeleteView,
|
||||
LabelEditView,
|
||||
LabelListView,
|
||||
OperationCreateView,
|
||||
OperationEditView,
|
||||
OperationPDFView,
|
||||
RefoundAccountView,
|
||||
SimplifiedAccountingTypeCreateView,
|
||||
SimplifiedAccountingTypeEditView,
|
||||
SimplifiedAccountingTypeListView,
|
||||
)
|
||||
|
||||
urlpatterns = [
|
||||
# Accounting types
|
||||
|
||||
+15
-20
@@ -182,7 +182,7 @@ class ClubAccountCreateView(CanCreateMixin, CreateView):
|
||||
|
||||
def get_initial(self):
|
||||
ret = super().get_initial()
|
||||
if "parent" in self.request.GET.keys():
|
||||
if "parent" in self.request.GET:
|
||||
obj = BankAccount.objects.filter(id=int(self.request.GET["parent"])).first()
|
||||
if obj is not None:
|
||||
ret["bank_account"] = obj.id
|
||||
@@ -264,7 +264,7 @@ class JournalCreateView(CanCreateMixin, CreateView):
|
||||
|
||||
def get_initial(self):
|
||||
ret = super().get_initial()
|
||||
if "parent" in self.request.GET.keys():
|
||||
if "parent" in self.request.GET:
|
||||
obj = ClubAccount.objects.filter(id=int(self.request.GET["parent"])).first()
|
||||
if obj is not None:
|
||||
ret["club_account"] = obj.id
|
||||
@@ -362,7 +362,7 @@ class OperationForm(forms.ModelForm):
|
||||
|
||||
def clean(self):
|
||||
self.cleaned_data = super().clean()
|
||||
if "target_type" in self.cleaned_data.keys():
|
||||
if "target_type" in self.cleaned_data:
|
||||
if (
|
||||
self.cleaned_data.get("user") is None
|
||||
and self.cleaned_data.get("club") is None
|
||||
@@ -633,19 +633,17 @@ class JournalNatureStatementView(JournalTabsMixin, CanViewMixin, DetailView):
|
||||
ret = collections.OrderedDict()
|
||||
statement = collections.OrderedDict()
|
||||
total_sum = 0
|
||||
for sat in [None] + list(
|
||||
SimplifiedAccountingType.objects.order_by("label").all()
|
||||
):
|
||||
for sat in [
|
||||
None,
|
||||
*list(SimplifiedAccountingType.objects.order_by("label")),
|
||||
]:
|
||||
amount = queryset.filter(
|
||||
accounting_type__movement_type=movement_type, simpleaccounting_type=sat
|
||||
).aggregate(amount_sum=Sum("amount"))["amount_sum"]
|
||||
if sat:
|
||||
sat = sat.label
|
||||
else:
|
||||
sat = ""
|
||||
label = sat.label if sat is not None else ""
|
||||
if amount:
|
||||
total_sum += amount
|
||||
statement[sat] = amount
|
||||
statement[label] = amount
|
||||
ret[movement_type] = statement
|
||||
ret[movement_type + "_sum"] = total_sum
|
||||
return ret
|
||||
@@ -668,15 +666,12 @@ class JournalNatureStatementView(JournalTabsMixin, CanViewMixin, DetailView):
|
||||
self.statement(self.object.operations.filter(label=None).all(), "DEBIT")
|
||||
)
|
||||
statement[_("No label operations")] = no_label_statement
|
||||
for l in labels:
|
||||
for label in labels:
|
||||
l_stmt = collections.OrderedDict()
|
||||
l_stmt.update(
|
||||
self.statement(self.object.operations.filter(label=l).all(), "CREDIT")
|
||||
)
|
||||
l_stmt.update(
|
||||
self.statement(self.object.operations.filter(label=l).all(), "DEBIT")
|
||||
)
|
||||
statement[l] = l_stmt
|
||||
journals = self.object.operations.filter(label=label).all()
|
||||
l_stmt.update(self.statement(journals, "CREDIT"))
|
||||
l_stmt.update(self.statement(journals, "DEBIT"))
|
||||
statement[label] = l_stmt
|
||||
return statement
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
@@ -798,7 +793,7 @@ class LabelCreateView(
|
||||
|
||||
def get_initial(self):
|
||||
ret = super().get_initial()
|
||||
if "parent" in self.request.GET.keys():
|
||||
if "parent" in self.request.GET:
|
||||
obj = ClubAccount.objects.filter(id=int(self.request.GET["parent"])).first()
|
||||
if obj is not None:
|
||||
ret["club_account"] = obj.id
|
||||
|
||||
+1
-1
@@ -7,7 +7,7 @@
|
||||
},
|
||||
"files": {
|
||||
"ignoreUnknown": false,
|
||||
"ignore": ["core/static/vendored", "*.min.*", "staticfiles/generated"]
|
||||
"ignore": ["*.min.*", "staticfiles/generated"]
|
||||
},
|
||||
"formatter": {
|
||||
"enabled": true,
|
||||
|
||||
+3
-3
@@ -111,8 +111,8 @@ class MailingForm(forms.Form):
|
||||
"""Convert given users into real users and check their validity."""
|
||||
cleaned_data = super().clean()
|
||||
users = []
|
||||
for user in cleaned_data["subscription_users"]:
|
||||
user = User.objects.filter(id=user).first()
|
||||
for user_id in cleaned_data["subscription_users"]:
|
||||
user = User.objects.filter(id=user_id).first()
|
||||
if not user:
|
||||
raise forms.ValidationError(
|
||||
_("One of the selected users doesn't exist"), code="invalid"
|
||||
@@ -128,7 +128,7 @@ class MailingForm(forms.Form):
|
||||
def clean(self):
|
||||
cleaned_data = super().clean()
|
||||
|
||||
if not "action" in cleaned_data:
|
||||
if "action" not in cleaned_data:
|
||||
# If there is no action provided, we can stop here
|
||||
raise forms.ValidationError(_("An action is required"), code="invalid")
|
||||
|
||||
|
||||
+1
-3
@@ -389,9 +389,7 @@ class Membership(models.Model):
|
||||
if user.is_root or user.is_board_member:
|
||||
return True
|
||||
membership = self.club.get_membership_for(user)
|
||||
if membership is not None and membership.role >= self.role:
|
||||
return True
|
||||
return False
|
||||
return membership is not None and membership.role >= self.role
|
||||
|
||||
def delete(self, *args, **kwargs):
|
||||
super().delete(*args, **kwargs)
|
||||
|
||||
+31
-20
@@ -24,7 +24,32 @@
|
||||
|
||||
from django.urls import path
|
||||
|
||||
from club.views import *
|
||||
from club.views import (
|
||||
ClubCreateView,
|
||||
ClubEditPropView,
|
||||
ClubEditView,
|
||||
ClubListView,
|
||||
ClubMailingView,
|
||||
ClubMembersView,
|
||||
ClubOldMembersView,
|
||||
ClubPageEditView,
|
||||
ClubPageHistView,
|
||||
ClubRevView,
|
||||
ClubSellingCSVView,
|
||||
ClubSellingView,
|
||||
ClubStatView,
|
||||
ClubToolsView,
|
||||
ClubView,
|
||||
MailingAutoGenerationView,
|
||||
MailingDeleteView,
|
||||
MailingSubscriptionDeleteView,
|
||||
MembershipDeleteView,
|
||||
MembershipSetOldView,
|
||||
PosterCreateView,
|
||||
PosterDeleteView,
|
||||
PosterEditView,
|
||||
PosterListView,
|
||||
)
|
||||
|
||||
urlpatterns = [
|
||||
path("", ClubListView.as_view(), name="club_list"),
|
||||
@@ -32,32 +57,20 @@ urlpatterns = [
|
||||
path("stats/", ClubStatView.as_view(), name="club_stats"),
|
||||
path("<int:club_id>/", ClubView.as_view(), name="club_view"),
|
||||
path(
|
||||
"<int:club_id>/rev/<int:rev_id>/",
|
||||
ClubRevView.as_view(),
|
||||
name="club_view_rev",
|
||||
"<int:club_id>/rev/<int:rev_id>/", ClubRevView.as_view(), name="club_view_rev"
|
||||
),
|
||||
path("<int:club_id>/hist/", ClubPageHistView.as_view(), name="club_hist"),
|
||||
path("<int:club_id>/edit/", ClubEditView.as_view(), name="club_edit"),
|
||||
path(
|
||||
"<int:club_id>/edit/page/",
|
||||
ClubPageEditView.as_view(),
|
||||
name="club_edit_page",
|
||||
),
|
||||
path("<int:club_id>/edit/page/", ClubPageEditView.as_view(), name="club_edit_page"),
|
||||
path("<int:club_id>/members/", ClubMembersView.as_view(), name="club_members"),
|
||||
path(
|
||||
"<int:club_id>/elderlies/",
|
||||
ClubOldMembersView.as_view(),
|
||||
name="club_old_members",
|
||||
),
|
||||
path("<int:club_id>/sellings/", ClubSellingView.as_view(), name="club_sellings"),
|
||||
path(
|
||||
"<int:club_id>/sellings/",
|
||||
ClubSellingView.as_view(),
|
||||
name="club_sellings",
|
||||
),
|
||||
path(
|
||||
"<int:club_id>/sellings/csv/",
|
||||
ClubSellingCSVView.as_view(),
|
||||
name="sellings_csv",
|
||||
"<int:club_id>/sellings/csv/", ClubSellingCSVView.as_view(), name="sellings_csv"
|
||||
),
|
||||
path("<int:club_id>/prop/", ClubEditPropView.as_view(), name="club_prop"),
|
||||
path("<int:club_id>/tools/", ClubToolsView.as_view(), name="tools"),
|
||||
@@ -89,9 +102,7 @@ urlpatterns = [
|
||||
),
|
||||
path("<int:club_id>/poster/", PosterListView.as_view(), name="poster_list"),
|
||||
path(
|
||||
"<int:club_id>/poster/create/",
|
||||
PosterCreateView.as_view(),
|
||||
name="poster_create",
|
||||
"<int:club_id>/poster/create/", PosterCreateView.as_view(), name="poster_create"
|
||||
),
|
||||
path(
|
||||
"<int:club_id>/poster/<int:poster_id>/edit/",
|
||||
|
||||
+4
-5
@@ -397,7 +397,8 @@ class ClubSellingCSVView(ClubSellingView):
|
||||
row.append(selling.customer.user.get_display_name())
|
||||
else:
|
||||
row.append("")
|
||||
row = row + [
|
||||
row = [
|
||||
*row,
|
||||
selling.label,
|
||||
selling.quantity,
|
||||
selling.quantity * selling.unit_price,
|
||||
@@ -408,7 +409,7 @@ class ClubSellingCSVView(ClubSellingView):
|
||||
row.append(selling.product.purchase_price)
|
||||
row.append(selling.product.selling_price - selling.product.purchase_price)
|
||||
else:
|
||||
row = row + ["", "", ""]
|
||||
row = [*row, "", "", ""]
|
||||
return row
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
@@ -622,9 +623,7 @@ class ClubMailingView(ClubTabsMixin, CanEditMixin, DetailFormView):
|
||||
def remove_subscription(self, cleaned_data):
|
||||
"""Remove specified users from a mailing list."""
|
||||
fields = [
|
||||
cleaned_data[key]
|
||||
for key in cleaned_data.keys()
|
||||
if key.startswith("removal_")
|
||||
val for key, val in cleaned_data.items() if key.startswith("removal_")
|
||||
]
|
||||
for field in fields:
|
||||
for sub in field:
|
||||
|
||||
+1
-1
@@ -15,7 +15,7 @@
|
||||
from django.contrib import admin
|
||||
from haystack.admin import SearchModelAdmin
|
||||
|
||||
from com.models import *
|
||||
from com.models import News, Poster, Screen, Sith, Weekmail
|
||||
|
||||
|
||||
@admin.register(News)
|
||||
|
||||
+34
-19
@@ -16,7 +16,36 @@
|
||||
from django.urls import path
|
||||
|
||||
from club.views import MailingDeleteView
|
||||
from com.views import *
|
||||
from com.views import (
|
||||
AlertMsgEditView,
|
||||
InfoMsgEditView,
|
||||
MailingListAdminView,
|
||||
MailingModerateView,
|
||||
NewsAdminListView,
|
||||
NewsCreateView,
|
||||
NewsDeleteView,
|
||||
NewsDetailView,
|
||||
NewsEditView,
|
||||
NewsListView,
|
||||
NewsModerateView,
|
||||
PosterCreateView,
|
||||
PosterDeleteView,
|
||||
PosterEditView,
|
||||
PosterListView,
|
||||
PosterModerateListView,
|
||||
PosterModerateView,
|
||||
ScreenCreateView,
|
||||
ScreenDeleteView,
|
||||
ScreenEditView,
|
||||
ScreenListView,
|
||||
ScreenSlideshowView,
|
||||
WeekmailArticleCreateView,
|
||||
WeekmailArticleDeleteView,
|
||||
WeekmailArticleEditView,
|
||||
WeekmailDestinationEditView,
|
||||
WeekmailEditView,
|
||||
WeekmailPreviewView,
|
||||
)
|
||||
|
||||
urlpatterns = [
|
||||
path("sith/edit/alert/", AlertMsgEditView.as_view(), name="alert_edit"),
|
||||
@@ -46,15 +75,9 @@ urlpatterns = [
|
||||
path("news/", NewsListView.as_view(), name="news_list"),
|
||||
path("news/admin/", NewsAdminListView.as_view(), name="news_admin_list"),
|
||||
path("news/create/", NewsCreateView.as_view(), name="news_new"),
|
||||
path("news/<int:news_id>/delete/", NewsDeleteView.as_view(), name="news_delete"),
|
||||
path(
|
||||
"news/<int:news_id>/delete/",
|
||||
NewsDeleteView.as_view(),
|
||||
name="news_delete",
|
||||
),
|
||||
path(
|
||||
"news/<int:news_id>/moderate/",
|
||||
NewsModerateView.as_view(),
|
||||
name="news_moderate",
|
||||
"news/<int:news_id>/moderate/", NewsModerateView.as_view(), name="news_moderate"
|
||||
),
|
||||
path("news/<int:news_id>/edit/", NewsEditView.as_view(), name="news_edit"),
|
||||
path("news/<int:news_id>/", NewsDetailView.as_view(), name="news_detail"),
|
||||
@@ -71,11 +94,7 @@ urlpatterns = [
|
||||
),
|
||||
path("poster/", PosterListView.as_view(), name="poster_list"),
|
||||
path("poster/create/", PosterCreateView.as_view(), name="poster_create"),
|
||||
path(
|
||||
"poster/<int:poster_id>/edit/",
|
||||
PosterEditView.as_view(),
|
||||
name="poster_edit",
|
||||
),
|
||||
path("poster/<int:poster_id>/edit/", PosterEditView.as_view(), name="poster_edit"),
|
||||
path(
|
||||
"poster/<int:poster_id>/delete/",
|
||||
PosterDeleteView.as_view(),
|
||||
@@ -98,11 +117,7 @@ urlpatterns = [
|
||||
ScreenSlideshowView.as_view(),
|
||||
name="screen_slideshow",
|
||||
),
|
||||
path(
|
||||
"screen/<int:screen_id>/edit/",
|
||||
ScreenEditView.as_view(),
|
||||
name="screen_edit",
|
||||
),
|
||||
path("screen/<int:screen_id>/edit/", ScreenEditView.as_view(), name="screen_edit"),
|
||||
path(
|
||||
"screen/<int:screen_id>/delete/",
|
||||
ScreenDeleteView.as_view(),
|
||||
|
||||
+20
-27
@@ -86,12 +86,11 @@ class PosterForm(forms.ModelForm):
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.user = kwargs.pop("user", None)
|
||||
super().__init__(*args, **kwargs)
|
||||
if self.user:
|
||||
if not self.user.is_com_admin:
|
||||
self.fields["club"].queryset = Club.objects.filter(
|
||||
id__in=self.user.clubs_with_rights
|
||||
)
|
||||
self.fields.pop("display_time")
|
||||
if self.user and not self.user.is_com_admin:
|
||||
self.fields["club"].queryset = Club.objects.filter(
|
||||
id__in=self.user.clubs_with_rights
|
||||
)
|
||||
self.fields.pop("display_time")
|
||||
|
||||
|
||||
class ComTabsMixin(TabedViewMixin):
|
||||
@@ -312,7 +311,7 @@ class NewsCreateView(CanCreateMixin, CreateView):
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
form = self.get_form()
|
||||
if form.is_valid() and "preview" not in request.POST.keys():
|
||||
if form.is_valid() and "preview" not in request.POST:
|
||||
return self.form_valid(form)
|
||||
else:
|
||||
self.object = form.instance
|
||||
@@ -354,13 +353,13 @@ class NewsModerateView(CanEditMixin, SingleObjectMixin):
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
self.object = self.get_object()
|
||||
if "remove" in request.GET.keys():
|
||||
if "remove" in request.GET:
|
||||
self.object.is_moderated = False
|
||||
else:
|
||||
self.object.is_moderated = True
|
||||
self.object.moderator = request.user
|
||||
self.object.save()
|
||||
if "next" in self.request.GET.keys():
|
||||
if "next" in self.request.GET:
|
||||
return redirect(self.request.GET["next"])
|
||||
return redirect("com:news_admin_list")
|
||||
|
||||
@@ -424,7 +423,7 @@ class WeekmailPreviewView(ComTabsMixin, QuickNotifMixin, CanEditPropMixin, Detai
|
||||
try:
|
||||
self.object.send() # This should fail
|
||||
except SMTPRecipientsRefused as e:
|
||||
users = User.objects.filter(email__in=e.recipients.keys())
|
||||
users = User.objects.filter(email__in=e.recipients)
|
||||
for u in users:
|
||||
u.preferences.receive_weekmail = False
|
||||
u.preferences.save()
|
||||
@@ -471,7 +470,7 @@ class WeekmailEditView(ComTabsMixin, QuickNotifMixin, CanEditPropMixin, UpdateVi
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
self.object = self.get_object()
|
||||
if "up_article" in request.GET.keys():
|
||||
if "up_article" in request.GET:
|
||||
art = get_object_or_404(
|
||||
WeekmailArticle, id=request.GET["up_article"], weekmail=self.object
|
||||
)
|
||||
@@ -483,7 +482,7 @@ class WeekmailEditView(ComTabsMixin, QuickNotifMixin, CanEditPropMixin, UpdateVi
|
||||
art.save()
|
||||
prev_art.save()
|
||||
self.quick_notif_list += ["qn_success"]
|
||||
if "down_article" in request.GET.keys():
|
||||
if "down_article" in request.GET:
|
||||
art = get_object_or_404(
|
||||
WeekmailArticle, id=request.GET["down_article"], weekmail=self.object
|
||||
)
|
||||
@@ -495,7 +494,7 @@ class WeekmailEditView(ComTabsMixin, QuickNotifMixin, CanEditPropMixin, UpdateVi
|
||||
art.save()
|
||||
next_art.save()
|
||||
self.quick_notif_list += ["qn_success"]
|
||||
if "add_article" in request.GET.keys():
|
||||
if "add_article" in request.GET:
|
||||
art = get_object_or_404(
|
||||
WeekmailArticle, id=request.GET["add_article"], weekmail=None
|
||||
)
|
||||
@@ -504,7 +503,7 @@ class WeekmailEditView(ComTabsMixin, QuickNotifMixin, CanEditPropMixin, UpdateVi
|
||||
art.rank += 1
|
||||
art.save()
|
||||
self.quick_notif_list += ["qn_success"]
|
||||
if "del_article" in request.GET.keys():
|
||||
if "del_article" in request.GET:
|
||||
art = get_object_or_404(
|
||||
WeekmailArticle, id=request.GET["del_article"], weekmail=self.object
|
||||
)
|
||||
@@ -571,7 +570,7 @@ class WeekmailArticleCreateView(QuickNotifMixin, CreateView):
|
||||
)
|
||||
),
|
||||
)
|
||||
if form.is_valid() and not "preview" in request.POST.keys():
|
||||
if form.is_valid() and "preview" not in request.POST:
|
||||
return self.form_valid(form)
|
||||
else:
|
||||
return self.form_invalid(form)
|
||||
@@ -689,19 +688,13 @@ class PosterEditBaseView(UpdateView):
|
||||
template_name = "com/poster_edit.jinja"
|
||||
|
||||
def get_initial(self):
|
||||
init = {}
|
||||
try:
|
||||
init["date_begin"] = self.object.date_begin.strftime("%Y-%m-%d %H:%M:%S")
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
init["date_end"] = self.object.date_end.strftime("%Y-%m-%d %H:%M:%S")
|
||||
except Exception:
|
||||
pass
|
||||
return init
|
||||
return {
|
||||
"date_begin": self.object.date_begin.strftime("%Y-%m-%d %H:%M:%S"),
|
||||
"date_end": self.object.date_end.strftime("%Y-%m-%d %H:%M:%S"),
|
||||
}
|
||||
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
if "club_id" in kwargs and kwargs["club_id"]:
|
||||
if kwargs.get("club_id"):
|
||||
try:
|
||||
self.club = Club.objects.get(pk=kwargs["club_id"])
|
||||
except Club.DoesNotExist as e:
|
||||
@@ -737,7 +730,7 @@ class PosterDeleteBaseView(DeleteView):
|
||||
template_name = "core/delete_confirm.jinja"
|
||||
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
if "club_id" in kwargs and kwargs["club_id"]:
|
||||
if kwargs.get("club_id"):
|
||||
try:
|
||||
self.club = Club.objects.get(pk=kwargs["club_id"])
|
||||
except Club.DoesNotExist as e:
|
||||
|
||||
@@ -1,117 +0,0 @@
|
||||
import re
|
||||
from subprocess import PIPE, Popen, TimeoutExpired
|
||||
|
||||
from django.conf import settings
|
||||
from django.core.management.base import BaseCommand
|
||||
|
||||
# see https://semver.org/#is-there-a-suggested-regular-expression-regex-to-check-a-semver-string
|
||||
# added "v?"
|
||||
# Please note that this does not match the version of the three.js library.
|
||||
# Hence, you shall have to check this one by yourself
|
||||
semver_regex = re.compile(
|
||||
r"^v?"
|
||||
r"(?P<major>\d+)"
|
||||
r"\.(?P<minor>\d+)"
|
||||
r"\.(?P<patch>\d+)"
|
||||
r"(?:-(?P<prerelease>(?:\d+|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:\d+|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?"
|
||||
r"(?:\+(?P<buildmetadata>[0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$"
|
||||
)
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
help = "Checks the front dependencies are up to date."
|
||||
|
||||
def handle(self, *args, **options):
|
||||
deps = settings.SITH_FRONT_DEP_VERSIONS
|
||||
|
||||
processes = {
|
||||
url: create_process(url)
|
||||
for url in deps.keys()
|
||||
if parse_semver(deps[url]) is not None
|
||||
}
|
||||
|
||||
for url, process in processes.items():
|
||||
try:
|
||||
stdout, stderr = process.communicate(timeout=15)
|
||||
except TimeoutExpired:
|
||||
process.kill()
|
||||
self.stderr.write(self.style.WARNING("{}: timeout".format(url)))
|
||||
continue
|
||||
# error, notice, warning
|
||||
|
||||
stdout = stdout.decode("utf-8")
|
||||
stderr = stderr.decode("utf-8")
|
||||
|
||||
if stderr != "":
|
||||
self.stderr.write(self.style.WARNING(stderr.strip()))
|
||||
continue
|
||||
|
||||
# get all tags, parse them as semvers and find the biggest
|
||||
tags = list_tags(stdout)
|
||||
tags = map(parse_semver, tags)
|
||||
tags = filter(lambda tag: tag is not None, tags)
|
||||
latest_version = max(tags)
|
||||
|
||||
# cannot fail as those which fail are filtered in the processes dict creation
|
||||
current_version = parse_semver(deps[url])
|
||||
assert current_version is not None
|
||||
|
||||
if latest_version == current_version:
|
||||
msg = "{}: {}".format(url, semver_to_s(current_version))
|
||||
self.stdout.write(self.style.SUCCESS(msg))
|
||||
else:
|
||||
msg = "{}: {} < {}".format(
|
||||
url, semver_to_s(current_version), semver_to_s(latest_version)
|
||||
)
|
||||
self.stdout.write(self.style.ERROR(msg))
|
||||
|
||||
|
||||
def create_process(url):
|
||||
"""Spawn a "git ls-remote --tags" child process."""
|
||||
return Popen(["git", "ls-remote", "--tags", url], stdout=PIPE, stderr=PIPE)
|
||||
|
||||
|
||||
def list_tags(s):
|
||||
"""Parses "git ls-remote --tags" output. Takes a string."""
|
||||
tag_prefix = "refs/tags/"
|
||||
|
||||
for line in s.strip().split("\n"):
|
||||
# an example line could be:
|
||||
# "1f41e2293f9c3c1962d2d97afa666207b98a222a\trefs/tags/foo"
|
||||
parts = line.split("\t")
|
||||
|
||||
# check we have a commit ID (SHA-1 hash) and a tag name
|
||||
assert len(parts) == 2
|
||||
assert len(parts[0]) == 40
|
||||
assert parts[1].startswith(tag_prefix)
|
||||
|
||||
# avoid duplicates (a peeled tag will appear twice: as "name" and as "name^{}")
|
||||
if not parts[1].endswith("^{}"):
|
||||
yield parts[1][len(tag_prefix) :]
|
||||
|
||||
|
||||
def parse_semver(s) -> tuple[int, int, int] | None:
|
||||
"""Parse a semver string.
|
||||
|
||||
See https://semver.org
|
||||
|
||||
Returns:
|
||||
A tuple, if the parsing was successful, else None.
|
||||
In the latter case, it must probably be a prerelease
|
||||
or include build metadata.
|
||||
"""
|
||||
m = semver_regex.match(s)
|
||||
|
||||
if (
|
||||
m is None
|
||||
or m.group("prerelease") is not None
|
||||
or m.group("buildmetadata") is not None
|
||||
):
|
||||
return None
|
||||
|
||||
return int(m.group("major")), int(m.group("minor")), int(m.group("patch"))
|
||||
|
||||
|
||||
def semver_to_s(t):
|
||||
"""Expects a 3-tuple with ints and turns it into a string of type "1.2.3"."""
|
||||
return "{}.{}.{}".format(t[0], t[1], t[2])
|
||||
@@ -67,5 +67,6 @@ class Command(BaseCommand):
|
||||
subprocess.run(
|
||||
[str(Path(__file__).parent / "install_xapian.sh"), desired],
|
||||
env=dict(os.environ),
|
||||
).check_returncode()
|
||||
check=True,
|
||||
)
|
||||
self.stdout.write("Installation success")
|
||||
@@ -934,7 +934,7 @@ Welcome to the wiki page!
|
||||
# Adding subscription for sli
|
||||
s = Subscription(
|
||||
member=User.objects.filter(pk=sli.pk).first(),
|
||||
subscription_type=list(settings.SITH_SUBSCRIPTIONS.keys())[0],
|
||||
subscription_type=next(iter(settings.SITH_SUBSCRIPTIONS.keys())),
|
||||
payment_method=settings.SITH_SUBSCRIPTION_PAYMENT_METHOD[0][0],
|
||||
)
|
||||
s.subscription_start = s.compute_start()
|
||||
@@ -947,7 +947,7 @@ Welcome to the wiki page!
|
||||
# Adding subscription for Krophil
|
||||
s = Subscription(
|
||||
member=User.objects.filter(pk=krophil.pk).first(),
|
||||
subscription_type=list(settings.SITH_SUBSCRIPTIONS.keys())[0],
|
||||
subscription_type=next(iter(settings.SITH_SUBSCRIPTIONS.keys())),
|
||||
payment_method=settings.SITH_SUBSCRIPTION_PAYMENT_METHOD[0][0],
|
||||
)
|
||||
s.subscription_start = s.compute_start()
|
||||
|
||||
@@ -217,9 +217,9 @@ class Command(BaseCommand):
|
||||
UV.objects.bulk_create(uvs, ignore_conflicts=True)
|
||||
|
||||
def create_products(self):
|
||||
categories = []
|
||||
for _ in range(10):
|
||||
categories.append(ProductType(name=self.faker.text(max_nb_chars=30)))
|
||||
categories = [
|
||||
ProductType(name=self.faker.text(max_nb_chars=30)) for _ in range(10)
|
||||
]
|
||||
ProductType.objects.bulk_create(categories)
|
||||
categories = list(
|
||||
ProductType.objects.filter(name__in=[c.name for c in categories])
|
||||
@@ -254,16 +254,16 @@ class Command(BaseCommand):
|
||||
archived=bool(random.random() > 0.7),
|
||||
)
|
||||
products.append(product)
|
||||
for group in random.sample(groups, k=random.randint(0, 3)):
|
||||
# there will be products without buying groups
|
||||
# but there are also such products in the real database
|
||||
buying_groups.append(
|
||||
Product.buying_groups.through(product=product, group=group)
|
||||
)
|
||||
for counter in random.sample(counters, random.randint(0, 4)):
|
||||
selling_places.append(
|
||||
Counter.products.through(counter=counter, product=product)
|
||||
)
|
||||
# there will be products without buying groups
|
||||
# but there are also such products in the real database
|
||||
buying_groups.extend(
|
||||
Product.buying_groups.through(product=product, group=group)
|
||||
for group in random.sample(groups, k=random.randint(0, 3))
|
||||
)
|
||||
selling_places.extend(
|
||||
Counter.products.through(counter=counter, product=product)
|
||||
for counter in random.sample(counters, random.randint(0, 4))
|
||||
)
|
||||
Product.objects.bulk_create(products)
|
||||
Product.buying_groups.through.objects.bulk_create(buying_groups)
|
||||
Counter.products.through.objects.bulk_create(selling_places)
|
||||
|
||||
+31
-54
@@ -174,7 +174,7 @@ def validate_promo(value: int) -> None:
|
||||
)
|
||||
|
||||
|
||||
def get_group(*, pk: int = None, name: str = None) -> Group | None:
|
||||
def get_group(*, pk: int | None = None, name: str | None = None) -> Group | None:
|
||||
"""Search for a group by its primary key or its name.
|
||||
Either one of the two must be set.
|
||||
|
||||
@@ -445,7 +445,7 @@ class User(AbstractBaseUser):
|
||||
else:
|
||||
return 0
|
||||
|
||||
def is_in_group(self, *, pk: int = None, name: str = None) -> bool:
|
||||
def is_in_group(self, *, pk: int | None = None, name: str | None = None) -> bool:
|
||||
"""Check if this user is in the given group.
|
||||
Either a group id or a group name must be provided.
|
||||
If both are passed, only the id will be considered.
|
||||
@@ -649,7 +649,7 @@ class User(AbstractBaseUser):
|
||||
continue
|
||||
links = list(User.godfathers.through.objects.filter(**{key: self.id}))
|
||||
res.extend(links)
|
||||
for _ in range(1, depth):
|
||||
for _ in range(1, depth): # noqa: F402 we don't care about gettext here
|
||||
ids = [getattr(c, reverse_key) for c in links]
|
||||
links = list(
|
||||
User.godfathers.through.objects.filter(
|
||||
@@ -703,9 +703,7 @@ class User(AbstractBaseUser):
|
||||
return True
|
||||
if hasattr(obj, "owner_group") and self.is_in_group(pk=obj.owner_group.id):
|
||||
return True
|
||||
if self.is_root:
|
||||
return True
|
||||
return False
|
||||
return self.is_root
|
||||
|
||||
def can_edit(self, obj):
|
||||
"""Determine if the object can be edited by the user."""
|
||||
@@ -717,9 +715,7 @@ class User(AbstractBaseUser):
|
||||
return True
|
||||
if isinstance(obj, User) and obj == self:
|
||||
return True
|
||||
if self.is_owner(obj):
|
||||
return True
|
||||
return False
|
||||
return self.is_owner(obj)
|
||||
|
||||
def can_view(self, obj):
|
||||
"""Determine if the object can be viewed by the user."""
|
||||
@@ -729,9 +725,7 @@ class User(AbstractBaseUser):
|
||||
for pk in obj.view_groups.values_list("pk", flat=True):
|
||||
if self.is_in_group(pk=pk):
|
||||
return True
|
||||
if self.can_edit(obj):
|
||||
return True
|
||||
return False
|
||||
return self.can_edit(obj)
|
||||
|
||||
def can_be_edited_by(self, user):
|
||||
return user.is_root or user.is_board_member
|
||||
@@ -759,23 +753,17 @@ class User(AbstractBaseUser):
|
||||
|
||||
@cached_property
|
||||
def preferences(self):
|
||||
try:
|
||||
if hasattr(self, "_preferences"):
|
||||
return self._preferences
|
||||
except:
|
||||
prefs = Preferences(user=self)
|
||||
prefs.save()
|
||||
return prefs
|
||||
return Preferences.objects.create(user=self)
|
||||
|
||||
@cached_property
|
||||
def forum_infos(self):
|
||||
try:
|
||||
if hasattr(self, "_forum_infos"):
|
||||
return self._forum_infos
|
||||
except:
|
||||
from forum.models import ForumUserInfo
|
||||
from forum.models import ForumUserInfo
|
||||
|
||||
infos = ForumUserInfo(user=self)
|
||||
infos.save()
|
||||
return infos
|
||||
return ForumUserInfo.objects.create(user=self)
|
||||
|
||||
@cached_property
|
||||
def clubs_with_rights(self) -> list[Club]:
|
||||
@@ -840,7 +828,7 @@ class AnonymousUser(AuthAnonymousUser):
|
||||
def favorite_topics(self):
|
||||
raise PermissionDenied
|
||||
|
||||
def is_in_group(self, *, pk: int = None, name: str = None) -> bool:
|
||||
def is_in_group(self, *, pk: int | None = None, name: str | None = None) -> bool:
|
||||
"""The anonymous user is only in the public group."""
|
||||
allowed_id = settings.SITH_GROUP_PUBLIC_ID
|
||||
if pk is not None:
|
||||
@@ -867,9 +855,7 @@ class AnonymousUser(AuthAnonymousUser):
|
||||
and obj.view_groups.filter(id=settings.SITH_GROUP_PUBLIC_ID).exists()
|
||||
):
|
||||
return True
|
||||
if hasattr(obj, "can_be_viewed_by") and obj.can_be_viewed_by(self):
|
||||
return True
|
||||
return False
|
||||
return hasattr(obj, "can_be_viewed_by") and obj.can_be_viewed_by(self)
|
||||
|
||||
def get_display_name(self):
|
||||
return _("Visitor")
|
||||
@@ -1070,7 +1056,7 @@ class SithFile(models.Model):
|
||||
]:
|
||||
self.file.delete()
|
||||
self.file = None
|
||||
except:
|
||||
except: # noqa E722 I don't know the exception that can be raised
|
||||
self.file = None
|
||||
self.mime_type = "inode/directory"
|
||||
if self.is_file and (self.file is None or self.file == ""):
|
||||
@@ -1196,12 +1182,12 @@ class SithFile(models.Model):
|
||||
return Album.objects.filter(id=self.id).first()
|
||||
|
||||
def get_parent_list(self):
|
||||
l = []
|
||||
p = self.parent
|
||||
while p is not None:
|
||||
l.append(p)
|
||||
p = p.parent
|
||||
return l
|
||||
parents = []
|
||||
current = self.parent
|
||||
while current is not None:
|
||||
parents.append(current)
|
||||
current = current.parent
|
||||
return parents
|
||||
|
||||
def get_parent_path(self):
|
||||
return "/" + "/".join([p.name for p in self.get_parent_list()[::-1]])
|
||||
@@ -1359,22 +1345,18 @@ class Page(models.Model):
|
||||
if hasattr(self, "club") and self.club.can_be_edited_by(user):
|
||||
# Override normal behavior for clubs
|
||||
return True
|
||||
if self.name == settings.SITH_CLUB_ROOT_PAGE and user.is_board_member:
|
||||
return True
|
||||
return False
|
||||
return self.name == settings.SITH_CLUB_ROOT_PAGE and user.is_board_member
|
||||
|
||||
def can_be_viewed_by(self, user):
|
||||
if self.is_club_page:
|
||||
return True
|
||||
return False
|
||||
return self.is_club_page
|
||||
|
||||
def get_parent_list(self):
|
||||
l = []
|
||||
p = self.parent
|
||||
while p is not None:
|
||||
l.append(p)
|
||||
p = p.parent
|
||||
return l
|
||||
parents = []
|
||||
current = self.parent
|
||||
while current is not None:
|
||||
parents.append(current)
|
||||
current = current.parent
|
||||
return parents
|
||||
|
||||
def is_locked(self):
|
||||
"""Is True if the page is locked, False otherwise.
|
||||
@@ -1386,7 +1368,6 @@ class Page(models.Model):
|
||||
if self.lock_timeout and (
|
||||
timezone.now() - self.lock_timeout > timedelta(minutes=5)
|
||||
):
|
||||
# print("Lock timed out")
|
||||
self.unset_lock()
|
||||
return (
|
||||
self.lock_user
|
||||
@@ -1401,7 +1382,6 @@ class Page(models.Model):
|
||||
self.lock_user = user
|
||||
self.lock_timeout = timezone.now()
|
||||
super().save()
|
||||
# print("Locking page")
|
||||
|
||||
def set_lock_recursive(self, user):
|
||||
"""Locks recursively all the child pages for editing properties."""
|
||||
@@ -1420,7 +1400,6 @@ class Page(models.Model):
|
||||
self.lock_user = None
|
||||
self.lock_timeout = None
|
||||
super().save()
|
||||
# print("Unlocking page")
|
||||
|
||||
def get_lock(self):
|
||||
"""Returns the page's mutex containing the time and the user in a dict."""
|
||||
@@ -1435,13 +1414,11 @@ class Page(models.Model):
|
||||
"""
|
||||
if self.parent is None:
|
||||
return self.name
|
||||
return "/".join([self.parent.get_full_name(), self.name])
|
||||
return f"{self.parent.get_full_name()}/{self.name}"
|
||||
|
||||
def get_display_name(self):
|
||||
try:
|
||||
return self.revisions.last().title
|
||||
except:
|
||||
return self.name
|
||||
rev = self.revisions.last()
|
||||
return rev.title if rev is not None else self.name
|
||||
|
||||
@cached_property
|
||||
def is_club_page(self):
|
||||
|
||||
@@ -1,265 +0,0 @@
|
||||
/**
|
||||
* Builders to use Select2 in our templates.
|
||||
*
|
||||
* This comes with two flavours : local data or remote data.
|
||||
*
|
||||
* # Local data source
|
||||
*
|
||||
* To use local data source, you must define an array
|
||||
* in your JS code, having the fields `id` and `text`.
|
||||
*
|
||||
* ```js
|
||||
* const data = [
|
||||
* {id: 1, text: "foo"},
|
||||
* {id: 2, text: "bar"},
|
||||
* ];
|
||||
* document.addEventListener("DOMContentLoaded", () => sithSelect2({
|
||||
* element: document.getElementById("select2-input"),
|
||||
* dataSource: localDataSource(data)
|
||||
* }));
|
||||
* ```
|
||||
*
|
||||
* You can also define a callback that return ids to exclude :
|
||||
*
|
||||
* ```js
|
||||
* const data = [
|
||||
* {id: 1, text: "foo"},
|
||||
* {id: 2, text: "bar"},
|
||||
* {id: 3, text: "to exclude"},
|
||||
* ];
|
||||
* document.addEventListener("DOMContentLoaded", () => sithSelect2({
|
||||
* element: document.getElementById("select2-input"),
|
||||
* dataSource: localDataSource(data, {
|
||||
* excluded: () => data.filter((i) => i.text === "to exclude").map((i) => parseInt(i))
|
||||
* })
|
||||
* }));
|
||||
* ```
|
||||
*
|
||||
* # Remote data source
|
||||
*
|
||||
* Select2 with remote data sources are similar to those with local
|
||||
* data, but with some more parameters, like `resultConverter`,
|
||||
* which takes a callback that must return a `Select2Object`.
|
||||
*
|
||||
* ```js
|
||||
* document.addEventListener("DOMContentLoaded", () => sithSelect2({
|
||||
* element: document.getElementById("select2-input"),
|
||||
* dataSource: remoteDataSource("/api/user/search", {
|
||||
* excluded: () => [1, 2], // exclude users 1 and 2 from the search
|
||||
* resultConverter: (user) => Object({id: user.id, text: user.firstName})
|
||||
* })
|
||||
* }));
|
||||
* ```
|
||||
*
|
||||
* # Overrides
|
||||
*
|
||||
* Dealing with a select2 may be complex.
|
||||
* That's why, when defining a select,
|
||||
* you may add an override parameter,
|
||||
* in which you can declare any parameter defined in the
|
||||
* Select2 documentation.
|
||||
*
|
||||
* ```js
|
||||
* document.addEventListener("DOMContentLoaded", () => sithSelect2({
|
||||
* element: document.getElementById("select2-input"),
|
||||
* dataSource: remoteDataSource("/api/user/search", {
|
||||
* resultConverter: (user) => Object({id: user.id, text: user.firstName}),
|
||||
* overrides: {
|
||||
* delay: 500
|
||||
* }
|
||||
* })
|
||||
* }));
|
||||
* ```
|
||||
*
|
||||
* # Caveats with exclude
|
||||
*
|
||||
* With local data source, select2 evaluates the data only once.
|
||||
* Thus, modify the exclude after the initialisation is a no-op.
|
||||
*
|
||||
* With remote data source, the exclude list will be evaluated
|
||||
* after each api response.
|
||||
* It makes it possible to bind the data returned by the callback
|
||||
* to some reactive data, thus making the exclude list dynamic.
|
||||
*
|
||||
* # Images
|
||||
*
|
||||
* Sometimes, you would like to display an image besides
|
||||
* the text on the select items.
|
||||
* In this case, fill the `pictureGetter` option :
|
||||
*
|
||||
* ```js
|
||||
* document.addEventListener("DOMContentLoaded", () => sithSelect2({
|
||||
* element: document.getElementById("select2-input"),
|
||||
* dataSource: remoteDataSource("/api/user/search", {
|
||||
* resultConverter: (user) => Object({id: user.id, text: user.firstName})
|
||||
* })
|
||||
* pictureGetter: (user) => user.profilePict,
|
||||
* }));
|
||||
* ```
|
||||
*
|
||||
* # Binding with alpine
|
||||
*
|
||||
* You can declare your select2 component in an Alpine data.
|
||||
*
|
||||
* ```html
|
||||
* <body>
|
||||
* <div x-data="select2_test">
|
||||
* <select x-ref="search" x-ref="select"></select>
|
||||
* <p x-text="currentSelection.id"></p>
|
||||
* <p x-text="currentSelection.text"></p>
|
||||
* </div>
|
||||
* </body>
|
||||
*
|
||||
* <script>
|
||||
* document.addEventListener("alpine:init", () => {
|
||||
* Alpine.data("select2_test", () => ({
|
||||
* selector: undefined,
|
||||
* currentSelect: {id: "", text: ""},
|
||||
*
|
||||
* init() {
|
||||
* this.selector = sithSelect2({
|
||||
* element: $(this.$refs.select),
|
||||
* dataSource: localDataSource(
|
||||
* [{id: 1, text: "foo"}, {id: 2, text: "bar"}]
|
||||
* ),
|
||||
* });
|
||||
* this.selector.on("select2:select", (event) => {
|
||||
* // select2 => Alpine signals here
|
||||
* this.currentSelect = this.selector.select2("data")
|
||||
* });
|
||||
* this.$watch("currentSelected" (value) => {
|
||||
* // Alpine => select2 signals here
|
||||
* });
|
||||
* },
|
||||
* }));
|
||||
* })
|
||||
* </script>
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef Select2Object
|
||||
* @property {number} id
|
||||
* @property {string} text
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef Select2Options
|
||||
* @property {Element} element
|
||||
* @property {Object} dataSource
|
||||
* the data source, built with `localDataSource` or `remoteDataSource`
|
||||
* @property {number[]} excluded A list of ids to exclude from search
|
||||
* @property {undefined | function(Object): string} pictureGetter
|
||||
* A callback to get the picture field from the API response
|
||||
* @property {Object | undefined} overrides
|
||||
* Any other select2 parameter to apply on the config
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param {Select2Options} options
|
||||
*/
|
||||
// biome-ignore lint/correctness/noUnusedVariables: used in other scripts
|
||||
function sithSelect2(options) {
|
||||
const elem = $(options.element);
|
||||
return elem.select2({
|
||||
theme: elem[0].multiple ? "classic" : "default",
|
||||
minimumInputLength: 2,
|
||||
templateResult: selectItemBuilder(options.pictureGetter),
|
||||
...options.dataSource,
|
||||
...(options.overrides || {}),
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef LocalSourceOptions
|
||||
* @property {undefined | function(): number[]} excluded
|
||||
* A callback to the ids to exclude from the search
|
||||
*/
|
||||
|
||||
/**
|
||||
* Build a data source for a Select2 from a local array
|
||||
* @param {Select2Object[]} source The array containing the data
|
||||
* @param {RemoteSourceOptions} options
|
||||
*/
|
||||
// biome-ignore lint/correctness/noUnusedVariables: used in other scripts
|
||||
function localDataSource(source, options) {
|
||||
if (options.excluded) {
|
||||
const ids = options.excluded();
|
||||
return { data: source.filter((i) => !ids.includes(i.id)) };
|
||||
}
|
||||
return { data: source };
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef RemoteSourceOptions
|
||||
* @property {undefined | function(): number[]} excluded
|
||||
* A callback to the ids to exclude from the search
|
||||
* @property {undefined | function(): Select2Object} resultConverter
|
||||
* A converter for a value coming from the remote api
|
||||
* @property {undefined | Object} overrides
|
||||
* Any other select2 parameter to apply on the config
|
||||
*/
|
||||
|
||||
/**
|
||||
* Build a data source for a Select2 from a remote url
|
||||
* @param {string} source The url of the endpoint
|
||||
* @param {RemoteSourceOptions} options
|
||||
*/
|
||||
|
||||
// biome-ignore lint/correctness/noUnusedVariables: used in other scripts
|
||||
function remoteDataSource(source, options) {
|
||||
jQuery.ajaxSettings.traditional = true;
|
||||
const params = {
|
||||
url: source,
|
||||
dataType: "json",
|
||||
cache: true,
|
||||
delay: 250,
|
||||
data: function (params) {
|
||||
return {
|
||||
search: params.term,
|
||||
exclude: [
|
||||
...(this.val() || []).map((i) => Number.parseInt(i)),
|
||||
...(options.excluded ? options.excluded() : []),
|
||||
],
|
||||
};
|
||||
},
|
||||
};
|
||||
if (options.resultConverter) {
|
||||
params.processResults = (data) => ({
|
||||
results: data.results.map(options.resultConverter),
|
||||
});
|
||||
}
|
||||
if (options.overrides) {
|
||||
Object.assign(params, options.overrides);
|
||||
}
|
||||
return { ajax: params };
|
||||
}
|
||||
|
||||
// biome-ignore lint/correctness/noUnusedVariables: used in other scripts
|
||||
function itemFormatter(user) {
|
||||
if (user.loading) {
|
||||
return user.text;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a function to display the results
|
||||
* @param {null | function(Object):string} pictureGetter
|
||||
* @return {function(string): jQuery|HTMLElement}
|
||||
*/
|
||||
function selectItemBuilder(pictureGetter) {
|
||||
return (item) => {
|
||||
const picture = typeof pictureGetter === "function" ? pictureGetter(item) : null;
|
||||
const imgHtml = picture
|
||||
? `<img
|
||||
src="${pictureGetter(item)}"
|
||||
alt="${item.text}"
|
||||
onerror="this.src = '/static/core/img/unknown.jpg'"
|
||||
/>`
|
||||
: "";
|
||||
|
||||
return $(`<div class="select-item">
|
||||
${imgHtml}
|
||||
<span class="select-item-text">${item.text}</span>
|
||||
</div>`);
|
||||
};
|
||||
}
|
||||
+198
-23
@@ -28,6 +28,7 @@ input[type="file"] {
|
||||
font-size: 1.2em;
|
||||
border-radius: 5px;
|
||||
color: black;
|
||||
|
||||
&:hover {
|
||||
background: hsl(0, 0%, 83%);
|
||||
}
|
||||
@@ -63,6 +64,7 @@ textarea[type="text"],
|
||||
border-radius: 5px;
|
||||
max-width: 95%;
|
||||
}
|
||||
|
||||
textarea {
|
||||
border: none;
|
||||
text-decoration: none;
|
||||
@@ -72,6 +74,7 @@ textarea {
|
||||
border-radius: 5px;
|
||||
font-family: sans-serif;
|
||||
}
|
||||
|
||||
select {
|
||||
border: none;
|
||||
text-decoration: none;
|
||||
@@ -85,9 +88,11 @@ select {
|
||||
a:not(.button) {
|
||||
text-decoration: none;
|
||||
color: $primary-dark-color;
|
||||
|
||||
&:hover {
|
||||
color: $primary-light-color;
|
||||
}
|
||||
|
||||
&:active {
|
||||
color: $primary-color;
|
||||
}
|
||||
@@ -116,7 +121,9 @@ a:not(.button) {
|
||||
}
|
||||
|
||||
@keyframes rotate {
|
||||
100% { transform: rotate(360deg); }
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
.ib {
|
||||
@@ -143,11 +150,13 @@ a:not(.button) {
|
||||
|
||||
.collapse-header-icon {
|
||||
transition: all ease-in-out 150ms;
|
||||
|
||||
&.reverse {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.collapse-body {
|
||||
padding: 10px;
|
||||
}
|
||||
@@ -202,9 +211,11 @@ a:not(.button) {
|
||||
font-size: 0.9em;
|
||||
margin: 0.2em;
|
||||
border-radius: 0.6em;
|
||||
|
||||
.markdown {
|
||||
margin: 0.5em;
|
||||
}
|
||||
|
||||
&:before {
|
||||
font-family: FontAwesome;
|
||||
font-size: 4em;
|
||||
@@ -212,15 +223,19 @@ a:not(.button) {
|
||||
margin: 0.2em;
|
||||
}
|
||||
}
|
||||
|
||||
#info_box {
|
||||
background: $primary-neutral-light-color;
|
||||
|
||||
&:before {
|
||||
content: "\f05a";
|
||||
color: hsl(210, 100%, 56%);
|
||||
}
|
||||
}
|
||||
|
||||
#alert_box {
|
||||
background: $second-color;
|
||||
|
||||
&:before {
|
||||
content: "\f06a";
|
||||
color: $white-color;
|
||||
@@ -240,7 +255,8 @@ a:not(.button) {
|
||||
#page {
|
||||
width: 90%;
|
||||
margin: 20px auto 0;
|
||||
/*---------------------------------NAV---------------------------------*/
|
||||
|
||||
/*---------------------------------NAV---------------------------------*/
|
||||
.btn {
|
||||
font-size: 15px;
|
||||
font-weight: normal;
|
||||
@@ -252,9 +268,11 @@ a:not(.button) {
|
||||
|
||||
&.btn-blue {
|
||||
background-color: $deepblue;
|
||||
|
||||
&:not(:disabled):hover {
|
||||
background-color: darken($deepblue, 10%);
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
background-color: rgba(70, 90, 126, 0.4);
|
||||
}
|
||||
@@ -262,9 +280,11 @@ a:not(.button) {
|
||||
|
||||
&.btn-grey {
|
||||
background-color: grey;
|
||||
|
||||
&:not(:disabled):hover {
|
||||
background-color: darken(gray, 15%);
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
background-color: lighten(gray, 15%);
|
||||
}
|
||||
@@ -273,9 +293,11 @@ a:not(.button) {
|
||||
&.btn-red {
|
||||
background-color: #fc8181;
|
||||
color: black;
|
||||
|
||||
&:not(:disabled):hover {
|
||||
background-color: darken(#fc8181, 15%);
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
background-color: lighten(#fc8181, 15%);
|
||||
color: grey;
|
||||
@@ -287,12 +309,13 @@ a:not(.button) {
|
||||
}
|
||||
}
|
||||
|
||||
/*--------------------------------CONTENT------------------------------*/
|
||||
/*--------------------------------CONTENT------------------------------*/
|
||||
#quick_notif {
|
||||
width: 100%;
|
||||
margin: 0 auto;
|
||||
list-style-type: none;
|
||||
background: $second-color;
|
||||
|
||||
li {
|
||||
padding: 10px;
|
||||
}
|
||||
@@ -333,14 +356,24 @@ a:not(.button) {
|
||||
border: #fc8181 1px solid;
|
||||
}
|
||||
|
||||
.alert-title {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.alert-main {
|
||||
flex: 2;
|
||||
}
|
||||
|
||||
.alert-aside {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
|
||||
.tool_bar {
|
||||
overflow: auto;
|
||||
padding: 4px;
|
||||
|
||||
.tools {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
@@ -349,6 +382,7 @@ a:not(.button) {
|
||||
padding: 5px;
|
||||
border-radius: 6px;
|
||||
text-align: center;
|
||||
|
||||
a {
|
||||
padding: 7px;
|
||||
display: inline-block;
|
||||
@@ -358,11 +392,13 @@ a:not(.button) {
|
||||
flex: 1;
|
||||
flex-wrap: nowrap;
|
||||
white-space: nowrap;
|
||||
|
||||
&.selected_tab {
|
||||
background: $primary-color;
|
||||
color: $white-color;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: $primary-color;
|
||||
color: $white-color;
|
||||
@@ -372,7 +408,7 @@ a:not(.button) {
|
||||
}
|
||||
}
|
||||
|
||||
/*---------------------------------NEWS--------------------------------*/
|
||||
/*---------------------------------NEWS--------------------------------*/
|
||||
#news {
|
||||
display: flex;
|
||||
|
||||
@@ -385,17 +421,21 @@ a:not(.button) {
|
||||
margin: 0;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
#news_admin {
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
#right_column {
|
||||
flex: 20%;
|
||||
float: right;
|
||||
margin: 0.2em;
|
||||
}
|
||||
|
||||
#left_column {
|
||||
flex: 79%;
|
||||
margin: 0.2em;
|
||||
|
||||
h3 {
|
||||
background: $second-color;
|
||||
box-shadow: $shadow-color 1px 1px 1px;
|
||||
@@ -403,19 +443,22 @@ a:not(.button) {
|
||||
margin: 0 0 0.5em 0;
|
||||
text-transform: uppercase;
|
||||
font-size: 1.1em;
|
||||
|
||||
&:not(:first-of-type) {
|
||||
margin: 2em 0 1em 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: $small-devices) {
|
||||
|
||||
#left_column,
|
||||
#right_column {
|
||||
flex: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
/* AGENDA/BIRTHDAYS */
|
||||
/* AGENDA/BIRTHDAYS */
|
||||
#agenda,
|
||||
#birthdays {
|
||||
display: block;
|
||||
@@ -423,6 +466,7 @@ a:not(.button) {
|
||||
background: white;
|
||||
font-size: 70%;
|
||||
margin-bottom: 1em;
|
||||
|
||||
#agenda_title,
|
||||
#birthdays_title {
|
||||
margin: 0;
|
||||
@@ -435,39 +479,48 @@ a:not(.button) {
|
||||
text-transform: uppercase;
|
||||
background: $second-color;
|
||||
}
|
||||
|
||||
#agenda_content {
|
||||
overflow: auto;
|
||||
box-shadow: $shadow-color 1px 1px 1px;
|
||||
height: 20em;
|
||||
}
|
||||
|
||||
#agenda_content,
|
||||
#birthdays_content {
|
||||
.agenda_item {
|
||||
padding: 0.5em;
|
||||
margin-bottom: 0.5em;
|
||||
|
||||
&:nth-of-type(even) {
|
||||
background: $secondary-neutral-light-color;
|
||||
}
|
||||
|
||||
.agenda_time {
|
||||
font-size: 90%;
|
||||
color: grey;
|
||||
}
|
||||
|
||||
.agenda_item_content {
|
||||
p {
|
||||
margin-top: 0.2em;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ul.birthdays_year {
|
||||
margin: 0;
|
||||
list-style-type: none;
|
||||
font-weight: bold;
|
||||
> li {
|
||||
|
||||
>li {
|
||||
padding: 0.5em;
|
||||
|
||||
&:nth-child(even) {
|
||||
background: $secondary-neutral-light-color;
|
||||
}
|
||||
}
|
||||
|
||||
ul {
|
||||
margin: 0;
|
||||
margin-left: 1em;
|
||||
@@ -478,13 +531,15 @@ a:not(.button) {
|
||||
}
|
||||
}
|
||||
}
|
||||
/* END AGENDA/BIRTHDAYS */
|
||||
|
||||
/* EVENTS TODAY AND NEXT FEW DAYS */
|
||||
/* END AGENDA/BIRTHDAYS */
|
||||
|
||||
/* EVENTS TODAY AND NEXT FEW DAYS */
|
||||
.news_events_group {
|
||||
box-shadow: $shadow-color 1px 1px 1px;
|
||||
margin-left: 1em;
|
||||
margin-bottom: 0.5em;
|
||||
|
||||
.news_events_group_date {
|
||||
display: table-cell;
|
||||
padding: 0.6em;
|
||||
@@ -500,33 +555,42 @@ a:not(.button) {
|
||||
|
||||
div {
|
||||
margin: 0 auto;
|
||||
|
||||
.day {
|
||||
font-size: 1.5em;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.news_events_group_items {
|
||||
display: table-cell;
|
||||
width: 100%;
|
||||
|
||||
.news_event:nth-of-type(odd) {
|
||||
background: white;
|
||||
}
|
||||
|
||||
.news_event:nth-of-type(even) {
|
||||
background: $primary-neutral-light-color;
|
||||
}
|
||||
|
||||
.news_event {
|
||||
display: block;
|
||||
padding: 0.4em;
|
||||
|
||||
&:not(:last-child) {
|
||||
border-bottom: 1px solid grey;
|
||||
}
|
||||
|
||||
div {
|
||||
margin: 0.2em;
|
||||
}
|
||||
|
||||
h4 {
|
||||
margin-top: 1em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.club_logo {
|
||||
float: left;
|
||||
min-width: 7em;
|
||||
@@ -534,6 +598,7 @@ a:not(.button) {
|
||||
margin: 0;
|
||||
margin-right: 1em;
|
||||
margin-top: 0.8em;
|
||||
|
||||
img {
|
||||
max-height: 6em;
|
||||
max-width: 8em;
|
||||
@@ -541,16 +606,21 @@ a:not(.button) {
|
||||
margin: 0 auto;
|
||||
}
|
||||
}
|
||||
|
||||
.news_date {
|
||||
font-size: 100%;
|
||||
}
|
||||
|
||||
.news_content {
|
||||
clear: left;
|
||||
|
||||
.button_bar {
|
||||
text-align: right;
|
||||
|
||||
.fb {
|
||||
color: $faceblue;
|
||||
}
|
||||
|
||||
.twitter {
|
||||
color: $twitblue;
|
||||
}
|
||||
@@ -559,26 +629,30 @@ a:not(.button) {
|
||||
}
|
||||
}
|
||||
}
|
||||
/* END EVENTS TODAY AND NEXT FEW DAYS */
|
||||
|
||||
/* COMING SOON */
|
||||
/* END EVENTS TODAY AND NEXT FEW DAYS */
|
||||
|
||||
/* COMING SOON */
|
||||
.news_coming_soon {
|
||||
display: list-item;
|
||||
list-style-type: square;
|
||||
list-style-position: inside;
|
||||
margin-left: 1em;
|
||||
padding-left: 0;
|
||||
|
||||
a {
|
||||
font-weight: bold;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.news_date {
|
||||
font-size: 0.9em;
|
||||
}
|
||||
}
|
||||
/* END COMING SOON */
|
||||
|
||||
/* NOTICES */
|
||||
/* END COMING SOON */
|
||||
|
||||
/* NOTICES */
|
||||
.news_notice {
|
||||
margin: 0 0 1em 1em;
|
||||
padding: 0.4em;
|
||||
@@ -586,16 +660,19 @@ a:not(.button) {
|
||||
background: $secondary-neutral-light-color;
|
||||
box-shadow: $shadow-color 0 0 2px;
|
||||
border-radius: 18px 5px 18px 5px;
|
||||
|
||||
h4 {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.news_content {
|
||||
margin-left: 1em;
|
||||
}
|
||||
}
|
||||
/* END NOTICES */
|
||||
|
||||
/* CALLS */
|
||||
/* END NOTICES */
|
||||
|
||||
/* CALLS */
|
||||
.news_call {
|
||||
margin: 0 0 1em 1em;
|
||||
padding: 0.4em;
|
||||
@@ -603,21 +680,26 @@ a:not(.button) {
|
||||
background: $secondary-neutral-light-color;
|
||||
border: 1px solid grey;
|
||||
box-shadow: $shadow-color 1px 1px 1px;
|
||||
|
||||
h4 {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.news_date {
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
.news_content {
|
||||
margin-left: 1em;
|
||||
}
|
||||
}
|
||||
/* END CALLS */
|
||||
|
||||
/* END CALLS */
|
||||
|
||||
.news_empty {
|
||||
margin-left: 1em;
|
||||
}
|
||||
|
||||
.news_date {
|
||||
color: grey;
|
||||
}
|
||||
@@ -631,8 +713,8 @@ a:not(.button) {
|
||||
}
|
||||
|
||||
|
||||
.select2 {
|
||||
margin: 10px 0!important;
|
||||
.tomselected {
|
||||
margin: 10px 0 !important;
|
||||
max-width: 100%;
|
||||
min-width: 100%;
|
||||
|
||||
@@ -648,7 +730,9 @@ a:not(.button) {
|
||||
color: black;
|
||||
}
|
||||
}
|
||||
.select2-results {
|
||||
|
||||
.ts-dropdown {
|
||||
|
||||
.select-item {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
@@ -664,16 +748,39 @@ a:not(.button) {
|
||||
}
|
||||
}
|
||||
|
||||
.ts-control {
|
||||
|
||||
.item {
|
||||
.fa-times {
|
||||
margin-left: 5px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
cursor: pointer;
|
||||
background-color: #e4e4e4;
|
||||
border: 1px solid #aaa;
|
||||
border-radius: 4px;
|
||||
display: inline-block;
|
||||
margin-left: 5px;
|
||||
margin-top: 5px;
|
||||
margin-bottom: 5px;
|
||||
padding-right: 10px;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#news_details {
|
||||
display: inline-block;
|
||||
margin-top: 20px;
|
||||
padding: 0.4em;
|
||||
width: 80%;
|
||||
background: $white-color;
|
||||
|
||||
h4 {
|
||||
margin-top: 1em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.club_logo {
|
||||
display: inline-block;
|
||||
text-align: center;
|
||||
@@ -681,6 +788,7 @@ a:not(.button) {
|
||||
float: left;
|
||||
min-width: 15em;
|
||||
margin: 0;
|
||||
|
||||
img {
|
||||
max-height: 15em;
|
||||
max-width: 12em;
|
||||
@@ -689,6 +797,7 @@ a:not(.button) {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.share_button {
|
||||
border: none;
|
||||
color: white;
|
||||
@@ -700,6 +809,7 @@ a:not(.button) {
|
||||
float: right;
|
||||
display: block;
|
||||
margin-left: 0.3em;
|
||||
|
||||
&:hover {
|
||||
color: lightgrey;
|
||||
}
|
||||
@@ -731,26 +841,32 @@ a:not(.button) {
|
||||
#poster_edit,
|
||||
#screen_edit {
|
||||
position: relative;
|
||||
|
||||
#title {
|
||||
position: relative;
|
||||
padding: 10px;
|
||||
margin: 10px;
|
||||
border-bottom: 2px solid black;
|
||||
|
||||
h3 {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
#links {
|
||||
position: absolute;
|
||||
display: flex;
|
||||
bottom: 5px;
|
||||
|
||||
&.left {
|
||||
left: 0;
|
||||
}
|
||||
|
||||
&.right {
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.link {
|
||||
padding: 5px;
|
||||
padding-left: 20px;
|
||||
@@ -759,27 +875,32 @@ a:not(.button) {
|
||||
border-radius: 20px;
|
||||
background-color: hsl(40, 100%, 50%);
|
||||
color: black;
|
||||
|
||||
&:hover {
|
||||
color: black;
|
||||
background-color: hsl(40, 58%, 50%);
|
||||
}
|
||||
|
||||
&.delete {
|
||||
background-color: hsl(0, 100%, 40%);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#posters,
|
||||
#screens {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
|
||||
#no-posters,
|
||||
#no-screens {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.poster,
|
||||
.screen {
|
||||
min-width: 10%;
|
||||
@@ -791,26 +912,31 @@ a:not(.button) {
|
||||
border-radius: 4px;
|
||||
padding: 10px;
|
||||
background-color: lightgrey;
|
||||
|
||||
* {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.name {
|
||||
padding-bottom: 5px;
|
||||
margin-bottom: 5px;
|
||||
border-bottom: 1px solid whitesmoke;
|
||||
}
|
||||
|
||||
.image {
|
||||
flex-grow: 1;
|
||||
position: relative;
|
||||
padding-bottom: 5px;
|
||||
margin-bottom: 5px;
|
||||
border-bottom: 1px solid whitesmoke;
|
||||
|
||||
img {
|
||||
max-height: 20vw;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
&::before {
|
||||
position: absolute;
|
||||
@@ -829,10 +955,12 @@ a:not(.button) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.dates {
|
||||
padding-bottom: 5px;
|
||||
margin-bottom: 5px;
|
||||
border-bottom: 1px solid whitesmoke;
|
||||
|
||||
* {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
@@ -841,15 +969,18 @@ a:not(.button) {
|
||||
margin-left: 5px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.begin,
|
||||
.end {
|
||||
width: 48%;
|
||||
}
|
||||
|
||||
.begin {
|
||||
border-right: 1px solid whitesmoke;
|
||||
padding-right: 2%;
|
||||
}
|
||||
}
|
||||
|
||||
.edit,
|
||||
.moderate,
|
||||
.slideshow {
|
||||
@@ -857,15 +988,18 @@ a:not(.button) {
|
||||
border-radius: 20px;
|
||||
background-color: hsl(40, 100%, 50%);
|
||||
color: black;
|
||||
|
||||
&:hover {
|
||||
color: black;
|
||||
background-color: hsl(40, 58%, 50%);
|
||||
}
|
||||
|
||||
&:nth-child(2n) {
|
||||
margin-top: 5px;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.tooltip {
|
||||
visibility: hidden;
|
||||
width: 120px;
|
||||
@@ -876,23 +1010,28 @@ a:not(.button) {
|
||||
border-radius: 6px;
|
||||
position: absolute;
|
||||
z-index: 10;
|
||||
|
||||
ul {
|
||||
margin-left: 0;
|
||||
display: inline-block;
|
||||
|
||||
li {
|
||||
display: list-item;
|
||||
list-style-type: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.not_moderated {
|
||||
border: 1px solid red;
|
||||
}
|
||||
|
||||
&:hover .tooltip {
|
||||
visibility: visible;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#view {
|
||||
position: fixed;
|
||||
width: 100vw;
|
||||
@@ -906,9 +1045,11 @@ a:not(.button) {
|
||||
visibility: hidden;
|
||||
background-color: rgba(10, 10, 10, 0.9);
|
||||
overflow: hidden;
|
||||
|
||||
&.active {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
#placeholder {
|
||||
width: 80vw;
|
||||
height: 80vh;
|
||||
@@ -917,6 +1058,7 @@ a:not(.button) {
|
||||
align-items: center;
|
||||
top: 0;
|
||||
left: 0;
|
||||
|
||||
img {
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
@@ -931,14 +1073,17 @@ a:not(.button) {
|
||||
tbody {
|
||||
.neg-amount {
|
||||
color: red;
|
||||
|
||||
&:before {
|
||||
font-family: FontAwesome;
|
||||
font-size: 1em;
|
||||
content: "\f063";
|
||||
}
|
||||
}
|
||||
|
||||
.pos-amount {
|
||||
color: green;
|
||||
|
||||
&:before {
|
||||
font-family: FontAwesome;
|
||||
font-size: 1em;
|
||||
@@ -1005,6 +1150,7 @@ dt {
|
||||
.edit-bar {
|
||||
display: block;
|
||||
margin: 4px;
|
||||
|
||||
a {
|
||||
display: inline-block;
|
||||
margin: 4px;
|
||||
@@ -1044,7 +1190,8 @@ th {
|
||||
vertical-align: middle;
|
||||
text-align: center;
|
||||
padding: 5px 10px;
|
||||
> ul {
|
||||
|
||||
>ul {
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
@@ -1055,7 +1202,8 @@ td {
|
||||
vertical-align: top;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
> ul {
|
||||
|
||||
>ul {
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
@@ -1071,15 +1219,17 @@ thead {
|
||||
color: white;
|
||||
}
|
||||
|
||||
tbody > tr {
|
||||
tbody>tr {
|
||||
&:nth-child(even):not(.highlight) {
|
||||
background: $primary-neutral-light-color;
|
||||
}
|
||||
|
||||
&.clickable:hover {
|
||||
cursor: pointer;
|
||||
background: $secondary-neutral-light-color;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
&.highlight {
|
||||
color: $primary-dark-color;
|
||||
font-style: italic;
|
||||
@@ -1139,9 +1289,11 @@ u,
|
||||
margin: 0.2em;
|
||||
height: 100%;
|
||||
background: $secondary-neutral-light-color;
|
||||
|
||||
img {
|
||||
max-width: 70%;
|
||||
}
|
||||
|
||||
input {
|
||||
background: white;
|
||||
}
|
||||
@@ -1153,10 +1305,12 @@ u,
|
||||
.user_mini_profile {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
|
||||
img {
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
}
|
||||
|
||||
.user_mini_profile_infos {
|
||||
padding: 0.2em;
|
||||
height: 20%;
|
||||
@@ -1164,16 +1318,20 @@ u,
|
||||
flex-wrap: nowrap;
|
||||
justify-content: space-around;
|
||||
font-size: 0.9em;
|
||||
|
||||
div {
|
||||
max-height: 100%;
|
||||
}
|
||||
|
||||
.user_mini_profile_infos_text {
|
||||
text-align: center;
|
||||
|
||||
.user_mini_profile_nick {
|
||||
font-style: italic;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.user_mini_profile_picture {
|
||||
height: 80%;
|
||||
display: flex;
|
||||
@@ -1185,14 +1343,17 @@ u,
|
||||
.mini_profile_link {
|
||||
display: block;
|
||||
text-decoration: none;
|
||||
|
||||
span {
|
||||
display: inline-block;
|
||||
width: 50px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
em {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
img {
|
||||
max-width: 40px;
|
||||
max-height: 60px;
|
||||
@@ -1214,6 +1375,7 @@ u,
|
||||
border: solid 1px red;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
img {
|
||||
width: 500px;
|
||||
}
|
||||
@@ -1223,6 +1385,7 @@ u,
|
||||
.matmat_results {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
|
||||
.matmat_user {
|
||||
flex-basis: 14em;
|
||||
align-self: flex-start;
|
||||
@@ -1231,10 +1394,12 @@ u,
|
||||
overflow: hidden;
|
||||
border: 1px solid black;
|
||||
box-shadow: $shadow-color 1px 1px 1px;
|
||||
|
||||
&:hover {
|
||||
box-shadow: 1px 1px 5px $second-color;
|
||||
}
|
||||
}
|
||||
|
||||
.matmat_user a {
|
||||
color: $primary-neutral-dark-color;
|
||||
height: 100%;
|
||||
@@ -1274,6 +1439,7 @@ footer {
|
||||
font-size: 90%;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
|
||||
div {
|
||||
margin: 0.6em 0;
|
||||
color: $white-color;
|
||||
@@ -1283,18 +1449,20 @@ footer {
|
||||
align-items: center;
|
||||
background-color: $primary-neutral-dark-color;
|
||||
box-shadow: $shadow-color 0 0 15px;
|
||||
|
||||
a {
|
||||
padding: 0.8em;
|
||||
flex: 1;
|
||||
font-weight: bold;
|
||||
color: $white-color !important;
|
||||
|
||||
&:hover {
|
||||
color: $primary-dark-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
> .version {
|
||||
>.version {
|
||||
margin-top: 3px;
|
||||
color: rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
@@ -1326,6 +1494,7 @@ label {
|
||||
* {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
img {
|
||||
width: 100px;
|
||||
}
|
||||
@@ -1342,19 +1511,23 @@ label {
|
||||
padding: 2px;
|
||||
display: inline-block;
|
||||
font-size: 0.8em;
|
||||
|
||||
span {
|
||||
width: 70px;
|
||||
float: right;
|
||||
}
|
||||
|
||||
img {
|
||||
max-width: 50px;
|
||||
max-height: 50px;
|
||||
float: left;
|
||||
}
|
||||
|
||||
strong {
|
||||
font-weight: bold;
|
||||
font-size: 1.2em;
|
||||
}
|
||||
|
||||
button {
|
||||
vertical-align: middle;
|
||||
}
|
||||
@@ -1371,6 +1544,7 @@ a.ui-button:active,
|
||||
background: $primary-color;
|
||||
border-color: $primary-color;
|
||||
}
|
||||
|
||||
.ui-corner-all,
|
||||
.ui-corner-bottom,
|
||||
.ui-corner-right,
|
||||
@@ -1382,10 +1556,11 @@ a.ui-button:active,
|
||||
#club_detail {
|
||||
.club_logo {
|
||||
float: right;
|
||||
|
||||
img {
|
||||
display: block;
|
||||
max-height: 10em;
|
||||
max-width: 10em;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-16
@@ -1,16 +0,0 @@
|
||||
/*!
|
||||
* Chart.js
|
||||
* http://chartjs.org/
|
||||
* Version: 2.6.0
|
||||
*
|
||||
* Copyright 2017 Nick Downie
|
||||
* Released under the MIT license
|
||||
* https://github.com/chartjs/Chart.js/blob/master/LICENSE.md
|
||||
*/
|
||||
!function(t){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=t();else if("function"==typeof define&&define.amd)define([],t);else{var e;e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this,e.Chart=t()}}(function(){var t;return function t(e,n,i){function a(o,s){if(!n[o]){if(!e[o]){var l="function"==typeof require&&require;if(!s&&l)return l(o,!0);if(r)return r(o,!0);var u=new Error("Cannot find module '"+o+"'");throw u.code="MODULE_NOT_FOUND",u}var d=n[o]={exports:{}};e[o][0].call(d.exports,function(t){var n=e[o][1][t];return a(n?n:t)},d,d.exports,t,e,n,i)}return n[o].exports}for(var r="function"==typeof require&&require,o=0;o<i.length;o++)a(i[o]);return a}({1:[function(t,e,n){function i(t){if(t){var e=/^#([a-fA-F0-9]{3})$/,n=/^#([a-fA-F0-9]{6})$/,i=/^rgba?\(\s*([+-]?\d+)\s*,\s*([+-]?\d+)\s*,\s*([+-]?\d+)\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)$/,a=/^rgba?\(\s*([+-]?[\d\.]+)\%\s*,\s*([+-]?[\d\.]+)\%\s*,\s*([+-]?[\d\.]+)\%\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)$/,r=/(\w+)/,o=[0,0,0],s=1,l=t.match(e);if(l){l=l[1];for(var u=0;u<o.length;u++)o[u]=parseInt(l[u]+l[u],16)}else if(l=t.match(n)){l=l[1];for(var u=0;u<o.length;u++)o[u]=parseInt(l.slice(2*u,2*u+2),16)}else if(l=t.match(i)){for(var u=0;u<o.length;u++)o[u]=parseInt(l[u+1]);s=parseFloat(l[4])}else if(l=t.match(a)){for(var u=0;u<o.length;u++)o[u]=Math.round(2.55*parseFloat(l[u+1]));s=parseFloat(l[4])}else if(l=t.match(r)){if("transparent"==l[1])return[0,0,0,0];if(o=x[l[1]],!o)return}for(var u=0;u<o.length;u++)o[u]=y(o[u],0,255);return s=s||0==s?y(s,0,1):1,o[3]=s,o}}function a(t){if(t){var e=/^hsla?\(\s*([+-]?\d+)(?:deg)?\s*,\s*([+-]?[\d\.]+)%\s*,\s*([+-]?[\d\.]+)%\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)/,n=t.match(e);if(n){var i=parseFloat(n[4]),a=y(parseInt(n[1]),0,360),r=y(parseFloat(n[2]),0,100),o=y(parseFloat(n[3]),0,100),s=y(isNaN(i)?1:i,0,1);return[a,r,o,s]}}}function r(t){if(t){var e=/^hwb\(\s*([+-]?\d+)(?:deg)?\s*,\s*([+-]?[\d\.]+)%\s*,\s*([+-]?[\d\.]+)%\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)/,n=t.match(e);if(n){var i=parseFloat(n[4]),a=y(parseInt(n[1]),0,360),r=y(parseFloat(n[2]),0,100),o=y(parseFloat(n[3]),0,100),s=y(isNaN(i)?1:i,0,1);return[a,r,o,s]}}}function o(t){var e=i(t);return e&&e.slice(0,3)}function s(t){var e=a(t);return e&&e.slice(0,3)}function l(t){var e=i(t);return e?e[3]:(e=a(t))?e[3]:(e=r(t))?e[3]:void 0}function u(t){return"#"+b(t[0])+b(t[1])+b(t[2])}function d(t,e){return e<1||t[3]&&t[3]<1?c(t,e):"rgb("+t[0]+", "+t[1]+", "+t[2]+")"}function c(t,e){return void 0===e&&(e=void 0!==t[3]?t[3]:1),"rgba("+t[0]+", "+t[1]+", "+t[2]+", "+e+")"}function h(t,e){if(e<1||t[3]&&t[3]<1)return f(t,e);var n=Math.round(t[0]/255*100),i=Math.round(t[1]/255*100),a=Math.round(t[2]/255*100);return"rgb("+n+"%, "+i+"%, "+a+"%)"}function f(t,e){var n=Math.round(t[0]/255*100),i=Math.round(t[1]/255*100),a=Math.round(t[2]/255*100);return"rgba("+n+"%, "+i+"%, "+a+"%, "+(e||t[3]||1)+")"}function g(t,e){return e<1||t[3]&&t[3]<1?p(t,e):"hsl("+t[0]+", "+t[1]+"%, "+t[2]+"%)"}function p(t,e){return void 0===e&&(e=void 0!==t[3]?t[3]:1),"hsla("+t[0]+", "+t[1]+"%, "+t[2]+"%, "+e+")"}function m(t,e){return void 0===e&&(e=void 0!==t[3]?t[3]:1),"hwb("+t[0]+", "+t[1]+"%, "+t[2]+"%"+(void 0!==e&&1!==e?", "+e:"")+")"}function v(t){return _[t.slice(0,3)]}function y(t,e,n){return Math.min(Math.max(e,t),n)}function b(t){var e=t.toString(16).toUpperCase();return e.length<2?"0"+e:e}var x=t(5);e.exports={getRgba:i,getHsla:a,getRgb:o,getHsl:s,getHwb:r,getAlpha:l,hexString:u,rgbString:d,rgbaString:c,percentString:h,percentaString:f,hslString:g,hslaString:p,hwbString:m,keyword:v};var _={};for(var k in x)_[x[k]]=k},{5:5}],2:[function(t,e,n){var i=t(4),a=t(1),r=function(t){if(t instanceof r)return t;if(!(this instanceof r))return new r(t);this.valid=!1,this.values={rgb:[0,0,0],hsl:[0,0,0],hsv:[0,0,0],hwb:[0,0,0],cmyk:[0,0,0,0],alpha:1};var e;"string"==typeof t?(e=a.getRgba(t),e?this.setValues("rgb",e):(e=a.getHsla(t))?this.setValues("hsl",e):(e=a.getHwb(t))&&this.setValues("hwb",e)):"object"==typeof t&&(e=t,void 0!==e.r||void 0!==e.red?this.setValues("rgb",e):void 0!==e.l||void 0!==e.lightness?this.setValues("hsl",e):void 0!==e.v||void 0!==e.value?this.setValues("hsv",e):void 0!==e.w||void 0!==e.whiteness?this.setValues("hwb",e):void 0===e.c&&void 0===e.cyan||this.setValues("cmyk",e))};r.prototype={isValid:function(){return this.valid},rgb:function(){return this.setSpace("rgb",arguments)},hsl:function(){return this.setSpace("hsl",arguments)},hsv:function(){return this.setSpace("hsv",arguments)},hwb:function(){return this.setSpace("hwb",arguments)},cmyk:function(){return this.setSpace("cmyk",arguments)},rgbArray:function(){return this.values.rgb},hslArray:function(){return this.values.hsl},hsvArray:function(){return this.values.hsv},hwbArray:function(){var t=this.values;return 1!==t.alpha?t.hwb.concat([t.alpha]):t.hwb},cmykArray:function(){return this.values.cmyk},rgbaArray:function(){var t=this.values;return t.rgb.concat([t.alpha])},hslaArray:funcLine truncated
|
||||
!l(e))return t;return n=Math.min(t.date(),ot(t.year(),e)),t._d["set"+(t._isUTC?"UTC":"")+"Month"](e,n),t}function ht(e){return null!=e?(ct(this,e),t.updateOffset(this,!0),this):H(this,"Month")}function ft(){return ot(this.year(),this.month())}function gt(t){return this._monthsParseExact?(c(this,"_monthsRegex")||mt.call(this),t?this._monthsShortStrictRegex:this._monthsShortRegex):(c(this,"_monthsShortRegex")||(this._monthsShortRegex=xa),this._monthsShortStrictRegex&&t?this._monthsShortStrictRegex:this._monthsShortRegex)}function pt(t){return this._monthsParseExact?(c(this,"_monthsRegex")||mt.call(this),t?this._monthsStrictRegex:this._monthsRegex):(c(this,"_monthsRegex")||(this._monthsRegex=_a),this._monthsStrictRegex&&t?this._monthsStrictRegex:this._monthsRegex)}function mt(){function t(t,e){return e.length-t.length}var e,n,i=[],a=[],r=[];for(e=0;e<12;e++)n=f([2e3,e]),i.push(this.monthsShort(n,"")),a.push(this.months(n,"")),r.push(this.months(n,"")),r.push(this.monthsShort(n,""));for(i.sort(t),a.sort(t),r.sort(t),e=0;e<12;e++)i[e]=nt(i[e]),a[e]=nt(a[e]);for(e=0;e<24;e++)r[e]=nt(r[e]);this._monthsRegex=new RegExp("^("+r.join("|")+")","i"),this._monthsShortRegex=this._monthsRegex,this._monthsStrictRegex=new RegExp("^("+a.join("|")+")","i"),this._monthsShortStrictRegex=new RegExp("^("+i.join("|")+")","i")}function vt(t){return yt(t)?366:365}function yt(t){return t%4===0&&t%100!==0||t%400===0}function bt(){return yt(this.year())}function xt(t,e,n,i,a,r,o){var s=new Date(t,e,n,i,a,r,o);return t<100&&t>=0&&isFinite(s.getFullYear())&&s.setFullYear(t),s}function _t(t){var e=new Date(Date.UTC.apply(null,arguments));return t<100&&t>=0&&isFinite(e.getUTCFullYear())&&e.setUTCFullYear(t),e}function kt(t,e,n){var i=7+e-n,a=(7+_t(t,0,i).getUTCDay()-e)%7;return-a+i-1}function wt(t,e,n,i,a){var r,o,s=(7+n-i)%7,l=kt(t,i,a),u=1+7*(e-1)+s+l;return u<=0?(r=t-1,o=vt(r)+u):u>vt(t)?(r=t+1,o=u-vt(t)):(r=t,o=u),{year:r,dayOfYear:o}}function Mt(t,e,n){var i,a,r=kt(t.year(),e,n),o=Math.floor((t.dayOfYear()-r-1)/7)+1;return o<1?(a=t.year()-1,i=o+St(a,e,n)):o>St(t.year(),e,n)?(i=o-St(t.year(),e,n),a=t.year()+1):(a=t.year(),i=o),{week:i,year:a}}function St(t,e,n){var i=kt(t,e,n),a=kt(t+1,e,n);return(vt(t)-i+a)/7}function Dt(t){return Mt(t,this._week.dow,this._week.doy).week}function Ct(){return this._week.dow}function Pt(){return this._week.doy}function Tt(t){var e=this.localeData().week(this);return null==t?e:this.add(7*(t-e),"d")}function It(t){var e=Mt(this,1,4).week;return null==t?e:this.add(7*(t-e),"d")}function At(t,e){return"string"!=typeof t?t:isNaN(t)?(t=e.weekdaysParse(t),"number"==typeof t?t:null):parseInt(t,10)}function Ft(t,e){return"string"==typeof t?e.weekdaysParse(t)%7||7:isNaN(t)?null:t}function Ot(t,e){return t?a(this._weekdays)?this._weekdays[t.day()]:this._weekdays[this._weekdays.isFormat.test(e)?"format":"standalone"][t.day()]:a(this._weekdays)?this._weekdays:this._weekdays.standalone}function Rt(t){return t?this._weekdaysShort[t.day()]:this._weekdaysShort}function Lt(t){return t?this._weekdaysMin[t.day()]:this._weekdaysMin}function Vt(t,e,n){var i,a,r,o=t.toLocaleLowerCase();if(!this._weekdaysParse)for(this._weekdaysParse=[],this._shortWeekdaysParse=[],this._minWeekdaysParse=[],i=0;i<7;++i)r=f([2e3,1]).day(i),this._minWeekdaysParse[i]=this.weekdaysMin(r,"").toLocaleLowerCase(),this._shortWeekdaysParse[i]=this.weekdaysShort(r,"").toLocaleLowerCase(),this._weekdaysParse[i]=this.weekdays(r,"").toLocaleLowerCase();return n?"dddd"===e?(a=ma.call(this._weekdaysParse,o),a!==-1?a:null):"ddd"===e?(a=ma.call(this._shortWeekdaysParse,o),a!==-1?a:null):(a=ma.call(this._minWeekdaysParse,o),a!==-1?a:null):"dddd"===e?(a=ma.call(this._weekdaysParse,o),a!==-1?a:(a=ma.call(this._shortWeekdaysParse,o),a!==-1?a:(a=ma.call(this._minWeekdaysParse,o),a!==-1?a:null))):"ddd"===e?(a=ma.call(this._shortWeekdaysParse,o),a!==-1?a:(a=ma.call(this._weekdaysParse,o),a!==-1?a:(a=ma.call(this._minWeekdaysParse,o),a!==-1?a:null))):(a=ma.call(this._minWeekdaysParse,o),a!==-1?a:(a=ma.call(this._weekdaysParse,o),a!==-1?a:(a=ma.call(this._shortWeekdaysParse,o),a!==-1?a:null)))}function Wt(t,e,n){var i,a,r;if(this._weekdaysParseExact)return Vt.call(this,t,e,n);for(this._weekdaysParse||(this._weekdaysParse=[],this._minWeekdaysParse=[],this._shortWeekdaysParse=[],this._fullWeekdaysParse=[]),i=0;i<7;i++){if(a=f([2e3,1]).day(i),n&&!this._fullWeekdaysParse[i]&&(this._fullWeekdaysParse[i]=new RegExp("^"+this.weekdays(a,"").replace(".",".?")+"$","i"),this._shortWeekdaysParse[i]=new RegExp("^"+this.weekdaysShort(a,"").replace(".",".?")+"$","i"),this._minWeekdaysParse[i]=new RegExp("^"+this.weekdaysMin(a,"").replace(".",".?")+"$","i")),this._weekdaysParse[i]||(r="^"+this.weekdays(a,"")+"|^"+this.weekdaysShort(a,"")+"|^"+this.weekdaysMin(a,""),this._weekdaysParse[i]=new RegExp(r.replace(".",""),"i")),n&&"dddd"===e&&this._fullWeekdaysParse[i].test(t))return i;if(n&&"ddd"===e&&this._shortWeekdaysParse[i].test(t))return i;if(n&&"dd"===e&&this._minWeekdaysParse[i].testLine truncated
|
||||
}),at(["d","e","E"],function(t,e,n,i){e[i]=k(t)});var Ma="Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),Sa="Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),Da="Su_Mo_Tu_We_Th_Fr_Sa".split("_"),Ca=aa,Pa=aa,Ta=aa;Z("H",["HH",2],0,"hour"),Z("h",["hh",2],0,Ut),Z("k",["kk",2],0,Gt),Z("hmm",0,0,function(){return""+Ut.apply(this)+q(this.minutes(),2)}),Z("hmmss",0,0,function(){return""+Ut.apply(this)+q(this.minutes(),2)+q(this.seconds(),2)}),Z("Hmm",0,0,function(){return""+this.hours()+q(this.minutes(),2)}),Z("Hmmss",0,0,function(){return""+this.hours()+q(this.minutes(),2)+q(this.seconds(),2)}),qt("a",!0),qt("A",!1),W("hour","h"),N("hour",13),$("a",Zt),$("A",Zt),$("H",qi),$("h",qi),$("k",qi),$("HH",qi,Hi),$("hh",qi,Hi),$("kk",qi,Hi),$("hmm",Zi),$("hmmss",Xi),$("Hmm",Zi),$("Hmmss",Xi),it(["H","HH"],da),it(["k","kk"],function(t,e,n){var i=k(t);e[da]=24===i?0:i}),it(["a","A"],function(t,e,n){n._isPm=n._locale.isPM(t),n._meridiem=t}),it(["h","hh"],function(t,e,n){e[da]=k(t),p(n).bigHour=!0}),it("hmm",function(t,e,n){var i=t.length-2;e[da]=k(t.substr(0,i)),e[ca]=k(t.substr(i)),p(n).bigHour=!0}),it("hmmss",function(t,e,n){var i=t.length-4,a=t.length-2;e[da]=k(t.substr(0,i)),e[ca]=k(t.substr(i,2)),e[ha]=k(t.substr(a)),p(n).bigHour=!0}),it("Hmm",function(t,e,n){var i=t.length-2;e[da]=k(t.substr(0,i)),e[ca]=k(t.substr(i))}),it("Hmmss",function(t,e,n){var i=t.length-4,a=t.length-2;e[da]=k(t.substr(0,i)),e[ca]=k(t.substr(i,2)),e[ha]=k(t.substr(a))});var Ia,Aa=/[ap]\.?m?\.?/i,Fa=E("Hours",!0),Oa={calendar:Ii,longDateFormat:Ai,invalidDate:Fi,ordinal:Oi,dayOfMonthOrdinalParse:Ri,relativeTime:Li,months:ya,monthsShort:ba,week:wa,weekdays:Ma,weekdaysMin:Da,weekdaysShort:Sa,meridiemParse:Aa},Ra={},La={},Va=/^\s*((?:[+-]\d{6}|\d{4})-(?:\d\d-\d\d|W\d\d-\d|W\d\d|\d\d\d|\d\d))(?:(T| )(\d\d(?::\d\d(?::\d\d(?:[.,]\d+)?)?)?)([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?$/,Wa=/^\s*((?:[+-]\d{6}|\d{4})(?:\d\d\d\d|W\d\d\d|W\d\d|\d\d\d|\d\d))(?:(T| )(\d\d(?:\d\d(?:\d\d(?:[.,]\d+)?)?)?)([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?$/,Ya=/Z|[+-]\d\d(?::?\d\d)?/,za=[["YYYYYY-MM-DD",/[+-]\d{6}-\d\d-\d\d/],["YYYY-MM-DD",/\d{4}-\d\d-\d\d/],["GGGG-[W]WW-E",/\d{4}-W\d\d-\d/],["GGGG-[W]WW",/\d{4}-W\d\d/,!1],["YYYY-DDD",/\d{4}-\d{3}/],["YYYY-MM",/\d{4}-\d\d/,!1],["YYYYYYMMDD",/[+-]\d{10}/],["YYYYMMDD",/\d{8}/],["GGGG[W]WWE",/\d{4}W\d{3}/],["GGGG[W]WW",/\d{4}W\d{2}/,!1],["YYYYDDD",/\d{7}/]],Na=[["HH:mm:ss.SSSS",/\d\d:\d\d:\d\d\.\d+/],["HH:mm:ss,SSSS",/\d\d:\d\d:\d\d,\d+/],["HH:mm:ss",/\d\d:\d\d:\d\d/],["HH:mm",/\d\d:\d\d/],["HHmmss.SSSS",/\d\d\d\d\d\d\.\d+/],["HHmmss,SSSS",/\d\d\d\d\d\d,\d+/],["HHmmss",/\d\d\d\d\d\d/],["HHmm",/\d\d\d\d/],["HH",/\d\d/]],Ba=/^\/?Date\((\-?\d+)/i,Ea=/^((?:Mon|Tue|Wed|Thu|Fri|Sat|Sun),?\s)?(\d?\d\s(?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\s(?:\d\d)?\d\d\s)(\d\d:\d\d)(\:\d\d)?(\s(?:UT|GMT|[ECMP][SD]T|[A-IK-Za-ik-z]|[+-]\d{4}))$/;t.createFromInputFallback=S("value provided is not in a recognized RFC2822 or ISO format. moment construction falls back to js Date(), which is not reliable across all browsers and versions. Non RFC2822/ISO date formats are discouraged and will be removed in an upcoming major release. Please refer to http://momentjs.com/guides/#/warnings/js-date/ for more info.",function(t){t._d=new Date(t._i+(t._useUTC?" UTC":""))}),t.ISO_8601=function(){},t.RFC_2822=function(){};var Ha=S("moment().min is deprecated, use moment.max instead. http://momentjs.com/guides/#/warnings/min-max/",function(){var t=_e.apply(null,arguments);return this.isValid()&&t.isValid()?t<this?this:t:v()}),ja=S("moment().max is deprecated, use moment.min instead. http://momentjs.com/guides/#/warnings/min-max/",function(){var t=_e.apply(null,arguments);return this.isValid()&&t.isValid()?t>this?this:t:v()}),Ua=function(){return Date.now?Date.now():+new Date},Ga=["year","quarter","month","week","day","hour","minute","second","millisecond"];Ae("Z",":"),Ae("ZZ",""),$("Z",na),$("ZZ",na),it(["Z","ZZ"],function(t,e,n){n._useUTC=!0,n._tzm=Fe(na,t)});var qa=/([\+\-]|\d\d)/gi;t.updateOffset=function(){};var Za=/^(\-)?(?:(\d*)[. ])?(\d+)\:(\d+)(?:\:(\d+)(\.\d*)?)?$/,Xa=/^(-)?P(?:(-?[0-9,.]*)Y)?(?:(-?[0-9,.]*)M)?(?:(-?[0-9,.]*)W)?(?:(-?[0-9,.]*)D)?(?:T(?:(-?[0-9,.]*)H)?(?:(-?[0-9,.]*)M)?(?:(-?[0-9,.]*)S)?)?$/;Ge.fn=Pe.prototype,Ge.invalid=Ce;var Ja=Je(1,"add"),Ka=Je(-1,"subtract");t.defaultFormat="YYYY-MM-DDTHH:mm:ssZ",t.defaultFormatUtc="YYYY-MM-DDTHH:mm:ss[Z]";var Qa=S("moment().lang() is deprecated. Instead, use moment().localeData() to get the language configuration. Use moment().locale() to change languages.",function(t){return void 0===t?this.localeData():this.locale(t)});Z(0,["gg",2],0,function(){return this.weekYear()%100}),Z(0,["GG",2],0,function(){return this.isoWeekYear()%100}),Fn("gggg","weekYear"),Fn("ggggg","weekYear"),Fn("GGGG","isoWeekYear"),Fn("GGGGG","isoWeekYear"),W("weekYear","gg"),W("isoWeekYear","GG"),N("weekYear",1),N("isoWeekYear",1),$("G",ta),$("g",ta),$("GG",qi,Hi),$("gg",qi,Hi),$("GGGG",Ki,Ui),$("gggg",Ki,Ui),$("GGGGG",Qi,Gi),$("gLine truncated
|
||||
return""},label:function(t,e){return e.labels[t.index]+": "+t.yLabel}}}},t.controllers.polarArea=t.DatasetController.extend({dataElementType:t.elements.Arc,linkScales:e.noop,update:function(t){var n=this,i=n.chart,a=i.chartArea,r=n.getMeta(),o=i.options,s=o.elements.arc,l=Math.min(a.right-a.left,a.bottom-a.top);i.outerRadius=Math.max((l-s.borderWidth/2)/2,0),i.innerRadius=Math.max(o.cutoutPercentage?i.outerRadius/100*o.cutoutPercentage:1,0),i.radiusLength=(i.outerRadius-i.innerRadius)/i.getVisibleDatasetCount(),n.outerRadius=i.outerRadius-i.radiusLength*n.index,n.innerRadius=n.outerRadius-i.radiusLength,r.count=n.countVisibleElements(),e.each(r.data,function(e,i){n.updateElement(e,i,t)})},updateElement:function(t,n,i){for(var a=this,r=a.chart,o=a.getDataset(),s=r.options,l=s.animation,u=r.scale,d=e.getValueAtIndexOrDefault,c=r.data.labels,h=a.calculateCircumference(o.data[n]),f=u.xCenter,g=u.yCenter,p=0,m=a.getMeta(),v=0;v<n;++v)isNaN(o.data[v])||m.data[v].hidden||++p;var y=s.startAngle,b=t.hidden?0:u.getDistanceFromCenterForValue(o.data[n]),x=y+h*p,_=x+(t.hidden?0:h),k=l.animateScale?0:u.getDistanceFromCenterForValue(o.data[n]);e.extend(t,{_datasetIndex:a.index,_index:n,_scale:u,_model:{x:f,y:g,innerRadius:0,outerRadius:i?k:b,startAngle:i&&l.animateRotate?y:x,endAngle:i&&l.animateRotate?y:_,label:d(c,n,c[n])}}),a.removeHoverStyle(t),t.pivot()},removeHoverStyle:function(e){t.DatasetController.prototype.removeHoverStyle.call(this,e,this.chart.options.elements.arc)},countVisibleElements:function(){var t=this.getDataset(),n=this.getMeta(),i=0;return e.each(n.data,function(e,n){isNaN(t.data[n])||e.hidden||i++}),i},calculateCircumference:function(t){var e=this.getMeta().count;return e>0&&!isNaN(t)?2*Math.PI/e:0}})}},{}],20:[function(t,e,n){"use strict";e.exports=function(t){var e=t.helpers;t.defaults.radar={aspectRatio:1,scale:{type:"radialLinear"},elements:{line:{tension:0}}},t.controllers.radar=t.DatasetController.extend({datasetElementType:t.elements.Line,dataElementType:t.elements.Point,linkScales:e.noop,update:function(t){var n=this,i=n.getMeta(),a=i.dataset,r=i.data,o=a.custom||{},s=n.getDataset(),l=n.chart.options.elements.line,u=n.chart.scale;void 0!==s.tension&&void 0===s.lineTension&&(s.lineTension=s.tension),e.extend(i.dataset,{_datasetIndex:n.index,_scale:u,_children:r,_loop:!0,_model:{tension:o.tension?o.tension:e.getValueOrDefault(s.lineTension,l.tension),backgroundColor:o.backgroundColor?o.backgroundColor:s.backgroundColor||l.backgroundColor,borderWidth:o.borderWidth?o.borderWidth:s.borderWidth||l.borderWidth,borderColor:o.borderColor?o.borderColor:s.borderColor||l.borderColor,fill:o.fill?o.fill:void 0!==s.fill?s.fill:l.fill,borderCapStyle:o.borderCapStyle?o.borderCapStyle:s.borderCapStyle||l.borderCapStyle,borderDash:o.borderDash?o.borderDash:s.borderDash||l.borderDash,borderDashOffset:o.borderDashOffset?o.borderDashOffset:s.borderDashOffset||l.borderDashOffset,borderJoinStyle:o.borderJoinStyle?o.borderJoinStyle:s.borderJoinStyle||l.borderJoinStyle}}),i.dataset.pivot(),e.each(r,function(e,i){n.updateElement(e,i,t)},n),n.updateBezierControlPoints()},updateElement:function(t,n,i){var a=this,r=t.custom||{},o=a.getDataset(),s=a.chart.scale,l=a.chart.options.elements.point,u=s.getPointPositionForValue(n,o.data[n]);void 0!==o.radius&&void 0===o.pointRadius&&(o.pointRadius=o.radius),void 0!==o.hitRadius&&void 0===o.pointHitRadius&&(o.pointHitRadius=o.hitRadius),e.extend(t,{_datasetIndex:a.index,_index:n,_scale:s,_model:{x:i?s.xCenter:u.x,y:i?s.yCenter:u.y,tension:r.tension?r.tension:e.getValueOrDefault(o.lineTension,a.chart.options.elements.line.tension),radius:r.radius?r.radius:e.getValueAtIndexOrDefault(o.pointRadius,n,l.radius),backgroundColor:r.backgroundColor?r.backgroundColor:e.getValueAtIndexOrDefault(o.pointBackgroundColor,n,l.backgroundColor),borderColor:r.borderColor?r.borderColor:e.getValueAtIndexOrDefault(o.pointBorderColor,n,l.borderColor),borderWidth:r.borderWidth?r.borderWidth:e.getValueAtIndexOrDefault(o.pointBorderWidth,n,l.borderWidth),pointStyle:r.pointStyle?r.pointStyle:e.getValueAtIndexOrDefault(o.pointStyle,n,l.pointStyle),hitRadius:r.hitRadius?r.hitRadius:e.getValueAtIndexOrDefault(o.pointHitRadius,n,l.hitRadius)}}),t._model.skip=r.skip?r.skip:isNaN(t._model.x)||isNaN(t._model.y)},updateBezierControlPoints:function(){var t=this.chart.chartArea,n=this.getMeta();e.each(n.data,function(i,a){var r=i._model,o=e.splineCurve(e.previousItem(n.data,a,!0)._model,r,e.nextItem(n.data,a,!0)._model,r.tension);r.controlPointPreviousX=Math.max(Math.min(o.previous.x,t.right),t.left),r.controlPointPreviousY=Math.max(Math.min(o.previous.y,t.bottom),t.top),r.controlPointNextX=Math.max(Math.min(o.next.x,t.right),t.left),r.controlPointNextY=Math.max(Math.min(o.next.y,t.bottom),t.top),i.pivot()})},setHoverStyle:function(t){var n=this.chart.data.datasets[t._datasetIndex],i=t.custom||{},a=t._index,r=t._model;r.radius=i.hoverRadius?i.hoverRadius:e.getValueAtIndexOrDefault(n.pointHoverRadius,a,this.chart.oLine truncated
|
||||
return(t/=.5)<1?.5*(t*t*(((e*=1.525)+1)*t-e)):.5*((t-=2)*t*(((e*=1.525)+1)*t+e)+2)},easeInBounce:function(t){return 1-o.easeOutBounce(1-t)},easeOutBounce:function(t){return(t/=1)<1/2.75?1*(7.5625*t*t):t<2/2.75?1*(7.5625*(t-=1.5/2.75)*t+.75):t<2.5/2.75?1*(7.5625*(t-=2.25/2.75)*t+.9375):1*(7.5625*(t-=2.625/2.75)*t+.984375)},easeInOutBounce:function(t){return t<.5?.5*o.easeInBounce(2*t):.5*o.easeOutBounce(2*t-1)+.5}};r.requestAnimFrame=function(){return"undefined"==typeof window?function(t){t()}:window.requestAnimationFrame||window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame||window.oRequestAnimationFrame||window.msRequestAnimationFrame||function(t){return window.setTimeout(t,1e3/60)}}(),r.getRelativePosition=function(t,e){var n,i,a=t.originalEvent||t,o=t.currentTarget||t.srcElement,s=o.getBoundingClientRect(),l=a.touches;l&&l.length>0?(n=l[0].clientX,i=l[0].clientY):(n=a.clientX,i=a.clientY);var u=parseFloat(r.getStyle(o,"padding-left")),d=parseFloat(r.getStyle(o,"padding-top")),c=parseFloat(r.getStyle(o,"padding-right")),h=parseFloat(r.getStyle(o,"padding-bottom")),f=s.right-s.left-u-c,g=s.bottom-s.top-d-h;return n=Math.round((n-s.left-u)/f*o.width/e.currentDevicePixelRatio),i=Math.round((i-s.top-d)/g*o.height/e.currentDevicePixelRatio),{x:n,y:i}},r.addEvent=function(t,e,n){t.addEventListener?t.addEventListener(e,n):t.attachEvent?t.attachEvent("on"+e,n):t["on"+e]=n},r.removeEvent=function(t,e,n){t.removeEventListener?t.removeEventListener(e,n,!1):t.detachEvent?t.detachEvent("on"+e,n):t["on"+e]=r.noop},r.getConstraintWidth=function(t){return a(t,"max-width","clientWidth")},r.getConstraintHeight=function(t){return a(t,"max-height","clientHeight")},r.getMaximumWidth=function(t){var e=t.parentNode,n=parseInt(r.getStyle(e,"padding-left"),10),i=parseInt(r.getStyle(e,"padding-right"),10),a=e.clientWidth-n-i,o=r.getConstraintWidth(t);return isNaN(o)?a:Math.min(a,o)},r.getMaximumHeight=function(t){var e=t.parentNode,n=parseInt(r.getStyle(e,"padding-top"),10),i=parseInt(r.getStyle(e,"padding-bottom"),10),a=e.clientHeight-n-i,o=r.getConstraintHeight(t);return isNaN(o)?a:Math.min(a,o)},r.getStyle=function(t,e){return t.currentStyle?t.currentStyle[e]:document.defaultView.getComputedStyle(t,null).getPropertyValue(e)},r.retinaScale=function(t){var e=t.currentDevicePixelRatio=window.devicePixelRatio||1;if(1!==e){var n=t.canvas,i=t.height,a=t.width;n.height=i*e,n.width=a*e,t.ctx.scale(e,e),n.style.height=i+"px",n.style.width=a+"px"}},r.clear=function(t){t.ctx.clearRect(0,0,t.width,t.height)},r.fontString=function(t,e,n){return e+" "+t+"px "+n},r.longestText=function(t,e,n,i){i=i||{};var a=i.data=i.data||{},o=i.garbageCollect=i.garbageCollect||[];i.font!==e&&(a=i.data={},o=i.garbageCollect=[],i.font=e),t.font=e;var s=0;r.each(n,function(e){void 0!==e&&null!==e&&r.isArray(e)!==!0?s=r.measureText(t,a,o,s,e):r.isArray(e)&&r.each(e,function(e){void 0===e||null===e||r.isArray(e)||(s=r.measureText(t,a,o,s,e))})});var l=o.length/2;if(l>n.length){for(var u=0;u<l;u++)delete a[o[u]];o.splice(0,l)}return s},r.measureText=function(t,e,n,i,a){var r=e[a];return r||(r=e[a]=t.measureText(a).width,n.push(a)),r>i&&(i=r),i},r.numberOfLabelLines=function(t){var e=1;return r.each(t,function(t){r.isArray(t)&&t.length>e&&(e=t.length)}),e},r.drawRoundedRectangle=function(t,e,n,i,a,r){t.beginPath(),t.moveTo(e+r,n),t.lineTo(e+i-r,n),t.quadraticCurveTo(e+i,n,e+i,n+r),t.lineTo(e+i,n+a-r),t.quadraticCurveTo(e+i,n+a,e+i-r,n+a),t.lineTo(e+r,n+a),t.quadraticCurveTo(e,n+a,e,n+a-r),t.lineTo(e,n+r),t.quadraticCurveTo(e,n,e+r,n),t.closePath()},r.color=i?function(e){return e instanceof CanvasGradient&&(e=t.defaults.global.defaultColor),i(e)}:function(t){return console.error("Color.js not found!"),t},r.isArray=Array.isArray?function(t){return Array.isArray(t)}:function(t){return"[object Array]"===Object.prototype.toString.call(t)},r.arrayEquals=function(t,e){var n,i,a,o;if(!t||!e||t.length!==e.length)return!1;for(n=0,i=t.length;n<i;++n)if(a=t[n],o=e[n],a instanceof Array&&o instanceof Array){if(!r.arrayEquals(a,o))return!1}else if(a!==o)return!1;return!0},r.callback=function(t,e,n){t&&"function"==typeof t.call&&t.apply(n,e)},r.getHoverColor=function(t){return t instanceof CanvasPattern?t:r.color(t).saturate(.5).darken(.1).rgbString()},r.callCallback=r.callback}},{2:2}],27:[function(t,e,n){"use strict";e.exports=function(t){function e(t,e){return t.native?{x:t.x,y:t.y}:o.getRelativePosition(t,e)}function n(t,e){var n,i,a,r,o,s=t.data.datasets;for(i=0,r=s.length;i<r;++i)if(t.isDatasetVisible(i))for(n=t.getDatasetMeta(i),a=0,o=n.data.length;a<o;++a){var l=n.data[a];l._view.skip||e(l)}}function i(t,e){var i=[];return n(t,function(t){t.inRange(e.x,e.y)&&i.push(t)}),i}function a(t,e,i,a){var r=Number.POSITIVE_INFINITY,s=[];return a||(a=o.distanceBetweenPoints),n(t,function(t){if(!i||t.inRange(e.x,e.y)){var n=t.getCenterPoint(),o=a(e,n);o<r?(s=[t],r=o):o===r&&s.push(t)}}),s}function r(t,n,r){var o=e(n,t),s=function(t,e){return Math.abs(t.x-e.x)},l=r.inLine truncated
|
||||
"center"===s&&"right"===o&&this.drawCaret(t,a),i.lineTo(l+d,u+c-h),i.quadraticCurveTo(l+d,u+c,l+d-h,u+c),"bottom"===s&&this.drawCaret(t,a),i.lineTo(l+h,u+c),i.quadraticCurveTo(l,u+c,l,u+c-h),"center"===s&&"left"===o&&this.drawCaret(t,a),i.lineTo(l,u+h),i.quadraticCurveTo(l,u,l+h,u),i.closePath(),i.fill(),n.borderWidth>0&&i.stroke()},draw:function(){var t=this._chart.ctx,e=this._view;if(0!==e.opacity){var n={width:e.width,height:e.height},i={x:e.x,y:e.y},a=Math.abs(e.opacity<.001)?0:e.opacity,r=e.title.length||e.beforeBody.length||e.body.length||e.afterBody.length||e.footer.length;this._options.enabled&&r&&(this.drawBackground(i,e,t,n,a),i.x+=e.xPadding,i.y+=e.yPadding,this.drawTitle(i,e,t,a),this.drawBody(i,e,t,a),this.drawFooter(i,e,t,a))}},handleEvent:function(t){var e=this,n=e._options,i=!1;if(e._lastActive=e._lastActive||[],"mouseout"===t.type?e._active=[]:e._active=e._chart.getElementsAtEventForMode(t,n.mode,n),i=!l.arrayEquals(e._active,e._lastActive),!i)return!1;if(e._lastActive=e._active,n.enabled||n.custom){e._eventPosition={x:t.x,y:t.y};var a=e._model;e.update(!0),e.pivot(),i|=a.x!==e._model.x||a.y!==e._model.y}return i}}),t.Tooltip.positioners={average:function(t){if(!t.length)return!1;var e,n,i=0,a=0,r=0;for(e=0,n=t.length;e<n;++e){var o=t[e];if(o&&o.hasValue()){var s=o.tooltipPosition();i+=s.x,a+=s.y,++r}}return{x:Math.round(i/r),y:Math.round(a/r)}},nearest:function(t,e){var n,i,a,r=e.x,o=e.y,s=Number.POSITIVE_INFINITY;for(i=0,a=t.length;i<a;++i){var u=t[i];if(u&&u.hasValue()){var d=u.getCenterPoint(),c=l.distanceBetweenPoints(e,d);c<s&&(s=c,n=u)}}if(n){var h=n.tooltipPosition();r=h.x,o=h.y}return{x:r,y:o}}}}},{}],35:[function(t,e,n){"use strict";e.exports=function(t){var e=t.helpers,n=t.defaults.global;n.elements.arc={backgroundColor:n.defaultColor,borderColor:"#fff",borderWidth:2},t.elements.Arc=t.Element.extend({inLabelRange:function(t){var e=this._view;return!!e&&Math.pow(t-e.x,2)<Math.pow(e.radius+e.hoverRadius,2)},inRange:function(t,n){var i=this._view;if(i){for(var a=e.getAngleFromPoint(i,{x:t,y:n}),r=a.angle,o=a.distance,s=i.startAngle,l=i.endAngle;l<s;)l+=2*Math.PI;for(;r>l;)r-=2*Math.PI;for(;r<s;)r+=2*Math.PI;var u=r>=s&&r<=l,d=o>=i.innerRadius&&o<=i.outerRadius;return u&&d}return!1},getCenterPoint:function(){var t=this._view,e=(t.startAngle+t.endAngle)/2,n=(t.innerRadius+t.outerRadius)/2;return{x:t.x+Math.cos(e)*n,y:t.y+Math.sin(e)*n}},getArea:function(){var t=this._view;return Math.PI*((t.endAngle-t.startAngle)/(2*Math.PI))*(Math.pow(t.outerRadius,2)-Math.pow(t.innerRadius,2))},tooltipPosition:function(){var t=this._view,e=t.startAngle+(t.endAngle-t.startAngle)/2,n=(t.outerRadius-t.innerRadius)/2+t.innerRadius;return{x:t.x+Math.cos(e)*n,y:t.y+Math.sin(e)*n}},draw:function(){var t=this._chart.ctx,e=this._view,n=e.startAngle,i=e.endAngle;t.beginPath(),t.arc(e.x,e.y,e.outerRadius,n,i),t.arc(e.x,e.y,e.innerRadius,i,n,!0),t.closePath(),t.strokeStyle=e.borderColor,t.lineWidth=e.borderWidth,t.fillStyle=e.backgroundColor,t.fill(),t.lineJoin="bevel",e.borderWidth&&t.stroke()}})}},{}],36:[function(t,e,n){"use strict";e.exports=function(t){var e=t.helpers,n=t.defaults.global;t.defaults.global.elements.line={tension:.4,backgroundColor:n.defaultColor,borderWidth:3,borderColor:n.defaultColor,borderCapStyle:"butt",borderDash:[],borderDashOffset:0,borderJoinStyle:"miter",capBezierPoints:!0,fill:!0},t.elements.Line=t.Element.extend({draw:function(){var t,i,a,r,o=this,s=o._view,l=o._chart.ctx,u=s.spanGaps,d=o._children.slice(),c=n.elements.line,h=-1;for(o._loop&&d.length&&d.push(d[0]),l.save(),l.lineCap=s.borderCapStyle||c.borderCapStyle,l.setLineDash&&l.setLineDash(s.borderDash||c.borderDash),l.lineDashOffset=s.borderDashOffset||c.borderDashOffset,l.lineJoin=s.borderJoinStyle||c.borderJoinStyle,l.lineWidth=s.borderWidth||c.borderWidth,l.strokeStyle=s.borderColor||n.defaultColor,l.beginPath(),h=-1,t=0;t<d.length;++t)i=d[t],a=e.previousItem(d,t),r=i._view,0===t?r.skip||(l.moveTo(r.x,r.y),h=t):(a=h===-1?a:d[h],r.skip||(h!==t-1&&!u||h===-1?l.moveTo(r.x,r.y):e.canvas.lineTo(l,a._view,i._view),h=t));l.stroke(),l.restore()}})}},{}],37:[function(t,e,n){"use strict";e.exports=function(t){function e(t){var e=this._view;return!!e&&Math.pow(t-e.x,2)<Math.pow(e.radius+e.hitRadius,2)}function n(t){var e=this._view;return!!e&&Math.pow(t-e.y,2)<Math.pow(e.radius+e.hitRadius,2)}var i=t.helpers,a=t.defaults.global,r=a.defaultColor;a.elements.point={radius:3,pointStyle:"circle",backgroundColor:r,borderWidth:1,borderColor:r,hitRadius:1,hoverRadius:4,hoverBorderWidth:1},t.elements.Point=t.Element.extend({inRange:function(t,e){var n=this._view;return!!n&&Math.pow(t-n.x,2)+Math.pow(e-n.y,2)<Math.pow(n.hitRadius+n.radius,2)},inLabelRange:e,inXRange:e,inYRange:n,getCenterPoint:function(){var t=this._view;return{x:t.x,y:t.y}},getArea:function(){return Math.PI*Math.pow(this._view.radius,2)},tooltipPosition:function(){var t=this._view;return{x:t.x,y:t.y,padding:t.radius+t.borderWidth}},draw:function(e){var n=this._view,o=thisLine truncated
|
||||
r.closePath(),r.stroke();else{var o=e(t);if(0===o)return;r.beginPath();var s=t.getPointPosition(0,i);r.moveTo(s.x,s.y);for(var l=1;l<o;l++)s=t.getPointPosition(l,i),r.lineTo(s.x,s.y);r.closePath(),r.stroke()}}function h(t){return f.isNumber(t)?t:0}var f=t.helpers,g=t.defaults.global,p={display:!0,animate:!0,position:"chartArea",angleLines:{display:!0,color:"rgba(0, 0, 0, 0.1)",lineWidth:1},gridLines:{circular:!1},ticks:{showLabelBackdrop:!0,backdropColor:"rgba(255,255,255,0.75)",backdropPaddingY:2,backdropPaddingX:2,callback:t.Ticks.formatters.linear},pointLabels:{display:!0,fontSize:10,callback:function(t){return t}}},m=t.LinearScaleBase.extend({setDimensions:function(){var t=this,e=t.options,n=e.ticks;t.width=t.maxWidth,t.height=t.maxHeight,t.xCenter=Math.round(t.width/2),t.yCenter=Math.round(t.height/2);var i=f.min([t.height,t.width]),a=f.getValueOrDefault(n.fontSize,g.defaultFontSize);t.drawingArea=e.display?i/2-(a/2+n.backdropPaddingY):i/2},determineDataLimits:function(){var t=this,e=t.chart,n=Number.POSITIVE_INFINITY,i=Number.NEGATIVE_INFINITY;f.each(e.data.datasets,function(a,r){if(e.isDatasetVisible(r)){var o=e.getDatasetMeta(r);f.each(a.data,function(e,a){var r=+t.getRightValue(e);isNaN(r)||o.data[a].hidden||(n=Math.min(r,n),i=Math.max(r,i))})}}),t.min=n===Number.POSITIVE_INFINITY?0:n,t.max=i===Number.NEGATIVE_INFINITY?0:i,t.handleTickRangeOptions()},getTickLimit:function(){var t=this.options.ticks,e=f.getValueOrDefault(t.fontSize,g.defaultFontSize);return Math.min(t.maxTicksLimit?t.maxTicksLimit:11,Math.ceil(this.drawingArea/(1.5*e)))},convertTicksToLabels:function(){var e=this;t.LinearScaleBase.prototype.convertTicksToLabels.call(e),e.pointLabels=e.chart.data.labels.map(e.options.pointLabels.callback,e)},getLabelForIndex:function(t,e){return+this.getRightValue(this.chart.data.datasets[e].data[t])},fit:function(){this.options.pointLabels.display?r(this):o(this)},setReductions:function(t,e,n){var i=this,a=e.l/Math.sin(n.l),r=Math.max(e.r-i.width,0)/Math.sin(n.r),o=-e.t/Math.cos(n.t),s=-Math.max(e.b-i.height,0)/Math.cos(n.b);a=h(a),r=h(r),o=h(o),s=h(s),i.drawingArea=Math.min(Math.round(t-(a+r)/2),Math.round(t-(o+s)/2)),i.setCenterPoint(a,r,o,s)},setCenterPoint:function(t,e,n,i){var a=this,r=a.width-e-a.drawingArea,o=t+a.drawingArea,s=n+a.drawingArea,l=a.height-i-a.drawingArea;a.xCenter=Math.round((o+r)/2+a.left),a.yCenter=Math.round((s+l)/2+a.top)},getIndexAngle:function(t){var n=2*Math.PI/e(this),i=this.chart.options&&this.chart.options.startAngle?this.chart.options.startAngle:0,a=i*Math.PI*2/360;return t*n+a},getDistanceFromCenterForValue:function(t){var e=this;if(null===t)return 0;var n=e.drawingArea/(e.max-e.min);return e.options.reverse?(e.max-t)*n:(t-e.min)*n},getPointPosition:function(t,e){var n=this,i=n.getIndexAngle(t)-Math.PI/2;return{x:Math.round(Math.cos(i)*e)+n.xCenter,y:Math.round(Math.sin(i)*e)+n.yCenter}},getPointPositionForValue:function(t,e){return this.getPointPosition(t,this.getDistanceFromCenterForValue(e))},getBasePosition:function(){var t=this,e=t.min,n=t.max;return t.getPointPositionForValue(0,t.beginAtZero?0:e<0&&n<0?n:e>0&&n>0?e:0)},draw:function(){var t=this,e=t.options,n=e.gridLines,i=e.ticks,a=f.getValueOrDefault;if(e.display){var r=t.ctx,o=a(i.fontSize,g.defaultFontSize),s=a(i.fontStyle,g.defaultFontStyle),l=a(i.fontFamily,g.defaultFontFamily),u=f.fontString(o,s,l);f.each(t.ticks,function(s,l){if(l>0||e.reverse){var d=t.getDistanceFromCenterForValue(t.ticksAsNumbers[l]),h=t.yCenter-d;if(n.display&&0!==l&&c(t,n,d,l),i.display){var f=a(i.fontColor,g.defaultFontColor);if(r.font=u,i.showLabelBackdrop){var p=r.measureText(s).width;r.fillStyle=i.backdropColor,r.fillRect(t.xCenter-p/2-i.backdropPaddingX,h-o/2-i.backdropPaddingY,p+2*i.backdropPaddingX,o+2*i.backdropPaddingY)}r.textAlign="center",r.textBaseline="middle",r.fillStyle=f,r.fillText(s,t.xCenter,h)}}}),(e.angleLines.display||e.pointLabels.display)&&d(t)}}});t.scaleService.registerScaleType("radialLinear",m,p)}},{}],49:[function(t,e,n){"use strict";var i=t(6);i="function"==typeof i?i:window.moment,e.exports=function(t){function e(t,e){var n=t.options.time;if("string"==typeof n.parser)return i(e,n.parser);if("function"==typeof n.parser)return n.parser(e);if("function"==typeof e.getMonth||"number"==typeof e)return i(e);if(e.isValid&&e.isValid())return e;var a=n.format;return"string"!=typeof a&&a.call?(console.warn("options.time.format is deprecated and replaced by options.time.parser."),a(e)):i(e,a)}function n(t,e,n,i){for(var a,r=Object.keys(s),o=r.length,l=r.indexOf(t);l<o;l++){a=r[l];var u=s[a],d=u.steps&&u.steps[u.steps.length-1]||u.maxStep;if(void 0===d||Math.ceil((n-e)/(d*u.size))<=i)break}return a}function a(t,e,n,i){var a=s[n],r=a.size,o=Math.ceil((e-t)/r),l=1,u=e-t;if(a.steps)for(var d=a.steps.length,c=0;c<d&&o>i;c++)l=a.steps[c],o=Math.ceil(u/(r*l));else for(;o>i&&i>0;)++l,o=Math.ceil(u/(r*l));return l}function r(t,e,n){var a=[];if(t.maxTicks){var r=t.stepSize;a.push(void 0!==t.min?t.min:n.min);for(var o=i(n.mLine truncated
|
||||
@@ -1 +0,0 @@
|
||||
.select2-container{box-sizing:border-box;display:inline-block;margin:0;position:relative;vertical-align:middle}.select2-container .select2-selection--single{box-sizing:border-box;cursor:pointer;display:block;height:28px;user-select:none;-webkit-user-select:none}.select2-container .select2-selection--single .select2-selection__rendered{display:block;padding-left:8px;padding-right:20px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.select2-container .select2-selection--single .select2-selection__clear{background-color:transparent;border:none;font-size:1em}.select2-container[dir="rtl"] .select2-selection--single .select2-selection__rendered{padding-right:8px;padding-left:20px}.select2-container .select2-selection--multiple{box-sizing:border-box;cursor:pointer;display:block;min-height:32px;user-select:none;-webkit-user-select:none}.select2-container .select2-selection--multiple .select2-selection__rendered{display:inline;list-style:none;padding:0}.select2-container .select2-selection--multiple .select2-selection__clear{background-color:transparent;border:none;font-size:1em}.select2-container .select2-search--inline .select2-search__field{box-sizing:border-box;border:none;font-size:100%;margin-top:5px;margin-left:5px;padding:0;max-width:100%;resize:none;height:18px;vertical-align:bottom;font-family:sans-serif;overflow:hidden;word-break:keep-all}.select2-container .select2-search--inline .select2-search__field::-webkit-search-cancel-button{-webkit-appearance:none}.select2-dropdown{background-color:white;border:1px solid #aaa;border-radius:4px;box-sizing:border-box;display:block;position:absolute;left:-100000px;width:100%;z-index:1051}.select2-results{display:block}.select2-results__options{list-style:none;margin:0;padding:0}.select2-results__option{padding:6px;user-select:none;-webkit-user-select:none}.select2-results__option--selectable{cursor:pointer}.select2-container--open .select2-dropdown{left:0}.select2-container--open .select2-dropdown--above{border-bottom:none;border-bottom-left-radius:0;border-bottom-right-radius:0}.select2-container--open .select2-dropdown--below{border-top:none;border-top-left-radius:0;border-top-right-radius:0}.select2-search--dropdown{display:block;padding:4px}.select2-search--dropdown .select2-search__field{padding:4px;width:100%;box-sizing:border-box}.select2-search--dropdown .select2-search__field::-webkit-search-cancel-button{-webkit-appearance:none}.select2-search--dropdown.select2-search--hide{display:none}.select2-close-mask{border:0;margin:0;padding:0;display:block;position:fixed;left:0;top:0;min-height:100%;min-width:100%;height:auto;width:auto;opacity:0;z-index:99;background-color:#fff;filter:alpha(opacity=0)}.select2-hidden-accessible{border:0 !important;clip:rect(0 0 0 0) !important;-webkit-clip-path:inset(50%) !important;clip-path:inset(50%) !important;height:1px !important;overflow:hidden !important;padding:0 !important;position:absolute !important;width:1px !important;white-space:nowrap !important}.select2-container--default .select2-selection--single{background-color:#fff;border:1px solid #aaa;border-radius:4px}.select2-container--default .select2-selection--single .select2-selection__rendered{color:#444;line-height:28px}.select2-container--default .select2-selection--single .select2-selection__clear{cursor:pointer;float:right;font-weight:bold;height:26px;margin-right:20px;padding-right:0px}.select2-container--default .select2-selection--single .select2-selection__placeholder{color:#999}.select2-container--default .select2-selection--single .select2-selection__arrow{height:26px;position:absolute;top:1px;right:1px;width:20px}.select2-container--default .select2-selection--single .select2-selection__arrow b{border-color:#888 transparent transparent transparent;border-style:solid;border-width:5px 4px 0 4px;height:0;left:50%;margin-left:-4px;margin-top:-2px;position:absolute;top:50%;width:0}.select2-container--default[dir="rtl"] .select2-selection--single .select2-selection__clear{float:left}.select2-container--default[dir="rtl"] .select2-selection--single .select2-selection__arrow{left:1px;right:auto}.select2-container--default.select2-container--disabled .select2-selection--single{background-color:#eee;cursor:default}.select2-container--default.select2-container--disabled .select2-selection--single .select2-selection__clear{display:none}.select2-container--default.select2-container--open .select2-selection--single .select2-selection__arrow b{border-color:transparent transparent #888 transparent;border-width:0 4px 5px 4px}.select2-container--default .select2-selection--multiple{background-color:white;border:1px solid #aaa;border-radius:4px;cursor:text;padding-bottom:5px;padding-right:5px;position:relative}.select2-container--default .select2-selection--multiple.select2-selection--clearable{padding-right:25px}.select2-container--default .select2-selection--multiple .select2-selection__clear{cursor:pointer;font-weight:bold;height:20px;margin-right:10px;marginLine truncated
|
||||
-2
@@ -1,2 +0,0 @@
|
||||
/*! Select2 4.0.13 | https://github.com/select2/select2/blob/master/LICENSE.md */
|
||||
!function(n){"function"==typeof define&&define.amd?define(["jquery"],n):"object"==typeof module&&module.exports?module.exports=function(e,t){return void 0===t&&(t="undefined"!=typeof window?require("jquery"):require("jquery")(e)),n(t),t}:n(jQuery)}(function(t){var e,n,s,p,r,o,h,f,g,m,y,v,i,a,_,s=((u=t&&t.fn&&t.fn.select2&&t.fn.select2.amd?t.fn.select2.amd:u)&&u.requirejs||(u?n=u:u={},g={},m={},y={},v={},i=Object.prototype.hasOwnProperty,a=[].slice,_=/\.js$/,h=function(e,t){var n,s,i=c(e),r=i[0],t=t[1];return e=i[1],r&&(n=x(r=l(r,t))),r?e=n&&n.normalize?n.normalize(e,(s=t,function(e){return l(e,s)})):l(e,t):(r=(i=c(e=l(e,t)))[0],e=i[1],r&&(n=x(r))),{f:r?r+"!"+e:e,n:e,pr:r,p:n}},f={require:function(e){return w(e)},exports:function(e){var t=g[e];return void 0!==t?t:g[e]={}},module:function(e){return{id:e,uri:"",exports:g[e],config:(t=e,function(){return y&&y.config&&y.config[t]||{}})};var t}},r=function(e,t,n,s){var i,r,o,a,l,c=[],u=typeof n,d=A(s=s||e);if("undefined"==u||"function"==u){for(t=!t.length&&n.length?["require","exports","module"]:t,a=0;a<t.length;a+=1)if("require"===(r=(o=h(t[a],d)).f))c[a]=f.require(e);else if("exports"===r)c[a]=f.exports(e),l=!0;else if("module"===r)i=c[a]=f.module(e);else if(b(g,r)||b(m,r)||b(v,r))c[a]=x(r);else{if(!o.p)throw new Error(e+" missing "+r);o.p.load(o.n,w(s,!0),function(t){return function(e){g[t]=e}}(r),{}),c[a]=g[r]}u=n?n.apply(g[e],c):void 0,e&&(i&&i.exports!==p&&i.exports!==g[e]?g[e]=i.exports:u===p&&l||(g[e]=u))}else e&&(g[e]=n)},e=n=o=function(e,t,n,s,i){if("string"==typeof e)return f[e]?f[e](t):x(h(e,A(t)).f);if(!e.splice){if((y=e).deps&&o(y.deps,y.callback),!t)return;t.splice?(e=t,t=n,n=null):e=p}return t=t||function(){},"function"==typeof n&&(n=s,s=i),s?r(p,e,t,n):setTimeout(function(){r(p,e,t,n)},4),o},o.config=function(e){return o(e)},e._defined=g,(s=function(e,t,n){if("string"!=typeof e)throw new Error("See almond README: incorrect module build, no module name");t.splice||(n=t,t=[]),b(g,e)||b(m,e)||(m[e]=[e,t,n])}).amd={jQuery:!0},u.requirejs=e,u.require=n,u.define=s),u.define("almond",function(){}),u.define("jquery",[],function(){var e=t||$;return null==e&&console&&console.error&&console.error("Select2: An instance of jQuery or a jQuery-compatible library was not found. Make sure that you are including jQuery before Select2 on your web page."),e}),u.define("select2/utils",["jquery"],function(r){var s={};function c(e){var t,n=e.prototype,s=[];for(t in n)"function"==typeof n[t]&&"constructor"!==t&&s.push(t);return s}s.Extend=function(e,t){var n,s={}.hasOwnProperty;function i(){this.constructor=e}for(n in t)s.call(t,n)&&(e[n]=t[n]);return i.prototype=t.prototype,e.prototype=new i,e.__super__=t.prototype,e},s.Decorate=function(s,i){var e=c(i),t=c(s);function r(){var e=Array.prototype.unshift,t=i.prototype.constructor.length,n=s.prototype.constructor;0<t&&(e.call(arguments,s.prototype.constructor),n=i.prototype.constructor),n.apply(this,arguments)}i.displayName=s.displayName,r.prototype=new function(){this.constructor=r};for(var n=0;n<t.length;n++){var o=t[n];r.prototype[o]=s.prototype[o]}for(var a=0;a<e.length;a++){var l=e[a];r.prototype[l]=function(e){var t=function(){};e in r.prototype&&(t=r.prototype[e]);var n=i.prototype[e];return function(){return Array.prototype.unshift.call(arguments,t),n.apply(this,arguments)}}(l)}return r};function e(){this.listeners={}}e.prototype.on=function(e,t){this.listeners=this.listeners||{},e in this.listeners?this.listeners[e].push(t):this.listeners[e]=[t]},e.prototype.trigger=function(e){var t=Array.prototype.slice,n=t.call(arguments,1);this.listeners=this.listeners||{},0===(n=null==n?[]:n).length&&n.push({}),(n[0]._type=e)in this.listeners&&this.invoke(this.listeners[e],t.call(arguments,1)),"*"in this.listeners&&this.invoke(this.listeners["*"],arguments)},e.prototype.invoke=function(e,t){for(var n=0,s=e.length;n<s;n++)e[n].apply(this,t)},s.Observable=e,s.generateChars=function(e){for(var t="",n=0;n<e;n++)t+=Math.floor(36*Math.random()).toString(36);return t},s.bind=function(e,t){return function(){e.apply(t,arguments)}},s._convertData=function(e){for(var t in e){var n=t.split("-"),s=e;if(1!==n.length){for(var i=0;i<n.length;i++){var r=n[i];(r=r.substring(0,1).toLowerCase()+r.substring(1))in s||(s[r]={}),i==n.length-1&&(s[r]=e[t]),s=s[r]}delete e[t]}}return e},s.hasScroll=function(e,t){var n=r(t),s=t.style.overflowX,i=t.style.overflowY;return(s!==i||"hidden"!==i&&"visible"!==i)&&("scroll"===s||"scroll"===i||(n.innerHeight()<t.scrollHeight||n.innerWidth()<t.scrollWidth))},s.escapeMarkup=function(e){var t={"\\":"\","&":"&","<":"<",">":">",'"':""","'":"'","/":"/"};return"string"!=typeof e?e:String(e).replace(/[&<>"'\/\\]/g,function(e){return t[e]})},s.__cache={};var n=0;return s.GetUniqueElementId=function(e){var t=e.getAttribute("data-select2-id");return null!=t||(t=e.id?"select2-data-"+e.id:"select2-data-"+(++n).toString()+"-"+s.generateChars(4),e.setAttribute("data-select2-id",t)),t},s.StoreData=Line truncated
|
||||
-1
@@ -1 +0,0 @@
|
||||
!function(n,e,r,t,i,o,a,c,s){for(var u=s,f=0;f<document.scripts.length;f++)if(document.scripts[f].src.indexOf(o)>-1){u&&"no"===document.scripts[f].getAttribute("data-lazy")&&(u=!1);break}var p=[];function l(n){return"e"in n}function d(n){return"p"in n}function _(n){return"f"in n}var v=[];function y(n){u&&(l(n)||d(n)||_(n)&&n.f.indexOf("capture")>-1||_(n)&&n.f.indexOf("showReportDialog")>-1)&&m(),v.push(n)}function g(){y({e:[].slice.call(arguments)})}function h(n){y({p:n})}function E(){try{n.SENTRY_SDK_SOURCE="loader";var e=n[i],o=e.init;e.init=function(i){n.removeEventListener(r,g),n.removeEventListener(t,h);var a=c;for(var s in i)Object.prototype.hasOwnProperty.call(i,s)&&(a[s]=i[s]);!function(n,e){var r=n.integrations||[];if(!Array.isArray(r))return;var t=r.map((function(n){return n.name}));n.tracesSampleRate&&-1===t.indexOf("BrowserTracing")&&(e.browserTracingIntegration?r.push(e.browserTracingIntegration({enableInp:!0})):e.BrowserTracing&&r.push(new e.BrowserTracing));(n.replaysSessionSampleRate||n.replaysOnErrorSampleRate)&&-1===t.indexOf("Replay")&&(e.replayIntegration?r.push(e.replayIntegration()):e.Replay&&r.push(new e.Replay));n.integrations=r}(a,e),o(a)},setTimeout((function(){return function(e){try{"function"==typeof n.sentryOnLoad&&(n.sentryOnLoad(),n.sentryOnLoad=void 0);for(var r=0;r<p.length;r++)"function"==typeof p[r]&&p[r]();p.splice(0);for(r=0;r<v.length;r++){_(o=v[r])&&"init"===o.f&&e.init.apply(e,o.a)}L()||e.init();var t=n.onerror,i=n.onunhandledrejection;for(r=0;r<v.length;r++){var o;if(_(o=v[r])){if("init"===o.f)continue;e[o.f].apply(e,o.a)}else l(o)&&t?t.apply(n,o.e):d(o)&&i&&i.apply(n,[o.p])}}catch(n){console.error(n)}}(e)}))}catch(n){console.error(n)}}var O=!1;function m(){if(!O){O=!0;var n=e.scripts[0],r=e.createElement("script");r.src=a,r.crossOrigin="anonymous",r.addEventListener("load",E,{once:!0,passive:!0}),n.parentNode.insertBefore(r,n)}}function L(){var e=n.__SENTRY__,r=void 0!==e&&e.version;return r?!!e[r]:!(void 0===e||!e.hub||!e.hub.getClient())}n[i]=n[i]||{},n[i].onLoad=function(n){L()?n():p.push(n)},n[i].forceLoad=function(){setTimeout((function(){m()}))},["init","addBreadcrumb","captureMessage","captureException","captureEvent","configureScope","withScope","showReportDialog"].forEach((function(e){n[i][e]=function(){y({f:e,a:arguments})}})),n.addEventListener(r,g),n.addEventListener(t,h),u||setTimeout((function(){m()}))}(window,document,"error","unhandledrejection","Sentry",'ab63c6820882cab2883218a4b9deba4d','https://browser.sentry-cdn.com/8.26.0/bundle.min.js',{"dsn":"https://ab63c6820882cab2883218a4b9deba4d@o4505360748642304.ingest.us.sentry.io/4507633486266368"},true);
|
||||
@@ -0,0 +1,93 @@
|
||||
import "tom-select/dist/css/tom-select.css";
|
||||
import { inheritHtmlElement, registerComponent } from "#core:utils/web-components";
|
||||
import TomSelect from "tom-select";
|
||||
import type { TomItem, TomLoadCallback, TomOption } from "tom-select/dist/types/types";
|
||||
import type { escape_html } from "tom-select/dist/types/utils";
|
||||
import { type UserProfileSchema, userSearchUsers } from "#openapi";
|
||||
|
||||
@registerComponent("ajax-select")
|
||||
export class AjaxSelect extends inheritHtmlElement("select") {
|
||||
public widget: TomSelect;
|
||||
public filter?: <T>(items: T[]) => T[];
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
window.addEventListener("DOMContentLoaded", () => {
|
||||
this.loadTomSelect();
|
||||
});
|
||||
}
|
||||
|
||||
loadTomSelect() {
|
||||
const minCharNumberForSearch = 2;
|
||||
let maxItems = 1;
|
||||
|
||||
if (this.node.multiple) {
|
||||
maxItems = Number.parseInt(this.node.dataset.max) ?? null;
|
||||
}
|
||||
|
||||
this.widget = new TomSelect(this.node, {
|
||||
hideSelected: true,
|
||||
diacritics: true,
|
||||
duplicates: false,
|
||||
maxItems: maxItems,
|
||||
loadThrottle: Number.parseInt(this.node.dataset.delay) ?? null,
|
||||
valueField: "id",
|
||||
labelField: "display_name",
|
||||
searchField: ["display_name", "nick_name", "first_name", "last_name"],
|
||||
placeholder: this.node.dataset.placeholder ?? "",
|
||||
shouldLoad: (query: string) => {
|
||||
return query.length >= minCharNumberForSearch; // Avoid launching search with less than 2 characters
|
||||
},
|
||||
load: (query: string, callback: TomLoadCallback) => {
|
||||
userSearchUsers({
|
||||
query: {
|
||||
search: query,
|
||||
},
|
||||
}).then((response) => {
|
||||
if (response.data) {
|
||||
if (this.filter) {
|
||||
callback(this.filter(response.data.results), []);
|
||||
} else {
|
||||
callback(response.data.results, []);
|
||||
}
|
||||
return;
|
||||
}
|
||||
callback([], []);
|
||||
});
|
||||
},
|
||||
render: {
|
||||
option: (item: UserProfileSchema, sanitize: typeof escape_html) => {
|
||||
return `<div class="select-item">
|
||||
<img
|
||||
src="${sanitize(item.profile_pict)}"
|
||||
alt="${sanitize(item.display_name)}"
|
||||
onerror="this.src = '/static/core/img/unknown.jpg'"
|
||||
/>
|
||||
<span class="select-item-text">${sanitize(item.display_name)}</span>
|
||||
</div>`;
|
||||
},
|
||||
item: (item: UserProfileSchema, sanitize: typeof escape_html) => {
|
||||
return `<span><i class="fa fa-times"></i>${sanitize(item.display_name)}</span>`;
|
||||
},
|
||||
// biome-ignore lint/style/useNamingConvention: that's how it's defined
|
||||
not_loading: (data: TomOption, _sanitize: typeof escape_html) => {
|
||||
return `<div class="no-results">${interpolate(gettext("You need to type %(number)s more characters"), { number: minCharNumberForSearch - data.input.length }, true)}</div>`;
|
||||
},
|
||||
// biome-ignore lint/style/useNamingConvention: that's how it's defined
|
||||
no_results: (_data: TomOption, _sanitize: typeof escape_html) => {
|
||||
return `<div class="no-results">${gettext("No results found")}</div>`;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// Allow removing selected items by clicking on them
|
||||
this.widget.on("item_select", (item: TomItem) => {
|
||||
this.widget.removeItem(item);
|
||||
});
|
||||
// Remove typed text once an item has been selected
|
||||
this.widget.on("item_add", () => {
|
||||
this.widget.setTextboxValue("");
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,53 +1,57 @@
|
||||
// biome-ignore lint/correctness/noUndeclaredDependencies: shipped by easymde
|
||||
import "codemirror/lib/codemirror.css";
|
||||
import "easymde/src/css/easymde.css";
|
||||
import easyMde from "easymde";
|
||||
import { inheritHtmlElement, registerComponent } from "#core:utils/web-components";
|
||||
// biome-ignore lint/correctness/noUndeclaredDependencies: Imported by EasyMDE
|
||||
import type CodeMirror from "codemirror";
|
||||
// biome-ignore lint/style/useNamingConvention: This is how they called their namespace
|
||||
import EasyMDE from "easymde";
|
||||
import { markdownRenderMarkdown } from "#openapi";
|
||||
|
||||
/**
|
||||
* Create a new easymde based textarea
|
||||
* @param {HTMLTextAreaElement} textarea to use
|
||||
**/
|
||||
window.easymdeFactory = (textarea) => {
|
||||
const easymde = new easyMde({
|
||||
const loadEasyMde = (textarea: HTMLTextAreaElement) => {
|
||||
new EasyMDE({
|
||||
element: textarea,
|
||||
spellChecker: false,
|
||||
autoDownloadFontAwesome: false,
|
||||
previewRender: Alpine.debounce(async (plainText, preview) => {
|
||||
preview.innerHTML = (
|
||||
await markdownRenderMarkdown({ body: { text: plainText } })
|
||||
).data;
|
||||
previewRender: Alpine.debounce((plainText: string, preview: MarkdownInput) => {
|
||||
const func = async (plainText: string, preview: MarkdownInput): Promise<null> => {
|
||||
preview.innerHTML = (
|
||||
await markdownRenderMarkdown({ body: { text: plainText } })
|
||||
).data as string;
|
||||
return null;
|
||||
};
|
||||
func(plainText, preview);
|
||||
return null;
|
||||
}, 300),
|
||||
forceSync: true, // Avoid validation error on generic create view
|
||||
toolbar: [
|
||||
{
|
||||
name: "heading-smaller",
|
||||
action: easyMde.toggleHeadingSmaller,
|
||||
action: EasyMDE.toggleHeadingSmaller,
|
||||
className: "fa fa-header",
|
||||
title: gettext("Heading"),
|
||||
},
|
||||
{
|
||||
name: "italic",
|
||||
action: easyMde.toggleItalic,
|
||||
action: EasyMDE.toggleItalic,
|
||||
className: "fa fa-italic",
|
||||
title: gettext("Italic"),
|
||||
},
|
||||
{
|
||||
name: "bold",
|
||||
action: easyMde.toggleBold,
|
||||
action: EasyMDE.toggleBold,
|
||||
className: "fa fa-bold",
|
||||
title: gettext("Bold"),
|
||||
},
|
||||
{
|
||||
name: "strikethrough",
|
||||
action: easyMde.toggleStrikethrough,
|
||||
action: EasyMDE.toggleStrikethrough,
|
||||
className: "fa fa-strikethrough",
|
||||
title: gettext("Strikethrough"),
|
||||
},
|
||||
{
|
||||
name: "underline",
|
||||
action: function customFunction(editor) {
|
||||
action: function customFunction(editor: { codemirror: CodeMirror.Editor }) {
|
||||
const cm = editor.codemirror;
|
||||
cm.replaceSelection(`__${cm.getSelection()}__`);
|
||||
},
|
||||
@@ -56,7 +60,7 @@ window.easymdeFactory = (textarea) => {
|
||||
},
|
||||
{
|
||||
name: "superscript",
|
||||
action: function customFunction(editor) {
|
||||
action: function customFunction(editor: { codemirror: CodeMirror.Editor }) {
|
||||
const cm = editor.codemirror;
|
||||
cm.replaceSelection(`^${cm.getSelection()}^`);
|
||||
},
|
||||
@@ -65,7 +69,7 @@ window.easymdeFactory = (textarea) => {
|
||||
},
|
||||
{
|
||||
name: "subscript",
|
||||
action: function customFunction(editor) {
|
||||
action: function customFunction(editor: { codemirror: CodeMirror.Editor }) {
|
||||
const cm = editor.codemirror;
|
||||
cm.replaceSelection(`~${cm.getSelection()}~`);
|
||||
},
|
||||
@@ -74,71 +78,71 @@ window.easymdeFactory = (textarea) => {
|
||||
},
|
||||
{
|
||||
name: "code",
|
||||
action: easyMde.toggleCodeBlock,
|
||||
action: EasyMDE.toggleCodeBlock,
|
||||
className: "fa fa-code",
|
||||
title: gettext("Code"),
|
||||
},
|
||||
"|",
|
||||
{
|
||||
name: "quote",
|
||||
action: easyMde.toggleBlockquote,
|
||||
action: EasyMDE.toggleBlockquote,
|
||||
className: "fa fa-quote-left",
|
||||
title: gettext("Quote"),
|
||||
},
|
||||
{
|
||||
name: "unordered-list",
|
||||
action: easyMde.toggleUnorderedList,
|
||||
action: EasyMDE.toggleUnorderedList,
|
||||
className: "fa fa-list-ul",
|
||||
title: gettext("Unordered list"),
|
||||
},
|
||||
{
|
||||
name: "ordered-list",
|
||||
action: easyMde.toggleOrderedList,
|
||||
action: EasyMDE.toggleOrderedList,
|
||||
className: "fa fa-list-ol",
|
||||
title: gettext("Ordered list"),
|
||||
},
|
||||
"|",
|
||||
{
|
||||
name: "link",
|
||||
action: easyMde.drawLink,
|
||||
action: EasyMDE.drawLink,
|
||||
className: "fa fa-link",
|
||||
title: gettext("Insert link"),
|
||||
},
|
||||
{
|
||||
name: "image",
|
||||
action: easyMde.drawImage,
|
||||
action: EasyMDE.drawImage,
|
||||
className: "fa-regular fa-image",
|
||||
title: gettext("Insert image"),
|
||||
},
|
||||
{
|
||||
name: "table",
|
||||
action: easyMde.drawTable,
|
||||
action: EasyMDE.drawTable,
|
||||
className: "fa fa-table",
|
||||
title: gettext("Insert table"),
|
||||
},
|
||||
"|",
|
||||
{
|
||||
name: "clean-block",
|
||||
action: easyMde.cleanBlock,
|
||||
action: EasyMDE.cleanBlock,
|
||||
className: "fa fa-eraser fa-clean-block",
|
||||
title: gettext("Clean block"),
|
||||
},
|
||||
"|",
|
||||
{
|
||||
name: "preview",
|
||||
action: easyMde.togglePreview,
|
||||
action: EasyMDE.togglePreview,
|
||||
className: "fa fa-eye no-disable",
|
||||
title: gettext("Toggle preview"),
|
||||
},
|
||||
{
|
||||
name: "side-by-side",
|
||||
action: easyMde.toggleSideBySide,
|
||||
action: EasyMDE.toggleSideBySide,
|
||||
className: "fa fa-columns no-disable no-mobile",
|
||||
title: gettext("Toggle side by side"),
|
||||
},
|
||||
{
|
||||
name: "fullscreen",
|
||||
action: easyMde.toggleFullScreen,
|
||||
action: EasyMDE.toggleFullScreen,
|
||||
className: "fa fa-expand no-mobile",
|
||||
title: gettext("Toggle fullscreen"),
|
||||
},
|
||||
@@ -152,27 +156,25 @@ window.easymdeFactory = (textarea) => {
|
||||
],
|
||||
});
|
||||
|
||||
const submits = textarea.closest("form").querySelectorAll('input[type="submit"]');
|
||||
const parentDiv = textarea.parentElement;
|
||||
let submitPressed = false;
|
||||
const submits: HTMLInputElement[] = Array.from(
|
||||
textarea.closest("form").querySelectorAll('input[type="submit"]'),
|
||||
);
|
||||
const parentDiv = textarea.parentElement.parentElement;
|
||||
|
||||
function checkMarkdownInput() {
|
||||
function checkMarkdownInput(event: Event) {
|
||||
// an attribute is null if it does not exist, else a string
|
||||
const required = textarea.getAttribute("required") != null;
|
||||
const length = textarea.value.trim().length;
|
||||
|
||||
if (required && length === 0) {
|
||||
parentDiv.style.boxShadow = "red 0px 0px 1.5px 1px";
|
||||
event.preventDefault();
|
||||
} else {
|
||||
parentDiv.style.boxShadow = "";
|
||||
}
|
||||
}
|
||||
|
||||
function onSubmitClick(e) {
|
||||
if (!submitPressed) {
|
||||
easymde.codemirror.on("change", checkMarkdownInput);
|
||||
}
|
||||
submitPressed = true;
|
||||
function onSubmitClick(e: Event) {
|
||||
checkMarkdownInput(e);
|
||||
}
|
||||
|
||||
@@ -180,3 +182,11 @@ window.easymdeFactory = (textarea) => {
|
||||
submit.addEventListener("click", onSubmitClick);
|
||||
}
|
||||
};
|
||||
|
||||
@registerComponent("markdown-input")
|
||||
class MarkdownInput extends inheritHtmlElement("textarea") {
|
||||
constructor() {
|
||||
super();
|
||||
window.addEventListener("DOMContentLoaded", () => loadEasyMde(this.node));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
import { exportToHtml } from "#core:utils/globals";
|
||||
// biome-ignore lint/style/noNamespaceImport: this is the recommended way from the documentation
|
||||
import * as Sentry from "@sentry/browser";
|
||||
|
||||
interface LoggedUser {
|
||||
name: string;
|
||||
email: string;
|
||||
}
|
||||
|
||||
interface SentryOptions {
|
||||
dsn: string;
|
||||
eventId: string;
|
||||
user?: LoggedUser;
|
||||
}
|
||||
|
||||
exportToHtml("loadSentryPopup", (options: SentryOptions) => {
|
||||
Sentry.init({
|
||||
dsn: options.dsn,
|
||||
});
|
||||
Sentry.showReportDialog({
|
||||
eventId: options.eventId,
|
||||
...(options.user ?? {}),
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,21 @@
|
||||
import type { Alpine as AlpineType } from "alpinejs";
|
||||
|
||||
declare global {
|
||||
const Alpine: AlpineType;
|
||||
const gettext: (text: string) => string;
|
||||
const interpolate: <T>(fmt: string, args: string[] | T, isNamed?: boolean) => string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to export typescript functions to regular html and jinja files
|
||||
* Without it, you either have to use the any keyword and suppress warnings or do a
|
||||
* very painful type conversion workaround which is only here to please the linter
|
||||
*
|
||||
* This is only useful if you're using typescript, this is equivalent to doing
|
||||
* window.yourFunction = yourFunction
|
||||
**/
|
||||
// biome-ignore lint/suspicious/noExplicitAny: Avoid strange tricks to export functions
|
||||
export function exportToHtml(name: string, func: any) {
|
||||
// biome-ignore lint/suspicious/noExplicitAny: Avoid strange tricks to export functions
|
||||
(window as any)[name] = func;
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/**
|
||||
* Class decorator to register components easily
|
||||
* It's a wrapper around window.customElements.define
|
||||
* What's nice about it is that you don't separate the component registration
|
||||
* and the class definition
|
||||
**/
|
||||
export function registerComponent(name: string, options?: ElementDefinitionOptions) {
|
||||
return (component: CustomElementConstructor) => {
|
||||
window.customElements.define(name, component, options);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Safari doesn't support inheriting from HTML tags on web components
|
||||
* The technique is to:
|
||||
* create a new web component
|
||||
* create the desired type inside
|
||||
* pass all attributes to the child component
|
||||
* store is at as `node` inside the parent
|
||||
*
|
||||
* Since we can't use the generic type to instantiate the node, we create a generator function
|
||||
*
|
||||
* ```js
|
||||
* class MyClass extends inheritHtmlElement("select") {
|
||||
* // do whatever
|
||||
* }
|
||||
* ```
|
||||
**/
|
||||
export function inheritHtmlElement<K extends keyof HTMLElementTagNameMap>(tagName: K) {
|
||||
return class Inherited extends HTMLElement {
|
||||
protected node: HTMLElementTagNameMap[K];
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.node = document.createElement(tagName);
|
||||
const attributes: Attr[] = []; // We need to make a copy to delete while iterating
|
||||
for (const attr of this.attributes) {
|
||||
if (attr.name in this.node) {
|
||||
attributes.push(attr);
|
||||
}
|
||||
}
|
||||
|
||||
for (const attr of attributes) {
|
||||
this.removeAttributeNode(attr);
|
||||
this.node.setAttributeNode(attr);
|
||||
}
|
||||
this.appendChild(this.node);
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -1,26 +1,26 @@
|
||||
{% extends "core/base.jinja" %}
|
||||
{% block head %}
|
||||
{{ super() }}
|
||||
<script
|
||||
src="{{ static('vendored/sentry/bundle.min.js') }}"
|
||||
crossorigin="anonymous"
|
||||
></script>
|
||||
{% endblock head %}
|
||||
{% block additional_js %}
|
||||
{% if settings.SENTRY_DSN %}
|
||||
<script src="{{ static('webpack/sentry-popup-index.ts') }}" defer ></script>
|
||||
{% endif %}
|
||||
{% endblock additional_js %}
|
||||
|
||||
{% block content %}
|
||||
<h3>{% trans %}500, Server Error{% endtrans %}</h3>
|
||||
{% if settings.SENTRY_DSN %}
|
||||
<script>
|
||||
Sentry.init({ dsn: '{{ settings.SENTRY_DSN }}' });
|
||||
Sentry.showReportDialog({
|
||||
eventId: '{{ request.sentry_last_event_id() }}',
|
||||
{% if user.is_authenticated %}
|
||||
user: {
|
||||
'name': '{{user.first_name}} {{user.last_name}}',
|
||||
'email': '{{user.email}}'
|
||||
}
|
||||
{% endif %}
|
||||
})
|
||||
window.addEventListener("DOMContentLoaded", () => {
|
||||
loadSentryPopup({
|
||||
dsn: "{{ settings.SENTRY_DSN }}",
|
||||
eventId: "{{ request.sentry_last_event_id() }}",
|
||||
{% if user.is_authenticated %}
|
||||
user: {
|
||||
name: '{{user.first_name}} {{user.last_name}}',
|
||||
email: '{{user.email}}'
|
||||
}
|
||||
{% endif %}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{% endif %}
|
||||
{% endblock content %}
|
||||
@@ -5,6 +5,7 @@
|
||||
<title>{% block title %}{% trans %}Welcome!{% endtrans %}{% endblock %} - Association des Étudiants UTBM</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link rel="shortcut icon" href="{{ static('core/img/favicon.ico') }}">
|
||||
<link rel="stylesheet" href="{{ static('user/user_stats.scss') }}">
|
||||
<link rel="stylesheet" href="{{ static('core/base.css') }}">
|
||||
<link rel="stylesheet" href="{{ static('ajax_select/css/ajax_select.css') }}">
|
||||
<link rel="stylesheet" href="{{ static('core/style.scss') }}">
|
||||
@@ -12,7 +13,6 @@
|
||||
<link rel="stylesheet" href="{{ static('core/header.scss') }}">
|
||||
<link rel="stylesheet" href="{{ static('core/navbar.scss') }}">
|
||||
<link rel="stylesheet" href="{{ static('core/pagination.scss') }}">
|
||||
<link rel="stylesheet" href="{{ static('vendored/select2/select2.min.css') }}">
|
||||
|
||||
{% block jquery_css %}
|
||||
{# Thile file is quite heavy (around 250kb), so declaring it in a block allows easy removal #}
|
||||
@@ -26,8 +26,6 @@
|
||||
<script src="{{ static('webpack/jquery-index.js') }}"></script>
|
||||
<!-- Put here to always have access to those functions on django widgets -->
|
||||
<script src="{{ static('core/js/script.js') }}"></script>
|
||||
<script defer src="{{ static('vendored/select2/select2.min.js') }}"></script>
|
||||
<script defer src="{{ static('core/js/sith-select2.js') }}"></script>
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,13 +1,7 @@
|
||||
<div>
|
||||
<textarea name="{{ widget.name }}"{% include "django/forms/widgets/attrs.html" %}>{% if widget.value %}{{ widget.value }}{% endif %}</textarea>
|
||||
<markdown-input name="{{ widget.name }}"{% include "django/forms/widgets/attrs.html" %}>{% if widget.value %}{{ widget.value }}{% endif %}</markdown-input>
|
||||
|
||||
{# The easymde script can be included twice, it's safe in the code #}
|
||||
<script src="{{ statics.js }}" defer> </script>
|
||||
<link rel="stylesheet" type="text/css" href="{{ statics.css }}" defer>
|
||||
<script type="text/javascript">
|
||||
addEventListener("DOMContentLoaded", (event) => {
|
||||
easymdeFactory(
|
||||
document.getElementById("{{ widget.attrs.id }}"));
|
||||
})
|
||||
</script>
|
||||
</div>
|
||||
@@ -50,7 +50,7 @@ def phonenumber(
|
||||
try:
|
||||
parsed = phonenumbers.parse(value, country)
|
||||
return phonenumbers.format_number(parsed, number_format)
|
||||
except phonenumbers.NumberParseException as e:
|
||||
except phonenumbers.NumberParseException:
|
||||
return value
|
||||
|
||||
|
||||
|
||||
@@ -343,7 +343,7 @@ class TestUserTools:
|
||||
response = client.get(reverse("core:user_tools"))
|
||||
assertRedirects(
|
||||
response,
|
||||
expected_url=f"/login?next=%2Fuser%2Ftools%2F",
|
||||
expected_url="/login?next=%2Fuser%2Ftools%2F",
|
||||
target_status_code=301,
|
||||
)
|
||||
|
||||
|
||||
@@ -73,7 +73,7 @@ class TestFetchFamilyApi(TestCase):
|
||||
self.client.force_login(self.main_user)
|
||||
response = self.client.get(
|
||||
reverse("api:family_graph", args=[self.main_user.id])
|
||||
+ f"?godfathers_depth=0&godchildren_depth=0"
|
||||
+ "?godfathers_depth=0&godchildren_depth=0"
|
||||
)
|
||||
assert response.status_code == 200
|
||||
assert [u["id"] for u in response.json()["users"]] == [self.main_user.id]
|
||||
@@ -91,7 +91,7 @@ class TestFetchFamilyApi(TestCase):
|
||||
self.client.force_login(self.main_user)
|
||||
response = self.client.get(
|
||||
reverse("api:family_graph", args=[self.main_user.id])
|
||||
+ f"?godfathers_depth=10&godchildren_depth=10"
|
||||
+ "?godfathers_depth=10&godchildren_depth=10"
|
||||
)
|
||||
assert response.status_code == 200
|
||||
assert [u["id"] for u in response.json()["users"]] == [
|
||||
@@ -126,7 +126,7 @@ class TestFetchFamilyApi(TestCase):
|
||||
self.client.force_login(self.main_user)
|
||||
response = self.client.get(
|
||||
reverse("api:family_graph", args=[self.main_user.id])
|
||||
+ f"?godfathers_depth=1&godchildren_depth=1"
|
||||
+ "?godfathers_depth=1&godchildren_depth=1"
|
||||
)
|
||||
assert response.status_code == 200
|
||||
assert [u["id"] for u in response.json()["users"]] == [
|
||||
@@ -150,7 +150,7 @@ class TestFetchFamilyApi(TestCase):
|
||||
self.client.force_login(self.main_user)
|
||||
response = self.client.get(
|
||||
reverse("api:family_graph", args=[self.main_user.id])
|
||||
+ f"?godfathers_depth=10&godchildren_depth=0"
|
||||
+ "?godfathers_depth=10&godchildren_depth=0"
|
||||
)
|
||||
assert response.status_code == 200
|
||||
assert [u["id"] for u in response.json()["users"]] == [
|
||||
|
||||
@@ -158,10 +158,11 @@ def test_user_invoice_with_multiple_items():
|
||||
item_recipe = Recipe(InvoiceItem, invoice=foreign_key(Recipe(Invoice, user=user)))
|
||||
item_recipe.make(_quantity=3, quantity=1, product_unit_price=5)
|
||||
item_recipe.make(_quantity=1, quantity=1, product_unit_price=5)
|
||||
item_recipe.make(_quantity=2, quantity=1, product_unit_price=iter([5, 8]))
|
||||
res = list(
|
||||
Invoice.objects.filter(user=user)
|
||||
.annotate_total()
|
||||
.order_by("-total")
|
||||
.values_list("total", flat=True)
|
||||
)
|
||||
assert res == [15, 5]
|
||||
assert res == [15, 13, 5]
|
||||
+71
-65
@@ -29,13 +29,67 @@ from core.converters import (
|
||||
FourDigitYearConverter,
|
||||
TwoDigitMonthConverter,
|
||||
)
|
||||
from core.views import *
|
||||
from core.views import (
|
||||
FileDeleteView,
|
||||
FileEditPropView,
|
||||
FileEditView,
|
||||
FileListView,
|
||||
FileModerateView,
|
||||
FileModerationView,
|
||||
FileView,
|
||||
GiftCreateView,
|
||||
GiftDeleteView,
|
||||
GroupCreateView,
|
||||
GroupDeleteView,
|
||||
GroupEditView,
|
||||
GroupListView,
|
||||
GroupTemplateView,
|
||||
NotificationList,
|
||||
PageCreateView,
|
||||
PageDeleteView,
|
||||
PageEditView,
|
||||
PageHistView,
|
||||
PageListView,
|
||||
PagePropView,
|
||||
PageRevView,
|
||||
PageView,
|
||||
SithLoginView,
|
||||
SithPasswordChangeDoneView,
|
||||
SithPasswordChangeView,
|
||||
SithPasswordResetCompleteView,
|
||||
SithPasswordResetConfirmView,
|
||||
SithPasswordResetDoneView,
|
||||
SithPasswordResetView,
|
||||
UserAccountDetailView,
|
||||
UserAccountView,
|
||||
UserClubView,
|
||||
UserCreationView,
|
||||
UserGodfathersTreeView,
|
||||
UserGodfathersView,
|
||||
UserListView,
|
||||
UserMiniView,
|
||||
UserPicturesView,
|
||||
UserPreferencesView,
|
||||
UserStatsView,
|
||||
UserToolsView,
|
||||
UserUpdateGroupView,
|
||||
UserUpdateProfileView,
|
||||
UserView,
|
||||
delete_user_godfather,
|
||||
index,
|
||||
logout,
|
||||
notification,
|
||||
password_root_change,
|
||||
search_json,
|
||||
search_user_json,
|
||||
search_view,
|
||||
send_file,
|
||||
)
|
||||
|
||||
register_converter(FourDigitYearConverter, "yyyy")
|
||||
register_converter(TwoDigitMonthConverter, "mm")
|
||||
register_converter(BooleanStringConverter, "bool")
|
||||
|
||||
|
||||
urlpatterns = [
|
||||
path("", index, name="index"),
|
||||
path("notifications/", NotificationList.as_view(), name="notification_list"),
|
||||
@@ -80,27 +134,17 @@ urlpatterns = [
|
||||
path("group/new/", GroupCreateView.as_view(), name="group_new"),
|
||||
path("group/<int:group_id>/", GroupEditView.as_view(), name="group_edit"),
|
||||
path(
|
||||
"group/<int:group_id>/delete/",
|
||||
GroupDeleteView.as_view(),
|
||||
name="group_delete",
|
||||
"group/<int:group_id>/delete/", GroupDeleteView.as_view(), name="group_delete"
|
||||
),
|
||||
path(
|
||||
"group/<int:group_id>/detail/",
|
||||
GroupTemplateView.as_view(),
|
||||
name="group_detail",
|
||||
"group/<int:group_id>/detail/", GroupTemplateView.as_view(), name="group_detail"
|
||||
),
|
||||
# User views
|
||||
path("user/", UserListView.as_view(), name="user_list"),
|
||||
path(
|
||||
"user/<int:user_id>/mini/",
|
||||
UserMiniView.as_view(),
|
||||
name="user_profile_mini",
|
||||
),
|
||||
path("user/<int:user_id>/mini/", UserMiniView.as_view(), name="user_profile_mini"),
|
||||
path("user/<int:user_id>/", UserView.as_view(), name="user_profile"),
|
||||
path(
|
||||
"user/<int:user_id>/pictures/",
|
||||
UserPicturesView.as_view(),
|
||||
name="user_pictures",
|
||||
"user/<int:user_id>/pictures/", UserPicturesView.as_view(), name="user_pictures"
|
||||
),
|
||||
path(
|
||||
"user/<int:user_id>/godfathers/",
|
||||
@@ -117,28 +161,14 @@ urlpatterns = [
|
||||
delete_user_godfather,
|
||||
name="user_godfathers_delete",
|
||||
),
|
||||
path(
|
||||
"user/<int:user_id>/edit/",
|
||||
UserUpdateProfileView.as_view(),
|
||||
name="user_edit",
|
||||
),
|
||||
path("user/<int:user_id>/edit/", UserUpdateProfileView.as_view(), name="user_edit"),
|
||||
path("user/<int:user_id>/clubs/", UserClubView.as_view(), name="user_clubs"),
|
||||
path("user/<int:user_id>/prefs/", UserPreferencesView.as_view(), name="user_prefs"),
|
||||
path(
|
||||
"user/<int:user_id>/prefs/",
|
||||
UserPreferencesView.as_view(),
|
||||
name="user_prefs",
|
||||
),
|
||||
path(
|
||||
"user/<int:user_id>/groups/",
|
||||
UserUpdateGroupView.as_view(),
|
||||
name="user_groups",
|
||||
"user/<int:user_id>/groups/", UserUpdateGroupView.as_view(), name="user_groups"
|
||||
),
|
||||
path("user/tools/", UserToolsView.as_view(), name="user_tools"),
|
||||
path(
|
||||
"user/<int:user_id>/account/",
|
||||
UserAccountView.as_view(),
|
||||
name="user_account",
|
||||
),
|
||||
path("user/<int:user_id>/account/", UserAccountView.as_view(), name="user_account"),
|
||||
path(
|
||||
"user/<int:user_id>/account/<yyyy:year>/<mm:month>/",
|
||||
UserAccountDetailView.as_view(),
|
||||
@@ -179,42 +209,18 @@ urlpatterns = [
|
||||
),
|
||||
path("file/moderation/", FileModerationView.as_view(), name="file_moderation"),
|
||||
path(
|
||||
"file/<int:file_id>/moderate/",
|
||||
FileModerateView.as_view(),
|
||||
name="file_moderate",
|
||||
"file/<int:file_id>/moderate/", FileModerateView.as_view(), name="file_moderate"
|
||||
),
|
||||
path("file/<int:file_id>/download/", send_file, name="download"),
|
||||
# Page views
|
||||
path("page/", PageListView.as_view(), name="page_list"),
|
||||
path("page/create/", PageCreateView.as_view(), name="page_new"),
|
||||
path("page/<int:page_id>/delete/", PageDeleteView.as_view(), name="page_delete"),
|
||||
path("page/<path:page_name>/edit/", PageEditView.as_view(), name="page_edit"),
|
||||
path("page/<path:page_name>/prop/", PagePropView.as_view(), name="page_prop"),
|
||||
path("page/<path:page_name>/hist/", PageHistView.as_view(), name="page_hist"),
|
||||
path(
|
||||
"page/<int:page_id>/delete/",
|
||||
PageDeleteView.as_view(),
|
||||
name="page_delete",
|
||||
),
|
||||
path(
|
||||
"page/<path:page_name>/edit/",
|
||||
PageEditView.as_view(),
|
||||
name="page_edit",
|
||||
),
|
||||
path(
|
||||
"page/<path:page_name>/prop/",
|
||||
PagePropView.as_view(),
|
||||
name="page_prop",
|
||||
),
|
||||
path(
|
||||
"page/<path:page_name>/hist/",
|
||||
PageHistView.as_view(),
|
||||
name="page_hist",
|
||||
),
|
||||
path(
|
||||
"page/<path:page_name>/rev/<int:rev>/",
|
||||
PageRevView.as_view(),
|
||||
name="page_rev",
|
||||
),
|
||||
path(
|
||||
"page/<path:page_name>/",
|
||||
PageView.as_view(),
|
||||
name="page",
|
||||
"page/<path:page_name>/rev/<int:rev>/", PageRevView.as_view(), name="page_rev"
|
||||
),
|
||||
path("page/<path:page_name>/", PageView.as_view(), name="page"),
|
||||
]
|
||||
+1
-1
@@ -127,7 +127,7 @@ def resize_image_explicit(
|
||||
|
||||
|
||||
def exif_auto_rotate(image):
|
||||
for orientation in ExifTags.TAGS.keys():
|
||||
for orientation in ExifTags.TAGS:
|
||||
if ExifTags.TAGS[orientation] == "Orientation":
|
||||
break
|
||||
exif = dict(image._getexif().items())
|
||||
|
||||
+17
-13
@@ -25,6 +25,7 @@
|
||||
import types
|
||||
from typing import Any
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.mixins import AccessMixin
|
||||
from django.core.exceptions import (
|
||||
ImproperlyConfigured,
|
||||
@@ -35,6 +36,7 @@ from django.http import (
|
||||
HttpResponseNotFound,
|
||||
HttpResponseServerError,
|
||||
)
|
||||
from django.shortcuts import render
|
||||
from django.utils.functional import cached_property
|
||||
from django.views.generic.base import View
|
||||
from django.views.generic.detail import SingleObjectMixin
|
||||
@@ -79,9 +81,7 @@ def can_edit_prop(obj: Any, user: User) -> bool:
|
||||
raise PermissionDenied
|
||||
```
|
||||
"""
|
||||
if obj is None or user.is_owner(obj):
|
||||
return True
|
||||
return False
|
||||
return obj is None or user.is_owner(obj)
|
||||
|
||||
|
||||
def can_edit(obj: Any, user: User) -> bool:
|
||||
@@ -232,7 +232,9 @@ class UserIsRootMixin(GenericContentPermissionMixinBuilder):
|
||||
PermissionDenied: if the user isn't root
|
||||
"""
|
||||
|
||||
permission_function = lambda obj, user: user.is_root
|
||||
@staticmethod
|
||||
def permission_function(obj: Any, user: User):
|
||||
return user.is_root
|
||||
|
||||
|
||||
class FormerSubscriberMixin(AccessMixin):
|
||||
@@ -304,10 +306,10 @@ class QuickNotifMixin:
|
||||
kwargs["quick_notifs"] = []
|
||||
for n in self.quick_notif_list:
|
||||
kwargs["quick_notifs"].append(settings.SITH_QUICK_NOTIF[n])
|
||||
for k, v in settings.SITH_QUICK_NOTIF.items():
|
||||
for gk in self.request.GET.keys():
|
||||
if k == gk:
|
||||
kwargs["quick_notifs"].append(v)
|
||||
for key, val in settings.SITH_QUICK_NOTIF.items():
|
||||
for gk in self.request.GET:
|
||||
if key == gk:
|
||||
kwargs["quick_notifs"].append(val)
|
||||
return kwargs
|
||||
|
||||
|
||||
@@ -324,8 +326,10 @@ class DetailFormView(SingleObjectMixin, FormView):
|
||||
return super().get_object()
|
||||
|
||||
|
||||
from .files import *
|
||||
from .group import *
|
||||
from .page import *
|
||||
from .site import *
|
||||
from .user import *
|
||||
# F403: those star-imports would be hellish to refactor
|
||||
# E402: putting those import at the top of the file would also be difficult
|
||||
from .files import * # noqa: F403 E402
|
||||
from .group import * # noqa: F403 E402
|
||||
from .page import * # noqa: F403 E402
|
||||
from .site import * # noqa: F403 E402
|
||||
from .user import * # noqa: F403 E402
|
||||
+16
-16
@@ -193,7 +193,7 @@ class FileEditView(CanEditMixin, UpdateView):
|
||||
def get_form_class(self):
|
||||
fields = ["name", "is_moderated"]
|
||||
if self.object.is_file:
|
||||
fields = ["file"] + fields
|
||||
fields = ["file", *fields]
|
||||
return modelform_factory(SithFile, fields=fields)
|
||||
|
||||
def get_success_url(self):
|
||||
@@ -283,38 +283,38 @@ class FileView(CanViewMixin, DetailView, FormMixin):
|
||||
`obj` is the SithFile object you want to put in the clipboard, or
|
||||
where you want to paste the clipboard
|
||||
"""
|
||||
if "delete" in request.POST.keys():
|
||||
if "delete" in request.POST:
|
||||
for f_id in request.POST.getlist("file_list"):
|
||||
sf = SithFile.objects.filter(id=f_id).first()
|
||||
if sf:
|
||||
sf.delete()
|
||||
if "clear" in request.POST.keys():
|
||||
file = SithFile.objects.filter(id=f_id).first()
|
||||
if file:
|
||||
file.delete()
|
||||
if "clear" in request.POST:
|
||||
request.session["clipboard"] = []
|
||||
if "cut" in request.POST.keys():
|
||||
for f_id in request.POST.getlist("file_list"):
|
||||
f_id = int(f_id)
|
||||
if "cut" in request.POST:
|
||||
for f_id_str in request.POST.getlist("file_list"):
|
||||
f_id = int(f_id_str)
|
||||
if (
|
||||
f_id in [c.id for c in obj.children.all()]
|
||||
and f_id not in request.session["clipboard"]
|
||||
):
|
||||
request.session["clipboard"].append(f_id)
|
||||
if "paste" in request.POST.keys():
|
||||
if "paste" in request.POST:
|
||||
for f_id in request.session["clipboard"]:
|
||||
sf = SithFile.objects.filter(id=f_id).first()
|
||||
if sf:
|
||||
sf.move_to(obj)
|
||||
file = SithFile.objects.filter(id=f_id).first()
|
||||
if file:
|
||||
file.move_to(obj)
|
||||
request.session["clipboard"] = []
|
||||
request.session.modified = True
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
self.form = self.get_form()
|
||||
if "clipboard" not in request.session.keys():
|
||||
if "clipboard" not in request.session:
|
||||
request.session["clipboard"] = []
|
||||
return super().get(request, *args, **kwargs)
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
self.object = self.get_object()
|
||||
if "clipboard" not in request.session.keys():
|
||||
if "clipboard" not in request.session:
|
||||
request.session["clipboard"] = []
|
||||
if request.user.can_edit(self.object):
|
||||
# XXX this call can fail!
|
||||
@@ -398,6 +398,6 @@ class FileModerateView(CanEditPropMixin, SingleObjectMixin):
|
||||
self.object.is_moderated = True
|
||||
self.object.moderator = request.user
|
||||
self.object.save()
|
||||
if "next" in self.request.GET.keys():
|
||||
if "next" in self.request.GET:
|
||||
return redirect(self.request.GET["next"])
|
||||
return redirect("core:file_moderation")
|
||||
+5
-5
@@ -29,6 +29,7 @@ from captcha.fields import CaptchaField
|
||||
from django import forms
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.forms import AuthenticationForm, UserCreationForm
|
||||
from django.contrib.staticfiles.storage import staticfiles_storage
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.db import transaction
|
||||
from django.forms import (
|
||||
@@ -38,7 +39,6 @@ from django.forms import (
|
||||
Textarea,
|
||||
TextInput,
|
||||
)
|
||||
from django.templatetags.static import static
|
||||
from django.utils.translation import gettext
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from phonenumber_field.widgets import RegionalPhoneNumberWidget
|
||||
@@ -72,8 +72,8 @@ class MarkdownInput(Textarea):
|
||||
context = super().get_context(name, value, attrs)
|
||||
|
||||
context["statics"] = {
|
||||
"js": static("webpack/easymde-index.js"),
|
||||
"css": static("webpack/easymde-index.css"),
|
||||
"js": staticfiles_storage.url("webpack/easymde-index.ts"),
|
||||
"css": staticfiles_storage.url("webpack/easymde-index.css"),
|
||||
}
|
||||
return context
|
||||
|
||||
@@ -140,7 +140,7 @@ class SelectUser(TextInput):
|
||||
|
||||
class LoginForm(AuthenticationForm):
|
||||
def __init__(self, *arg, **kwargs):
|
||||
if "data" in kwargs.keys():
|
||||
if "data" in kwargs:
|
||||
from counter.models import Customer
|
||||
|
||||
data = kwargs["data"].copy()
|
||||
@@ -157,7 +157,7 @@ class LoginForm(AuthenticationForm):
|
||||
else:
|
||||
user = User.objects.filter(username=data["username"]).first()
|
||||
data["username"] = user.username
|
||||
except:
|
||||
except: # noqa E722 I don't know what error is supposed to be raised here
|
||||
pass
|
||||
kwargs["data"] = data
|
||||
super().__init__(*arg, **kwargs)
|
||||
|
||||
+9
-25
@@ -55,7 +55,7 @@ class PageView(CanViewMixin, DetailView):
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
if "page" not in context.keys():
|
||||
if "page" not in context:
|
||||
context["new_page"] = self.kwargs["page_name"]
|
||||
return context
|
||||
|
||||
@@ -92,22 +92,16 @@ class PageRevView(CanViewMixin, DetailView):
|
||||
)
|
||||
return res
|
||||
|
||||
def get_object(self):
|
||||
def get_object(self, *args, **kwargs):
|
||||
self.page = Page.get_page_by_full_name(self.kwargs["page_name"])
|
||||
return self.page
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
if self.page is not None:
|
||||
context["page"] = self.page
|
||||
try:
|
||||
rev = self.page.revisions.get(id=self.kwargs["rev"])
|
||||
context["rev"] = rev
|
||||
except:
|
||||
# By passing, the template will just display the normal page without taking revision into account
|
||||
pass
|
||||
else:
|
||||
context["new_page"] = self.kwargs["page_name"]
|
||||
if not self.page:
|
||||
return context | {"new_page": self.kwargs["page_name"]}
|
||||
context["page"] = self.page
|
||||
context["rev"] = self.page.revisions.filter(id=self.kwargs["rev"]).first()
|
||||
return context
|
||||
|
||||
|
||||
@@ -118,7 +112,7 @@ class PageCreateView(CanCreateMixin, CreateView):
|
||||
|
||||
def get_initial(self):
|
||||
init = {}
|
||||
if "page" in self.request.GET.keys():
|
||||
if "page" in self.request.GET:
|
||||
page_name = self.request.GET["page"]
|
||||
parent_name = "/".join(page_name.split("/")[:-1])
|
||||
parent = Page.get_page_by_full_name(parent_name)
|
||||
@@ -145,18 +139,8 @@ class PagePropView(CanEditPagePropMixin, UpdateView):
|
||||
slug_field = "_full_name"
|
||||
slug_url_kwarg = "page_name"
|
||||
|
||||
def get_object(self):
|
||||
o = super().get_object()
|
||||
# Create the page if it does not exists
|
||||
# if p == None:
|
||||
# parent_name = '/'.join(page_name.split('/')[:-1])
|
||||
# name = page_name.split('/')[-1]
|
||||
# if parent_name == "":
|
||||
# p = Page(name=name)
|
||||
# else:
|
||||
# parent = Page.get_page_by_full_name(parent_name)
|
||||
# p = Page(name=name, parent=parent)
|
||||
self.page = o
|
||||
def get_object(self, queryset=None):
|
||||
self.page = super().get_object()
|
||||
try:
|
||||
self.page.set_lock_recursive(self.request.user)
|
||||
except LockError as e:
|
||||
|
||||
+2
-5
@@ -53,11 +53,8 @@ class NotificationList(ListView):
|
||||
if self.request.user.is_anonymous:
|
||||
return Notification.objects.none()
|
||||
# TODO: Bulk update in django 2.2
|
||||
if "see_all" in self.request.GET.keys():
|
||||
for n in self.request.user.notifications.filter(viewed=False):
|
||||
n.viewed = True
|
||||
n.save()
|
||||
|
||||
if "see_all" in self.request.GET:
|
||||
self.request.user.notifications.filter(viewed=False).update(viewed=True)
|
||||
return self.request.user.notifications.order_by("-date")[:20]
|
||||
|
||||
|
||||
|
||||
+31
-24
@@ -21,9 +21,11 @@
|
||||
# Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
#
|
||||
#
|
||||
import itertools
|
||||
|
||||
# This file contains all the views that concern the user model
|
||||
from datetime import date, timedelta
|
||||
from operator import itemgetter
|
||||
from smtplib import SMTPException
|
||||
|
||||
from django.conf import settings
|
||||
@@ -253,8 +255,10 @@ class UserTabsMixin(TabedViewMixin):
|
||||
"name": _("Groups"),
|
||||
}
|
||||
)
|
||||
try:
|
||||
if user.customer and (
|
||||
if (
|
||||
hasattr(user, "customer")
|
||||
and user.customer
|
||||
and (
|
||||
user == self.request.user
|
||||
or self.request.user.is_in_group(
|
||||
pk=settings.SITH_GROUP_ACCOUNTING_ADMIN_ID
|
||||
@@ -264,25 +268,22 @@ class UserTabsMixin(TabedViewMixin):
|
||||
+ settings.SITH_BOARD_SUFFIX
|
||||
)
|
||||
or self.request.user.is_root
|
||||
):
|
||||
tab_list.append(
|
||||
{
|
||||
"url": reverse("core:user_stats", kwargs={"user_id": user.id}),
|
||||
"slug": "stats",
|
||||
"name": _("Stats"),
|
||||
}
|
||||
)
|
||||
tab_list.append(
|
||||
{
|
||||
"url": reverse(
|
||||
"core:user_account", kwargs={"user_id": user.id}
|
||||
),
|
||||
"slug": "account",
|
||||
"name": _("Account") + " (%s €)" % user.customer.amount,
|
||||
}
|
||||
)
|
||||
except:
|
||||
pass
|
||||
)
|
||||
):
|
||||
tab_list.append(
|
||||
{
|
||||
"url": reverse("core:user_stats", kwargs={"user_id": user.id}),
|
||||
"slug": "stats",
|
||||
"name": _("Stats"),
|
||||
}
|
||||
)
|
||||
tab_list.append(
|
||||
{
|
||||
"url": reverse("core:user_account", kwargs={"user_id": user.id}),
|
||||
"slug": "account",
|
||||
"name": _("Account") + " (%s €)" % user.customer.amount,
|
||||
}
|
||||
)
|
||||
return tab_list
|
||||
|
||||
|
||||
@@ -665,9 +666,15 @@ class UserAccountView(UserAccountBase):
|
||||
kwargs["refilling_month"] = self.expense_by_month(
|
||||
Refilling.objects.filter(customer=self.object.customer)
|
||||
)
|
||||
kwargs["invoices_month"] = self.expense_by_month(
|
||||
Invoice.objects.filter(user=self.object)
|
||||
)
|
||||
kwargs["invoices_month"] = [
|
||||
# the django ORM removes the `group by` clause in this query,
|
||||
# so a little of post-processing is needed
|
||||
{"grouped_date": key, "total": sum(i["total"] for i in group)}
|
||||
for key, group in itertools.groupby(
|
||||
self.expense_by_month(Invoice.objects.filter(user=self.object)),
|
||||
key=itemgetter("grouped_date"),
|
||||
)
|
||||
]
|
||||
kwargs["etickets"] = self.object.customer.buyings.exclude(product__eticket=None)
|
||||
return kwargs
|
||||
|
||||
|
||||
+13
-1
@@ -15,7 +15,19 @@
|
||||
from django.contrib import admin
|
||||
from haystack.admin import SearchModelAdmin
|
||||
|
||||
from counter.models import *
|
||||
from counter.models import (
|
||||
AccountDump,
|
||||
BillingInfo,
|
||||
CashRegisterSummary,
|
||||
Counter,
|
||||
Customer,
|
||||
Eticket,
|
||||
Permanency,
|
||||
Product,
|
||||
ProductType,
|
||||
Refilling,
|
||||
Selling,
|
||||
)
|
||||
|
||||
|
||||
@admin.register(Product)
|
||||
|
||||
+23
-40
@@ -154,7 +154,7 @@ class Customer(models.Model):
|
||||
self.save()
|
||||
|
||||
def get_full_url(self):
|
||||
return "".join(["https://", settings.SITH_URL, self.get_absolute_url()])
|
||||
return f"https://{settings.SITH_URL}{self.get_absolute_url()}"
|
||||
|
||||
|
||||
class BillingInfo(models.Model):
|
||||
@@ -287,9 +287,7 @@ class ProductType(models.Model):
|
||||
"""Method to see if that object can be edited by the given user."""
|
||||
if user.is_anonymous:
|
||||
return False
|
||||
if user.is_in_group(pk=settings.SITH_GROUP_ACCOUNTING_ADMIN_ID):
|
||||
return True
|
||||
return False
|
||||
return user.is_in_group(pk=settings.SITH_GROUP_ACCOUNTING_ADMIN_ID)
|
||||
|
||||
|
||||
class Product(models.Model):
|
||||
@@ -346,21 +344,19 @@ class Product(models.Model):
|
||||
|
||||
@property
|
||||
def is_record_product(self):
|
||||
return settings.SITH_ECOCUP_CONS == self.id
|
||||
return self.id == settings.SITH_ECOCUP_CONS
|
||||
|
||||
@property
|
||||
def is_unrecord_product(self):
|
||||
return settings.SITH_ECOCUP_DECO == self.id
|
||||
return self.id == settings.SITH_ECOCUP_DECO
|
||||
|
||||
def is_owned_by(self, user):
|
||||
"""Method to see if that object can be edited by the given user."""
|
||||
if user.is_anonymous:
|
||||
return False
|
||||
if user.is_in_group(
|
||||
return user.is_in_group(
|
||||
pk=settings.SITH_GROUP_ACCOUNTING_ADMIN_ID
|
||||
) or user.is_in_group(pk=settings.SITH_GROUP_COUNTER_ADMIN_ID):
|
||||
return True
|
||||
return False
|
||||
) or user.is_in_group(pk=settings.SITH_GROUP_COUNTER_ADMIN_ID)
|
||||
|
||||
def can_be_sold_to(self, user: User) -> bool:
|
||||
"""Check if whether the user given in parameter has the right to buy
|
||||
@@ -392,10 +388,7 @@ class Product(models.Model):
|
||||
buying_groups = list(self.buying_groups.all())
|
||||
if not buying_groups:
|
||||
return True
|
||||
for group in buying_groups:
|
||||
if user.is_in_group(pk=group.id):
|
||||
return True
|
||||
return False
|
||||
return any(user.is_in_group(pk=group.id) for group in buying_groups)
|
||||
|
||||
@property
|
||||
def profit(self):
|
||||
@@ -887,27 +880,19 @@ class Selling(models.Model):
|
||||
"You bought an eticket for the event %(event)s.\nYou can download it directly from this link %(eticket)s.\nYou can also retrieve all your e-tickets on your account page %(url)s."
|
||||
) % {
|
||||
"event": event,
|
||||
"url": "".join(
|
||||
(
|
||||
'<a href="',
|
||||
self.customer.get_full_url(),
|
||||
'">',
|
||||
self.customer.get_full_url(),
|
||||
"</a>",
|
||||
)
|
||||
"url": (
|
||||
f'<a href="{self.customer.get_full_url()}">'
|
||||
f"{self.customer.get_full_url()}</a>"
|
||||
),
|
||||
"eticket": "".join(
|
||||
(
|
||||
'<a href="',
|
||||
self.get_eticket_full_url(),
|
||||
'">',
|
||||
self.get_eticket_full_url(),
|
||||
"</a>",
|
||||
)
|
||||
"eticket": (
|
||||
f'<a href="{self.get_eticket_full_url()}">'
|
||||
f"{self.get_eticket_full_url()}</a>"
|
||||
),
|
||||
}
|
||||
message_txt = _(
|
||||
"You bought an eticket for the event %(event)s.\nYou can download it directly from this link %(eticket)s.\nYou can also retrieve all your e-tickets on your account page %(url)s."
|
||||
"You bought an eticket for the event %(event)s.\n"
|
||||
"You can download it directly from this link %(eticket)s.\n"
|
||||
"You can also retrieve all your e-tickets on your account page %(url)s."
|
||||
) % {
|
||||
"event": event,
|
||||
"url": self.customer.get_full_url(),
|
||||
@@ -919,7 +904,7 @@ class Selling(models.Model):
|
||||
|
||||
def get_eticket_full_url(self):
|
||||
eticket_url = reverse("counter:eticket_pdf", kwargs={"selling_id": self.id})
|
||||
return "".join(["https://", settings.SITH_URL, eticket_url])
|
||||
return f"https://{settings.SITH_URL}{eticket_url}"
|
||||
|
||||
|
||||
class Permanency(models.Model):
|
||||
@@ -1019,15 +1004,15 @@ class CashRegisterSummary(models.Model):
|
||||
elif name == "hundred_euros":
|
||||
return self.items.filter(value=100, is_check=False).first()
|
||||
elif name == "check_1":
|
||||
return checks[0] if 0 < len(checks) else None
|
||||
return checks[0] if len(checks) > 0 else None
|
||||
elif name == "check_2":
|
||||
return checks[1] if 1 < len(checks) else None
|
||||
return checks[1] if len(checks) > 1 else None
|
||||
elif name == "check_3":
|
||||
return checks[2] if 2 < len(checks) else None
|
||||
return checks[2] if len(checks) > 2 else None
|
||||
elif name == "check_4":
|
||||
return checks[3] if 3 < len(checks) else None
|
||||
return checks[3] if len(checks) > 3 else None
|
||||
elif name == "check_5":
|
||||
return checks[4] if 4 < len(checks) else None
|
||||
return checks[4] if len(checks) > 4 else None
|
||||
else:
|
||||
return object.__getattribute__(self, name)
|
||||
|
||||
@@ -1035,9 +1020,7 @@ class CashRegisterSummary(models.Model):
|
||||
"""Method to see if that object can be edited by the given user."""
|
||||
if user.is_anonymous:
|
||||
return False
|
||||
if user.is_in_group(pk=settings.SITH_GROUP_COUNTER_ADMIN_ID):
|
||||
return True
|
||||
return False
|
||||
return user.is_in_group(pk=settings.SITH_GROUP_COUNTER_ADMIN_ID)
|
||||
|
||||
def get_total(self):
|
||||
t = 0
|
||||
|
||||
+1
-1
@@ -51,7 +51,7 @@ def write_log(instance, operation_type):
|
||||
# Return None by default
|
||||
return None
|
||||
|
||||
log = OperationLog(
|
||||
OperationLog(
|
||||
label=str(instance),
|
||||
operator=get_user(),
|
||||
operation_type=operation_type,
|
||||
|
||||
@@ -8,25 +8,17 @@
|
||||
{% if current_tab == "products" %}
|
||||
<p><a href="{{ url('counter:new_product') }}">{% trans %}New product{% endtrans %}</a></p>
|
||||
{% endif %}
|
||||
{% if product_list %}
|
||||
<h3>{% trans %}Product list{% endtrans %}</h3>
|
||||
{% for t in ProductType.objects.all().order_by('name') %}
|
||||
<h4>{{ t }}</h4>
|
||||
<ul>
|
||||
{% for p in product_list.filter(product_type=t).all().order_by('name') %}
|
||||
<li><a href="{{ url('counter:product_edit', product_id=p.id) }}">{{ p }} ({{ p.code }})</a></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endfor %}
|
||||
<h4>{% trans %}Uncategorized{% endtrans %}</h4>
|
||||
<h3>{% trans %}Product list{% endtrans %}</h3>
|
||||
{%- for product_type, products in object_list -%}
|
||||
<h4>{{ product_type or _("Uncategorized") }}</h4>
|
||||
<ul>
|
||||
{% for p in product_list.filter(product_type=None).all().order_by('name') %}
|
||||
<li><a href="{{ url('counter:product_edit', product_id=p.id) }}">{{ p }} ({{ p.code }})</a></li>
|
||||
{% endfor %}
|
||||
{%- for product in products -%}
|
||||
<li><a href="{{ url('counter:product_edit', product_id=product.id) }}">{{ product }} ({{ product.code }})</a></li>
|
||||
{%- endfor -%}
|
||||
</ul>
|
||||
{% else %}
|
||||
{%- else -%}
|
||||
{% trans %}There is no products in this website.{% endtrans %}
|
||||
{% endif %}
|
||||
{%- endfor -%}
|
||||
{% endblock %}
|
||||
|
||||
|
||||
|
||||
@@ -503,7 +503,7 @@ class TestBarmanConnection(TestCase):
|
||||
)
|
||||
response = self.client.get(reverse("counter:activity", args=[self.counter.id]))
|
||||
|
||||
assert not '<li><a href="/user/1/">S' Kia</a></li>' in str(response.content)
|
||||
assert '<li><a href="/user/1/">S' Kia</a></li>' not in str(response.content)
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
@@ -853,7 +853,7 @@ class TestCustomerAccountId(TestCase):
|
||||
number = account_id[:-1]
|
||||
assert created is True
|
||||
assert number == "12346"
|
||||
assert 6 == len(account_id)
|
||||
assert len(account_id) == 6
|
||||
assert account_id[-1] in string.ascii_lowercase
|
||||
assert customer.amount == 0
|
||||
|
||||
|
||||
+41
-26
@@ -15,15 +15,44 @@
|
||||
|
||||
from django.urls import path
|
||||
|
||||
from counter.views import *
|
||||
from counter.views import (
|
||||
ActiveProductListView,
|
||||
ArchivedProductListView,
|
||||
CashSummaryEditView,
|
||||
CashSummaryListView,
|
||||
CounterActivityView,
|
||||
CounterCashSummaryView,
|
||||
CounterClick,
|
||||
CounterCreateView,
|
||||
CounterDeleteView,
|
||||
CounterEditPropView,
|
||||
CounterEditView,
|
||||
CounterLastOperationsView,
|
||||
CounterListView,
|
||||
CounterMain,
|
||||
CounterRefillingListView,
|
||||
CounterStatView,
|
||||
EticketCreateView,
|
||||
EticketEditView,
|
||||
EticketListView,
|
||||
EticketPDFView,
|
||||
InvoiceCallView,
|
||||
ProductCreateView,
|
||||
ProductEditView,
|
||||
ProductTypeCreateView,
|
||||
ProductTypeEditView,
|
||||
ProductTypeListView,
|
||||
RefillingDeleteView,
|
||||
SellingDeleteView,
|
||||
StudentCardDeleteView,
|
||||
StudentCardFormView,
|
||||
counter_login,
|
||||
counter_logout,
|
||||
)
|
||||
|
||||
urlpatterns = [
|
||||
path("<int:counter_id>/", CounterMain.as_view(), name="details"),
|
||||
path(
|
||||
"<int:counter_id>/click/<int:user_id>/",
|
||||
CounterClick.as_view(),
|
||||
name="click",
|
||||
),
|
||||
path("<int:counter_id>/click/<int:user_id>/", CounterClick.as_view(), name="click"),
|
||||
path(
|
||||
"<int:counter_id>/last_ops/",
|
||||
CounterLastOperationsView.as_view(),
|
||||
@@ -34,19 +63,11 @@ urlpatterns = [
|
||||
CounterCashSummaryView.as_view(),
|
||||
name="cash_summary",
|
||||
),
|
||||
path(
|
||||
"<int:counter_id>/activity/",
|
||||
CounterActivityView.as_view(),
|
||||
name="activity",
|
||||
),
|
||||
path("<int:counter_id>/activity/", CounterActivityView.as_view(), name="activity"),
|
||||
path("<int:counter_id>/stats/", CounterStatView.as_view(), name="stats"),
|
||||
path("<int:counter_id>/login/", counter_login, name="login"),
|
||||
path("<int:counter_id>/logout/", counter_logout, name="logout"),
|
||||
path(
|
||||
"eticket/<int:selling_id>/pdf/",
|
||||
EticketPDFView.as_view(),
|
||||
name="eticket_pdf",
|
||||
),
|
||||
path("eticket/<int:selling_id>/pdf/", EticketPDFView.as_view(), name="eticket_pdf"),
|
||||
path(
|
||||
"customer/<int:customer_id>/card/add/",
|
||||
StudentCardFormView.as_view(),
|
||||
@@ -59,17 +80,11 @@ urlpatterns = [
|
||||
),
|
||||
path("admin/<int:counter_id>/", CounterEditView.as_view(), name="admin"),
|
||||
path(
|
||||
"admin/<int:counter_id>/prop/",
|
||||
CounterEditPropView.as_view(),
|
||||
name="prop_admin",
|
||||
"admin/<int:counter_id>/prop/", CounterEditPropView.as_view(), name="prop_admin"
|
||||
),
|
||||
path("admin/", CounterListView.as_view(), name="admin_list"),
|
||||
path("admin/new/", CounterCreateView.as_view(), name="new"),
|
||||
path(
|
||||
"admin/delete/<int:counter_id>/",
|
||||
CounterDeleteView.as_view(),
|
||||
name="delete",
|
||||
),
|
||||
path("admin/delete/<int:counter_id>/", CounterDeleteView.as_view(), name="delete"),
|
||||
path("admin/invoices_call/", InvoiceCallView.as_view(), name="invoices_call"),
|
||||
path(
|
||||
"admin/cash_summary/list/",
|
||||
@@ -81,10 +96,10 @@ urlpatterns = [
|
||||
CashSummaryEditView.as_view(),
|
||||
name="cash_summary_edit",
|
||||
),
|
||||
path("admin/product/list/", ProductListView.as_view(), name="product_list"),
|
||||
path("admin/product/list/", ActiveProductListView.as_view(), name="product_list"),
|
||||
path(
|
||||
"admin/product/list_archived/",
|
||||
ProductArchivedListView.as_view(),
|
||||
ArchivedProductListView.as_view(),
|
||||
name="product_list_archived",
|
||||
),
|
||||
path("admin/product/create/", ProductCreateView.as_view(), name="new_product"),
|
||||
|
||||
+40
-29
@@ -12,10 +12,12 @@
|
||||
# OR WITHIN THE LOCAL FILE "LICENSE"
|
||||
#
|
||||
#
|
||||
import itertools
|
||||
import re
|
||||
from datetime import datetime, timedelta
|
||||
from datetime import timezone as tz
|
||||
from http import HTTPStatus
|
||||
from operator import attrgetter
|
||||
from typing import TYPE_CHECKING
|
||||
from urllib.parse import parse_qs
|
||||
|
||||
@@ -89,16 +91,10 @@ class CounterAdminMixin(View):
|
||||
edit_club = []
|
||||
|
||||
def _test_group(self, user):
|
||||
for grp_id in self.edit_group:
|
||||
if user.is_in_group(pk=grp_id):
|
||||
return True
|
||||
return False
|
||||
return any(user.is_in_group(pk=grp_id) for grp_id in self.edit_group)
|
||||
|
||||
def _test_club(self, user):
|
||||
for c in self.edit_club:
|
||||
if c.can_be_edited_by(user):
|
||||
return True
|
||||
return False
|
||||
return any(c.can_be_edited_by(user) for c in self.edit_club)
|
||||
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
if not (
|
||||
@@ -179,7 +175,7 @@ class CounterMain(
|
||||
def post(self, request, *args, **kwargs):
|
||||
self.object = self.get_object()
|
||||
if self.object.type == "BAR" and not (
|
||||
"counter_token" in self.request.session.keys()
|
||||
"counter_token" in self.request.session
|
||||
and self.request.session["counter_token"] == self.object.token
|
||||
): # Check the token to avoid the bar to be stolen
|
||||
return HttpResponseRedirect(
|
||||
@@ -217,7 +213,7 @@ class CounterMain(
|
||||
kwargs["barmen"] = self.object.barmen_list
|
||||
elif self.request.user.is_authenticated:
|
||||
kwargs["barmen"] = [self.request.user]
|
||||
if "last_basket" in self.request.session.keys():
|
||||
if "last_basket" in self.request.session:
|
||||
kwargs["last_basket"] = self.request.session.pop("last_basket")
|
||||
kwargs["last_customer"] = self.request.session.pop("last_customer")
|
||||
kwargs["last_total"] = self.request.session.pop("last_total")
|
||||
@@ -292,7 +288,7 @@ class CounterClick(CounterTabsMixin, CanViewMixin, DetailView):
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
"""Simple get view."""
|
||||
if "basket" not in request.session.keys(): # Init the basket session entry
|
||||
if "basket" not in request.session: # Init the basket session entry
|
||||
request.session["basket"] = {}
|
||||
request.session["basket_total"] = 0
|
||||
request.session["not_enough"] = False # Reset every variable
|
||||
@@ -316,7 +312,7 @@ class CounterClick(CounterTabsMixin, CanViewMixin, DetailView):
|
||||
): # Check that at least one barman is logged in
|
||||
return self.cancel(request)
|
||||
if self.object.type == "BAR" and not (
|
||||
"counter_token" in self.request.session.keys()
|
||||
"counter_token" in self.request.session
|
||||
and self.request.session["counter_token"] == self.object.token
|
||||
): # Also check the token to avoid the bar to be stolen
|
||||
return HttpResponseRedirect(
|
||||
@@ -327,7 +323,7 @@ class CounterClick(CounterTabsMixin, CanViewMixin, DetailView):
|
||||
)
|
||||
+ "?bad_location"
|
||||
)
|
||||
if "basket" not in request.session.keys():
|
||||
if "basket" not in request.session:
|
||||
request.session["basket"] = {}
|
||||
request.session["basket_total"] = 0
|
||||
request.session["not_enough"] = False # Reset every variable
|
||||
@@ -384,13 +380,12 @@ class CounterClick(CounterTabsMixin, CanViewMixin, DetailView):
|
||||
|
||||
def get_total_quantity_for_pid(self, request, pid):
|
||||
pid = str(pid)
|
||||
try:
|
||||
return (
|
||||
request.session["basket"][pid]["qty"]
|
||||
+ request.session["basket"][pid]["bonus_qty"]
|
||||
)
|
||||
except:
|
||||
if pid not in request.session["basket"]:
|
||||
return 0
|
||||
return (
|
||||
request.session["basket"][pid]["qty"]
|
||||
+ request.session["basket"][pid]["bonus_qty"]
|
||||
)
|
||||
|
||||
def compute_record_product(self, request, product=None):
|
||||
recorded = 0
|
||||
@@ -804,25 +799,41 @@ class ProductTypeEditView(CounterAdminTabsMixin, CounterAdminMixin, UpdateView):
|
||||
current_tab = "products"
|
||||
|
||||
|
||||
class ProductArchivedListView(CounterAdminTabsMixin, CounterAdminMixin, ListView):
|
||||
class ProductListView(CounterAdminTabsMixin, CounterAdminMixin, ListView):
|
||||
model = Product
|
||||
queryset = Product.objects.annotate(type_name=F("product_type__name"))
|
||||
template_name = "counter/product_list.jinja"
|
||||
ordering = [
|
||||
F("product_type__priority").desc(nulls_last=True),
|
||||
"product_type",
|
||||
"name",
|
||||
]
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
res = super().get_context_data(**kwargs)
|
||||
res["object_list"] = itertools.groupby(
|
||||
res["object_list"], key=attrgetter("type_name")
|
||||
)
|
||||
return res
|
||||
|
||||
|
||||
class ArchivedProductListView(ProductListView):
|
||||
"""A list view for the admins."""
|
||||
|
||||
model = Product
|
||||
template_name = "counter/product_list.jinja"
|
||||
queryset = Product.objects.filter(archived=True)
|
||||
ordering = ["name"]
|
||||
current_tab = "archive"
|
||||
|
||||
def get_queryset(self):
|
||||
return super().get_queryset().filter(archived=True)
|
||||
|
||||
class ProductListView(CounterAdminTabsMixin, CounterAdminMixin, ListView):
|
||||
|
||||
class ActiveProductListView(ProductListView):
|
||||
"""A list view for the admins."""
|
||||
|
||||
model = Product
|
||||
template_name = "counter/product_list.jinja"
|
||||
queryset = Product.objects.filter(archived=False)
|
||||
ordering = ["name"]
|
||||
current_tab = "products"
|
||||
|
||||
def get_queryset(self):
|
||||
return super().get_queryset().filter(archived=False)
|
||||
|
||||
|
||||
class ProductCreateView(CounterAdminTabsMixin, CounterAdminMixin, CreateView):
|
||||
"""A create view for the admins."""
|
||||
|
||||
@@ -24,6 +24,12 @@ Si le mot apparaît dans le template Jinja :
|
||||
{% trans %}Hello{% endtrans %}
|
||||
```
|
||||
|
||||
Si on est dans un fichier javascript ou typescript :
|
||||
|
||||
```js
|
||||
gettext("Hello");
|
||||
```
|
||||
|
||||
## Générer le fichier django.po
|
||||
|
||||
La traduction se fait en trois étapes.
|
||||
@@ -32,7 +38,7 @@ l'éditer et enfin le compiler au format binaire pour qu'il soit lu par le serve
|
||||
|
||||
```bash
|
||||
./manage.py makemessages --locale=fr -e py,jinja --ignore=node_modules # Pour le backend
|
||||
./manage.py makemessages --locale=fr -d djangojs --ignore=node_modules # Pour le frontend
|
||||
./manage.py makemessages --locale=fr -d djangojs -e js,ts --ignore=node_modules # Pour le frontend
|
||||
```
|
||||
|
||||
## Éditer le fichier django.po
|
||||
|
||||
@@ -190,6 +190,10 @@ que sont VsCode et Sublime Text.
|
||||
"[javascript]": {
|
||||
"editor.formatOnSave": true,
|
||||
"editor.defaultFormatter": "biomejs.biome"
|
||||
},
|
||||
"[typescript]": {
|
||||
"editor.formatOnSave": true,
|
||||
"editor.defaultFormatter": "biomejs.biome"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@@ -246,15 +246,3 @@ pytest core/tests/tests_core.py::TestUserRegistration
|
||||
tous les tests avant de push un commit.
|
||||
|
||||
|
||||
|
||||
## Vérifier les dépendances Javascript
|
||||
|
||||
Une commande a été écrite pour vérifier les éventuelles mises
|
||||
à jour à faire sur les librairies Javascript utilisées.
|
||||
N'oubliez pas de mettre à jour à la fois le fichier
|
||||
de la librairie, mais également sa version dans `sith/settings.py`.
|
||||
|
||||
```bash
|
||||
# Vérifier les mises à jour
|
||||
python manage.py check_front
|
||||
```
|
||||
+2
-1
@@ -13,8 +13,9 @@
|
||||
#
|
||||
#
|
||||
from django.contrib import admin
|
||||
from django.db.models import F, Sum
|
||||
|
||||
from eboutic.models import *
|
||||
from eboutic.models import Basket, BasketItem, Invoice, InvoiceItem
|
||||
|
||||
|
||||
@admin.register(Basket)
|
||||
|
||||
+1
-3
@@ -117,9 +117,7 @@ class BasketForm:
|
||||
"""
|
||||
if not self.error_messages and not self.correct_items:
|
||||
self.clean()
|
||||
if self.error_messages:
|
||||
return False
|
||||
return True
|
||||
return not self.error_messages
|
||||
|
||||
@cached_property
|
||||
def errors(self) -> list[str]:
|
||||
|
||||
+2
-3
@@ -173,9 +173,8 @@ class InvoiceQueryset(models.QuerySet):
|
||||
return self.annotate(
|
||||
total=Subquery(
|
||||
InvoiceItem.objects.filter(invoice_id=OuterRef("pk"))
|
||||
.annotate(item_amount=F("product_unit_price") * F("quantity"))
|
||||
.values("item_amount")
|
||||
.annotate(total=Sum("item_amount"))
|
||||
.values("invoice_id")
|
||||
.annotate(total=Sum(F("product_unit_price") * F("quantity")))
|
||||
.values("total")
|
||||
)
|
||||
)
|
||||
|
||||
@@ -2,8 +2,6 @@ from typing import Annotated
|
||||
|
||||
from ninja import ModelSchema, Schema
|
||||
from pydantic import Field, NonNegativeInt, PositiveInt, TypeAdapter
|
||||
|
||||
# from phonenumber_field.phonenumber import PhoneNumber
|
||||
from pydantic_extra_types.phone_numbers import PhoneNumber, PhoneNumberValidator
|
||||
|
||||
from counter.models import BillingInfo
|
||||
|
||||
@@ -7,11 +7,11 @@
|
||||
|
||||
import base64
|
||||
from pathlib import Path
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
import pytest
|
||||
from cryptography.exceptions import InvalidSignature
|
||||
from cryptography.hazmat.primitives.asymmetric.padding import PKCS1v15
|
||||
from cryptography.hazmat.primitives.asymmetric.rsa import RSAPrivateKey, RSAPublicKey
|
||||
from cryptography.hazmat.primitives.hashes import SHA1
|
||||
from cryptography.hazmat.primitives.serialization import (
|
||||
load_pem_private_key,
|
||||
@@ -19,6 +19,12 @@ from cryptography.hazmat.primitives.serialization import (
|
||||
)
|
||||
from django.conf import settings
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from cryptography.hazmat.primitives.asymmetric.rsa import (
|
||||
RSAPrivateKey,
|
||||
RSAPublicKey,
|
||||
)
|
||||
|
||||
|
||||
def test_signature_valid():
|
||||
"""Test that data sent to the bank is correctly signed."""
|
||||
|
||||
@@ -24,9 +24,9 @@
|
||||
import base64
|
||||
import json
|
||||
import urllib
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from cryptography.hazmat.primitives.asymmetric.padding import PKCS1v15
|
||||
from cryptography.hazmat.primitives.asymmetric.rsa import RSAPrivateKey
|
||||
from cryptography.hazmat.primitives.hashes import SHA1
|
||||
from cryptography.hazmat.primitives.serialization import load_pem_private_key
|
||||
from django.conf import settings
|
||||
@@ -38,6 +38,9 @@ from core.models import User
|
||||
from counter.models import Counter, Customer, Product, Selling
|
||||
from eboutic.models import Basket, BasketItem
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from cryptography.hazmat.primitives.asymmetric.rsa import RSAPrivateKey
|
||||
|
||||
|
||||
class TestEboutic(TestCase):
|
||||
@classmethod
|
||||
|
||||
+8
-1
@@ -25,7 +25,14 @@
|
||||
from django.urls import path, register_converter
|
||||
|
||||
from eboutic.converters import PaymentResultConverter
|
||||
from eboutic.views import *
|
||||
from eboutic.views import (
|
||||
EbouticCommand,
|
||||
EtransactionAutoAnswer,
|
||||
e_transaction_data,
|
||||
eboutic_main,
|
||||
pay_with_sith,
|
||||
payment_result,
|
||||
)
|
||||
|
||||
register_converter(PaymentResultConverter, "res")
|
||||
|
||||
|
||||
+5
-2
@@ -17,11 +17,11 @@ import base64
|
||||
import json
|
||||
from datetime import datetime
|
||||
from enum import Enum
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
import sentry_sdk
|
||||
from cryptography.exceptions import InvalidSignature
|
||||
from cryptography.hazmat.primitives.asymmetric.padding import PKCS1v15
|
||||
from cryptography.hazmat.primitives.asymmetric.rsa import RSAPublicKey
|
||||
from cryptography.hazmat.primitives.hashes import SHA1
|
||||
from cryptography.hazmat.primitives.serialization import load_pem_public_key
|
||||
from django.conf import settings
|
||||
@@ -47,6 +47,9 @@ from eboutic.models import (
|
||||
)
|
||||
from eboutic.schemas import PurchaseItemList, PurchaseItemSchema
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from cryptography.hazmat.primitives.asymmetric.rsa import RSAPublicKey
|
||||
|
||||
|
||||
@login_required
|
||||
@require_GET
|
||||
@@ -221,7 +224,7 @@ class EtransactionAutoAnswer(View):
|
||||
# Payment authorized:
|
||||
# * 'Error' is '00000'
|
||||
# * 'Auto' is in the request
|
||||
if request.GET["Error"] == "00000" and "Auto" in request.GET.keys():
|
||||
if request.GET["Error"] == "00000" and "Auto" in request.GET:
|
||||
try:
|
||||
with transaction.atomic():
|
||||
b = (
|
||||
|
||||
+19
-9
@@ -1,6 +1,22 @@
|
||||
from django.urls import path
|
||||
|
||||
from election.views import *
|
||||
from election.views import (
|
||||
CandidatureCreateView,
|
||||
CandidatureDeleteView,
|
||||
CandidatureUpdateView,
|
||||
ElectionCreateView,
|
||||
ElectionDeleteView,
|
||||
ElectionDetailView,
|
||||
ElectionListArchivedView,
|
||||
ElectionListCreateView,
|
||||
ElectionListDeleteView,
|
||||
ElectionsListView,
|
||||
ElectionUpdateView,
|
||||
RoleCreateView,
|
||||
RoleDeleteView,
|
||||
RoleUpdateView,
|
||||
VoteFormView,
|
||||
)
|
||||
|
||||
urlpatterns = [
|
||||
path("", ElectionsListView.as_view(), name="list"),
|
||||
@@ -19,16 +35,10 @@ urlpatterns = [
|
||||
name="delete_list",
|
||||
),
|
||||
path(
|
||||
"<int:election_id>/role/create/",
|
||||
RoleCreateView.as_view(),
|
||||
name="create_role",
|
||||
"<int:election_id>/role/create/", RoleCreateView.as_view(), name="create_role"
|
||||
),
|
||||
path("<int:role_id>/role/edit/", RoleUpdateView.as_view(), name="update_role"),
|
||||
path(
|
||||
"<int:role_id>/role/delete/",
|
||||
RoleDeleteView.as_view(),
|
||||
name="delete_role",
|
||||
),
|
||||
path("<int:role_id>/role/delete/", RoleDeleteView.as_view(), name="delete_role"),
|
||||
path(
|
||||
"<int:election_id>/candidate/add/",
|
||||
CandidatureCreateView.as_view(),
|
||||
|
||||
+15
-23
@@ -1,3 +1,5 @@
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from ajax_select import make_ajax_field
|
||||
from ajax_select.fields import AutoCompleteSelectField
|
||||
from django import forms
|
||||
@@ -10,11 +12,14 @@ from django.utils.translation import gettext_lazy as _
|
||||
from django.views.generic import DetailView, ListView
|
||||
from django.views.generic.edit import CreateView, DeleteView, FormView, UpdateView
|
||||
|
||||
from core.models import User
|
||||
from core.views import CanCreateMixin, CanEditMixin, CanViewMixin
|
||||
from core.views.forms import MarkdownInput, SelectDateTime
|
||||
from election.models import Candidature, Election, ElectionList, Role, Vote
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from core.models import User
|
||||
|
||||
|
||||
# Custom form field
|
||||
|
||||
|
||||
@@ -23,7 +28,6 @@ class LimitedCheckboxField(forms.ModelMultipleChoiceField):
|
||||
|
||||
def __init__(self, queryset, max_choice, **kwargs):
|
||||
self.max_choice = max_choice
|
||||
widget = forms.CheckboxSelectMultiple()
|
||||
super().__init__(queryset, **kwargs)
|
||||
|
||||
def clean(self, value):
|
||||
@@ -251,7 +255,7 @@ class VoteFormView(CanCreateMixin, FormView):
|
||||
|
||||
def vote(self, election_data):
|
||||
with transaction.atomic():
|
||||
for role_title in election_data.keys():
|
||||
for role_title in election_data:
|
||||
# If we have a multiple choice field
|
||||
if isinstance(election_data[role_title], QuerySet):
|
||||
if election_data[role_title].count() > 0:
|
||||
@@ -444,28 +448,16 @@ class ElectionUpdateView(CanEditMixin, UpdateView):
|
||||
pk_url_kwarg = "election_id"
|
||||
|
||||
def get_initial(self):
|
||||
init = {}
|
||||
try:
|
||||
init["start_date"] = self.object.start_date.strftime("%Y-%m-%d %H:%M:%S")
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
init["end_date"] = self.object.end_date.strftime("%Y-%m-%d %H:%M:%S")
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
init["start_candidature"] = self.object.start_candidature.strftime(
|
||||
return {
|
||||
"start_date": self.object.start_date.strftime("%Y-%m-%d %H:%M:%S"),
|
||||
"end_date": self.object.end_date.strftime("%Y-%m-%d %H:%M:%S"),
|
||||
"start_candidature": self.object.start_candidature.strftime(
|
||||
"%Y-%m-%d %H:%M:%S"
|
||||
)
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
init["end_candidature"] = self.object.end_candidature.strftime(
|
||||
),
|
||||
"end_candidature": self.object.end_candidature.strftime(
|
||||
"%Y-%m-%d %H:%M:%S"
|
||||
)
|
||||
except Exception:
|
||||
pass
|
||||
return init
|
||||
),
|
||||
}
|
||||
|
||||
def get_success_url(self, **kwargs):
|
||||
return reverse_lazy("election:detail", kwargs={"election_id": self.object.id})
|
||||
|
||||
+1
-1
@@ -16,7 +16,7 @@
|
||||
from django.contrib import admin
|
||||
from haystack.admin import SearchModelAdmin
|
||||
|
||||
from forum.models import *
|
||||
from forum.models import Forum, ForumMessage, ForumTopic
|
||||
|
||||
|
||||
@admin.register(Forum)
|
||||
|
||||
+12
-11
@@ -25,6 +25,7 @@ from __future__ import annotations
|
||||
from datetime import datetime
|
||||
from datetime import timezone as tz
|
||||
from itertools import chain
|
||||
from typing import Self
|
||||
|
||||
from django.conf import settings
|
||||
from django.core.exceptions import ValidationError
|
||||
@@ -207,12 +208,12 @@ class Forum(models.Model):
|
||||
return self.get_parent_list()
|
||||
|
||||
def get_parent_list(self):
|
||||
l = []
|
||||
p = self.parent
|
||||
while p is not None:
|
||||
l.append(p)
|
||||
p = p.parent
|
||||
return l
|
||||
parents = []
|
||||
current = self.parent
|
||||
while current is not None:
|
||||
parents.append(current)
|
||||
current = current.parent
|
||||
return parents
|
||||
|
||||
@property
|
||||
def topic_number(self):
|
||||
@@ -228,12 +229,12 @@ class Forum(models.Model):
|
||||
def last_message(self):
|
||||
return self._last_message
|
||||
|
||||
def get_children_list(self):
|
||||
l = [self.id]
|
||||
def get_children_list(self) -> list[Self]:
|
||||
children = [self.id]
|
||||
for c in self.children.all():
|
||||
l.append(c.id)
|
||||
l += c.get_children_list()
|
||||
return l
|
||||
children.append(c.id)
|
||||
children.extend(c.get_children_list())
|
||||
return children
|
||||
|
||||
|
||||
class ForumTopic(models.Model):
|
||||
|
||||
+24
-21
@@ -23,7 +23,26 @@
|
||||
|
||||
from django.urls import path
|
||||
|
||||
from forum.views import *
|
||||
from forum.views import (
|
||||
ForumCreateView,
|
||||
ForumDeleteView,
|
||||
ForumDetailView,
|
||||
ForumEditView,
|
||||
ForumFavoriteTopics,
|
||||
ForumLastUnread,
|
||||
ForumMainView,
|
||||
ForumMarkAllAsRead,
|
||||
ForumMessageCreateView,
|
||||
ForumMessageDeleteView,
|
||||
ForumMessageEditView,
|
||||
ForumMessageUndeleteView,
|
||||
ForumMessageView,
|
||||
ForumSearchView,
|
||||
ForumTopicCreateView,
|
||||
ForumTopicDetailView,
|
||||
ForumTopicEditView,
|
||||
ForumTopicSubscribeView,
|
||||
)
|
||||
|
||||
urlpatterns = [
|
||||
path("", ForumMainView.as_view(), name="main"),
|
||||
@@ -35,21 +54,9 @@ urlpatterns = [
|
||||
path("<int:forum_id>/", ForumDetailView.as_view(), name="view_forum"),
|
||||
path("<int:forum_id>/edit/", ForumEditView.as_view(), name="edit_forum"),
|
||||
path("<int:forum_id>/delete/", ForumDeleteView.as_view(), name="delete_forum"),
|
||||
path(
|
||||
"<int:forum_id>/new_topic/",
|
||||
ForumTopicCreateView.as_view(),
|
||||
name="new_topic",
|
||||
),
|
||||
path(
|
||||
"topic/<int:topic_id>/",
|
||||
ForumTopicDetailView.as_view(),
|
||||
name="view_topic",
|
||||
),
|
||||
path(
|
||||
"topic/<int:topic_id>/edit/",
|
||||
ForumTopicEditView.as_view(),
|
||||
name="edit_topic",
|
||||
),
|
||||
path("<int:forum_id>/new_topic/", ForumTopicCreateView.as_view(), name="new_topic"),
|
||||
path("topic/<int:topic_id>/", ForumTopicDetailView.as_view(), name="view_topic"),
|
||||
path("topic/<int:topic_id>/edit/", ForumTopicEditView.as_view(), name="edit_topic"),
|
||||
path(
|
||||
"topic/<int:topic_id>/new_message/",
|
||||
ForumMessageCreateView.as_view(),
|
||||
@@ -60,11 +67,7 @@ urlpatterns = [
|
||||
ForumTopicSubscribeView.as_view(),
|
||||
name="toggle_subscribe_topic",
|
||||
),
|
||||
path(
|
||||
"message/<int:message_id>/",
|
||||
ForumMessageView.as_view(),
|
||||
name="view_message",
|
||||
),
|
||||
path("message/<int:message_id>/", ForumMessageView.as_view(), name="view_message"),
|
||||
path(
|
||||
"message/<int:message_id>/edit/",
|
||||
ForumMessageEditView.as_view(),
|
||||
|
||||
@@ -71,7 +71,7 @@ class Command(BaseCommand):
|
||||
|
||||
def handle(self, *args, **options):
|
||||
self.logger = logging.getLogger("main")
|
||||
if options["verbosity"] < 0 or 2 < options["verbosity"]:
|
||||
if not 0 <= options["verbosity"] <= 2:
|
||||
warnings.warn(
|
||||
"verbosity level should be between 0 and 2 included", stacklevel=2
|
||||
)
|
||||
|
||||
@@ -40,7 +40,7 @@ class Command(BaseCommand):
|
||||
|
||||
def handle(self, *args, **options):
|
||||
logger = logging.getLogger("main")
|
||||
if options["verbosity"] < 0 or 2 < options["verbosity"]:
|
||||
if not 0 <= options["verbosity"] <= 2:
|
||||
warnings.warn(
|
||||
"verbosity level should be between 0 and 2 included", stacklevel=2
|
||||
)
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
// Version 1.70.19 3d-force-graph - https://github.com/vasturiano/3d-force-graph
|
||||
!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):(t="undefined"!=typeof globalThis?globalThis:t||self).ForceGraph3D=e()}(this,(function(){"use strict";function t(t,e){var n=Object.keys(t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(t);e&&(i=i.filter((function(e){return Object.getOwnPropertyDescriptor(t,e).enumerable}))),n.push.apply(n,i)}return n}function e(e){for(var i=1;i<arguments.length;i++){var r=null!=arguments[i]?arguments[i]:{};i%2?t(Object(r),!0).forEach((function(t){n(e,t,r[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(r)):t(Object(r)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(r,t))}))}return e}function n(t,e,n){return(e=function(t){var e=function(t,e){if("object"!=typeof t||null===t)return t;var n=t[Symbol.toPrimitive];if(void 0!==n){var i=n.call(t,e||"default");if("object"!=typeof i)return i;throw new TypeError("@@toPrimitive must return a primitive value.")}return("string"===e?String:Number)(t)}(t,"string");return"symbol"==typeof e?e:String(e)}(e))in t?Object.defineProperty(t,e,{value:n,enumerable:!0,configurable:!0,writable:!0}):t[e]=n,t}function i(t){return function(t){if(Array.isArray(t))return r(t)}(t)||function(t){if("undefined"!=typeof Symbol&&null!=t[Symbol.iterator]||null!=t["@@iterator"])return Array.from(t)}(t)||function(t,e){if(!t)return;if("string"==typeof t)return r(t,e);var n=Object.prototype.toString.call(t).slice(8,-1);"Object"===n&&t.constructor&&(n=t.constructor.name);if("Map"===n||"Set"===n)return Array.from(t);if("Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n))return r(t,e)}(t)||function(){throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}function r(t,e){(null==e||e>t.length)&&(e=t.length);for(var n=0,i=new Array(e);n<e;n++)i[n]=t[n];return i}!function(t,e){void 0===e&&(e={});var n=e.insertAt;if(t&&"undefined"!=typeof document){var i=document.head||document.getElementsByTagName("head")[0],r=document.createElement("style");r.type="text/css","top"===n&&i.firstChild?i.insertBefore(r,i.firstChild):i.appendChild(r),r.styleSheet?r.styleSheet.cssText=t:r.appendChild(document.createTextNode(t))}}(".graph-info-msg {\n top: 50%;\n width: 100%;\n text-align: center;\n color: lavender;\n opacity: 0.7;\n font-size: 22px;\n position: absolute;\n font-family: Sans-serif;\n}\n\n.scene-container .clickable {\n cursor: pointer;\n}\n\n.scene-container .grabbable {\n cursor: move;\n cursor: grab;\n cursor: -moz-grab;\n cursor: -webkit-grab;\n}\n\n.scene-container .grabbable:active {\n cursor: grabbing;\n cursor: -moz-grabbing;\n cursor: -webkit-grabbing;\n}");const a="148",o={LEFT:0,MIDDLE:1,RIGHT:2,ROTATE:0,DOLLY:1,PAN:2},s=0,l=1,c=2,u=3,h=100,d=301,p=302,f=306,m=1e3,g=1001,v=1002,_=1003,y=1005,x=1006,b=1008,w=1009,M=1014,S=1015,E=1016,T=1020,A=1023,C=1026,L=1027,P=33776,R=33777,D=33778,O=33779,I=3e3,k=3001,N="srgb",z="srgb-linear",U=7680,F="300 es",B=1035;class j{addEventListener(t,e){void 0===this._listeners&&(this._listeners={});const n=this._listeners;void 0===n[t]&&(n[t]=[]),-1===n[t].indexOf(e)&&n[t].push(e)}hasEventListener(t,e){if(void 0===this._listeners)return!1;const n=this._listeners;return void 0!==n[t]&&-1!==n[t].indexOf(e)}removeEventListener(t,e){if(void 0===this._listeners)return;const n=this._listeners[t];if(void 0!==n){const t=n.indexOf(e);-1!==t&&n.splice(t,1)}}dispatchEvent(t){if(void 0===this._listeners)return;const e=this._listeners[t.type];if(void 0!==e){t.target=this;const n=e.slice(0);for(let e=0,i=n.length;e<i;e++)n[e].call(this,t);t.target=null}}}const G=["00","01","02","03","04","05","06","07","08","09","0a","0b","0c","0d","0e","0f","10","11","12","13","14","15","16","17","18","19","1a","1b","1c","1d","1e","1f","20","21","22","23","24","25","26","27","28","29","2a","2b","2c","2d","2e","2f","30","31","32","33","34","35","36","37","38","39","3a","3b","3c","3d","3e","3f","40","41","42","43","44","45","46","47","48","49","4a","4b","4c","4d","4e","4f","50","51","52","53","54","55","56","57","58","59","5a","5b","5c","5d","5e","5f","60","61","62","63","64","65","66","67","68","69","6a","6b","6c","6d","6e","6f","70","71","72","73","74","75","76","77","78","79","7a","7b","7c","7d","7e","7f","80","81","82","83","84","85","86","87","88","89","8a","8b","8c","8d","8e","8f","90","91","92","93","94","95","96","97","98","99","9a","9b","9c","9d","9e","9f","a0","a1","a2","a3","a4","a5","a6","a7","a8","a9","aa","ab","ac","ad","ae","af","b0","b1","b2","b3","b4","b5","b6","b7","b8","b9","ba","bb","bc","bd","be","bf","c0","c1","c2","c3","c4","c5","c6","c7","c8","c9","ca","cb","cc","cd","ce","cf","d0","d1","d2","d3","d4","d5","d6","d7","d8","d9","da","db","dc","dd","de","df","e0","e1","e2","e3","e4","e5","e6","e7","e8","e9","ea","eb","ec","ed","ee"Line truncated
|
||||
// <http://www.w3.org/TR/2008/REC-WCAG20-20081211/#contrast-ratiodef (WCAG Version 2)
|
||||
// Analyze the 2 colors and returns the color contrast defined by (WCAG Version 2)
|
||||
c.readability=function(t,n){var i=c(t),r=c(n);return(e.max(i.getLuminance(),r.getLuminance())+.05)/(e.min(i.getLuminance(),r.getLuminance())+.05)},c.isReadable=function(t,e,n){var i,r,a=c.readability(t,e);switch(r=!1,(i=function(t){var e,n;e=((t=t||{level:"AA",size:"small"}).level||"AA").toUpperCase(),n=(t.size||"small").toLowerCase(),"AA"!==e&&"AAA"!==e&&(e="AA");"small"!==n&&"large"!==n&&(n="small");return{level:e,size:n}}(n)).level+i.size){case"AAsmall":case"AAAlarge":r=a>=4.5;break;case"AAlarge":r=a>=3;break;case"AAAsmall":r=a>=7}return r},c.mostReadable=function(t,e,n){var i,r,a,o,s=null,l=0;r=(n=n||{}).includeFallbackColors,a=n.level,o=n.size;for(var u=0;u<e.length;u++)(i=c.readability(t,e[u]))>l&&(l=i,s=c(e[u]));return c.isReadable(t,s,{level:a,size:o})||!r?s:(n.includeFallbackColors=!1,c.mostReadable(t,["#fff","#000"],n))};var A=c.names={aliceblue:"f0f8ff",antiquewhite:"faebd7",aqua:"0ff",aquamarine:"7fffd4",azure:"f0ffff",beige:"f5f5dc",bisque:"ffe4c4",black:"000",blanchedalmond:"ffebcd",blue:"00f",blueviolet:"8a2be2",brown:"a52a2a",burlywood:"deb887",burntsienna:"ea7e5d",cadetblue:"5f9ea0",chartreuse:"7fff00",chocolate:"d2691e",coral:"ff7f50",cornflowerblue:"6495ed",cornsilk:"fff8dc",crimson:"dc143c",cyan:"0ff",darkblue:"00008b",darkcyan:"008b8b",darkgoldenrod:"b8860b",darkgray:"a9a9a9",darkgreen:"006400",darkgrey:"a9a9a9",darkkhaki:"bdb76b",darkmagenta:"8b008b",darkolivegreen:"556b2f",darkorange:"ff8c00",darkorchid:"9932cc",darkred:"8b0000",darksalmon:"e9967a",darkseagreen:"8fbc8f",darkslateblue:"483d8b",darkslategray:"2f4f4f",darkslategrey:"2f4f4f",darkturquoise:"00ced1",darkviolet:"9400d3",deeppink:"ff1493",deepskyblue:"00bfff",dimgray:"696969",dimgrey:"696969",dodgerblue:"1e90ff",firebrick:"b22222",floralwhite:"fffaf0",forestgreen:"228b22",fuchsia:"f0f",gainsboro:"dcdcdc",ghostwhite:"f8f8ff",gold:"ffd700",goldenrod:"daa520",gray:"808080",green:"008000",greenyellow:"adff2f",grey:"808080",honeydew:"f0fff0",hotpink:"ff69b4",indianred:"cd5c5c",indigo:"4b0082",ivory:"fffff0",khaki:"f0e68c",lavender:"e6e6fa",lavenderblush:"fff0f5",lawngreen:"7cfc00",lemonchiffon:"fffacd",lightblue:"add8e6",lightcoral:"f08080",lightcyan:"e0ffff",lightgoldenrodyellow:"fafad2",lightgray:"d3d3d3",lightgreen:"90ee90",lightgrey:"d3d3d3",lightpink:"ffb6c1",lightsalmon:"ffa07a",lightseagreen:"20b2aa",lightskyblue:"87cefa",lightslategray:"789",lightslategrey:"789",lightsteelblue:"b0c4de",lightyellow:"ffffe0",lime:"0f0",limegreen:"32cd32",linen:"faf0e6",magenta:"f0f",maroon:"800000",mediumaquamarine:"66cdaa",mediumblue:"0000cd",mediumorchid:"ba55d3",mediumpurple:"9370db",mediumseagreen:"3cb371",mediumslateblue:"7b68ee",mediumspringgreen:"00fa9a",mediumturquoise:"48d1cc",mediumvioletred:"c71585",midnightblue:"191970",mintcream:"f5fffa",mistyrose:"ffe4e1",moccasin:"ffe4b5",navajowhite:"ffdead",navy:"000080",oldlace:"fdf5e6",olive:"808000",olivedrab:"6b8e23",orange:"ffa500",orangered:"ff4500",orchid:"da70d6",palegoldenrod:"eee8aa",palegreen:"98fb98",paleturquoise:"afeeee",palevioletred:"db7093",papayawhip:"ffefd5",peachpuff:"ffdab9",peru:"cd853f",pink:"ffc0cb",plum:"dda0dd",powderblue:"b0e0e6",purple:"800080",rebeccapurple:"663399",red:"f00",rosybrown:"bc8f8f",royalblue:"4169e1",saddlebrown:"8b4513",salmon:"fa8072",sandybrown:"f4a460",seagreen:"2e8b57",seashell:"fff5ee",sienna:"a0522d",silver:"c0c0c0",skyblue:"87ceeb",slateblue:"6a5acd",slategray:"708090",slategrey:"708090",snow:"fffafa",springgreen:"00ff7f",steelblue:"4682b4",tan:"d2b48c",teal:"008080",thistle:"d8bfd8",tomato:"ff6347",turquoise:"40e0d0",violet:"ee82ee",wheat:"f5deb3",white:"fff",whitesmoke:"f5f5f5",yellow:"ff0",yellowgreen:"9acd32"},C=c.hexNames=function(t){var e={};for(var n in t)t.hasOwnProperty(n)&&(e[t[n]]=n);return e}(A);function L(t){return t=parseFloat(t),(isNaN(t)||t<0||t>1)&&(t=1),t}function P(t,n){(function(t){return"string"==typeof t&&-1!=t.indexOf(".")&&1===parseFloat(t)})(t)&&(t="100%");var i=function(t){return"string"==typeof t&&-1!=t.indexOf("%")}(t);return t=o(n,s(0,parseFloat(t))),i&&(t=parseInt(t*n,10)/100),e.abs(t-n)<1e-6?1:t%n/parseFloat(n)}function R(t){return o(1,s(0,t))}function D(t){return parseInt(t,16)}function O(t){return 1==t.length?"0"+t:""+t}function I(t){return t<=1&&(t=100*t+"%"),t}function k(t){return e.round(255*parseFloat(t)).toString(16)}function N(t){return D(t)/255}var z,U,F,B=(U="[\\s|\\(]+("+(z="(?:[-\\+]?\\d*\\.\\d+%?)|(?:[-\\+]?\\d+%?)")+")[,|\\s]+("+z+")[,|\\s]+("+z+")\\s*\\)?",F="[\\s|\\(]+("+z+")[,|\\s]+("+z+")[,|\\s]+("+z+")[,|\\s]+("+z+")\\s*\\)?",{CSS_UNIT:new RegExp(z),rgb:new RegExp("rgb"+U),rgba:new RegExp("rgba"+F),hsl:new RegExp("hsl"+U),hsla:new RegExp("hsla"+F),hsv:new RegExp("hsv"+U),hsva:new RegExp("hsva"+F),hex3:/^#?([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})$/,hex6:/^#?([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/,hex4:/^#?([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})$/,hex8:/^#?([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/});function j(t){return!!B.CSSLine truncated
|
||||
-2
@@ -1,2 +0,0 @@
|
||||
// https://github.com/vasturiano/d3-force-3d v3.0.3 Copyright 2022 Vasco Asturiano
|
||||
!function(n,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports,require("d3-binarytree"),require("d3-quadtree"),require("d3-octree"),require("d3-dispatch"),require("d3-timer")):"function"==typeof define&&define.amd?define(["exports","d3-binarytree","d3-quadtree","d3-octree","d3-dispatch","d3-timer"],t):t((n="undefined"!=typeof globalThis?globalThis:n||self).d3=n.d3||{},n.d3,n.d3,n.d3,n.d3,n.d3)}(this,(function(n,t,e,r,i,u){"use strict";function o(n){return function(){return n}}function f(n){return 1e-6*(n()-.5)}function a(n){return n.x+n.vx}function c(n){return n.y+n.vy}function l(n){return n.z+n.vz}function h(n){return n.index}function v(n,t){var e=n.get(t);if(!e)throw new Error("node not found: "+t);return e}const d=4294967296;function y(n){return n.x}function s(n){return n.y}function g(n){return n.z}var x=Math.PI*(3-Math.sqrt(5)),z=20*Math.PI/(9+Math.sqrt(221));n.forceCenter=function(n,t,e){var r,i=1;function u(){var u,o,f=r.length,a=0,c=0,l=0;for(u=0;u<f;++u)a+=(o=r[u]).x||0,c+=o.y||0,l+=o.z||0;for(a=(a/f-n)*i,c=(c/f-t)*i,l=(l/f-e)*i,u=0;u<f;++u)o=r[u],a&&(o.x-=a),c&&(o.y-=c),l&&(o.z-=l)}return null==n&&(n=0),null==t&&(t=0),null==e&&(e=0),u.initialize=function(n){r=n},u.x=function(t){return arguments.length?(n=+t,u):n},u.y=function(n){return arguments.length?(t=+n,u):t},u.z=function(n){return arguments.length?(e=+n,u):e},u.strength=function(n){return arguments.length?(i=+n,u):i},u},n.forceCollide=function(n){var i,u,h,v,d=1,y=1;function s(){for(var n,o,s,x,z,p,M,w,q=i.length,N=0;N<y;++N)for(o=(1===u?t.binarytree(i,a):2===u?e.quadtree(i,a,c):3===u?r.octree(i,a,c,l):null).visitAfter(g),n=0;n<q;++n)s=i[n],M=h[s.index],w=M*M,x=s.x+s.vx,u>1&&(z=s.y+s.vy),u>2&&(p=s.z+s.vz),o.visit(m);function m(n,t,e,r,i,o,a){var c=[t,e,r,i,o,a],l=c[0],h=c[1],y=c[2],g=c[u],q=c[u+1],N=c[u+2],m=n.data,A=n.r,b=M+A;if(!m)return l>x+b||g<x-b||u>1&&(h>z+b||q<z-b)||u>2&&(y>p+b||N<p-b);if(m.index>s.index){var k=x-m.x-m.vx,E=u>1?z-m.y-m.vy:0,j=u>2?p-m.z-m.vz:0,D=k*k+E*E+j*j;D<b*b&&(0===k&&(D+=(k=f(v))*k),u>1&&0===E&&(D+=(E=f(v))*E),u>2&&0===j&&(D+=(j=f(v))*j),D=(b-(D=Math.sqrt(D)))/D*d,s.vx+=(k*=D)*(b=(A*=A)/(w+A)),u>1&&(s.vy+=(E*=D)*b),u>2&&(s.vz+=(j*=D)*b),m.vx-=k*(b=1-b),u>1&&(m.vy-=E*b),u>2&&(m.vz-=j*b))}}}function g(n){if(n.data)return n.r=h[n.data.index];for(var t=n.r=0;t<Math.pow(2,u);++t)n[t]&&n[t].r>n.r&&(n.r=n[t].r)}function x(){if(i){var t,e,r=i.length;for(h=new Array(r),t=0;t<r;++t)e=i[t],h[e.index]=+n(e,t,i)}}return"function"!=typeof n&&(n=o(null==n?1:+n)),s.initialize=function(n,...t){i=n,v=t.find((n=>"function"==typeof n))||Math.random,u=t.find((n=>[1,2,3].includes(n)))||2,x()},s.iterations=function(n){return arguments.length?(y=+n,s):y},s.strength=function(n){return arguments.length?(d=+n,s):d},s.radius=function(t){return arguments.length?(n="function"==typeof t?t:o(+t),x(),s):n},s},n.forceLink=function(n){var t,e,r,i,u,a,c,l=h,d=function(n){return 1/Math.min(u[n.source.index],u[n.target.index])},y=o(30),s=1;function g(r){for(var u=0,o=n.length;u<s;++u)for(var l,h,v,d,y,g=0,x=0,z=0,p=0;g<o;++g)h=(l=n[g]).source,x=(v=l.target).x+v.vx-h.x-h.vx||f(c),i>1&&(z=v.y+v.vy-h.y-h.vy||f(c)),i>2&&(p=v.z+v.vz-h.z-h.vz||f(c)),x*=d=((d=Math.sqrt(x*x+z*z+p*p))-e[g])/d*r*t[g],z*=d,p*=d,v.vx-=x*(y=a[g]),i>1&&(v.vy-=z*y),i>2&&(v.vz-=p*y),h.vx+=x*(y=1-y),i>1&&(h.vy+=z*y),i>2&&(h.vz+=p*y)}function x(){if(r){var i,o,f=r.length,c=n.length,h=new Map(r.map(((n,t)=>[l(n,t,r),n])));for(i=0,u=new Array(f);i<c;++i)(o=n[i]).index=i,"object"!=typeof o.source&&(o.source=v(h,o.source)),"object"!=typeof o.target&&(o.target=v(h,o.target)),u[o.source.index]=(u[o.source.index]||0)+1,u[o.target.index]=(u[o.target.index]||0)+1;for(i=0,a=new Array(c);i<c;++i)o=n[i],a[i]=u[o.source.index]/(u[o.source.index]+u[o.target.index]);t=new Array(c),z(),e=new Array(c),p()}}function z(){if(r)for(var e=0,i=n.length;e<i;++e)t[e]=+d(n[e],e,n)}function p(){if(r)for(var t=0,i=n.length;t<i;++t)e[t]=+y(n[t],t,n)}return null==n&&(n=[]),g.initialize=function(n,...t){r=n,c=t.find((n=>"function"==typeof n))||Math.random,i=t.find((n=>[1,2,3].includes(n)))||2,x()},g.links=function(t){return arguments.length?(n=t,x(),g):n},g.id=function(n){return arguments.length?(l=n,g):l},g.iterations=function(n){return arguments.length?(s=+n,g):s},g.strength=function(n){return arguments.length?(d="function"==typeof n?n:o(+n),z(),g):d},g.distance=function(n){return arguments.length?(y="function"==typeof n?n:o(+n),p(),g):y},g},n.forceManyBody=function(){var n,i,u,a,c,l,h=o(-30),v=1,d=1/0,x=.81;function z(o){var f,a=n.length,l=(1===i?t.binarytree(n,y):2===i?e.quadtree(n,y,s):3===i?r.octree(n,y,s,g):null).visitAfter(M);for(c=o,f=0;f<a;++f)u=n[f],l.visit(w)}function p(){if(n){var t,e,r=n.length;for(l=new Array(r),t=0;t<r;++t)e=n[t],l[e.index]=+h(e,t,n)}}function M(n){var t,e,r,u,o,f,a=0,c=0,h=n.length;if(h){for(r=u=o=f=0;f<h;++f)(t=n[f])&&(e=Math.abs(t.value))&&(a+=t.value,c+=e,r+=e*(t.x||0),u+=e*(t.y||0),o+=e*(t.z||0));a*=Math.sqrt(4/h),n.x=r/c,i>1&&(n.y=u/c),i>2&&(n.z=o/c)}else{(t=n).Line truncated
|
||||
@@ -1,2 +0,0 @@
|
||||
// Version 1.6.5 three-spritetext - https://github.com/vasturiano/three-spritetext
|
||||
!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e(require("three")):"function"==typeof define&&define.amd?define(["three"],e):(t="undefined"!=typeof globalThis?globalThis:t||self).SpriteText=e(t.THREE)}(this,(function(t){"use strict";function e(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function r(t,e){for(var r=0;r<e.length;r++){var i=e[r];i.enumerable=i.enumerable||!1,i.configurable=!0,"value"in i&&(i.writable=!0),Object.defineProperty(t,i.key,i)}}function i(t){return i=Object.setPrototypeOf?Object.getPrototypeOf:function(t){return t.__proto__||Object.getPrototypeOf(t)},i(t)}function n(t,e){return n=Object.setPrototypeOf||function(t,e){return t.__proto__=e,t},n(t,e)}function o(t,e){if(e&&("object"==typeof e||"function"==typeof e))return e;if(void 0!==e)throw new TypeError("Derived constructors may only return object or undefined");return function(t){if(void 0===t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return t}(t)}function a(t){var e=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(t){return!1}}();return function(){var r,n=i(t);if(e){var a=i(this).constructor;r=Reflect.construct(n,arguments,a)}else r=n.apply(this,arguments);return o(this,r)}}function h(t,e){return function(t){if(Array.isArray(t))return t}(t)||function(t,e){var r=null==t?null:"undefined"!=typeof Symbol&&t[Symbol.iterator]||t["@@iterator"];if(null==r)return;var i,n,o=[],a=!0,h=!1;try{for(r=r.call(t);!(a=(i=r.next()).done)&&(o.push(i.value),!e||o.length!==e);a=!0);}catch(t){h=!0,n=t}finally{try{a||null==r.return||r.return()}finally{if(h)throw n}}return o}(t,e)||u(t,e)||function(){throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}function s(t){return function(t){if(Array.isArray(t))return c(t)}(t)||function(t){if("undefined"!=typeof Symbol&&null!=t[Symbol.iterator]||null!=t["@@iterator"])return Array.from(t)}(t)||u(t)||function(){throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}function u(t,e){if(t){if("string"==typeof t)return c(t,e);var r=Object.prototype.toString.call(t).slice(8,-1);return"Object"===r&&t.constructor&&(r=t.constructor.name),"Map"===r||"Set"===r?Array.from(t):"Arguments"===r||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(r)?c(t,e):void 0}}function c(t,e){(null==e||e>t.length)&&(e=t.length);for(var r=0,i=new Array(e);r<e;r++)i[r]=t[r];return i}var f="undefined"!=typeof window&&window.THREE?window.THREE:{LinearFilter:t.LinearFilter,Sprite:t.Sprite,SpriteMaterial:t.SpriteMaterial,Texture:t.Texture},l=function(t){!function(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function");t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,writable:!0,configurable:!0}}),Object.defineProperty(t,"prototype",{writable:!1}),e&&n(t,e)}(l,t);var i,o,u,c=a(l);function l(){var t,r=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"",i=arguments.length>1&&void 0!==arguments[1]?arguments[1]:10,n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"rgba(255, 255, 255, 1)";return e(this,l),(t=c.call(this,new f.SpriteMaterial))._text="".concat(r),t._textHeight=i,t._color=n,t._backgroundColor=!1,t._padding=0,t._borderWidth=0,t._borderRadius=0,t._borderColor="white",t._strokeWidth=0,t._strokeColor="white",t._fontFace="Arial",t._fontSize=90,t._fontWeight="normal",t._canvas=document.createElement("canvas"),t._genCanvas(),t}return i=l,(o=[{key:"text",get:function(){return this._text},set:function(t){this._text=t,this._genCanvas()}},{key:"textHeight",get:function(){return this._textHeight},set:function(t){this._textHeight=t,this._genCanvas()}},{key:"color",get:function(){return this._color},set:function(t){this._color=t,this._genCanvas()}},{key:"backgroundColor",get:function(){return this._backgroundColor},set:function(t){this._backgroundColor=t,this._genCanvas()}},{key:"padding",get:function(){return this._padding},set:function(t){this._padding=t,this._genCanvas()}},{key:"borderWidth",get:function(){return this._borderWidth},set:function(t){this._borderWidth=t,this._genCanvas()}},{key:"borderRadius",get:function(){return this._borderRadius},set:function(t){this._borderRadius=t,this._genCanvas()}},{key:"borderColor",get:function(){return this._borderColor},set:function(t){this._borderColor=t,this._genCanvas()}},{key:"fontFace",get:function(){return this._fontFace},set:function(t){this._fontFace=t,this._genCanvas()}},{key:"fontSize",get:function(){return this._fontSize},set:function(t){this._fontSize=t,this._genCanvas()}},{key:"fontWeight",get:function(){Line truncated
|
||||
Vendored
-6
@@ -1,6 +0,0 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2010-2022 Three.js Authors
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports):"function"==typeof define&&define.amd?define(["exports"],e):e((t="undefined"!=typeof globalThis?globalThis:t||self).THREE={})}(this,(function(t){"use strict";const e="148",i=100,n=300,r=301,s=302,a=303,o=304,l=306,c=1e3,h=1001,u=1002,d=1003,p=1004,m=1005,f=1006,g=1007,v=1008,x=1009,_=1012,y=1014,M=1015,b=1016,S=1020,w=1023,T=1026,A=1027,E=33776,C=33777,L=33778,R=33779,P=35840,I=35841,D=35842,N=35843,O=37492,z=37496,U=37808,B=37809,F=37810,k=37811,G=37812,V=37813,H=37814,W=37815,j=37816,q=37817,X=37818,Y=37819,Z=37820,J=37821,K=36492,$=2300,Q=2301,tt=2302,et=2400,it=2401,nt=2402,rt=2500,st=2501,at=3e3,ot=3001,lt="srgb",ct="srgb-linear",ht=7680,ut=35044,dt="300 es",pt=1035;class mt{addEventListener(t,e){void 0===this._listeners&&(this._listeners={});const i=this._listeners;void 0===i[t]&&(i[t]=[]),-1===i[t].indexOf(e)&&i[t].push(e)}hasEventListener(t,e){if(void 0===this._listeners)return!1;const i=this._listeners;return void 0!==i[t]&&-1!==i[t].indexOf(e)}removeEventListener(t,e){if(void 0===this._listeners)return;const i=this._listeners[t];if(void 0!==i){const t=i.indexOf(e);-1!==t&&i.splice(t,1)}}dispatchEvent(t){if(void 0===this._listeners)return;const e=this._listeners[t.type];if(void 0!==e){t.target=this;const i=e.slice(0);for(let e=0,n=i.length;e<n;e++)i[e].call(this,t);t.target=null}}}const ft=["00","01","02","03","04","05","06","07","08","09","0a","0b","0c","0d","0e","0f","10","11","12","13","14","15","16","17","18","19","1a","1b","1c","1d","1e","1f","20","21","22","23","24","25","26","27","28","29","2a","2b","2c","2d","2e","2f","30","31","32","33","34","35","36","37","38","39","3a","3b","3c","3d","3e","3f","40","41","42","43","44","45","46","47","48","49","4a","4b","4c","4d","4e","4f","50","51","52","53","54","55","56","57","58","59","5a","5b","5c","5d","5e","5f","60","61","62","63","64","65","66","67","68","69","6a","6b","6c","6d","6e","6f","70","71","72","73","74","75","76","77","78","79","7a","7b","7c","7d","7e","7f","80","81","82","83","84","85","86","87","88","89","8a","8b","8c","8d","8e","8f","90","91","92","93","94","95","96","97","98","99","9a","9b","9c","9d","9e","9f","a0","a1","a2","a3","a4","a5","a6","a7","a8","a9","aa","ab","ac","ad","ae","af","b0","b1","b2","b3","b4","b5","b6","b7","b8","b9","ba","bb","bc","bd","be","bf","c0","c1","c2","c3","c4","c5","c6","c7","c8","c9","ca","cb","cc","cd","ce","cf","d0","d1","d2","d3","d4","d5","d6","d7","d8","d9","da","db","dc","dd","de","df","e0","e1","e2","e3","e4","e5","e6","e7","e8","e9","ea","eb","ec","ed","ee","ef","f0","f1","f2","f3","f4","f5","f6","f7","f8","f9","fa","fb","fc","fd","fe","ff"];let gt=1234567;const vt=Math.PI/180,xt=180/Math.PI;function _t(){const t=4294967295*Math.random()|0,e=4294967295*Math.random()|0,i=4294967295*Math.random()|0,n=4294967295*Math.random()|0;return(ft[255&t]+ft[t>>8&255]+ft[t>>16&255]+ft[t>>24&255]+"-"+ft[255&e]+ft[e>>8&255]+"-"+ft[e>>16&15|64]+ft[e>>24&255]+"-"+ft[63&i|128]+ft[i>>8&255]+"-"+ft[i>>16&255]+ft[i>>24&255]+ft[255&n]+ft[n>>8&255]+ft[n>>16&255]+ft[n>>24&255]).toLowerCase()}function yt(t,e,i){return Math.max(e,Math.min(i,t))}function Mt(t,e){return(t%e+e)%e}function bt(t,e,i){return(1-i)*t+i*e}function St(t){return 0==(t&t-1)&&0!==t}function wt(t){return Math.pow(2,Math.ceil(Math.log(t)/Math.LN2))}function Tt(t){return Math.pow(2,Math.floor(Math.log(t)/Math.LN2))}function At(t,e){switch(e.constructor){case Float32Array:return t;case Uint16Array:return t/65535;case Uint8Array:return t/255;case Int16Array:return Math.max(t/32767,-1);case Int8Array:return Math.max(t/127,-1);default:throw new Error("Invalid component type.")}}function Et(t,e){switch(e.constructor){case Float32Array:return t;case Uint16Array:return Math.round(65535*t);case Uint8Array:return Math.round(255*t);case Int16Array:return Math.round(32767*t);case Int8Array:return Math.round(127*t);default:throw new Error("Invalid component type.")}}var Ct=Object.freeze({__proto__:null,DEG2RAD:vt,RAD2DEG:xt,generateUUID:_t,clamp:yt,euclideanModulo:Mt,mapLinear:function(t,e,i,n,r){return n+(t-e)*(r-n)/(i-e)},inverseLerp:function(t,e,i){return t!==e?(i-t)/(e-t):0},lerp:bt,damp:function(t,e,i,n){return bt(t,e,1-Math.exp(-i*n))},pingpong:function(t,e=1){return e-Math.abs(Mt(t,2*e)-e)},smoothstep:function(t,e,i){return t<=e?0:t>=i?1:(t=(t-e)/(i-e))*t*(3-2*t)},smootherstep:function(t,e,i){return t<=e?0:t>=i?1:(t=(t-e)/(i-e))*t*t*(t*(6*t-15)+10)},randInt:function(t,e){return t+Math.floor(Math.random()*(e-t+1))},randFloat:function(t,e){return t+Math.random()*(e-t)},randFloatSpread:function(t){return t*(.5-Math.random())},seededRandom:function(t){void 0!==t&&(gt=t);let e=gt+=1831565813;return e=Math.imul(e^e>>>15,1|e),e^=e+Math.imul(e^e>>>7,61|e),((e^e>>>14)>>>0)/4294967296},degToRad:function(t){return t*vt},radToDeg:function(t){return t*xt},isPowerOfTwo:St,ceilPowerOfTwo:wt,floorPowerOfTwo:Tt,setQuaternionFromProperEuler:function(t,e,i,n,r){const s=Math.cos,a=Math.sin,o=s(i/2)Line truncated
|
||||
@@ -0,0 +1,138 @@
|
||||
import { default as ForceGraph3D } from "3d-force-graph";
|
||||
import { forceX, forceY, forceZ } from "d3-force-3d";
|
||||
// biome-ignore lint/style/noNamespaceImport: This is how it should be imported
|
||||
import * as Three from "three";
|
||||
import SpriteText from "three-spritetext";
|
||||
|
||||
/**
|
||||
* @typedef GalaxyConfig
|
||||
* @property {number} nodeId id of the current user node
|
||||
* @property {string} dataUrl url to fetch the galaxy data from
|
||||
**/
|
||||
|
||||
/**
|
||||
* Load the galaxy of an user
|
||||
* @param {GalaxyConfig} config
|
||||
**/
|
||||
window.loadGalaxy = (config) => {
|
||||
window.getNodeFromId = (id) => {
|
||||
return Graph.graphData().nodes.find((n) => n.id === id);
|
||||
};
|
||||
|
||||
window.getLinksFromNodeId = (id) => {
|
||||
return Graph.graphData().links.filter(
|
||||
(l) => l.source.id === id || l.target.id === id,
|
||||
);
|
||||
};
|
||||
|
||||
window.focusNode = (node) => {
|
||||
highlightNodes.clear();
|
||||
highlightLinks.clear();
|
||||
|
||||
hoverNode = node || null;
|
||||
if (node) {
|
||||
// collect neighbors and links for highlighting
|
||||
for (const link of window.getLinksFromNodeId(node.id)) {
|
||||
highlightLinks.add(link);
|
||||
highlightNodes.add(link.source);
|
||||
highlightNodes.add(link.target);
|
||||
}
|
||||
}
|
||||
|
||||
// refresh node and link display
|
||||
Graph.nodeThreeObject(Graph.nodeThreeObject())
|
||||
.linkWidth(Graph.linkWidth())
|
||||
.linkDirectionalParticles(Graph.linkDirectionalParticles());
|
||||
|
||||
// Aim at node from outside it
|
||||
const distance = 42;
|
||||
const distRatio = 1 + distance / Math.hypot(node.x, node.y, node.z);
|
||||
|
||||
const newPos =
|
||||
node.x || node.y || node.z
|
||||
? { x: node.x * distRatio, y: node.y * distRatio, z: node.z * distRatio }
|
||||
: { x: 0, y: 0, z: distance }; // special case if node is in (0,0,0)
|
||||
|
||||
Graph.cameraPosition(
|
||||
newPos, // new position
|
||||
node, // lookAt ({ x, y, z })
|
||||
3000, // ms transition duration
|
||||
);
|
||||
};
|
||||
|
||||
const highlightNodes = new Set();
|
||||
const highlightLinks = new Set();
|
||||
let hoverNode = null;
|
||||
|
||||
const grpahDiv = document.getElementById("3d-graph");
|
||||
const Graph = ForceGraph3D();
|
||||
Graph(grpahDiv);
|
||||
Graph.jsonUrl(config.dataUrl)
|
||||
.width(
|
||||
grpahDiv.parentElement.clientWidth > 1200
|
||||
? 1200
|
||||
: grpahDiv.parentElement.clientWidth,
|
||||
) // Not perfect at all. JS-fu master from the future, please fix this :-)
|
||||
.height(1000)
|
||||
.enableNodeDrag(false) // allow easier navigation
|
||||
.onNodeClick((node) => {
|
||||
const camera = Graph.cameraPosition();
|
||||
const distance = Math.sqrt(
|
||||
(node.x - camera.x) ** 2 + (node.y - camera.y) ** 2 + (node.z - camera.z) ** 2,
|
||||
);
|
||||
if (distance < 120 || highlightNodes.has(node)) {
|
||||
window.focusNode(node);
|
||||
}
|
||||
})
|
||||
.linkWidth((link) => (highlightLinks.has(link) ? 0.4 : 0.0))
|
||||
.linkColor((link) =>
|
||||
highlightLinks.has(link) ? "rgba(255,160,0,1)" : "rgba(128,255,255,0.6)",
|
||||
)
|
||||
.linkVisibility((link) => highlightLinks.has(link))
|
||||
.nodeVisibility((node) => highlightNodes.has(node) || node.mass > 4)
|
||||
// .linkDirectionalParticles(link => highlightLinks.has(link) ? 3 : 1) // kinda buggy for now, and slows this a bit, but would be great to help visualize lanes
|
||||
.linkDirectionalParticleWidth(0.2)
|
||||
.linkDirectionalParticleSpeed(-0.006)
|
||||
.nodeThreeObject((node) => {
|
||||
const sprite = new SpriteText(node.name);
|
||||
sprite.material.depthWrite = false; // make sprite background transparent
|
||||
sprite.color = highlightNodes.has(node)
|
||||
? node === hoverNode
|
||||
? "rgba(200,0,0,1)"
|
||||
: "rgba(255,160,0,0.8)"
|
||||
: "rgba(0,255,255,0.2)";
|
||||
sprite.textHeight = 2;
|
||||
sprite.center = new Three.Vector2(1.2, 0.5);
|
||||
return sprite;
|
||||
})
|
||||
.onEngineStop(() => {
|
||||
window.focusNode(window.getNodeFromId(config.nodeId));
|
||||
Graph.onEngineStop(() => {
|
||||
/* nope */
|
||||
}); // don't call ourselves in a loop while moving the focus
|
||||
});
|
||||
|
||||
// Set distance between stars
|
||||
Graph.d3Force("link").distance((link) => link.value);
|
||||
|
||||
// Set high masses nearer the center of the galaxy
|
||||
// TODO: quick and dirty strength computation, this will need tuning.
|
||||
Graph.d3Force(
|
||||
"positionX",
|
||||
forceX().strength((node) => {
|
||||
return 1 - 1 / node.mass;
|
||||
}),
|
||||
);
|
||||
Graph.d3Force(
|
||||
"positionY",
|
||||
forceY().strength((node) => {
|
||||
return 1 - 1 / node.mass;
|
||||
}),
|
||||
);
|
||||
Graph.d3Force(
|
||||
"positionZ",
|
||||
forceZ().strength((node) => {
|
||||
return 1 - 1 / node.mass;
|
||||
}),
|
||||
);
|
||||
};
|
||||
@@ -4,13 +4,18 @@
|
||||
{% trans user_name=object.get_display_name() %}{{ user_name }}'s Galaxy{% endtrans %}
|
||||
{% endblock %}
|
||||
|
||||
{% block additional_js %}
|
||||
<script src="{{ static('webpack/galaxy/galaxy-index.js') }}" defer></script>
|
||||
{% endblock %}
|
||||
|
||||
|
||||
{% block content %}
|
||||
{% if object.current_star %}
|
||||
<div style="display: flex; flex-wrap: wrap;">
|
||||
<div id="3d-graph"></div>
|
||||
|
||||
<div style="margin: 1em;">
|
||||
<p><a onclick="focus_node(get_node_from_id({{ object.id }}))">Reset on {{ object.get_display_name() }}</a></p>
|
||||
<p><a onclick="window.focusNode(window.getNodeFromId({{ object.id }}))">Reset on {{ object.get_display_name() }}</a></p>
|
||||
<p>Self score: {{ object.current_star.mass }}</p>
|
||||
<table style="width: initial;">
|
||||
<tr>
|
||||
@@ -24,7 +29,7 @@
|
||||
</tr>
|
||||
{% for lane in lanes %}
|
||||
<tr>
|
||||
<td><a onclick="focus_node(get_node_from_id({{ lane.other_star_id }}))">Locate</a></td>
|
||||
<td><a onclick="window.focusNode(window.getNodeFromId({{ lane.other_star_id }}))">Locate</a></td>
|
||||
<td><a href="{{ url("galaxy:user", user_id=lane.other_star_id) }}">{{ lane.other_star_name }}</a></td>
|
||||
<td>{{ lane.other_star_mass }}</td>
|
||||
<td>{{ lane.distance }}</td>
|
||||
@@ -45,106 +50,13 @@
|
||||
|
||||
{% block script %}
|
||||
{{ super() }}
|
||||
|
||||
<script src="{{ static('galaxy/js/three.min.js') }}" defer></script>
|
||||
<script src="{{ static('galaxy/js/three-spritetext.min.js') }}" defer></script>
|
||||
<script src="{{ static('galaxy/js/3d-force-graph.min.js') }}" defer></script>
|
||||
<script src="{{ static('galaxy/js/d3-force-3d.min.js') }}" defer></script>
|
||||
|
||||
<script>
|
||||
var Graph;
|
||||
|
||||
function get_node_from_id(id) {
|
||||
return Graph.graphData().nodes.find(n => n.id === id);
|
||||
}
|
||||
|
||||
function get_links_from_node_id(id) {
|
||||
return Graph.graphData().links.filter(l => l.source.id === id || l.target.id === id);
|
||||
}
|
||||
|
||||
function focus_node(node) {
|
||||
highlightNodes.clear();
|
||||
highlightLinks.clear();
|
||||
|
||||
hoverNode = node || null;
|
||||
if (node) { // collect neighbors and links for highlighting
|
||||
get_links_from_node_id(node.id).forEach(link => {
|
||||
highlightLinks.add(link);
|
||||
highlightNodes.add(link.source);
|
||||
highlightNodes.add(link.target);
|
||||
});
|
||||
}
|
||||
|
||||
// refresh node and link display
|
||||
Graph
|
||||
.nodeThreeObject(Graph.nodeThreeObject())
|
||||
.linkWidth(Graph.linkWidth())
|
||||
.linkDirectionalParticles(Graph.linkDirectionalParticles());
|
||||
|
||||
// Aim at node from outside it
|
||||
const distance = 42;
|
||||
const distRatio = 1 + distance/Math.hypot(node.x, node.y, node.z);
|
||||
|
||||
const newPos = node.x || node.y || node.z
|
||||
? { x: node.x * distRatio, y: node.y * distRatio, z: node.z * distRatio }
|
||||
: { x: 0, y: 0, z: distance }; // special case if node is in (0,0,0)
|
||||
|
||||
Graph.cameraPosition(
|
||||
newPos, // new position
|
||||
node, // lookAt ({ x, y, z })
|
||||
3000 // ms transition duration
|
||||
);
|
||||
}
|
||||
|
||||
const highlightNodes = new Set();
|
||||
const highlightLinks = new Set();
|
||||
let hoverNode = null;
|
||||
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
var graph_div = document.getElementById('3d-graph');
|
||||
Graph = ForceGraph3D();
|
||||
Graph(graph_div);
|
||||
Graph
|
||||
.jsonUrl('{{ url("galaxy:data") }}')
|
||||
.width(graph_div.parentElement.clientWidth > 1200 ? 1200 : graph_div.parentElement.clientWidth) // Not perfect at all. JS-fu master from the future, please fix this :-)
|
||||
.height(1000)
|
||||
.enableNodeDrag(false) // allow easier navigation
|
||||
.onNodeClick(node => {
|
||||
camera = Graph.cameraPosition();
|
||||
var distance = Math.sqrt(Math.pow(node.x - camera.x, 2) + Math.pow(node.y - camera.y, 2) + Math.pow(node.z - camera.z, 2))
|
||||
if (distance < 120 || highlightNodes.has(node)) {
|
||||
focus_node(node);
|
||||
}
|
||||
})
|
||||
.linkWidth(link => highlightLinks.has(link) ? 0.4 : 0.0)
|
||||
.linkColor(link => highlightLinks.has(link) ? 'rgba(255,160,0,1)' : 'rgba(128,255,255,0.6)')
|
||||
.linkVisibility(link => highlightLinks.has(link))
|
||||
.nodeVisibility(node => highlightNodes.has(node) || node.mass > 4)
|
||||
// .linkDirectionalParticles(link => highlightLinks.has(link) ? 3 : 1) // kinda buggy for now, and slows this a bit, but would be great to help visualize lanes
|
||||
.linkDirectionalParticleWidth(0.2)
|
||||
.linkDirectionalParticleSpeed(-0.006)
|
||||
.nodeThreeObject(node => {
|
||||
const sprite = new SpriteText(node.name);
|
||||
sprite.material.depthWrite = false; // make sprite background transparent
|
||||
sprite.color = highlightNodes.has(node) ? node === hoverNode ? 'rgba(200,0,0,1)' : 'rgba(255,160,0,0.8)' : 'rgba(0,255,255,0.2)';
|
||||
sprite.textHeight = 2;
|
||||
sprite.center = new THREE.Vector2(1.2, 0.5);
|
||||
return sprite;
|
||||
})
|
||||
.onEngineStop( () => {
|
||||
focus_node(get_node_from_id({{ object.id }}));
|
||||
Graph.onEngineStop(() => {}); // don't call ourselves in a loop while moving the focus
|
||||
});
|
||||
|
||||
// Set distance between stars
|
||||
Graph.d3Force('link').distance(link => link.value);
|
||||
|
||||
// Set high masses nearer the center of the galaxy
|
||||
// TODO: quick and dirty strength computation, this will need tuning.
|
||||
Graph.d3Force('positionX', d3.forceX().strength(node => { return 1 - (1 / node.mass); }));
|
||||
Graph.d3Force('positionY', d3.forceY().strength(node => { return 1 - (1 / node.mass); }));
|
||||
Graph.d3Force('positionZ', d3.forceZ().strength(node => { return 1 - (1 / node.mass); }));
|
||||
})
|
||||
window.loadGalaxy({
|
||||
nodeId: {{ object.id }},
|
||||
dataUrl: '{{ url("galaxy:data") }}',
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
|
||||
+1
-1
@@ -160,7 +160,7 @@ class TestGalaxyView(TestCase):
|
||||
response = self.client.get(reverse("galaxy:user", args=[user.id]))
|
||||
self.assertContains(
|
||||
response,
|
||||
f'<a onclick="focus_node(get_node_from_id({user.id}))">Reset on {user}</a>',
|
||||
f'<a onclick="window.focusNode(window.getNodeFromId({user.id}))">Reset on {user}</a>',
|
||||
status_code=200,
|
||||
)
|
||||
|
||||
|
||||
+3
-11
@@ -23,17 +23,9 @@
|
||||
|
||||
from django.urls import path
|
||||
|
||||
from galaxy.views import *
|
||||
from galaxy.views import GalaxyDataView, GalaxyUserView
|
||||
|
||||
urlpatterns = [
|
||||
path(
|
||||
"<int:user_id>/",
|
||||
GalaxyUserView.as_view(),
|
||||
name="user",
|
||||
),
|
||||
path(
|
||||
"data.json",
|
||||
GalaxyDataView.as_view(),
|
||||
name="data",
|
||||
),
|
||||
path("<int:user_id>/", GalaxyUserView.as_view(), name="user"),
|
||||
path("data.json", GalaxyDataView.as_view(), name="data"),
|
||||
]
|
||||
@@ -14,7 +14,7 @@
|
||||
#
|
||||
from django.contrib import admin
|
||||
|
||||
from launderette.models import *
|
||||
from launderette.models import Launderette, Machine, Slot, Token
|
||||
|
||||
|
||||
@admin.register(Launderette)
|
||||
|
||||
+4
-18
@@ -51,18 +51,14 @@ class Launderette(models.Model):
|
||||
unix_name=settings.SITH_LAUNDERETTE_MANAGER["unix_name"]
|
||||
).first()
|
||||
m = launderette_club.get_membership_for(user)
|
||||
if m and m.role >= 9:
|
||||
return True
|
||||
return False
|
||||
return bool(m and m.role >= 9)
|
||||
|
||||
def can_be_edited_by(self, user):
|
||||
launderette_club = Club.objects.filter(
|
||||
unix_name=settings.SITH_LAUNDERETTE_MANAGER["unix_name"]
|
||||
).first()
|
||||
m = launderette_club.get_membership_for(user)
|
||||
if m and m.role >= 2:
|
||||
return True
|
||||
return False
|
||||
return bool(m and m.role >= 2)
|
||||
|
||||
def can_be_viewed_by(self, user):
|
||||
return user.is_subscribed
|
||||
@@ -113,9 +109,7 @@ class Machine(models.Model):
|
||||
unix_name=settings.SITH_LAUNDERETTE_MANAGER["unix_name"]
|
||||
).first()
|
||||
m = launderette_club.get_membership_for(user)
|
||||
if m and m.role >= 9:
|
||||
return True
|
||||
return False
|
||||
return bool(m and m.role >= 9)
|
||||
|
||||
|
||||
class Token(models.Model):
|
||||
@@ -164,15 +158,7 @@ class Token(models.Model):
|
||||
unix_name=settings.SITH_LAUNDERETTE_MANAGER["unix_name"]
|
||||
).first()
|
||||
m = launderette_club.get_membership_for(user)
|
||||
if m and m.role >= 9:
|
||||
return True
|
||||
return False
|
||||
|
||||
def is_avaliable(self):
|
||||
if not self.borrow_date and not self.user:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
return bool(m and m.role >= 9)
|
||||
|
||||
|
||||
class Slot(models.Model):
|
||||
|
||||
+17
-11
@@ -15,22 +15,28 @@
|
||||
|
||||
from django.urls import path
|
||||
|
||||
from launderette.views import *
|
||||
from launderette.views import (
|
||||
LaunderetteAdminView,
|
||||
LaunderetteBookMainView,
|
||||
LaunderetteBookView,
|
||||
LaunderetteClickView,
|
||||
LaunderetteCreateView,
|
||||
LaunderetteEditView,
|
||||
LaunderetteListView,
|
||||
LaunderetteMainClickView,
|
||||
LaunderetteMainView,
|
||||
MachineCreateView,
|
||||
MachineDeleteView,
|
||||
MachineEditView,
|
||||
SlotDeleteView,
|
||||
)
|
||||
|
||||
urlpatterns = [
|
||||
# views
|
||||
path("", LaunderetteMainView.as_view(), name="launderette_main"),
|
||||
path(
|
||||
"slot/<int:slot_id>/delete/",
|
||||
SlotDeleteView.as_view(),
|
||||
name="delete_slot",
|
||||
),
|
||||
path("slot/<int:slot_id>/delete/", SlotDeleteView.as_view(), name="delete_slot"),
|
||||
path("book/", LaunderetteBookMainView.as_view(), name="book_main"),
|
||||
path(
|
||||
"book/<int:launderette_id>/",
|
||||
LaunderetteBookView.as_view(),
|
||||
name="book_slot",
|
||||
),
|
||||
path("book/<int:launderette_id>/", LaunderetteBookView.as_view(), name="book_slot"),
|
||||
path(
|
||||
"<int:launderette_id>/click/",
|
||||
LaunderetteMainClickView.as_view(),
|
||||
|
||||
+60
-76
@@ -19,7 +19,7 @@ from datetime import timezone as tz
|
||||
|
||||
from django import forms
|
||||
from django.conf import settings
|
||||
from django.db import DataError, transaction
|
||||
from django.db import transaction
|
||||
from django.template import defaultfilters
|
||||
from django.urls import reverse_lazy
|
||||
from django.utils import dateparse, timezone
|
||||
@@ -73,15 +73,15 @@ class LaunderetteBookView(CanViewMixin, DetailView):
|
||||
self.machines = {}
|
||||
with transaction.atomic():
|
||||
self.object = self.get_object()
|
||||
if "slot_type" in request.POST.keys():
|
||||
if "slot_type" in request.POST:
|
||||
self.slot_type = request.POST["slot_type"]
|
||||
if "slot" in request.POST.keys() and request.user.is_authenticated:
|
||||
if "slot" in request.POST and request.user.is_authenticated:
|
||||
self.subscriber = request.user
|
||||
if self.subscriber.is_subscribed:
|
||||
self.date = dateparse.parse_datetime(request.POST["slot"]).replace(
|
||||
tzinfo=tz.utc
|
||||
)
|
||||
if self.slot_type == "WASHING":
|
||||
if self.slot_type in ["WASHING", "DRYING"]:
|
||||
if self.check_slot(self.slot_type):
|
||||
Slot(
|
||||
user=self.subscriber,
|
||||
@@ -89,30 +89,21 @@ class LaunderetteBookView(CanViewMixin, DetailView):
|
||||
machine=self.machines[self.slot_type],
|
||||
type=self.slot_type,
|
||||
).save()
|
||||
elif self.slot_type == "DRYING":
|
||||
if self.check_slot(self.slot_type):
|
||||
Slot(
|
||||
user=self.subscriber,
|
||||
start_date=self.date,
|
||||
machine=self.machines[self.slot_type],
|
||||
type=self.slot_type,
|
||||
).save()
|
||||
else:
|
||||
if self.check_slot("WASHING") and self.check_slot(
|
||||
"DRYING", self.date + timedelta(hours=1)
|
||||
):
|
||||
Slot(
|
||||
user=self.subscriber,
|
||||
start_date=self.date,
|
||||
machine=self.machines["WASHING"],
|
||||
type="WASHING",
|
||||
).save()
|
||||
Slot(
|
||||
user=self.subscriber,
|
||||
start_date=self.date + timedelta(hours=1),
|
||||
machine=self.machines["DRYING"],
|
||||
type="DRYING",
|
||||
).save()
|
||||
elif self.check_slot("WASHING") and self.check_slot(
|
||||
"DRYING", self.date + timedelta(hours=1)
|
||||
):
|
||||
Slot(
|
||||
user=self.subscriber,
|
||||
start_date=self.date,
|
||||
machine=self.machines["WASHING"],
|
||||
type="WASHING",
|
||||
).save()
|
||||
Slot(
|
||||
user=self.subscriber,
|
||||
start_date=self.date + timedelta(hours=1),
|
||||
machine=self.machines["DRYING"],
|
||||
type="DRYING",
|
||||
).save()
|
||||
return super().get(request, *args, **kwargs)
|
||||
|
||||
def check_slot(self, machine_type, date=None):
|
||||
@@ -149,15 +140,17 @@ class LaunderetteBookView(CanViewMixin, DetailView):
|
||||
):
|
||||
free = False
|
||||
if (
|
||||
self.slot_type == "BOTH"
|
||||
(
|
||||
self.slot_type == "BOTH"
|
||||
and self.check_slot("WASHING", h)
|
||||
and self.check_slot("DRYING", h + timedelta(hours=1))
|
||||
)
|
||||
or self.slot_type == "WASHING"
|
||||
and self.check_slot("WASHING", h)
|
||||
and self.check_slot("DRYING", h + timedelta(hours=1))
|
||||
or self.slot_type == "DRYING"
|
||||
and self.check_slot("DRYING", h)
|
||||
):
|
||||
free = True
|
||||
elif self.slot_type == "WASHING" and self.check_slot("WASHING", h):
|
||||
free = True
|
||||
elif self.slot_type == "DRYING" and self.check_slot("DRYING", h):
|
||||
free = True
|
||||
if free and datetime.now().replace(tzinfo=tz.utc) < h:
|
||||
kwargs["planning"][date].append(h)
|
||||
else:
|
||||
@@ -236,42 +229,39 @@ class ManageTokenForm(forms.Form):
|
||||
token_list = cleaned_data["tokens"].strip(" \n\r").split(" ")
|
||||
token_type = cleaned_data["token_type"]
|
||||
self.data = {}
|
||||
|
||||
if cleaned_data["action"] not in ["BACK", "ADD", "DEL"]:
|
||||
return
|
||||
|
||||
tokens = list(
|
||||
Token.objects.filter(
|
||||
launderette=launderette, type=token_type, name__in=token_list
|
||||
)
|
||||
)
|
||||
existing_names = {t.name for t in tokens}
|
||||
if cleaned_data["action"] in ["BACK", "DEL"]:
|
||||
for t in set(token_list) - existing_names:
|
||||
self.add_error(
|
||||
None,
|
||||
_("Token %(token_name)s does not exists") % {"token_name": t},
|
||||
)
|
||||
if cleaned_data["action"] == "BACK":
|
||||
for t in token_list:
|
||||
try:
|
||||
tok = Token.objects.filter(
|
||||
launderette=launderette, type=token_type, name=t
|
||||
).first()
|
||||
tok.borrow_date = None
|
||||
tok.user = None
|
||||
tok.save()
|
||||
except:
|
||||
self.add_error(
|
||||
None,
|
||||
_("Token %(token_name)s does not exists") % {"token_name": t},
|
||||
)
|
||||
elif cleaned_data["action"] == "ADD":
|
||||
for t in token_list:
|
||||
try:
|
||||
Token(launderette=launderette, type=token_type, name=t).save()
|
||||
except DataError as e:
|
||||
self.add_error(None, e)
|
||||
except:
|
||||
self.add_error(
|
||||
None,
|
||||
_("Token %(token_name)s already exists") % {"token_name": t},
|
||||
)
|
||||
Token.objects.filter(id__in=[t.id for t in tokens]).update(
|
||||
borrow_date=None, user=None
|
||||
)
|
||||
elif cleaned_data["action"] == "DEL":
|
||||
Token.objects.filter(id__in=[t.id for t in tokens]).delete()
|
||||
elif cleaned_data["action"] == "ADD":
|
||||
for name in existing_names:
|
||||
self.add_error(
|
||||
None,
|
||||
_("Token %(token_name)s already exists") % {"token_name": name},
|
||||
)
|
||||
for t in token_list:
|
||||
try:
|
||||
Token.objects.filter(
|
||||
launderette=launderette, type=token_type, name=t
|
||||
).delete()
|
||||
except:
|
||||
self.add_error(
|
||||
None,
|
||||
_("Token %(token_name)s does not exists") % {"token_name": t},
|
||||
)
|
||||
if t == "":
|
||||
self.add_error(None, _("Token name can not be blank"))
|
||||
else:
|
||||
Token(launderette=launderette, type=token_type, name=t).save()
|
||||
|
||||
|
||||
class LaunderetteAdminView(CanEditPropMixin, BaseFormView, DetailView):
|
||||
@@ -288,13 +278,7 @@ class LaunderetteAdminView(CanEditPropMixin, BaseFormView, DetailView):
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
self.object = self.get_object()
|
||||
form = self.get_form()
|
||||
return super().post(request, *args, **kwargs)
|
||||
form.launderette = self.object
|
||||
if form.is_valid():
|
||||
return self.form_valid(form)
|
||||
else:
|
||||
return self.form_invalid(form)
|
||||
|
||||
def form_valid(self, form):
|
||||
"""We handle here the redirection, passing the user id of the asked customer."""
|
||||
@@ -353,7 +337,7 @@ class LaunderetteMainClickView(CanEditMixin, BaseFormView, DetailView):
|
||||
kwargs["counter"] = self.object.counter
|
||||
kwargs["form"] = self.get_form()
|
||||
kwargs["barmen"] = [self.request.user]
|
||||
if "last_basket" in self.request.session.keys():
|
||||
if "last_basket" in self.request.session:
|
||||
kwargs["last_basket"] = self.request.session.pop("last_basket", None)
|
||||
kwargs["last_customer"] = self.request.session.pop("last_customer", None)
|
||||
kwargs["last_total"] = self.request.session.pop("last_total", None)
|
||||
@@ -479,7 +463,7 @@ class LaunderetteClickView(CanEditMixin, DetailView, BaseFormView):
|
||||
def get_context_data(self, **kwargs):
|
||||
"""We handle here the login form for the barman."""
|
||||
kwargs = super().get_context_data(**kwargs)
|
||||
if "form" not in kwargs.keys():
|
||||
if "form" not in kwargs:
|
||||
kwargs["form"] = self.get_form()
|
||||
kwargs["counter"] = self.object.counter
|
||||
kwargs["customer"] = self.customer
|
||||
@@ -519,7 +503,7 @@ class MachineCreateView(CanCreateMixin, CreateView):
|
||||
|
||||
def get_initial(self):
|
||||
ret = super().get_initial()
|
||||
if "launderette" in self.request.GET.keys():
|
||||
if "launderette" in self.request.GET:
|
||||
obj = Launderette.objects.filter(
|
||||
id=int(self.request.GET["launderette"])
|
||||
).first()
|
||||
|
||||
+526
-488
File diff suppressed because it is too large.
Load diff
@@ -7,7 +7,7 @@
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2024-10-09 11:50+0200\n"
|
||||
"POT-Creation-Date: 2024-10-16 02:19+0200\n"
|
||||
"PO-Revision-Date: 2024-09-17 11:54+0200\n"
|
||||
"Last-Translator: Sli <antoine@bartuccio.fr>\n"
|
||||
"Language-Team: AE info <ae.info@utbm.fr>\n"
|
||||
@@ -22,87 +22,95 @@ msgstr ""
|
||||
msgid "captured.%s"
|
||||
msgstr "capture.%s"
|
||||
|
||||
#: core/static/webpack/easymde-index.js:32
|
||||
#: core/static/webpack/ajax-select-index.ts:73
|
||||
msgid "You need to type %(number)s more characters"
|
||||
msgstr "Vous devez taper %(number)s caractères de plus"
|
||||
|
||||
#: core/static/webpack/ajax-select-index.ts:76
|
||||
msgid "No results found"
|
||||
msgstr "Aucun résultat trouvé"
|
||||
|
||||
#: core/static/webpack/easymde-index.ts:31
|
||||
msgid "Heading"
|
||||
msgstr "Titre"
|
||||
|
||||
#: core/static/webpack/easymde-index.js:38
|
||||
#: core/static/webpack/easymde-index.ts:37
|
||||
msgid "Italic"
|
||||
msgstr "Italique"
|
||||
|
||||
#: core/static/webpack/easymde-index.js:44
|
||||
#: core/static/webpack/easymde-index.ts:43
|
||||
msgid "Bold"
|
||||
msgstr "Gras"
|
||||
|
||||
#: core/static/webpack/easymde-index.js:50
|
||||
#: core/static/webpack/easymde-index.ts:49
|
||||
msgid "Strikethrough"
|
||||
msgstr "Barré"
|
||||
|
||||
#: core/static/webpack/easymde-index.js:59
|
||||
#: core/static/webpack/easymde-index.ts:58
|
||||
msgid "Underline"
|
||||
msgstr "Souligné"
|
||||
|
||||
#: core/static/webpack/easymde-index.js:68
|
||||
#: core/static/webpack/easymde-index.ts:67
|
||||
msgid "Superscript"
|
||||
msgstr "Exposant"
|
||||
|
||||
#: core/static/webpack/easymde-index.js:77
|
||||
#: core/static/webpack/easymde-index.ts:76
|
||||
msgid "Subscript"
|
||||
msgstr "Indice"
|
||||
|
||||
#: core/static/webpack/easymde-index.js:83
|
||||
#: core/static/webpack/easymde-index.ts:82
|
||||
msgid "Code"
|
||||
msgstr "Code"
|
||||
|
||||
#: core/static/webpack/easymde-index.js:90
|
||||
#: core/static/webpack/easymde-index.ts:89
|
||||
msgid "Quote"
|
||||
msgstr "Citation"
|
||||
|
||||
#: core/static/webpack/easymde-index.js:96
|
||||
#: core/static/webpack/easymde-index.ts:95
|
||||
msgid "Unordered list"
|
||||
msgstr "Liste non ordonnée"
|
||||
|
||||
#: core/static/webpack/easymde-index.js:102
|
||||
#: core/static/webpack/easymde-index.ts:101
|
||||
msgid "Ordered list"
|
||||
msgstr "Liste ordonnée"
|
||||
|
||||
#: core/static/webpack/easymde-index.js:109
|
||||
#: core/static/webpack/easymde-index.ts:108
|
||||
msgid "Insert link"
|
||||
msgstr "Insérer lien"
|
||||
|
||||
#: core/static/webpack/easymde-index.js:115
|
||||
#: core/static/webpack/easymde-index.ts:114
|
||||
msgid "Insert image"
|
||||
msgstr "Insérer image"
|
||||
|
||||
#: core/static/webpack/easymde-index.js:121
|
||||
#: core/static/webpack/easymde-index.ts:120
|
||||
msgid "Insert table"
|
||||
msgstr "Insérer tableau"
|
||||
|
||||
#: core/static/webpack/easymde-index.js:128
|
||||
#: core/static/webpack/easymde-index.ts:127
|
||||
msgid "Clean block"
|
||||
msgstr "Nettoyer bloc"
|
||||
|
||||
#: core/static/webpack/easymde-index.js:135
|
||||
#: core/static/webpack/easymde-index.ts:134
|
||||
msgid "Toggle preview"
|
||||
msgstr "Activer la prévisualisation"
|
||||
|
||||
#: core/static/webpack/easymde-index.js:141
|
||||
#: core/static/webpack/easymde-index.ts:140
|
||||
msgid "Toggle side by side"
|
||||
msgstr "Activer la vue côte à côte"
|
||||
|
||||
#: core/static/webpack/easymde-index.js:147
|
||||
#: core/static/webpack/easymde-index.ts:146
|
||||
msgid "Toggle fullscreen"
|
||||
msgstr "Activer le plein écran"
|
||||
|
||||
#: core/static/webpack/easymde-index.js:154
|
||||
#: core/static/webpack/easymde-index.ts:153
|
||||
msgid "Markdown guide"
|
||||
msgstr "Guide markdown"
|
||||
|
||||
#: core/static/webpack/user/family-graph-index.js:222
|
||||
#: core/static/webpack/user/family-graph-index.js:233
|
||||
msgid "family_tree.%(extension)s"
|
||||
msgstr "arbre_genealogique.%(extension)s"
|
||||
|
||||
#: core/static/webpack/user/pictures-index.js:67
|
||||
#: core/static/webpack/user/pictures-index.js:76
|
||||
msgid "pictures.%(extension)s"
|
||||
msgstr "photos.%(extension)s"
|
||||
|
||||
@@ -110,10 +118,10 @@ msgstr "photos.%(extension)s"
|
||||
msgid "Incorrect value"
|
||||
msgstr "Valeur incorrecte"
|
||||
|
||||
#: sas/static/sas/js/viewer.js:205
|
||||
#: sas/static/webpack/sas/viewer-index.ts:271
|
||||
msgid "Couldn't moderate picture"
|
||||
msgstr "Il n'a pas été possible de modérer l'image"
|
||||
|
||||
#: sas/static/sas/js/viewer.js:217
|
||||
#: sas/static/webpack/sas/viewer-index.ts:284
|
||||
msgid "Couldn't delete picture"
|
||||
msgstr "Il n'a pas été possible de supprimer l'image"
|
||||
+6
-1
@@ -23,7 +23,12 @@
|
||||
|
||||
from django.urls import path
|
||||
|
||||
from matmat.views import *
|
||||
from matmat.views import (
|
||||
SearchClearFormView,
|
||||
SearchNormalFormView,
|
||||
SearchQuickFormView,
|
||||
SearchReverseFormView,
|
||||
)
|
||||
|
||||
urlpatterns = [
|
||||
path("", SearchNormalFormView.as_view(), name="search"),
|
||||
|
||||
+7
-10
@@ -71,15 +71,15 @@ class SearchForm(forms.ModelForm):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
for key in self.fields.keys():
|
||||
for key in self.fields:
|
||||
self.fields[key].required = False
|
||||
|
||||
@property
|
||||
def cleaned_data_json(self):
|
||||
data = self.cleaned_data
|
||||
for key in data.keys():
|
||||
if key in ("date_of_birth", "phone") and data[key] is not None:
|
||||
data[key] = str(data[key])
|
||||
for key, val in data.items():
|
||||
if key in ("date_of_birth", "phone") and val is not None:
|
||||
data[key] = str(val)
|
||||
return data
|
||||
|
||||
|
||||
@@ -98,10 +98,7 @@ class SearchFormListView(FormerSubscriberMixin, SingleObjectMixin, ListView):
|
||||
self.session = request.session
|
||||
self.last_search = self.session.get("matmat_search_result", str([]))
|
||||
self.last_search = literal_eval(self.last_search)
|
||||
if "valid_form" in kwargs.keys():
|
||||
self.valid_form = kwargs["valid_form"]
|
||||
else:
|
||||
self.valid_form = None
|
||||
self.valid_form = kwargs.get("valid_form")
|
||||
|
||||
self.init_query = self.model.objects
|
||||
self.can_see_hidden = True
|
||||
@@ -202,8 +199,8 @@ class SearchClearFormView(FormerSubscriberMixin, View):
|
||||
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
super().dispatch(request, *args, **kwargs)
|
||||
if "matmat_search_form" in request.session.keys():
|
||||
if "matmat_search_form" in request.session:
|
||||
request.session.pop("matmat_search_form")
|
||||
if "matmat_search_result" in request.session.keys():
|
||||
if "matmat_search_result" in request.session:
|
||||
request.session.pop("matmat_search_result")
|
||||
return HttpResponseRedirect(reverse("matmat:search"))
|
||||
Generated
+652
-4
@@ -11,23 +11,32 @@
|
||||
"dependencies": {
|
||||
"@fortawesome/fontawesome-free": "^6.6.0",
|
||||
"@hey-api/client-fetch": "^0.4.0",
|
||||
"@sentry/browser": "^8.34.0",
|
||||
"@zip.js/zip.js": "^2.7.52",
|
||||
"3d-force-graph": "^1.73.4",
|
||||
"alpinejs": "^3.14.1",
|
||||
"chart.js": "^4.4.4",
|
||||
"cytoscape": "^3.30.2",
|
||||
"cytoscape-cxtmenu": "^3.5.0",
|
||||
"cytoscape-klay": "^3.1.4",
|
||||
"d3-force-3d": "^3.0.5",
|
||||
"easymde": "^2.18.0",
|
||||
"glob": "^11.0.0",
|
||||
"jquery": "^3.7.1",
|
||||
"jquery-ui": "^1.14.0",
|
||||
"jquery.shorten": "^1.0.0",
|
||||
"native-file-system-adapter": "^3.0.1"
|
||||
"native-file-system-adapter": "^3.0.1",
|
||||
"three": "^0.169.0",
|
||||
"three-spritetext": "^1.9.0",
|
||||
"tom-select": "^2.3.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.25.2",
|
||||
"@babel/preset-env": "^7.25.4",
|
||||
"@biomejs/biome": "1.9.3",
|
||||
"@hey-api/openapi-ts": "^0.53.8",
|
||||
"@types/alpinejs": "^3.13.10",
|
||||
"@types/jquery": "^3.5.31",
|
||||
"babel-loader": "^9.2.1",
|
||||
"css-loader": "^7.1.2",
|
||||
"css-minimizer-webpack-plugin": "^7.0.0",
|
||||
@@ -38,6 +47,7 @@
|
||||
"ts-loader": "^9.5.1",
|
||||
"typescript": "^5.6.3",
|
||||
"webpack": "^5.94.0",
|
||||
"webpack-bundle-analyzer": "^4.10.2",
|
||||
"webpack-cli": "^5.1.4"
|
||||
}
|
||||
},
|
||||
@@ -1790,7 +1800,6 @@
|
||||
"version": "7.25.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.25.6.tgz",
|
||||
"integrity": "sha512-VBj9MYyDb9tuLq7yzqjgzt6Q+IBQLrGZfdjOekyEirZPHxXWoTSGUTMrpsfi58Up73d13NfYLv8HT9vmznjzhQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"regenerator-runtime": "^0.14.0"
|
||||
},
|
||||
@@ -2160,6 +2169,24 @@
|
||||
"integrity": "sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@kurkle/color": {
|
||||
"version": "0.3.2",
|
||||
"resolved": "https://registry.npmjs.org/@kurkle/color/-/color-0.3.2.tgz",
|
||||
"integrity": "sha512-fuscdXJ9G1qb7W8VdHi+IwRqij3lBkosAm4ydQtEmbY58OzHXqQhvlxqEkoz0yssNVn38bcpRWgA9PP+OGoisw=="
|
||||
},
|
||||
"node_modules/@orchidjs/sifter": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@orchidjs/sifter/-/sifter-1.0.3.tgz",
|
||||
"integrity": "sha512-zCZbwKegHytfsPm8Amcfh7v/4vHqTAaOu6xFswBYcn8nznBOuseu6COB2ON7ez0tFV0mKL0nRNnCiZZA+lU9/g==",
|
||||
"dependencies": {
|
||||
"@orchidjs/unicode-variants": "^1.0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@orchidjs/unicode-variants": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@orchidjs/unicode-variants/-/unicode-variants-1.0.4.tgz",
|
||||
"integrity": "sha512-NvVBRnZNE+dugiXERFsET1JlKZfM5lJDEpSMilKW4bToYJ7pxf0Zne78xyXB2ny2c2aHfJ6WLnz1AaTNHAmQeQ=="
|
||||
},
|
||||
"node_modules/@pkgjs/parseargs": {
|
||||
"version": "0.11.0",
|
||||
"resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
|
||||
@@ -2169,6 +2196,114 @@
|
||||
"node": ">=14"
|
||||
}
|
||||
},
|
||||
"node_modules/@polka/url": {
|
||||
"version": "1.0.0-next.28",
|
||||
"resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.28.tgz",
|
||||
"integrity": "sha512-8LduaNlMZGwdZ6qWrKlfa+2M4gahzFkprZiAt2TF8uS0qQgBizKXpXURqvTJ4WtmupWxaLqjRb2UCTe72mu+Aw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@sentry-internal/browser-utils": {
|
||||
"version": "8.34.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry-internal/browser-utils/-/browser-utils-8.34.0.tgz",
|
||||
"integrity": "sha512-4AcYOzPzD1tL5eSRQ/GpKv5enquZf4dMVUez99/Bh3va8qiJrNP55AcM7UzZ7WZLTqKygIYruJTU5Zu2SpEAPQ==",
|
||||
"dependencies": {
|
||||
"@sentry/core": "8.34.0",
|
||||
"@sentry/types": "8.34.0",
|
||||
"@sentry/utils": "8.34.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.18"
|
||||
}
|
||||
},
|
||||
"node_modules/@sentry-internal/feedback": {
|
||||
"version": "8.34.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry-internal/feedback/-/feedback-8.34.0.tgz",
|
||||
"integrity": "sha512-aYSM2KPUs0FLPxxbJCFSwCYG70VMzlT04xepD1Y/tTlPPOja/02tSv2tyOdZbv8Uw7xslZs3/8Lhj74oYcTBxw==",
|
||||
"dependencies": {
|
||||
"@sentry/core": "8.34.0",
|
||||
"@sentry/types": "8.34.0",
|
||||
"@sentry/utils": "8.34.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.18"
|
||||
}
|
||||
},
|
||||
"node_modules/@sentry-internal/replay": {
|
||||
"version": "8.34.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry-internal/replay/-/replay-8.34.0.tgz",
|
||||
"integrity": "sha512-EoMh9NYljNewZK1quY23YILgtNdGgrkzJ9TPsj6jXUG0LZ0Q7N7eFWd0xOEDBvFxrmI3cSXF1i4d1sBb+eyKRw==",
|
||||
"dependencies": {
|
||||
"@sentry-internal/browser-utils": "8.34.0",
|
||||
"@sentry/core": "8.34.0",
|
||||
"@sentry/types": "8.34.0",
|
||||
"@sentry/utils": "8.34.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.18"
|
||||
}
|
||||
},
|
||||
"node_modules/@sentry-internal/replay-canvas": {
|
||||
"version": "8.34.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry-internal/replay-canvas/-/replay-canvas-8.34.0.tgz",
|
||||
"integrity": "sha512-x8KhZcCDpbKHqFOykYXiamX6x0LRxv6N1OJHoH+XCrMtiDBZr4Yo30d/MaS6rjmKGMtSRij30v+Uq+YWIgxUrg==",
|
||||
"dependencies": {
|
||||
"@sentry-internal/replay": "8.34.0",
|
||||
"@sentry/core": "8.34.0",
|
||||
"@sentry/types": "8.34.0",
|
||||
"@sentry/utils": "8.34.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.18"
|
||||
}
|
||||
},
|
||||
"node_modules/@sentry/browser": {
|
||||
"version": "8.34.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-8.34.0.tgz",
|
||||
"integrity": "sha512-3HHG2NXxzHq1lVmDy2uRjYjGNf9NsJsTPlOC70vbQdOb+S49EdH/XMPy+J3ruIoyv6Cu0LwvA6bMOM6rHZOgNQ==",
|
||||
"dependencies": {
|
||||
"@sentry-internal/browser-utils": "8.34.0",
|
||||
"@sentry-internal/feedback": "8.34.0",
|
||||
"@sentry-internal/replay": "8.34.0",
|
||||
"@sentry-internal/replay-canvas": "8.34.0",
|
||||
"@sentry/core": "8.34.0",
|
||||
"@sentry/types": "8.34.0",
|
||||
"@sentry/utils": "8.34.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.18"
|
||||
}
|
||||
},
|
||||
"node_modules/@sentry/core": {
|
||||
"version": "8.34.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/core/-/core-8.34.0.tgz",
|
||||
"integrity": "sha512-adrXCTK/zsg5pJ67lgtZqdqHvyx6etMjQW3P82NgWdj83c8fb+zH+K79Z47pD4zQjX0ou2Ws5nwwi4wJbz4bfA==",
|
||||
"dependencies": {
|
||||
"@sentry/types": "8.34.0",
|
||||
"@sentry/utils": "8.34.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.18"
|
||||
}
|
||||
},
|
||||
"node_modules/@sentry/types": {
|
||||
"version": "8.34.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/types/-/types-8.34.0.tgz",
|
||||
"integrity": "sha512-zLRc60CzohGCo6zNsNeQ9JF3SiEeRE4aDCP9fDDdIVCOKovS+mn1rtSip0qd0Vp2fidOu0+2yY0ALCz1A3PJSQ==",
|
||||
"engines": {
|
||||
"node": ">=14.18"
|
||||
}
|
||||
},
|
||||
"node_modules/@sentry/utils": {
|
||||
"version": "8.34.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-8.34.0.tgz",
|
||||
"integrity": "sha512-W1KoRlFUjprlh3t86DZPFxLfM6mzjRzshVfMY7vRlJFymBelJsnJ3A1lPeBZM9nCraOSiw6GtOWu6k5BAkiGIg==",
|
||||
"dependencies": {
|
||||
"@sentry/types": "8.34.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.18"
|
||||
}
|
||||
},
|
||||
"node_modules/@sinclair/typebox": {
|
||||
"version": "0.27.8",
|
||||
"resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz",
|
||||
@@ -2184,6 +2319,17 @@
|
||||
"node": ">=10.13.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tweenjs/tween.js": {
|
||||
"version": "25.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@tweenjs/tween.js/-/tween.js-25.0.0.tgz",
|
||||
"integrity": "sha512-XKLA6syeBUaPzx4j3qwMqzzq+V4uo72BnlbOjmuljLrRqdsd3qnzvZZoxvMHZ23ndsRS4aufU6JOZYpCbU6T1A=="
|
||||
},
|
||||
"node_modules/@types/alpinejs": {
|
||||
"version": "3.13.10",
|
||||
"resolved": "https://registry.npmjs.org/@types/alpinejs/-/alpinejs-3.13.10.tgz",
|
||||
"integrity": "sha512-ah53tF6mWuuwerpDE7EHwbZErNDJQlsLISPqJhYj2RZ9nuTYbRknSkqebUd3igkhLIZKkPa7IiXjSn9qsU9O2w==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/codemirror": {
|
||||
"version": "5.60.15",
|
||||
"resolved": "https://registry.npmjs.org/@types/codemirror/-/codemirror-5.60.15.tgz",
|
||||
@@ -2221,6 +2367,15 @@
|
||||
"@types/istanbul-lib-report": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/jquery": {
|
||||
"version": "3.5.31",
|
||||
"resolved": "https://registry.npmjs.org/@types/jquery/-/jquery-3.5.31.tgz",
|
||||
"integrity": "sha512-rf/iB+cPJ/YZfMwr+FVuQbm7IaWC4y3FVYfVDxRGqmUCFjjPII0HWaP0vTPJGp6m4o13AXySCcMbWfrWtBFAKw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/sizzle": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/json-schema": {
|
||||
"version": "7.0.15",
|
||||
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
|
||||
@@ -2241,6 +2396,12 @@
|
||||
"undici-types": "~6.19.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/sizzle": {
|
||||
"version": "2.3.8",
|
||||
"resolved": "https://registry.npmjs.org/@types/sizzle/-/sizzle-2.3.8.tgz",
|
||||
"integrity": "sha512-0vWLNK2D5MT9dg0iOo8GlKguPAU02QjmZitPEsXRuJXU/OGIOt9vT9Fc26wtYuavLxtO45v9PGleoL9Z0k1LHg==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/tern": {
|
||||
"version": "0.23.9",
|
||||
"resolved": "https://registry.npmjs.org/@types/tern/-/tern-0.23.9.tgz",
|
||||
@@ -2489,6 +2650,29 @@
|
||||
"node": ">=16.5.0"
|
||||
}
|
||||
},
|
||||
"node_modules/3d-force-graph": {
|
||||
"version": "1.73.4",
|
||||
"resolved": "https://registry.npmjs.org/3d-force-graph/-/3d-force-graph-1.73.4.tgz",
|
||||
"integrity": "sha512-eMHZ1LVzh9APLv+An0AXz2dVPwasJlqAnJ61ABlb1qaO6DYuqIUTTErh0DN/24nIWJu1jCim2WiVujzz7slnWQ==",
|
||||
"dependencies": {
|
||||
"accessor-fn": "1",
|
||||
"kapsule": "1",
|
||||
"three": ">=0.118 <1",
|
||||
"three-forcegraph": "1",
|
||||
"three-render-objects": "^1.29"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/accessor-fn": {
|
||||
"version": "1.5.1",
|
||||
"resolved": "https://registry.npmjs.org/accessor-fn/-/accessor-fn-1.5.1.tgz",
|
||||
"integrity": "sha512-zZpFYBqIL1Aqg+f2qmYHJ8+yIZF7/tP6PUGx2/QM0uGPSO5UegpinmkNwDohxWtOj586BpMPVRUjce2HI6xB3A==",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/acorn": {
|
||||
"version": "8.12.1",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz",
|
||||
@@ -2510,6 +2694,18 @@
|
||||
"acorn": "^8"
|
||||
}
|
||||
},
|
||||
"node_modules/acorn-walk": {
|
||||
"version": "8.3.4",
|
||||
"resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz",
|
||||
"integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"acorn": "^8.11.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/ajv": {
|
||||
"version": "6.12.6",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
|
||||
@@ -2900,6 +3096,17 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/chart.js": {
|
||||
"version": "4.4.4",
|
||||
"resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.4.4.tgz",
|
||||
"integrity": "sha512-emICKGBABnxhMjUjlYRR12PmOXhJ2eJjEHL2/dZlWjxRAZT1D8xplLFq5M0tMQK8ja+wBS/tuVEJB5C6r7VxJA==",
|
||||
"dependencies": {
|
||||
"@kurkle/color": "^0.3.0"
|
||||
},
|
||||
"engines": {
|
||||
"pnpm": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/chokidar": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.1.tgz",
|
||||
@@ -3419,6 +3626,159 @@
|
||||
"cytoscape": "^3.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-array": {
|
||||
"version": "3.2.4",
|
||||
"resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz",
|
||||
"integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==",
|
||||
"dependencies": {
|
||||
"internmap": "1 - 2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-binarytree": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/d3-binarytree/-/d3-binarytree-1.0.2.tgz",
|
||||
"integrity": "sha512-cElUNH+sHu95L04m92pG73t2MEJXKu+GeKUN1TJkFsu93E5W8E9Sc3kHEGJKgenGvj19m6upSn2EunvMgMD2Yw=="
|
||||
},
|
||||
"node_modules/d3-color": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz",
|
||||
"integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-dispatch": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-3.0.1.tgz",
|
||||
"integrity": "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-force-3d": {
|
||||
"version": "3.0.5",
|
||||
"resolved": "https://registry.npmjs.org/d3-force-3d/-/d3-force-3d-3.0.5.tgz",
|
||||
"integrity": "sha512-tdwhAhoTYZY/a6eo9nR7HP3xSW/C6XvJTbeRpR92nlPzH6OiE+4MliN9feuSFd0tPtEUo+191qOhCTWx3NYifg==",
|
||||
"dependencies": {
|
||||
"d3-binarytree": "1",
|
||||
"d3-dispatch": "1 - 3",
|
||||
"d3-octree": "1",
|
||||
"d3-quadtree": "1 - 3",
|
||||
"d3-timer": "1 - 3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-format": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz",
|
||||
"integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-interpolate": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz",
|
||||
"integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==",
|
||||
"dependencies": {
|
||||
"d3-color": "1 - 3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-octree": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/d3-octree/-/d3-octree-1.0.2.tgz",
|
||||
"integrity": "sha512-Qxg4oirJrNXauiuC94uKMbgxwnhdda9xRLl9ihq45srlJ4Ga3CSgqGcAL8iW7N5CIv4Oz8x3E734ulxyvHPvwA=="
|
||||
},
|
||||
"node_modules/d3-quadtree": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-3.0.1.tgz",
|
||||
"integrity": "sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-scale": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz",
|
||||
"integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==",
|
||||
"dependencies": {
|
||||
"d3-array": "2.10.0 - 3",
|
||||
"d3-format": "1 - 3",
|
||||
"d3-interpolate": "1.2.0 - 3",
|
||||
"d3-time": "2.1.1 - 3",
|
||||
"d3-time-format": "2 - 4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-scale-chromatic": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz",
|
||||
"integrity": "sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ==",
|
||||
"dependencies": {
|
||||
"d3-color": "1 - 3",
|
||||
"d3-interpolate": "1 - 3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-time": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz",
|
||||
"integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==",
|
||||
"dependencies": {
|
||||
"d3-array": "2 - 3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-time-format": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz",
|
||||
"integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==",
|
||||
"dependencies": {
|
||||
"d3-time": "1 - 3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-timer": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz",
|
||||
"integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/data-joint": {
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/data-joint/-/data-joint-1.3.1.tgz",
|
||||
"integrity": "sha512-tMK0m4OVGqiA3zkn8JmO6YAqD8UwJqIAx4AAwFl1SKTtKAqcXePuT+n2aayiX9uITtlN3DFtKKTOxJRUc2+HvQ==",
|
||||
"dependencies": {
|
||||
"index-array-by": "^1.4.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/debounce": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/debounce/-/debounce-1.2.1.tgz",
|
||||
"integrity": "sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/debug": {
|
||||
"version": "4.3.7",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz",
|
||||
@@ -3515,6 +3875,12 @@
|
||||
"url": "https://dotenvx.com"
|
||||
}
|
||||
},
|
||||
"node_modules/duplexer": {
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz",
|
||||
"integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/eastasianwidth": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
|
||||
@@ -4026,6 +4392,21 @@
|
||||
"integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/gzip-size": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-6.0.0.tgz",
|
||||
"integrity": "sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"duplexer": "^0.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/handlebars": {
|
||||
"version": "4.7.8",
|
||||
"resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz",
|
||||
@@ -4068,6 +4449,12 @@
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/html-escaper": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz",
|
||||
"integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/human-signals": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz",
|
||||
@@ -4120,6 +4507,22 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/index-array-by": {
|
||||
"version": "1.4.2",
|
||||
"resolved": "https://registry.npmjs.org/index-array-by/-/index-array-by-1.4.2.tgz",
|
||||
"integrity": "sha512-SP23P27OUKzXWEC/TOyWlwLviofQkCSCKONnc62eItjp69yCZZPqDQtr3Pw5gJDnPeUMqExmKydNZaJO0FU9pw==",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/internmap": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz",
|
||||
"integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/interpret": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/interpret/-/interpret-3.1.1.tgz",
|
||||
@@ -4328,6 +4731,17 @@
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/kapsule": {
|
||||
"version": "1.14.6",
|
||||
"resolved": "https://registry.npmjs.org/kapsule/-/kapsule-1.14.6.tgz",
|
||||
"integrity": "sha512-wSi6tHNOfXrIK2Pvv6BhZ9ukzhbp+XZlOOPWSVGUbqfFsnnli4Eq8FN6TaWJv2e17sY5+fKYVxa4DP2oPGlKhg==",
|
||||
"dependencies": {
|
||||
"lodash-es": "4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/kind-of": {
|
||||
"version": "6.0.3",
|
||||
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz",
|
||||
@@ -4375,6 +4789,11 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/lodash-es": {
|
||||
"version": "4.17.21",
|
||||
"resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz",
|
||||
"integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw=="
|
||||
},
|
||||
"node_modules/lodash.debounce": {
|
||||
"version": "4.0.8",
|
||||
"resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
|
||||
@@ -4629,6 +5048,15 @@
|
||||
"ufo": "^1.5.4"
|
||||
}
|
||||
},
|
||||
"node_modules/mrmime": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.0.tgz",
|
||||
"integrity": "sha512-eu38+hdgojoyq63s+yTpN4XMBdt5l8HhMhc4VKLO9KM5caLIBvUm4thi7fFaxyTmCKeNnXZ5pAlBwCUnhA09uw==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/ms": {
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
||||
@@ -4680,6 +5108,39 @@
|
||||
"integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/ngraph.events": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/ngraph.events/-/ngraph.events-1.2.2.tgz",
|
||||
"integrity": "sha512-JsUbEOzANskax+WSYiAPETemLWYXmixuPAlmZmhIbIj6FH/WDgEGCGnRwUQBK0GjOnVm8Ui+e5IJ+5VZ4e32eQ=="
|
||||
},
|
||||
"node_modules/ngraph.forcelayout": {
|
||||
"version": "3.3.1",
|
||||
"resolved": "https://registry.npmjs.org/ngraph.forcelayout/-/ngraph.forcelayout-3.3.1.tgz",
|
||||
"integrity": "sha512-MKBuEh1wujyQHFTW57y5vd/uuEOK0XfXYxm3lC7kktjJLRdt/KEKEknyOlc6tjXflqBKEuYBBcu7Ax5VY+S6aw==",
|
||||
"dependencies": {
|
||||
"ngraph.events": "^1.0.0",
|
||||
"ngraph.merge": "^1.0.0",
|
||||
"ngraph.random": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/ngraph.graph": {
|
||||
"version": "20.0.1",
|
||||
"resolved": "https://registry.npmjs.org/ngraph.graph/-/ngraph.graph-20.0.1.tgz",
|
||||
"integrity": "sha512-VFsQ+EMkT+7lcJO1QP8Ik3w64WbHJl27Q53EO9hiFU9CRyxJ8HfcXtfWz/U8okuoYKDctbciL6pX3vG5dt1rYA==",
|
||||
"dependencies": {
|
||||
"ngraph.events": "^1.2.1"
|
||||
}
|
||||
},
|
||||
"node_modules/ngraph.merge": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ngraph.merge/-/ngraph.merge-1.0.0.tgz",
|
||||
"integrity": "sha512-5J8YjGITUJeapsomtTALYsw7rFveYkM+lBj3QiYZ79EymQcuri65Nw3knQtFxQBU1r5iOaVRXrSwMENUPK62Vg=="
|
||||
},
|
||||
"node_modules/ngraph.random": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/ngraph.random/-/ngraph.random-1.1.0.tgz",
|
||||
"integrity": "sha512-h25UdUN/g8U7y29TzQtRm/GvGr70lK37yQPvPKXXuVfs7gCm82WipYFZcksQfeKumtOemAzBIcT7lzzyK/edLw=="
|
||||
},
|
||||
"node_modules/node-domexception": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz",
|
||||
@@ -4791,6 +5252,15 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/opener": {
|
||||
"version": "1.5.2",
|
||||
"resolved": "https://registry.npmjs.org/opener/-/opener-1.5.2.tgz",
|
||||
"integrity": "sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"opener": "bin/opener-bin.js"
|
||||
}
|
||||
},
|
||||
"node_modules/p-limit": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
|
||||
@@ -4923,6 +5393,17 @@
|
||||
"pathe": "^1.1.2"
|
||||
}
|
||||
},
|
||||
"node_modules/polished": {
|
||||
"version": "4.3.1",
|
||||
"resolved": "https://registry.npmjs.org/polished/-/polished-4.3.1.tgz",
|
||||
"integrity": "sha512-OBatVyC/N7SCW/FaDHrSd+vn0o5cS855TOmYi4OkdWUMSJCET/xip//ch8xGUvtr3i44X9LVyWwQlRMTN3pwSA==",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.17.8"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/postcss": {
|
||||
"version": "8.4.47",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.47.tgz",
|
||||
@@ -5514,8 +5995,7 @@
|
||||
"node_modules/regenerator-runtime": {
|
||||
"version": "0.14.1",
|
||||
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz",
|
||||
"integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==",
|
||||
"dev": true
|
||||
"integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw=="
|
||||
},
|
||||
"node_modules/regenerator-transform": {
|
||||
"version": "0.15.2",
|
||||
@@ -5718,6 +6198,20 @@
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/sirv": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/sirv/-/sirv-2.0.4.tgz",
|
||||
"integrity": "sha512-94Bdh3cC2PKrbgSOUqTiGPWVZeSiXfKOVZNJniWoqrWrRkB1CJzBU3NEbiTsPcYy1lDsANA/THzS+9WBiy5nfQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@polka/url": "^1.0.0-next.24",
|
||||
"mrmime": "^2.0.0",
|
||||
"totalist": "^3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
}
|
||||
},
|
||||
"node_modules/source-map": {
|
||||
"version": "0.6.1",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
|
||||
@@ -6036,6 +6530,67 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/three": {
|
||||
"version": "0.169.0",
|
||||
"resolved": "https://registry.npmjs.org/three/-/three-0.169.0.tgz",
|
||||
"integrity": "sha512-Ed906MA3dR4TS5riErd4QBsRGPcx+HBDX2O5yYE5GqJeFQTPU+M56Va/f/Oph9X7uZo3W3o4l2ZhBZ6f6qUv0w=="
|
||||
},
|
||||
"node_modules/three-forcegraph": {
|
||||
"version": "1.41.15",
|
||||
"resolved": "https://registry.npmjs.org/three-forcegraph/-/three-forcegraph-1.41.15.tgz",
|
||||
"integrity": "sha512-E1j6bKt7lWg9t/ERdEiuxYfPbAioTCd9RG2bgqyC0yM3rwkBqn5VZN3fvb7umaOuTB1Tqpq6m07iVfJSfzTnCQ==",
|
||||
"dependencies": {
|
||||
"accessor-fn": "1",
|
||||
"d3-array": "1 - 3",
|
||||
"d3-force-3d": "2 - 3",
|
||||
"d3-scale": "1 - 4",
|
||||
"d3-scale-chromatic": "1 - 3",
|
||||
"data-joint": "1",
|
||||
"kapsule": "1",
|
||||
"ngraph.forcelayout": "3",
|
||||
"ngraph.graph": "20",
|
||||
"tinycolor2": "1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"three": ">=0.118.3"
|
||||
}
|
||||
},
|
||||
"node_modules/three-render-objects": {
|
||||
"version": "1.29.5",
|
||||
"resolved": "https://registry.npmjs.org/three-render-objects/-/three-render-objects-1.29.5.tgz",
|
||||
"integrity": "sha512-OLtETrjF184NuaaI/vpRlIP9FxVNAgBBCgWYXhGFUDnPdl/2iX8rialUPGA1gEXvOTiKyepArVgm1LUkJw15rQ==",
|
||||
"dependencies": {
|
||||
"@tweenjs/tween.js": "18 - 25",
|
||||
"accessor-fn": "1",
|
||||
"kapsule": "1",
|
||||
"polished": "4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"three": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/three-spritetext": {
|
||||
"version": "1.9.0",
|
||||
"resolved": "https://registry.npmjs.org/three-spritetext/-/three-spritetext-1.9.0.tgz",
|
||||
"integrity": "sha512-+dMrxBsxTu5OviykIg5jTMry5TQ8u5yuS9zKH0mWElyldoFGdegEkIm71kDk34bxBp/NQhRLW+iom1b/GMTioA==",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"three": ">=0.86.0"
|
||||
}
|
||||
},
|
||||
"node_modules/tinycolor2": {
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.6.0.tgz",
|
||||
"integrity": "sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw=="
|
||||
},
|
||||
"node_modules/to-fast-properties": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
|
||||
@@ -6057,6 +6612,31 @@
|
||||
"node": ">=8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/tom-select": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/tom-select/-/tom-select-2.3.1.tgz",
|
||||
"integrity": "sha512-QS4vnOcB6StNGqX4sGboGXL2fkhBF2gIBB+8Hwv30FZXYPn0CyYO8kkdATRvwfCTThxiR4WcXwKJZ3cOmtI9eg==",
|
||||
"dependencies": {
|
||||
"@orchidjs/sifter": "^1.0.3",
|
||||
"@orchidjs/unicode-variants": "^1.0.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": "*"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/tom-select"
|
||||
}
|
||||
},
|
||||
"node_modules/totalist": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz",
|
||||
"integrity": "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/ts-loader": {
|
||||
"version": "9.5.1",
|
||||
"resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.5.1.tgz",
|
||||
@@ -6282,6 +6862,53 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/webpack-bundle-analyzer": {
|
||||
"version": "4.10.2",
|
||||
"resolved": "https://registry.npmjs.org/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.10.2.tgz",
|
||||
"integrity": "sha512-vJptkMm9pk5si4Bv922ZbKLV8UTT4zib4FPgXMhgzUny0bfDDkLXAVQs3ly3fS4/TN9ROFtb0NFrm04UXFE/Vw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@discoveryjs/json-ext": "0.5.7",
|
||||
"acorn": "^8.0.4",
|
||||
"acorn-walk": "^8.0.0",
|
||||
"commander": "^7.2.0",
|
||||
"debounce": "^1.2.1",
|
||||
"escape-string-regexp": "^4.0.0",
|
||||
"gzip-size": "^6.0.0",
|
||||
"html-escaper": "^2.0.2",
|
||||
"opener": "^1.5.2",
|
||||
"picocolors": "^1.0.0",
|
||||
"sirv": "^2.0.3",
|
||||
"ws": "^7.3.1"
|
||||
},
|
||||
"bin": {
|
||||
"webpack-bundle-analyzer": "lib/bin/analyzer.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 10.13.0"
|
||||
}
|
||||
},
|
||||
"node_modules/webpack-bundle-analyzer/node_modules/commander": {
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz",
|
||||
"integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
}
|
||||
},
|
||||
"node_modules/webpack-bundle-analyzer/node_modules/escape-string-regexp": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
|
||||
"integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/webpack-cli": {
|
||||
"version": "5.1.4",
|
||||
"resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-5.1.4.tgz",
|
||||
@@ -6469,6 +7096,27 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/ws": {
|
||||
"version": "7.5.10",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz",
|
||||
"integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=8.3.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"bufferutil": "^4.0.1",
|
||||
"utf-8-validate": "^5.0.2"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"bufferutil": {
|
||||
"optional": true
|
||||
},
|
||||
"utf-8-validate": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/yallist": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
|
||||
|
||||
+13
-1
@@ -7,6 +7,8 @@
|
||||
"compile": "webpack --mode production",
|
||||
"compile-dev": "webpack --mode development",
|
||||
"serve": "webpack --mode development --watch",
|
||||
"analyse-dev": "webpack --config webpack.analyze.config.js --mode development",
|
||||
"analyse-prod": "webpack --config webpack.analyze.config.js --mode production",
|
||||
"check": "biome check --write"
|
||||
},
|
||||
"keywords": [],
|
||||
@@ -23,6 +25,8 @@
|
||||
"@babel/preset-env": "^7.25.4",
|
||||
"@biomejs/biome": "1.9.3",
|
||||
"@hey-api/openapi-ts": "^0.53.8",
|
||||
"@types/alpinejs": "^3.13.10",
|
||||
"@types/jquery": "^3.5.31",
|
||||
"babel-loader": "^9.2.1",
|
||||
"css-loader": "^7.1.2",
|
||||
"css-minimizer-webpack-plugin": "^7.0.0",
|
||||
@@ -33,21 +37,29 @@
|
||||
"ts-loader": "^9.5.1",
|
||||
"typescript": "^5.6.3",
|
||||
"webpack": "^5.94.0",
|
||||
"webpack-bundle-analyzer": "^4.10.2",
|
||||
"webpack-cli": "^5.1.4"
|
||||
},
|
||||
"dependencies": {
|
||||
"@fortawesome/fontawesome-free": "^6.6.0",
|
||||
"@hey-api/client-fetch": "^0.4.0",
|
||||
"@sentry/browser": "^8.34.0",
|
||||
"@zip.js/zip.js": "^2.7.52",
|
||||
"3d-force-graph": "^1.73.4",
|
||||
"alpinejs": "^3.14.1",
|
||||
"chart.js": "^4.4.4",
|
||||
"cytoscape": "^3.30.2",
|
||||
"cytoscape-cxtmenu": "^3.5.0",
|
||||
"cytoscape-klay": "^3.1.4",
|
||||
"d3-force-3d": "^3.0.5",
|
||||
"easymde": "^2.18.0",
|
||||
"glob": "^11.0.0",
|
||||
"jquery": "^3.7.1",
|
||||
"jquery-ui": "^1.14.0",
|
||||
"jquery.shorten": "^1.0.0",
|
||||
"native-file-system-adapter": "^3.0.1"
|
||||
"native-file-system-adapter": "^3.0.1",
|
||||
"three": "^0.169.0",
|
||||
"three-spritetext": "^1.9.0",
|
||||
"tom-select": "^2.3.1"
|
||||
}
|
||||
}
|
||||
@@ -32,7 +32,10 @@ class Migration(migrations.Migration):
|
||||
unique=True,
|
||||
validators=[
|
||||
django.core.validators.RegexValidator(
|
||||
message="The code of an UV must only contains uppercase characters without accent and numbers",
|
||||
message=(
|
||||
"The code of an UV must only contains "
|
||||
"uppercase characters without accent and numbers"
|
||||
),
|
||||
regex="([A-Z0-9]+)",
|
||||
)
|
||||
],
|
||||
|
||||
+2
-1
@@ -45,7 +45,8 @@ class UV(models.Model):
|
||||
validators.RegexValidator(
|
||||
regex="([A-Z0-9]+)",
|
||||
message=_(
|
||||
"The code of an UV must only contains uppercase characters without accent and numbers"
|
||||
"The code of an UV must only contains "
|
||||
"uppercase characters without accent and numbers"
|
||||
),
|
||||
)
|
||||
],
|
||||
|
||||
@@ -27,7 +27,10 @@ class TestUVSearch(TestCase):
|
||||
semester="AUTUMN",
|
||||
department="GI",
|
||||
manager="francky",
|
||||
title="Programmation Orientée Objet: Concepts fondamentaux et mise en pratique avec le langage C++",
|
||||
title=(
|
||||
"Programmation Orientée Objet: "
|
||||
"Concepts fondamentaux et mise en pratique avec le langage C++"
|
||||
),
|
||||
),
|
||||
uv_recipe.prepare(
|
||||
code="MT01",
|
||||
@@ -118,7 +121,7 @@ class TestUVSearch(TestCase):
|
||||
("M", {"MT01", "MT10"}),
|
||||
("mt", {"MT01", "MT10"}),
|
||||
("MT", {"MT01", "MT10"}),
|
||||
("algèbre", {"MT01"}), # Title search case insensitive
|
||||
("algèbre", {"MT01"}), # Title search case insensitive
|
||||
# Manager search
|
||||
("moss", {"TNEV"}),
|
||||
("francky", {"DA50", "AP4A"}),
|
||||
|
||||
@@ -381,7 +381,9 @@ class TestUVCommentCreationAndDisplay(TestCase):
|
||||
self.assertContains(
|
||||
response,
|
||||
_(
|
||||
"You already posted a comment on this UV. If you want to comment again, please modify or delete your previous comment."
|
||||
"You already posted a comment on this UV. "
|
||||
"If you want to comment again, "
|
||||
"please modify or delete your previous comment."
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
+11
-1
@@ -23,7 +23,17 @@
|
||||
|
||||
from django.urls import path
|
||||
|
||||
from pedagogy.views import *
|
||||
from pedagogy.views import (
|
||||
UVCommentDeleteView,
|
||||
UVCommentReportCreateView,
|
||||
UVCommentUpdateView,
|
||||
UVCreateView,
|
||||
UVDeleteView,
|
||||
UVDetailFormView,
|
||||
UVGuideView,
|
||||
UVModerationFormView,
|
||||
UVUpdateView,
|
||||
)
|
||||
|
||||
urlpatterns = [
|
||||
# Urls displaying the actual application for visitors
|
||||
|
||||
+7
-13
@@ -23,7 +23,7 @@
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||
from django.core.exceptions import ObjectDoesNotExist, PermissionDenied
|
||||
from django.core.exceptions import PermissionDenied
|
||||
from django.shortcuts import get_object_or_404
|
||||
from django.urls import reverse, reverse_lazy
|
||||
from django.views.generic import (
|
||||
@@ -193,18 +193,12 @@ class UVModerationFormView(FormView):
|
||||
|
||||
def form_valid(self, form):
|
||||
form_clean = form.clean()
|
||||
for report in form_clean.get("accepted_reports", []):
|
||||
try:
|
||||
report.comment.delete() # Delete the related comment
|
||||
except ObjectDoesNotExist:
|
||||
# To avoid errors when two reports points the same comment
|
||||
pass
|
||||
for report in form_clean.get("denied_reports", []):
|
||||
try:
|
||||
report.delete() # Delete the report itself
|
||||
except ObjectDoesNotExist:
|
||||
# To avoid errors when two reports points the same comment
|
||||
pass
|
||||
accepted = form_clean.get("accepted_reports", [])
|
||||
if len(accepted) > 0: # delete the reported comments
|
||||
UVComment.objects.filter(reports__in=accepted).delete()
|
||||
denied = form_clean.get("denied_reports", [])
|
||||
if len(denied) > 0: # delete the comments themselves
|
||||
UVCommentReport.objects.filter(id__in={d.id for d in denied}).delete()
|
||||
return super().form_valid(form)
|
||||
|
||||
def get_success_url(self):
|
||||
|
||||
Generated
+148
-136
@@ -343,73 +343,73 @@ files = [
|
||||
|
||||
[[package]]
|
||||
name = "coverage"
|
||||
version = "7.6.2"
|
||||
version = "7.6.3"
|
||||
description = "Code coverage measurement for Python"
|
||||
optional = false
|
||||
python-versions = ">=3.9"
|
||||
files = [
|
||||
{file = "coverage-7.6.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c9df1950fb92d49970cce38100d7e7293c84ed3606eaa16ea0b6bc27175bb667"},
|
||||
{file = "coverage-7.6.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:24500f4b0e03aab60ce575c85365beab64b44d4db837021e08339f61d1fbfe52"},
|
||||
{file = "coverage-7.6.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a663b180b6669c400b4630a24cc776f23a992d38ce7ae72ede2a397ce6b0f170"},
|
||||
{file = "coverage-7.6.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bfde025e2793a22efe8c21f807d276bd1d6a4bcc5ba6f19dbdfc4e7a12160909"},
|
||||
{file = "coverage-7.6.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:087932079c065d7b8ebadd3a0160656c55954144af6439886c8bcf78bbbcde7f"},
|
||||
{file = "coverage-7.6.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:9c6b0c1cafd96213a0327cf680acb39f70e452caf8e9a25aeb05316db9c07f89"},
|
||||
{file = "coverage-7.6.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:6e85830eed5b5263ffa0c62428e43cb844296f3b4461f09e4bdb0d44ec190bc2"},
|
||||
{file = "coverage-7.6.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:62ab4231c01e156ece1b3a187c87173f31cbeee83a5e1f6dff17f288dca93345"},
|
||||
{file = "coverage-7.6.2-cp310-cp310-win32.whl", hash = "sha256:7b80fbb0da3aebde102a37ef0138aeedff45997e22f8962e5f16ae1742852676"},
|
||||
{file = "coverage-7.6.2-cp310-cp310-win_amd64.whl", hash = "sha256:d20c3d1f31f14d6962a4e2f549c21d31e670b90f777ef4171be540fb7fb70f02"},
|
||||
{file = "coverage-7.6.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:bb21bac7783c1bf6f4bbe68b1e0ff0d20e7e7732cfb7995bc8d96e23aa90fc7b"},
|
||||
{file = "coverage-7.6.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a7b2e437fbd8fae5bc7716b9c7ff97aecc95f0b4d56e4ca08b3c8d8adcaadb84"},
|
||||
{file = "coverage-7.6.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:536f77f2bf5797983652d1d55f1a7272a29afcc89e3ae51caa99b2db4e89d658"},
|
||||
{file = "coverage-7.6.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f361296ca7054f0936b02525646b2731b32c8074ba6defab524b79b2b7eeac72"},
|
||||
{file = "coverage-7.6.2-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7926d8d034e06b479797c199747dd774d5e86179f2ce44294423327a88d66ca7"},
|
||||
{file = "coverage-7.6.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0bbae11c138585c89fb4e991faefb174a80112e1a7557d507aaa07675c62e66b"},
|
||||
{file = "coverage-7.6.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:fcad7d5d2bbfeae1026b395036a8aa5abf67e8038ae7e6a25c7d0f88b10a8e6a"},
|
||||
{file = "coverage-7.6.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f01e53575f27097d75d42de33b1b289c74b16891ce576d767ad8c48d17aeb5e0"},
|
||||
{file = "coverage-7.6.2-cp311-cp311-win32.whl", hash = "sha256:7781f4f70c9b0b39e1b129b10c7d43a4e0c91f90c60435e6da8288efc2b73438"},
|
||||
{file = "coverage-7.6.2-cp311-cp311-win_amd64.whl", hash = "sha256:9bcd51eeca35a80e76dc5794a9dd7cb04b97f0e8af620d54711793bfc1fbba4b"},
|
||||
{file = "coverage-7.6.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:ebc94fadbd4a3f4215993326a6a00e47d79889391f5659bf310f55fe5d9f581c"},
|
||||
{file = "coverage-7.6.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9681516288e3dcf0aa7c26231178cc0be6cac9705cac06709f2353c5b406cfea"},
|
||||
{file = "coverage-7.6.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8d9c5d13927d77af4fbe453953810db766f75401e764727e73a6ee4f82527b3e"},
|
||||
{file = "coverage-7.6.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b92f9ca04b3e719d69b02dc4a69debb795af84cb7afd09c5eb5d54b4a1ae2191"},
|
||||
{file = "coverage-7.6.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0ff2ef83d6d0b527b5c9dad73819b24a2f76fdddcfd6c4e7a4d7e73ecb0656b4"},
|
||||
{file = "coverage-7.6.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:47ccb6e99a3031ffbbd6e7cc041e70770b4fe405370c66a54dbf26a500ded80b"},
|
||||
{file = "coverage-7.6.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a867d26f06bcd047ef716175b2696b315cb7571ccb951006d61ca80bbc356e9e"},
|
||||
{file = "coverage-7.6.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:cdfcf2e914e2ba653101157458afd0ad92a16731eeba9a611b5cbb3e7124e74b"},
|
||||
{file = "coverage-7.6.2-cp312-cp312-win32.whl", hash = "sha256:f9035695dadfb397bee9eeaf1dc7fbeda483bf7664a7397a629846800ce6e276"},
|
||||
{file = "coverage-7.6.2-cp312-cp312-win_amd64.whl", hash = "sha256:5ed69befa9a9fc796fe015a7040c9398722d6b97df73a6b608e9e275fa0932b0"},
|
||||
{file = "coverage-7.6.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:4eea60c79d36a8f39475b1af887663bc3ae4f31289cd216f514ce18d5938df40"},
|
||||
{file = "coverage-7.6.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:aa68a6cdbe1bc6793a9dbfc38302c11599bbe1837392ae9b1d238b9ef3dafcf1"},
|
||||
{file = "coverage-7.6.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3ec528ae69f0a139690fad6deac8a7d33629fa61ccce693fdd07ddf7e9931fba"},
|
||||
{file = "coverage-7.6.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ed5ac02126f74d190fa2cc14a9eb2a5d9837d5863920fa472b02eb1595cdc925"},
|
||||
{file = "coverage-7.6.2-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21c0ea0d4db8a36b275cb6fb2437a3715697a4ba3cb7b918d3525cc75f726304"},
|
||||
{file = "coverage-7.6.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:35a51598f29b2a19e26d0908bd196f771a9b1c5d9a07bf20be0adf28f1ad4f77"},
|
||||
{file = "coverage-7.6.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:c9192925acc33e146864b8cf037e2ed32a91fdf7644ae875f5d46cd2ef086a5f"},
|
||||
{file = "coverage-7.6.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:bf4eeecc9e10f5403ec06138978235af79c9a79af494eb6b1d60a50b49ed2869"},
|
||||
{file = "coverage-7.6.2-cp313-cp313-win32.whl", hash = "sha256:e4ee15b267d2dad3e8759ca441ad450c334f3733304c55210c2a44516e8d5530"},
|
||||
{file = "coverage-7.6.2-cp313-cp313-win_amd64.whl", hash = "sha256:c71965d1ced48bf97aab79fad56df82c566b4c498ffc09c2094605727c4b7e36"},
|
||||
{file = "coverage-7.6.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:7571e8bbecc6ac066256f9de40365ff833553e2e0c0c004f4482facb131820ef"},
|
||||
{file = "coverage-7.6.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:078a87519057dacb5d77e333f740708ec2a8f768655f1db07f8dfd28d7a005f0"},
|
||||
{file = "coverage-7.6.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1e5e92e3e84a8718d2de36cd8387459cba9a4508337b8c5f450ce42b87a9e760"},
|
||||
{file = "coverage-7.6.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ebabdf1c76593a09ee18c1a06cd3022919861365219ea3aca0247ededf6facd6"},
|
||||
{file = "coverage-7.6.2-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:12179eb0575b8900912711688e45474f04ab3934aaa7b624dea7b3c511ecc90f"},
|
||||
{file = "coverage-7.6.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:39d3b964abfe1519b9d313ab28abf1d02faea26cd14b27f5283849bf59479ff5"},
|
||||
{file = "coverage-7.6.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:84c4315577f7cd511d6250ffd0f695c825efe729f4205c0340f7004eda51191f"},
|
||||
{file = "coverage-7.6.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:ff797320dcbff57caa6b2301c3913784a010e13b1f6cf4ab3f563f3c5e7919db"},
|
||||
{file = "coverage-7.6.2-cp313-cp313t-win32.whl", hash = "sha256:2b636a301e53964550e2f3094484fa5a96e699db318d65398cfba438c5c92171"},
|
||||
{file = "coverage-7.6.2-cp313-cp313t-win_amd64.whl", hash = "sha256:d03a060ac1a08e10589c27d509bbdb35b65f2d7f3f8d81cf2fa199877c7bc58a"},
|
||||
{file = "coverage-7.6.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c37faddc8acd826cfc5e2392531aba734b229741d3daec7f4c777a8f0d4993e5"},
|
||||
{file = "coverage-7.6.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ab31fdd643f162c467cfe6a86e9cb5f1965b632e5e65c072d90854ff486d02cf"},
|
||||
{file = "coverage-7.6.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:97df87e1a20deb75ac7d920c812e9326096aa00a9a4b6d07679b4f1f14b06c90"},
|
||||
{file = "coverage-7.6.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:343056c5e0737487a5291f5691f4dfeb25b3e3c8699b4d36b92bb0e586219d14"},
|
||||
{file = "coverage-7.6.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ad4ef1c56b47b6b9024b939d503ab487231df1f722065a48f4fc61832130b90e"},
|
||||
{file = "coverage-7.6.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:7fca4a92c8a7a73dee6946471bce6d1443d94155694b893b79e19ca2a540d86e"},
|
||||
{file = "coverage-7.6.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:69f251804e052fc46d29d0e7348cdc5fcbfc4861dc4a1ebedef7e78d241ad39e"},
|
||||
{file = "coverage-7.6.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:e8ea055b3ea046c0f66217af65bc193bbbeca1c8661dc5fd42698db5795d2627"},
|
||||
{file = "coverage-7.6.2-cp39-cp39-win32.whl", hash = "sha256:6c2ba1e0c24d8fae8f2cf0aeb2fc0a2a7f69b6d20bd8d3749fd6b36ecef5edf0"},
|
||||
{file = "coverage-7.6.2-cp39-cp39-win_amd64.whl", hash = "sha256:2186369a654a15628e9c1c9921409a6b3eda833e4b91f3ca2a7d9f77abb4987c"},
|
||||
{file = "coverage-7.6.2-pp39.pp310-none-any.whl", hash = "sha256:667952739daafe9616db19fbedbdb87917eee253ac4f31d70c7587f7ab531b4e"},
|
||||
{file = "coverage-7.6.2.tar.gz", hash = "sha256:a5f81e68aa62bc0cfca04f7b19eaa8f9c826b53fc82ab9e2121976dc74f131f3"},
|
||||
{file = "coverage-7.6.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6da42bbcec130b188169107ecb6ee7bd7b4c849d24c9370a0c884cf728d8e976"},
|
||||
{file = "coverage-7.6.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c222958f59b0ae091f4535851cbb24eb57fc0baea07ba675af718fb5302dddb2"},
|
||||
{file = "coverage-7.6.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ab84a8b698ad5a6c365b08061920138e7a7dd9a04b6feb09ba1bfae68346ce6d"},
|
||||
{file = "coverage-7.6.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:70a6756ce66cd6fe8486c775b30889f0dc4cb20c157aa8c35b45fd7868255c5c"},
|
||||
{file = "coverage-7.6.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3c2e6fa98032fec8282f6b27e3f3986c6e05702828380618776ad794e938f53a"},
|
||||
{file = "coverage-7.6.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:921fbe13492caf6a69528f09d5d7c7d518c8d0e7b9f6701b7719715f29a71e6e"},
|
||||
{file = "coverage-7.6.3-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:6d99198203f0b9cb0b5d1c0393859555bc26b548223a769baf7e321a627ed4fc"},
|
||||
{file = "coverage-7.6.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:87cd2e29067ea397a47e352efb13f976eb1b03e18c999270bb50589323294c6e"},
|
||||
{file = "coverage-7.6.3-cp310-cp310-win32.whl", hash = "sha256:a3328c3e64ea4ab12b85999eb0779e6139295bbf5485f69d42cf794309e3d007"},
|
||||
{file = "coverage-7.6.3-cp310-cp310-win_amd64.whl", hash = "sha256:bca4c8abc50d38f9773c1ec80d43f3768df2e8576807d1656016b9d3eeaa96fd"},
|
||||
{file = "coverage-7.6.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c51ef82302386d686feea1c44dbeef744585da16fcf97deea2a8d6c1556f519b"},
|
||||
{file = "coverage-7.6.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0ca37993206402c6c35dc717f90d4c8f53568a8b80f0bf1a1b2b334f4d488fba"},
|
||||
{file = "coverage-7.6.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c77326300b839c44c3e5a8fe26c15b7e87b2f32dfd2fc9fee1d13604347c9b38"},
|
||||
{file = "coverage-7.6.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6e484e479860e00da1f005cd19d1c5d4a813324e5951319ac3f3eefb497cc549"},
|
||||
{file = "coverage-7.6.3-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c6c0f4d53ef603397fc894a895b960ecd7d44c727df42a8d500031716d4e8d2"},
|
||||
{file = "coverage-7.6.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:37be7b5ea3ff5b7c4a9db16074dc94523b5f10dd1f3b362a827af66a55198175"},
|
||||
{file = "coverage-7.6.3-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:43b32a06c47539fe275106b376658638b418c7cfdfff0e0259fbf877e845f14b"},
|
||||
{file = "coverage-7.6.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ee77c7bef0724165e795b6b7bf9c4c22a9b8468a6bdb9c6b4281293c6b22a90f"},
|
||||
{file = "coverage-7.6.3-cp311-cp311-win32.whl", hash = "sha256:43517e1f6b19f610a93d8227e47790722c8bf7422e46b365e0469fc3d3563d97"},
|
||||
{file = "coverage-7.6.3-cp311-cp311-win_amd64.whl", hash = "sha256:04f2189716e85ec9192df307f7c255f90e78b6e9863a03223c3b998d24a3c6c6"},
|
||||
{file = "coverage-7.6.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:27bd5f18d8f2879e45724b0ce74f61811639a846ff0e5c0395b7818fae87aec6"},
|
||||
{file = "coverage-7.6.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d546cfa78844b8b9c1c0533de1851569a13f87449897bbc95d698d1d3cb2a30f"},
|
||||
{file = "coverage-7.6.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9975442f2e7a5cfcf87299c26b5a45266ab0696348420049b9b94b2ad3d40234"},
|
||||
{file = "coverage-7.6.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:583049c63106c0555e3ae3931edab5669668bbef84c15861421b94e121878d3f"},
|
||||
{file = "coverage-7.6.3-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2341a78ae3a5ed454d524206a3fcb3cec408c2a0c7c2752cd78b606a2ff15af4"},
|
||||
{file = "coverage-7.6.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a4fb91d5f72b7e06a14ff4ae5be625a81cd7e5f869d7a54578fc271d08d58ae3"},
|
||||
{file = "coverage-7.6.3-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:e279f3db904e3b55f520f11f983cc8dc8a4ce9b65f11692d4718ed021ec58b83"},
|
||||
{file = "coverage-7.6.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:aa23ce39661a3e90eea5f99ec59b763b7d655c2cada10729ed920a38bfc2b167"},
|
||||
{file = "coverage-7.6.3-cp312-cp312-win32.whl", hash = "sha256:52ac29cc72ee7e25ace7807249638f94c9b6a862c56b1df015d2b2e388e51dbd"},
|
||||
{file = "coverage-7.6.3-cp312-cp312-win_amd64.whl", hash = "sha256:40e8b1983080439d4802d80b951f4a93d991ef3261f69e81095a66f86cf3c3c6"},
|
||||
{file = "coverage-7.6.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:9134032f5aa445ae591c2ba6991d10136a1f533b1d2fa8f8c21126468c5025c6"},
|
||||
{file = "coverage-7.6.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:99670790f21a96665a35849990b1df447993880bb6463a0a1d757897f30da929"},
|
||||
{file = "coverage-7.6.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2dc7d6b380ca76f5e817ac9eef0c3686e7834c8346bef30b041a4ad286449990"},
|
||||
{file = "coverage-7.6.3-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f7b26757b22faf88fcf232f5f0e62f6e0fd9e22a8a5d0d5016888cdfe1f6c1c4"},
|
||||
{file = "coverage-7.6.3-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4c59d6a4a4633fad297f943c03d0d2569867bd5372eb5684befdff8df8522e39"},
|
||||
{file = "coverage-7.6.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f263b18692f8ed52c8de7f40a0751e79015983dbd77b16906e5b310a39d3ca21"},
|
||||
{file = "coverage-7.6.3-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:79644f68a6ff23b251cae1c82b01a0b51bc40c8468ca9585c6c4b1aeee570e0b"},
|
||||
{file = "coverage-7.6.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:71967c35828c9ff94e8c7d405469a1fb68257f686bca7c1ed85ed34e7c2529c4"},
|
||||
{file = "coverage-7.6.3-cp313-cp313-win32.whl", hash = "sha256:e266af4da2c1a4cbc6135a570c64577fd3e6eb204607eaff99d8e9b710003c6f"},
|
||||
{file = "coverage-7.6.3-cp313-cp313-win_amd64.whl", hash = "sha256:ea52bd218d4ba260399a8ae4bb6b577d82adfc4518b93566ce1fddd4a49d1dce"},
|
||||
{file = "coverage-7.6.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:8d4c6ea0f498c7c79111033a290d060c517853a7bcb2f46516f591dab628ddd3"},
|
||||
{file = "coverage-7.6.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:331b200ad03dbaa44151d74daeb7da2cf382db424ab923574f6ecca7d3b30de3"},
|
||||
{file = "coverage-7.6.3-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:54356a76b67cf8a3085818026bb556545ebb8353951923b88292556dfa9f812d"},
|
||||
{file = "coverage-7.6.3-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ebec65f5068e7df2d49466aab9128510c4867e532e07cb6960075b27658dca38"},
|
||||
{file = "coverage-7.6.3-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d33a785ea8354c480515e781554d3be582a86297e41ccbea627a5c632647f2cd"},
|
||||
{file = "coverage-7.6.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:f7ddb920106bbbbcaf2a274d56f46956bf56ecbde210d88061824a95bdd94e92"},
|
||||
{file = "coverage-7.6.3-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:70d24936ca6c15a3bbc91ee9c7fc661132c6f4c9d42a23b31b6686c05073bde5"},
|
||||
{file = "coverage-7.6.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:c30e42ea11badb147f0d2e387115b15e2bd8205a5ad70d6ad79cf37f6ac08c91"},
|
||||
{file = "coverage-7.6.3-cp313-cp313t-win32.whl", hash = "sha256:365defc257c687ce3e7d275f39738dcd230777424117a6c76043459db131dd43"},
|
||||
{file = "coverage-7.6.3-cp313-cp313t-win_amd64.whl", hash = "sha256:23bb63ae3f4c645d2d82fa22697364b0046fbafb6261b258a58587441c5f7bd0"},
|
||||
{file = "coverage-7.6.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:da29ceabe3025a1e5a5aeeb331c5b1af686daab4ff0fb4f83df18b1180ea83e2"},
|
||||
{file = "coverage-7.6.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:df8c05a0f574d480947cba11b947dc41b1265d721c3777881da2fb8d3a1ddfba"},
|
||||
{file = "coverage-7.6.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec1e3b40b82236d100d259854840555469fad4db64f669ab817279eb95cd535c"},
|
||||
{file = "coverage-7.6.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b4adeb878a374126f1e5cf03b87f66279f479e01af0e9a654cf6d1509af46c40"},
|
||||
{file = "coverage-7.6.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:43d6a66e33b1455b98fc7312b124296dad97a2e191c80320587234a77b1b736e"},
|
||||
{file = "coverage-7.6.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:1990b1f4e2c402beb317840030bb9f1b6a363f86e14e21b4212e618acdfce7f6"},
|
||||
{file = "coverage-7.6.3-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:12f9515d875859faedb4144fd38694a761cd2a61ef9603bf887b13956d0bbfbb"},
|
||||
{file = "coverage-7.6.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:99ded130555c021d99729fabd4ddb91a6f4cc0707df4b1daf912c7850c373b13"},
|
||||
{file = "coverage-7.6.3-cp39-cp39-win32.whl", hash = "sha256:c3a79f56dee9136084cf84a6c7c4341427ef36e05ae6415bf7d787c96ff5eaa3"},
|
||||
{file = "coverage-7.6.3-cp39-cp39-win_amd64.whl", hash = "sha256:aac7501ae73d4a02f4b7ac8fcb9dc55342ca98ffb9ed9f2dfb8a25d53eda0e4d"},
|
||||
{file = "coverage-7.6.3-pp39.pp310-none-any.whl", hash = "sha256:b9853509b4bf57ba7b1f99b9d866c422c9c5248799ab20e652bbb8a184a38181"},
|
||||
{file = "coverage-7.6.3.tar.gz", hash = "sha256:bb7d5fe92bd0dc235f63ebe9f8c6e0884f7360f88f3411bfed1350c872ef2054"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
@@ -491,13 +491,13 @@ tests = ["noseofyeti[black] (==2.4.9)", "pytest (==8.3.2)"]
|
||||
|
||||
[[package]]
|
||||
name = "distlib"
|
||||
version = "0.3.8"
|
||||
version = "0.3.9"
|
||||
description = "Distribution utilities"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
files = [
|
||||
{file = "distlib-0.3.8-py2.py3-none-any.whl", hash = "sha256:034db59a0b96f8ca18035f36290806a9a6e6bd9d1ff91e45a7f172eb17e51784"},
|
||||
{file = "distlib-0.3.8.tar.gz", hash = "sha256:1530ea13e350031b6312d8580ddb6b27a104275a31106523b8f123787f494f64"},
|
||||
{file = "distlib-0.3.9-py2.py3-none-any.whl", hash = "sha256:47f8c22fd27c27e25a65601af709b38e4f0a45ea4fc2e710f65755fa8caaaf87"},
|
||||
{file = "distlib-0.3.9.tar.gz", hash = "sha256:a60f20dea646b8a33f3e7772f74dc0b2d0772d2837ee1342a00645c81edf9403"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -814,13 +814,13 @@ dev = ["flake8", "markdown", "twine", "wheel"]
|
||||
|
||||
[[package]]
|
||||
name = "griffe"
|
||||
version = "1.3.2"
|
||||
version = "1.4.1"
|
||||
description = "Signatures for entire Python programs. Extract the structure, the frame, the skeleton of your project, to generate API documentation or find breaking changes in your API."
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
python-versions = ">=3.9"
|
||||
files = [
|
||||
{file = "griffe-1.3.2-py3-none-any.whl", hash = "sha256:2e34b5e46507d615915c8e6288bb1a2234bd35dee44d01e40a2bc2f25bd4d10c"},
|
||||
{file = "griffe-1.3.2.tar.gz", hash = "sha256:1ec50335aa507ed2445f2dd45a15c9fa3a45f52c9527e880571dfc61912fd60c"},
|
||||
{file = "griffe-1.4.1-py3-none-any.whl", hash = "sha256:84295ee0b27743bd880aea75632830ef02ded65d16124025e4c263bb826ab645"},
|
||||
{file = "griffe-1.4.1.tar.gz", hash = "sha256:911a201b01dc92e08c0e84c38a301e9da5ec067f00e7d9f2e39bc24dbfa3c176"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
@@ -1283,13 +1283,13 @@ cache = ["platformdirs"]
|
||||
|
||||
[[package]]
|
||||
name = "mkdocs-material"
|
||||
version = "9.5.39"
|
||||
version = "9.5.40"
|
||||
description = "Documentation that simply works"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "mkdocs_material-9.5.39-py3-none-any.whl", hash = "sha256:0f2f68c8db89523cb4a59705cd01b4acd62b2f71218ccb67e1e004e560410d2b"},
|
||||
{file = "mkdocs_material-9.5.39.tar.gz", hash = "sha256:25faa06142afa38549d2b781d475a86fb61de93189f532b88e69bf11e5e5c3be"},
|
||||
{file = "mkdocs_material-9.5.40-py3-none-any.whl", hash = "sha256:8e7a16ada34e79a7b6459ff2602584222f522c738b6a023d1bea853d5049da6f"},
|
||||
{file = "mkdocs_material-9.5.40.tar.gz", hash = "sha256:b69d70e667ec51fc41f65e006a3184dd00d95b2439d982cb1586e4c018943156"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
@@ -1323,13 +1323,13 @@ files = [
|
||||
|
||||
[[package]]
|
||||
name = "mkdocstrings"
|
||||
version = "0.26.1"
|
||||
version = "0.26.2"
|
||||
description = "Automatic documentation from sources, for MkDocs."
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
python-versions = ">=3.9"
|
||||
files = [
|
||||
{file = "mkdocstrings-0.26.1-py3-none-any.whl", hash = "sha256:29738bfb72b4608e8e55cc50fb8a54f325dc7ebd2014e4e3881a49892d5983cf"},
|
||||
{file = "mkdocstrings-0.26.1.tar.gz", hash = "sha256:bb8b8854d6713d5348ad05b069a09f3b79edbc6a0f33a34c6821141adb03fe33"},
|
||||
{file = "mkdocstrings-0.26.2-py3-none-any.whl", hash = "sha256:1248f3228464f3b8d1a15bd91249ce1701fe3104ac517a5f167a0e01ca850ba5"},
|
||||
{file = "mkdocstrings-0.26.2.tar.gz", hash = "sha256:34a8b50f1e6cfd29546c6c09fbe02154adfb0b361bb758834bf56aa284ba876e"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
@@ -1349,13 +1349,13 @@ python-legacy = ["mkdocstrings-python-legacy (>=0.2.1)"]
|
||||
|
||||
[[package]]
|
||||
name = "mkdocstrings-python"
|
||||
version = "1.11.1"
|
||||
version = "1.12.1"
|
||||
description = "A Python handler for mkdocstrings."
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
python-versions = ">=3.9"
|
||||
files = [
|
||||
{file = "mkdocstrings_python-1.11.1-py3-none-any.whl", hash = "sha256:a21a1c05acef129a618517bb5aae3e33114f569b11588b1e7af3e9d4061a71af"},
|
||||
{file = "mkdocstrings_python-1.11.1.tar.gz", hash = "sha256:8824b115c5359304ab0b5378a91f6202324a849e1da907a3485b59208b797322"},
|
||||
{file = "mkdocstrings_python-1.12.1-py3-none-any.whl", hash = "sha256:205244488199c9aa2a39787ad6a0c862d39b74078ea9aa2be817bc972399563f"},
|
||||
{file = "mkdocstrings_python-1.12.1.tar.gz", hash = "sha256:60d6a5ca912c9af4ad431db6d0111ce9f79c6c48d33377dde6a05a8f5f48d792"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
@@ -1365,13 +1365,13 @@ mkdocstrings = ">=0.26"
|
||||
|
||||
[[package]]
|
||||
name = "model-bakery"
|
||||
version = "1.19.5"
|
||||
version = "1.20.0"
|
||||
description = "Smart object creation facility for Django."
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "model_bakery-1.19.5-py3-none-any.whl", hash = "sha256:09ecbbf124d32614339581b642c82ac4a73147442f598c7bad23eece24187e5c"},
|
||||
{file = "model_bakery-1.19.5.tar.gz", hash = "sha256:37cece544a33f8899ed8f0488cd6a9d2b0b6925e7b478a4ff2786dece8c63745"},
|
||||
{file = "model_bakery-1.20.0-py3-none-any.whl", hash = "sha256:875326466f5982ee8f0281abdfa774d78893d5473562575dfd5a9304ac7c5b8c"},
|
||||
{file = "model_bakery-1.20.0.tar.gz", hash = "sha256:ec9dc846b9a00b20f92df38fac310263323ab61b59b6eeebf77a4aefb0412724"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
@@ -1599,13 +1599,13 @@ testing = ["pytest", "pytest-benchmark"]
|
||||
|
||||
[[package]]
|
||||
name = "pre-commit"
|
||||
version = "3.8.0"
|
||||
version = "4.0.1"
|
||||
description = "A framework for managing and maintaining multi-language pre-commit hooks."
|
||||
optional = false
|
||||
python-versions = ">=3.9"
|
||||
files = [
|
||||
{file = "pre_commit-3.8.0-py2.py3-none-any.whl", hash = "sha256:9a90a53bf82fdd8778d58085faf8d83df56e40dfe18f45b19446e26bf1b3a63f"},
|
||||
{file = "pre_commit-3.8.0.tar.gz", hash = "sha256:8bb6494d4a20423842e198980c9ecf9f96607a07ea29549e180eef9ae80fe7af"},
|
||||
{file = "pre_commit-4.0.1-py2.py3-none-any.whl", hash = "sha256:efde913840816312445dc98787724647c65473daefe420785f885e8ed9a06878"},
|
||||
{file = "pre_commit-4.0.1.tar.gz", hash = "sha256:80905ac375958c0444c65e9cebebd948b3cdb518f335a091a670a89d652139d2"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
@@ -2192,58 +2192,70 @@ use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"]
|
||||
|
||||
[[package]]
|
||||
name = "rjsmin"
|
||||
version = "1.2.2"
|
||||
version = "1.2.3"
|
||||
description = "Javascript Minifier"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
files = [
|
||||
{file = "rjsmin-1.2.2-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:4420107304ba7a00b5b9b56cdcd166b9876b34e626829fc4552c85d8fdc3737a"},
|
||||
{file = "rjsmin-1.2.2-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:155a2f3312c1f8c6cec7b5080581cafc761dc0e41d64bfb5d46a772c5230ded8"},
|
||||
{file = "rjsmin-1.2.2-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:88fcb58d65f88cbfa752d51c1ebe5845553f9706def6d9671e98283411575e3e"},
|
||||
{file = "rjsmin-1.2.2-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:6eae13608b88f4ce32e0557c8fdef58e69bb4d293182202a03e800f0d33b5268"},
|
||||
{file = "rjsmin-1.2.2-cp310-cp310-manylinux1_i686.whl", hash = "sha256:81f92fb855fb613ebd04a6d6d46483e71fe3c4f22042dc30dcc938fbd748e59c"},
|
||||
{file = "rjsmin-1.2.2-cp310-cp310-manylinux1_x86_64.whl", hash = "sha256:897db9bf25538047e9388951d532dc291a629b5d041180a8a1a8c102e9d44b90"},
|
||||
{file = "rjsmin-1.2.2-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:5938af8c46734f92f74fdc4d0b6324137c0e09f0a8c3825c83e4cfca1b532e40"},
|
||||
{file = "rjsmin-1.2.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:0424a7b9096fa2b0ab577c4dc7acd683e6cfb5c718ad39a9fb293cb6cbaba95b"},
|
||||
{file = "rjsmin-1.2.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:1714ed93c2bd40c5f970905d2eeda4a6844e09087ae11277d4d43b3e68c32a47"},
|
||||
{file = "rjsmin-1.2.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:35596fa6d2d44a5471715c464657123995da78aa6f79bccfbb4b8d6ff7d0a4b4"},
|
||||
{file = "rjsmin-1.2.2-cp311-cp311-manylinux1_i686.whl", hash = "sha256:3968667158948355b9a62e9641497aac7ac069c076a595e93199d0fe3a40217a"},
|
||||
{file = "rjsmin-1.2.2-cp311-cp311-manylinux1_x86_64.whl", hash = "sha256:d07d14354694f6a47f572f2aa2a1ad74b76723e62a0d2b6df796138b71888247"},
|
||||
{file = "rjsmin-1.2.2-cp311-cp311-manylinux2014_aarch64.whl", hash = "sha256:a78dfa6009235b902454ac53264252b7b94f1e43e3a9e97c4cadae88e409b882"},
|
||||
{file = "rjsmin-1.2.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:9b7a45001e58243a455d11d2de925cadb8c2a0dc737001de646a0f4d90cf0034"},
|
||||
{file = "rjsmin-1.2.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:86c5e657b74b6c9482bb96f18a79d61750f4e8204759cce179f7eb17d395c683"},
|
||||
{file = "rjsmin-1.2.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:8c2c30b86c7232443a4a726e1bbee34f800556e581e95fc07194ecbf8e02d1d2"},
|
||||
{file = "rjsmin-1.2.2-cp312-cp312-manylinux1_i686.whl", hash = "sha256:8982c3ef27fac26dd6b7d0c55ae98fa550fee72da2db010b87211e4b5dd78a67"},
|
||||
{file = "rjsmin-1.2.2-cp312-cp312-manylinux1_x86_64.whl", hash = "sha256:3fc27ae4ece99e2c994cd79df2f0d3f7ac650249f632d19aa8ce85118e33bf0f"},
|
||||
{file = "rjsmin-1.2.2-cp312-cp312-manylinux2014_aarch64.whl", hash = "sha256:41113d8d6cae7f7406b30143cc49cc045bbb3fadc2f28df398cea30e1daa60b1"},
|
||||
{file = "rjsmin-1.2.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:3aa09a89b2b7aa2b9251329fe0c3e36c2dc2f10f78b8811e5be92a072596348b"},
|
||||
{file = "rjsmin-1.2.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:5abb8d1241f4ea97950b872fa97a422ba8413fe02358f64128ff0cf745017f07"},
|
||||
{file = "rjsmin-1.2.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:5abc686a9ef7eaf208f9ad1fb5fb949556ecb7cc1fee27290eb7f194e01d97bd"},
|
||||
{file = "rjsmin-1.2.2-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:076adcf04c34f712c9427fd9ba6a75bbf7aab975650dfc78cbdd0fbdbe49ca63"},
|
||||
{file = "rjsmin-1.2.2-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:8cb8947ddd250fce58261b0357846cd5d55419419c0f7dfb131dc4b733579a26"},
|
||||
{file = "rjsmin-1.2.2-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:9069c48b6508b9c5b05435e2c6042c2a0e2f97b35d7b9c27ceaea5fd377ffdc5"},
|
||||
{file = "rjsmin-1.2.2-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:02b61cf9b6bc518fdac667f3ca3dab051cb8bd1bf4cba28b6d29153ec27990ad"},
|
||||
{file = "rjsmin-1.2.2-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:09eca8581797244587916e5e07e36c4c86d54a4b7e5c7697484a95b75803515d"},
|
||||
{file = "rjsmin-1.2.2-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:c52b9dd45c837f1c5c2e8d40776f9e63257f8dbd5f79b85f648cc70da6c1e4e9"},
|
||||
{file = "rjsmin-1.2.2-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:4fe4ce990412c053a6bcd47d55133927e22fd3d100233d73355f60f9053054c5"},
|
||||
{file = "rjsmin-1.2.2-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:aa883b9363b5134239066060879d5eb422a0d4ccf24ccf871f65a5b34c64926f"},
|
||||
{file = "rjsmin-1.2.2-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:6f4e95c5ac95b4cbb519917b3aa1d3d92fc6939c371637674c4a42b67b2b3f44"},
|
||||
{file = "rjsmin-1.2.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:ae3cd64e18e62aa330b24dd6f7b9809ce0a694afd1f01fe99c21f9acd1cb0ea6"},
|
||||
{file = "rjsmin-1.2.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:7999d797fcf805844d2d91598651785497249f592f31674da0964e794b3be019"},
|
||||
{file = "rjsmin-1.2.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:e733fea039a7b5ad7c06cc8bf215ee7afac81d462e273b3ab55c1ccc906cf127"},
|
||||
{file = "rjsmin-1.2.2-cp38-cp38-manylinux1_i686.whl", hash = "sha256:ccca74461bd53a99ff3304fcf299ea861df89846be3207329cb82d717ce47ea6"},
|
||||
{file = "rjsmin-1.2.2-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:88f59ad24f91bf9c25d5c2ca3c84a72eed0028f57a98e3b85a915ece5c25be1e"},
|
||||
{file = "rjsmin-1.2.2-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:7a8b56fbd64adcc4402637f0e07b90b441e9981d720a10eb6265118018b42682"},
|
||||
{file = "rjsmin-1.2.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:2c24686cfdf86e55692183f7867e72c9e982add479c244eda7b8390f96db2c6c"},
|
||||
{file = "rjsmin-1.2.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:6c0d9f9ea8d9cd48cbcdc74a1c2e85d4d588af12bb8f0b672070ae7c9b6e6306"},
|
||||
{file = "rjsmin-1.2.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:27abd32c9f5b6e0c0a3bcad43e8e24108c6d6c13a4e6c50c97497ea2b4614bb4"},
|
||||
{file = "rjsmin-1.2.2-cp39-cp39-manylinux1_i686.whl", hash = "sha256:e0e009f6f8460901f5144b34ac2948f94af2f9b8c9b5425da705dbc8152c36c2"},
|
||||
{file = "rjsmin-1.2.2-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:41e6013cb37a5b3563c19aa35f8e659fa536aa4197a0e3b6a57a381638294a15"},
|
||||
{file = "rjsmin-1.2.2-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:62cbd38c9f5090f0a6378a45c415b4f96ae871216cedab0dfa21965620c0be4c"},
|
||||
{file = "rjsmin-1.2.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:2fd5254d36f10a17564b63e8bf9ac579c7b5f211364e11e9753ff5b562843c67"},
|
||||
{file = "rjsmin-1.2.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:6cf0309d001a0d45d731dbaab1afd0c23d135c9e029fe56c935c1798094686fc"},
|
||||
{file = "rjsmin-1.2.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:bfbe333dab8d23f0a71da90e2d8e8b762a739cbd55a6f948b2dfda089b6d5853"},
|
||||
{file = "rjsmin-1.2.2.tar.gz", hash = "sha256:8c1bcd821143fecf23242012b55e13610840a839cd467b358f16359010d62dae"},
|
||||
{file = "rjsmin-1.2.3-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:86e4257875d89b0f7968af9e7c0292e72454f6c75031d1818997782b2e8425a8"},
|
||||
{file = "rjsmin-1.2.3-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:f2ab72093591127e627b13c1243d4fef40c10593c733517999682f7f2ebf47ee"},
|
||||
{file = "rjsmin-1.2.3-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:448b9eb9fd7b6a70beb5c728a41bc23561dd011f0b8fcf7ed9855b6be198c9a2"},
|
||||
{file = "rjsmin-1.2.3-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:ebd3948f9bc912525bab93f61c694b11410296b5fd0806e988d42378ef302b8e"},
|
||||
{file = "rjsmin-1.2.3-cp310-cp310-manylinux1_i686.whl", hash = "sha256:823f856b40681328157e5dffc0a588dddefb4b6ce49f79de994dfca6084617be"},
|
||||
{file = "rjsmin-1.2.3-cp310-cp310-manylinux1_x86_64.whl", hash = "sha256:15e3019f0823a003741ddb93e0c70c5d22567acd0757a7edacc40face1517029"},
|
||||
{file = "rjsmin-1.2.3-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:dece04e309e29879c12dca8af166ea5d77c497ec932cf82e4a1eb24d1489c398"},
|
||||
{file = "rjsmin-1.2.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:dd4a1e527568c3a9711ff1d5251763645c14df02d52a45aec089836600b664ea"},
|
||||
{file = "rjsmin-1.2.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:78aaa3b79a244a4e21164ce355ce22a5a0d7f2d7841a10343009406a3d34d9bb"},
|
||||
{file = "rjsmin-1.2.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8ea4617618cbf78d98756878a292309f6f54fb4ea1b1ea406f79e88eda4d5d50"},
|
||||
{file = "rjsmin-1.2.3-cp311-cp311-manylinux1_i686.whl", hash = "sha256:85957171184ef2dee1957cef5e4adb93a7e2702c12c30bd74420ebace1756e89"},
|
||||
{file = "rjsmin-1.2.3-cp311-cp311-manylinux1_x86_64.whl", hash = "sha256:b6485014e9cbec9a41fb4a7b96ce511ab45a5db8c54ca57ad610f53747e7bab1"},
|
||||
{file = "rjsmin-1.2.3-cp311-cp311-manylinux2014_aarch64.whl", hash = "sha256:64ac6ef8753c56179a53e237ea4d2b3ccdef88b8b51141618311d48e31013207"},
|
||||
{file = "rjsmin-1.2.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:dbd5f653b5ebcd4920793009ffa210ad5523c523e39e45ee1a0770e4323126dc"},
|
||||
{file = "rjsmin-1.2.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:b0174d7786dcebab808485d1c27f049c74b97590cddcd62f6ed54796a2c6503b"},
|
||||
{file = "rjsmin-1.2.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:6cf24720ea214cbffa0ed96ba0dc24a5cf3ff3cbf59d44a1018292424b48452a"},
|
||||
{file = "rjsmin-1.2.3-cp312-cp312-manylinux1_i686.whl", hash = "sha256:ac911d1a12a6d7879ba52e08c56b0ad1a74377bae52610ea74f0f9d936d41785"},
|
||||
{file = "rjsmin-1.2.3-cp312-cp312-manylinux1_x86_64.whl", hash = "sha256:57a0b2f13402623e4ec44eb7ad8846387b2d5605aa8732a05ebefb2289c24b96"},
|
||||
{file = "rjsmin-1.2.3-cp312-cp312-manylinux2014_aarch64.whl", hash = "sha256:e28610cca3ab03e43113eadad4f7dd9ea235ddc29a8dc5462bb161a80e5d251f"},
|
||||
{file = "rjsmin-1.2.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d4afb4fc3624dc44a7fbae4e41c0b5dc5d861a7f5de865ad463041ec1b5d835c"},
|
||||
{file = "rjsmin-1.2.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:ca26b80c7e63cf0788b41571a4bd08d175df7719364e0dd9a3cf7b6cb1ab834c"},
|
||||
{file = "rjsmin-1.2.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:fcc22001588b84d34bbf2c77afa519894244150c4b0754a6e573298ffac24666"},
|
||||
{file = "rjsmin-1.2.3-cp313-cp313-manylinux1_i686.whl", hash = "sha256:624d1a0a35122f3f8955d160a39305cf6f786a5b346ee34c516b391cb153a106"},
|
||||
{file = "rjsmin-1.2.3-cp313-cp313-manylinux1_x86_64.whl", hash = "sha256:72bd04b7db6190339d8214a5fd289ca31fc1ed30a240f8b0ca13acb9ce3a88af"},
|
||||
{file = "rjsmin-1.2.3-cp313-cp313-manylinux2014_aarch64.whl", hash = "sha256:7559f59f4749519b92d72bb68e33b68463f479a82a2a739f1b28a853067aa0e7"},
|
||||
{file = "rjsmin-1.2.3-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:aa8bdecf278f754d1a133ab51119a63a4d38500557912bb0930ae0fd61437ec6"},
|
||||
{file = "rjsmin-1.2.3-cp313-cp313-musllinux_1_1_i686.whl", hash = "sha256:2078acc2d02a005ef122eb330e941462c8c3102cf798ad49f1c5ec18ac714240"},
|
||||
{file = "rjsmin-1.2.3-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:fa40584fddb4f1d2236119505f6c2fe2b57a1ebaf6eaee2bb2eaac33d2a4ca73"},
|
||||
{file = "rjsmin-1.2.3-cp313-cp313t-manylinux1_i686.whl", hash = "sha256:bbe5d8340878b38dd4f7b879ed7728f6fc3d7524ad81a5cfbe4eb8ae63951407"},
|
||||
{file = "rjsmin-1.2.3-cp313-cp313t-manylinux1_x86_64.whl", hash = "sha256:c298c93f5633cf894325907cf49fc7fb010c0f75dc9cda90b0fc1684ad19e5a3"},
|
||||
{file = "rjsmin-1.2.3-cp313-cp313t-manylinux2014_aarch64.whl", hash = "sha256:35f18cffe3f1bf6d96bcfd977199378ebfd641d823b08e235d1e0bb0fbaa5532"},
|
||||
{file = "rjsmin-1.2.3-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:9aeadf4dd5f941bebf110fe83960a4bafdac176647537819bb7662f5e9a37aaa"},
|
||||
{file = "rjsmin-1.2.3-cp313-cp313t-musllinux_1_1_i686.whl", hash = "sha256:c3219e6e22897b31c8598cb412ed56bc12a722c1d4f88a71710c16efe8c07d0c"},
|
||||
{file = "rjsmin-1.2.3-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:bceccb06b118be890fe735fc09ee256851f4993708cb3647f6c71dd0151cce89"},
|
||||
{file = "rjsmin-1.2.3-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:f3620271f00b8ba3c7c5134ca1d99cde5fd1bf1e84aa96aa65c177ee634122f7"},
|
||||
{file = "rjsmin-1.2.3-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:f3d86f70fcca5f68b65eabbce365d07d80404ecd6aa9c55ba9e9f1042a3514c7"},
|
||||
{file = "rjsmin-1.2.3-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:1dae9230eae6d7eb2820a511cb640ca6f2e5b91ff78805d71332e8a65a898ea1"},
|
||||
{file = "rjsmin-1.2.3-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:b788c3ec9d68d8fda2240eb7831bdfb2cc0c88d5fb38c9ed6e0fd090eb5d1490"},
|
||||
{file = "rjsmin-1.2.3-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:4763efbfad7fbf3240a33f08f64991bf0db07453caf283eea51ade84053e9bb7"},
|
||||
{file = "rjsmin-1.2.3-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:e1379e448da75e2426205c756e79d7b9ba1b7ed616fe97122d72c3fe054e8cac"},
|
||||
{file = "rjsmin-1.2.3-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:27e134f4d91a5986cba6dced5cb539947a3ec61544ab5ef31b74b384ddc03931"},
|
||||
{file = "rjsmin-1.2.3-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:2674fcad70d0fab4c1c71e4ac1d4d67935f67e6ecc3924de0dd1264c80a9f9a2"},
|
||||
{file = "rjsmin-1.2.3-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:b1f2540bd0ce7eda326df7b3bfa360f6edd526bfcb959b5d136afdbccddf0765"},
|
||||
{file = "rjsmin-1.2.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:23f3b4adde995a0d0b7835558840dd4673adf99d2473b6d40474d30801d6c57b"},
|
||||
{file = "rjsmin-1.2.3-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:c5fb0574eb541d374a2751e9c0ae019fdd86c9e3eb2e7cf893756886e7b3923f"},
|
||||
{file = "rjsmin-1.2.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:18d6a3229d1ed511a0b0a9a7271ef58ff3b02ba408b92b426857b33b137e7f15"},
|
||||
{file = "rjsmin-1.2.3-cp38-cp38-manylinux1_i686.whl", hash = "sha256:7fe1181452fca7713f377cb6c43cd139638a9edc8c8c29c67119626df164b317"},
|
||||
{file = "rjsmin-1.2.3-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:57708a4e637aac571c578424a7092d3ec64afb1eabbb73e0c71659457eac9ee4"},
|
||||
{file = "rjsmin-1.2.3-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:4c1b5a888d43063a22e2e2c2b4db4d6139dfa6e0d2903ae9bb050ed63a340f40"},
|
||||
{file = "rjsmin-1.2.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:e4ac3f85df88d636a9680432fbbf5d3fe1f171821688106a6710738f06575fc2"},
|
||||
{file = "rjsmin-1.2.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:9dff6b14f92ca7a9f6fbf13548358715e47c5e69576aa5dd8b0ad5048fdc967f"},
|
||||
{file = "rjsmin-1.2.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:07c4f1efbbbcd16a645ada1f012595f3eb3e5d5933395effb6104d3731de2d96"},
|
||||
{file = "rjsmin-1.2.3-cp39-cp39-manylinux1_i686.whl", hash = "sha256:37a73f6ff49dd8c662399575a249a2a028d098c1fa940c6e88aa9082beb44eca"},
|
||||
{file = "rjsmin-1.2.3-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:73357ec91465cf69173d637ccde7b46ed3a8001161c9650325fa305a486e89a3"},
|
||||
{file = "rjsmin-1.2.3-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:236c792fbe18c3b18d4e0ad5ff1b1145f1fbe02126aee9f21bca757b00b63b7e"},
|
||||
{file = "rjsmin-1.2.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:3a630a3131a4e63e10665a0ea7cfe0784a3e1e1c854edf79a8ac0654e3756648"},
|
||||
{file = "rjsmin-1.2.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:a1c98f60ca57adbae023cf989eec91d052f0601df63ddc52a0a48303b21a7f9e"},
|
||||
{file = "rjsmin-1.2.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:32a0174efac83ac72a681edcb9acf5e1c87c5b6aae65ed3424468b5945a90f9d"},
|
||||
{file = "rjsmin-1.2.3.tar.gz", hash = "sha256:1388b52493a4c04fbc970a2d757c301fa05a3c37640314c2ce9dfc8d8a730cc6"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2679,4 +2691,4 @@ filelock = ">=3.4"
|
||||
[metadata]
|
||||
lock-version = "2.0"
|
||||
python-versions = "^3.12"
|
||||
content-hash = "730b020b335ea67342069c40591f6959f1e5f01eef40806c95777def2f39eb37"
|
||||
content-hash = "cb47f6409e629d8369a19d82f44a57dbe9414c79e6e72bd88a6bcb34d78f0bc0"
|
||||
+21
-8
@@ -65,7 +65,7 @@ optional = true
|
||||
# deps used for development purposes, but unneeded in prod
|
||||
django-debug-toolbar = "^4.4.6"
|
||||
ipython = "^8.26.0"
|
||||
pre-commit = "^3.8.0"
|
||||
pre-commit = "^4.0.1"
|
||||
ruff = "^0.6.9" # Version used in pipeline is controlled by pre-commit hooks in .pre-commit.config.yaml
|
||||
djhtml = "^3.0.6"
|
||||
faker = "^30.3.0"
|
||||
@@ -77,14 +77,14 @@ freezegun = "^1.5.1" # used to test time-dependent code
|
||||
pytest = "^8.3.2"
|
||||
pytest-cov = "^5.0.0"
|
||||
pytest-django = "^4.9.0"
|
||||
model-bakery = "^1.19.5"
|
||||
model-bakery = "^1.20.0"
|
||||
|
||||
[tool.poetry.group.docs.dependencies]
|
||||
# deps used to work on the documentation
|
||||
mkdocs = "^1.6.1"
|
||||
mkdocs-material = "^9.5.39"
|
||||
mkdocstrings = "^0.26.1"
|
||||
mkdocstrings-python = "^1.11.1"
|
||||
mkdocs-material = "^9.5.40"
|
||||
mkdocstrings = "^0.26.2"
|
||||
mkdocstrings-python = "^1.12.0"
|
||||
mkdocs-include-markdown-plugin = "^6.2.2"
|
||||
|
||||
[tool.poetry.group.docs]
|
||||
@@ -101,17 +101,30 @@ select = [
|
||||
"A", # shadowing of Python builtins
|
||||
"B",
|
||||
"C4", # use comprehensions when possible
|
||||
"I", # isort
|
||||
"DJ", # django-specific rules,
|
||||
"F401", # unused import
|
||||
"E", # pycodestyle (https://docs.astral.sh/ruff/rules/#pycodestyle-e-w)
|
||||
"ERA", # commented code
|
||||
"F", # pyflakes (https://docs.astral.sh/ruff/rules/#pyflakes-f)
|
||||
"FBT", # boolean trap
|
||||
"FLY", # f-string instead of str.join
|
||||
"FURB", # https://docs.astral.sh/ruff/rules/#refurb-furb
|
||||
"I", # isort
|
||||
"INT", # gettext
|
||||
"PERF", # performance
|
||||
"PLW", # pylint warnings (https://docs.astral.sh/ruff/rules/#pylint-pl)
|
||||
"RUF", # Ruff specific rules
|
||||
"SIM", # simplify (https://docs.astral.sh/ruff/rules/#flake8-simplify-sim)
|
||||
"T100", # breakpoint()
|
||||
"T2", # print statements
|
||||
"TCH", # type-checking block
|
||||
"UP008", # Use super() instead of super(__class__, self)
|
||||
"UP009", # utf-8 encoding declaration is unnecessary
|
||||
"T2", # print statements
|
||||
]
|
||||
|
||||
ignore = [
|
||||
"DJ001", # null=True in CharField/TextField. this one would require a migration
|
||||
"E501", # line too long. The rule is too harsh, and the formatter deals with it in most cases
|
||||
"RUF012" # mutable class attributes. This rule doesn't integrate well with django
|
||||
]
|
||||
|
||||
[tool.ruff.lint.pydocstyle]
|
||||
|
||||
@@ -44,8 +44,8 @@ class Command(BaseCommand):
|
||||
exit(1)
|
||||
|
||||
confirm = input(
|
||||
"User selected: %s\nDo you really want to delete all message from this user ? [y/N] "
|
||||
% (user,)
|
||||
"User selected: %s\nDo you really want "
|
||||
"to delete all message from this user ? [y/N] " % (user,)
|
||||
)
|
||||
|
||||
if not confirm.lower().startswith("y"):
|
||||
|
||||
+5
-5
@@ -66,11 +66,11 @@ class TestMergeUser(TestCase):
|
||||
self.to_keep = User.objects.get(pk=self.to_keep.pk)
|
||||
# fields of to_delete should be assigned to to_keep
|
||||
# if they were not set beforehand
|
||||
assert "Biggus" == self.to_keep.first_name
|
||||
assert "Dickus" == self.to_keep.last_name
|
||||
assert "B'ian" == self.to_keep.nick_name
|
||||
assert "Jerusalem" == self.to_keep.address
|
||||
assert "Rome" == self.to_keep.parent_address
|
||||
assert self.to_keep.first_name == "Biggus"
|
||||
assert self.to_keep.last_name == "Dickus"
|
||||
assert self.to_keep.nick_name == "B'ian"
|
||||
assert self.to_keep.address == "Jerusalem"
|
||||
assert self.to_keep.parent_address == "Rome"
|
||||
assert self.to_keep.groups.count() == 3
|
||||
groups = sorted(self.to_keep.groups.all(), key=lambda i: i.id)
|
||||
expected = sorted([subscribers, mde_admin, sas_admin], key=lambda i: i.id)
|
||||
|
||||
+5
-1
@@ -24,7 +24,11 @@
|
||||
|
||||
from django.urls import path
|
||||
|
||||
from rootplace.views import *
|
||||
from rootplace.views import (
|
||||
DeleteAllForumUserMessagesView,
|
||||
MergeUsersView,
|
||||
OperationLogListView,
|
||||
)
|
||||
|
||||
urlpatterns = [
|
||||
path("merge/", MergeUsersView.as_view(), name="merge"),
|
||||
|
||||
+2
-1
@@ -48,7 +48,8 @@ def __merge_subscriptions(u1: User, u2: User):
|
||||
Some examples :
|
||||
- if u1 is not subscribed, his subscription end date become the one of u2
|
||||
- if u1 is subscribed but not u2, nothing happen
|
||||
- if u1 is subscribed for, let's say, 2 remaining months and u2 is subscribed for 3 remaining months,
|
||||
- if u1 is subscribed for, let's say,
|
||||
2 remaining months and u2 is subscribed for 3 remaining months,
|
||||
he shall then be subscribed for 5 months
|
||||
"""
|
||||
last_subscription = (
|
||||
|
||||
+13
-2
@@ -15,7 +15,7 @@
|
||||
|
||||
from django.contrib import admin
|
||||
|
||||
from sas.models import Album, PeoplePictureRelation, Picture
|
||||
from sas.models import Album, PeoplePictureRelation, Picture, PictureModerationRequest
|
||||
|
||||
|
||||
@admin.register(Picture)
|
||||
@@ -31,4 +31,15 @@ class PeoplePictureRelationAdmin(admin.ModelAdmin):
|
||||
autocomplete_fields = ("picture", "user")
|
||||
|
||||
|
||||
admin.site.register(Album)
|
||||
@admin.register(Album)
|
||||
class AlbumAdmin(admin.ModelAdmin):
|
||||
list_display = ("name", "parent", "date", "owner", "is_moderated")
|
||||
search_fields = ("name",)
|
||||
autocomplete_fields = ("owner", "parent", "edit_groups", "view_groups")
|
||||
|
||||
|
||||
@admin.register(PictureModerationRequest)
|
||||
class PictureModerationRequestAdmin(admin.ModelAdmin):
|
||||
list_display = ("author", "picture", "created_at")
|
||||
search_fields = ("author", "picture")
|
||||
autocomplete_fields = ("author", "picture")
|
||||
Loaded 100 of 132 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user