Merge branch 'nikolaik/up' into 'master'
chore: Modernize all the bits See merge request edb/kassa!1
This commit is contained in:
commit
cb3dfffbb8
41 files changed
+8153
-10763
No files matched your search
+1
-1
@@ -7,4 +7,4 @@ venv
|
||||
node_modules
|
||||
bower_components
|
||||
staticfiles
|
||||
static/dist/
|
||||
static/dist/
|
||||
@@ -0,0 +1,43 @@
|
||||
variables:
|
||||
PIP_CACHE_DIR: "${CI_PROJECT_DIR}/.cache/pip"
|
||||
|
||||
default:
|
||||
image: nikolaik/python-nodejs:python3.12-nodejs20
|
||||
cache:
|
||||
paths:
|
||||
- .cache/pip
|
||||
- .venv
|
||||
|
||||
django-checks:
|
||||
stage: build
|
||||
script:
|
||||
- uv run manage.py migrate
|
||||
- uv run manage.py check
|
||||
|
||||
tests:
|
||||
stage: test
|
||||
script:
|
||||
- uv run bin/test
|
||||
|
||||
lint:
|
||||
stage: test
|
||||
script:
|
||||
- uv run bin/pre-commit
|
||||
|
||||
deploy:
|
||||
stage: deploy
|
||||
before_script:
|
||||
# Install ssh if not already
|
||||
- 'command -v ssh-agent >/dev/null || ( apt-get update -y && apt-get install openssh-client -y )'
|
||||
- eval $(ssh-agent -s)
|
||||
# Make SSH key available
|
||||
- echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add -
|
||||
- mkdir -p ~/.ssh
|
||||
- chmod 700 ~/.ssh
|
||||
# Add known host keys
|
||||
- echo "$SSH_KNOWN_HOSTS" >> ~/.ssh/known_hosts
|
||||
- chmod 644 ~/.ssh/known_hosts
|
||||
script:
|
||||
- ssh gitdeploy@dreamcast.neuf.no "$(< bin/deploy)"
|
||||
only:
|
||||
- main
|
||||
@@ -0,0 +1,14 @@
|
||||
# See https://pre-commit.com for more information
|
||||
# See https://pre-commit.com/hooks.html for more hooks
|
||||
repos:
|
||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||
rev: 'v0.11.7'
|
||||
hooks:
|
||||
- id: ruff
|
||||
args: [--fix, --exit-non-zero-on-fix]
|
||||
- id: ruff-format
|
||||
- repo: https://github.com/adamchainz/django-upgrade
|
||||
rev: "1.24.0"
|
||||
hooks:
|
||||
- id: django-upgrade
|
||||
args: [--target-version, "4.2"]
|
||||
@@ -0,0 +1 @@
|
||||
3.12
|
||||
@@ -1,6 +1,9 @@
|
||||
# kassa
|
||||
|
||||
Kassa helps you register purchased membership cards and memberships. It's a tool for volunteers and bar employees at The Norwegian Student Society.
|
||||
|
||||
## TODO
|
||||
|
||||
* replace nunjucks with something that is maintained
|
||||
* autoprefixer
|
||||
* bootstrap 4.x
|
||||
@@ -8,21 +11,23 @@ Kassa helps you register purchased membership cards and memberships. It's a tool
|
||||
* Galtinn/LDAP auth
|
||||
|
||||
## Installation
|
||||
|
||||
```shell script
|
||||
apt install libldap2-dev libsasl2-dev # pyldap deps
|
||||
python3 -m venv venv
|
||||
. venv/bin/activate
|
||||
pip install -U pip wheel
|
||||
pip install -r requirements.txt
|
||||
python manage.py migrate
|
||||
uv run manage.py migrate
|
||||
uv run manage.py runserver
|
||||
```
|
||||
|
||||
### Development tasks
|
||||
|
||||
```shell script
|
||||
npm run build:templates # regenerate nunjucks templates
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
Create a user in Galtinn with the following permissions:
|
||||
|
||||
* galtinn | User | Can view User
|
||||
* galtinn | Member card | Can change Member card
|
||||
* galtinn | Member card | Can view Member card
|
||||
@@ -31,17 +36,20 @@ Create a user in Galtinn with the following permissions:
|
||||
* galtinn | Order | Can view Order
|
||||
|
||||
Fetch GALTINN_API_KEY:
|
||||
```shell script
|
||||
curl -H "Content-Type: application/json" -X POST -d '{"username":"apiuser","password":"test"}' GALTINN_API_URL/auth/obtain-token/
|
||||
|
||||
```shell
|
||||
curl -H "Content-Type: application/json" -X POST -d '{"username":"apiuser","password":"test"}' GALTINN_API_URL/auth/obtain-token/
|
||||
```
|
||||
|
||||
If developing you can disable calls to Tekstmelding in local_settings.py:
|
||||
|
||||
```python
|
||||
TEKSTMELDING_ENABLED = False
|
||||
```
|
||||
|
||||
## LDAP
|
||||
```shell script
|
||||
|
||||
```shell
|
||||
sudo docker run -e LDAP_DOMAIN=neuf.no -e LDAP_ORGANISATION="Neuf" -e LDAP_ADMIN_PASSWORD="toor" -p 389:389 -d nikolaik/openldap
|
||||
ldapadd -D "cn=admin,dc=neuf,dc=no" -w "toor" -f test/testdata.ldif # Testdata
|
||||
# Verify import
|
||||
|
||||
+15
-8
@@ -1,12 +1,19 @@
|
||||
from apps.kassa.models import KassaEvent
|
||||
from django.contrib import admin
|
||||
|
||||
from apps.kassa.models import KassaEvent
|
||||
|
||||
|
||||
@admin.register(KassaEvent)
|
||||
class KassaEventAdmin(admin.ModelAdmin):
|
||||
list_display = ['id', 'event', 'card_number', 'user_phone_number', 'user_galtinn_id',
|
||||
'user_inside_id', 'created', 'transaction_id']
|
||||
list_filter = ['event']
|
||||
search_fields = ['card_number', 'user_phone_number', 'user_galtinn_id',
|
||||
'user_inside_id', 'transaction_id']
|
||||
|
||||
admin.site.register(KassaEvent, KassaEventAdmin)
|
||||
list_display = [
|
||||
"id",
|
||||
"event",
|
||||
"card_number",
|
||||
"user_phone_number",
|
||||
"user_galtinn_id",
|
||||
"user_inside_id",
|
||||
"created",
|
||||
"transaction_id",
|
||||
]
|
||||
list_filter = ["event"]
|
||||
search_fields = ["card_number", "user_phone_number", "user_galtinn_id", "user_inside_id", "transaction_id"]
|
||||
@@ -1,5 +1,5 @@
|
||||
from django.conf import settings
|
||||
|
||||
|
||||
def sentry(request):
|
||||
return {'SENTRY_DSN': settings.SENTRY_DSN or '', 'SENTRY_ENVIRONMENT': settings.SENTRY_ENVIRONMENT}
|
||||
def sentry(_):
|
||||
return {"SENTRY_DSN": settings.SENTRY_DSN or "", "SENTRY_ENVIRONMENT": settings.SENTRY_ENVIRONMENT}
|
||||
+4
-5
@@ -1,17 +1,16 @@
|
||||
from django import forms
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from phonenumber_field.formfields import PhoneNumberField
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
|
||||
class SearchUserForm(forms.Form):
|
||||
query = forms.CharField(
|
||||
required=False,
|
||||
widget=forms.TextInput(attrs={'placeholder': _('Firstname, lastname, card number or email')})
|
||||
required=False, widget=forms.TextInput(attrs={"placeholder": _("Firstname, lastname, card number or email")})
|
||||
)
|
||||
|
||||
|
||||
class AddCardForm(forms.Form):
|
||||
card_number = forms.IntegerField(widget=forms.NumberInput(attrs={'autofocus': True}))
|
||||
phone_number = PhoneNumberField(widget=forms.TextInput(attrs={'type': 'tel'}))
|
||||
card_number = forms.IntegerField(widget=forms.NumberInput(attrs={"autofocus": True}))
|
||||
phone_number = PhoneNumberField(widget=forms.TextInput(attrs={"type": "tel"}))
|
||||
user_id = forms.IntegerField(widget=forms.HiddenInput(), required=False)
|
||||
order_uuid = forms.IntegerField(widget=forms.HiddenInput(), required=False)
|
||||
@@ -1,25 +1,30 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
]
|
||||
dependencies = []
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='KassaEvent',
|
||||
name="KassaEvent",
|
||||
fields=[
|
||||
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
||||
('event', models.TextField(max_length=255, choices=[('new_card_membership', 'Added new card membership (no user yet)'), ('update_card', 'Updated user card number'), ('add_or_renew', 'Added or renewed membership user membership')])),
|
||||
('card_number', models.IntegerField(null=True, blank=True)),
|
||||
('user_inside_id', models.IntegerField(null=True, blank=True)),
|
||||
('user_phone_number', models.TextField(max_length=100, null=True, blank=True)),
|
||||
('created', models.DateTimeField(auto_now_add=True)),
|
||||
('updated', models.DateTimeField(auto_now=True)),
|
||||
("id", models.AutoField(verbose_name="ID", serialize=False, auto_created=True, primary_key=True)),
|
||||
(
|
||||
"event",
|
||||
models.TextField(
|
||||
max_length=255,
|
||||
choices=[
|
||||
("new_card_membership", "Added new card membership (no user yet)"),
|
||||
("update_card", "Updated user card number"),
|
||||
("add_or_renew", "Added or renewed membership user membership"),
|
||||
],
|
||||
),
|
||||
),
|
||||
("card_number", models.IntegerField(null=True, blank=True)),
|
||||
("user_inside_id", models.IntegerField(null=True, blank=True)),
|
||||
("user_phone_number", models.TextField(max_length=100, null=True, blank=True)),
|
||||
("created", models.DateTimeField(auto_now_add=True)),
|
||||
("updated", models.DateTimeField(auto_now=True)),
|
||||
],
|
||||
),
|
||||
]
|
||||
@@ -1,24 +1,27 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('kassa', '0001_initial'),
|
||||
("kassa", "0001_initial"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='kassaevent',
|
||||
name='event',
|
||||
field=models.CharField(max_length=255, choices=[('new_card_membership', 'Added new card membership (no user yet)'), ('update_card', 'Updated user card number'), ('add_or_renew', 'Added or renewed membership user membership')]),
|
||||
model_name="kassaevent",
|
||||
name="event",
|
||||
field=models.CharField(
|
||||
max_length=255,
|
||||
choices=[
|
||||
("new_card_membership", "Added new card membership (no user yet)"),
|
||||
("update_card", "Updated user card number"),
|
||||
("add_or_renew", "Added or renewed membership user membership"),
|
||||
],
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='kassaevent',
|
||||
name='user_phone_number',
|
||||
model_name="kassaevent",
|
||||
name="user_phone_number",
|
||||
field=models.CharField(max_length=100, null=True, blank=True),
|
||||
),
|
||||
]
|
||||
@@ -1,19 +1,23 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('kassa', '0002_auto_20150810_1327'),
|
||||
("kassa", "0002_auto_20150810_1327"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='kassaevent',
|
||||
name='event',
|
||||
field=models.CharField(max_length=255, choices=[('new_card_membership', 'Added new card membership (no user yet)'), ('sms_card_notify', 'Notified phone number about activation'), ('update_card', 'Updated user card number'), ('add_or_renew', 'Added or renewed membership user membership')]),
|
||||
model_name="kassaevent",
|
||||
name="event",
|
||||
field=models.CharField(
|
||||
max_length=255,
|
||||
choices=[
|
||||
("new_card_membership", "Added new card membership (no user yet)"),
|
||||
("sms_card_notify", "Notified phone number about activation"),
|
||||
("update_card", "Updated user card number"),
|
||||
("add_or_renew", "Added or renewed membership user membership"),
|
||||
],
|
||||
),
|
||||
),
|
||||
]
|
||||
@@ -1,19 +1,24 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('kassa', '0003_auto_20150812_1530'),
|
||||
("kassa", "0003_auto_20150812_1530"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='kassaevent',
|
||||
name='event',
|
||||
field=models.CharField(max_length=255, choices=[('new_card_membership', 'Added new card membership (no user yet)'), ('sms_card_notify', 'Notified phone number about activation'), ('update_card', 'Updated user card number'), ('add_or_renew', 'Added or renewed membership user membership'), ('refund', 'Refunded membership')]),
|
||||
model_name="kassaevent",
|
||||
name="event",
|
||||
field=models.CharField(
|
||||
max_length=255,
|
||||
choices=[
|
||||
("new_card_membership", "Added new card membership (no user yet)"),
|
||||
("sms_card_notify", "Notified phone number about activation"),
|
||||
("update_card", "Updated user card number"),
|
||||
("add_or_renew", "Added or renewed membership user membership"),
|
||||
("refund", "Refunded membership"),
|
||||
],
|
||||
),
|
||||
),
|
||||
]
|
||||
@@ -1,19 +1,25 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('kassa', '0004_auto_20150825_1130'),
|
||||
("kassa", "0004_auto_20150825_1130"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='kassaevent',
|
||||
name='event',
|
||||
field=models.CharField(max_length=255, choices=[('new_card_membership', 'Added new card membership (no user yet)'), ('sms_card_notify', 'Notified phone number about activation'), ('update_card', 'Updated user card number'), ('add_or_renew', 'Added or renewed user membership'), ('renew_only', 'Renewed user membership'), ('refund', 'Refunded membership')]),
|
||||
model_name="kassaevent",
|
||||
name="event",
|
||||
field=models.CharField(
|
||||
max_length=255,
|
||||
choices=[
|
||||
("new_card_membership", "Added new card membership (no user yet)"),
|
||||
("sms_card_notify", "Notified phone number about activation"),
|
||||
("update_card", "Updated user card number"),
|
||||
("add_or_renew", "Added or renewed user membership"),
|
||||
("renew_only", "Renewed user membership"),
|
||||
("refund", "Refunded membership"),
|
||||
],
|
||||
),
|
||||
),
|
||||
]
|
||||
@@ -1,19 +1,26 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('kassa', '0005_auto_20150829_1525'),
|
||||
("kassa", "0005_auto_20150829_1525"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='kassaevent',
|
||||
name='event',
|
||||
field=models.CharField(max_length=255, choices=[('add_or_renew', 'New or renewed user membership'), ('new_card_membership', 'New card membership (no user yet)'), ('refund', 'Refunded membership'), ('renew_only', 'Renewed user membership'), ('sms_card_notify', 'Sent activation link to phone number'), ('update_card', 'Updated card'), ('membership_trial', 'New membership trial')]),
|
||||
model_name="kassaevent",
|
||||
name="event",
|
||||
field=models.CharField(
|
||||
max_length=255,
|
||||
choices=[
|
||||
("add_or_renew", "New or renewed user membership"),
|
||||
("new_card_membership", "New card membership (no user yet)"),
|
||||
("refund", "Refunded membership"),
|
||||
("renew_only", "Renewed user membership"),
|
||||
("sms_card_notify", "Sent activation link to phone number"),
|
||||
("update_card", "Updated card"),
|
||||
("membership_trial", "New membership trial"),
|
||||
],
|
||||
),
|
||||
),
|
||||
]
|
||||
@@ -1,20 +1,17 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11.3 on 2017-07-22 12:40
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('kassa', '0006_auto_20150829_1811'),
|
||||
("kassa", "0006_auto_20150829_1811"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='kassaevent',
|
||||
name='transaction_id',
|
||||
model_name="kassaevent",
|
||||
name="transaction_id",
|
||||
field=models.UUIDField(blank=True, null=True),
|
||||
),
|
||||
]
|
||||
@@ -1,20 +1,17 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11.3 on 2017-07-22 12:43
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('kassa', '0007_kassaevent_transaction_id'),
|
||||
("kassa", "0007_kassaevent_transaction_id"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='kassaevent',
|
||||
name='user_galtinn_id',
|
||||
model_name="kassaevent",
|
||||
name="user_galtinn_id",
|
||||
field=models.IntegerField(blank=True, null=True),
|
||||
),
|
||||
]
|
||||
+17
-17
@@ -1,32 +1,32 @@
|
||||
from django.db import models
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
|
||||
class KassaEvent(models.Model):
|
||||
ADD_OR_RENEW = 'add_or_renew'
|
||||
NEW_CARD_MEMBERSHIP = 'new_card_membership'
|
||||
REFUND = 'refund'
|
||||
RENEW_ONLY = 'renew_only'
|
||||
SMS_CARD_NOTIFY = 'sms_card_notify'
|
||||
UPDATE_CARD = 'update_card'
|
||||
MEMBERSHIP_TRIAL = 'membership_trial'
|
||||
ADD_OR_RENEW = "add_or_renew"
|
||||
NEW_CARD_MEMBERSHIP = "new_card_membership"
|
||||
REFUND = "refund"
|
||||
RENEW_ONLY = "renew_only"
|
||||
SMS_CARD_NOTIFY = "sms_card_notify"
|
||||
UPDATE_CARD = "update_card"
|
||||
MEMBERSHIP_TRIAL = "membership_trial"
|
||||
EVENT_CHOICES = (
|
||||
(ADD_OR_RENEW, _('New or renewed user membership')),
|
||||
(NEW_CARD_MEMBERSHIP, _('New card membership (no user yet)')),
|
||||
(REFUND, _('Refunded membership')),
|
||||
(RENEW_ONLY, _('Renewed user membership')),
|
||||
(SMS_CARD_NOTIFY, _('Sent activation link to phone number')),
|
||||
(UPDATE_CARD, _('Updated card')),
|
||||
(MEMBERSHIP_TRIAL, _('New membership trial'))
|
||||
(ADD_OR_RENEW, _("New or renewed user membership")),
|
||||
(NEW_CARD_MEMBERSHIP, _("New card membership (no user yet)")),
|
||||
(REFUND, _("Refunded membership")),
|
||||
(RENEW_ONLY, _("Renewed user membership")),
|
||||
(SMS_CARD_NOTIFY, _("Sent activation link to phone number")),
|
||||
(UPDATE_CARD, _("Updated card")),
|
||||
(MEMBERSHIP_TRIAL, _("New membership trial")),
|
||||
)
|
||||
event = models.CharField(max_length=255, choices=EVENT_CHOICES)
|
||||
card_number = models.IntegerField(null=True, blank=True)
|
||||
user_galtinn_id = models.IntegerField(null=True, blank=True)
|
||||
user_inside_id = models.IntegerField(null=True, blank=True)
|
||||
user_phone_number = models.CharField(max_length=100, null=True, blank=True)
|
||||
user_phone_number = models.CharField(max_length=100, null=True, blank=True) # noqa: DJ001
|
||||
transaction_id = models.UUIDField(null=True, blank=True)
|
||||
created = models.DateTimeField(auto_now_add=True)
|
||||
updated = models.DateTimeField(auto_now=True)
|
||||
|
||||
def __str__(self):
|
||||
return 'KassaEvent: <{}>'.format(self.pk)
|
||||
return f"KassaEvent: <{self.pk}>"
|
||||
@@ -1 +0,0 @@
|
||||
from django.test import TestCase
|
||||
+15
-9
@@ -1,13 +1,19 @@
|
||||
from django.urls import path, re_path
|
||||
from django.urls import path
|
||||
|
||||
from apps.kassa.views import (register, user_search, check_card, check_phone_number, register_card_and_membership,
|
||||
renew_membership)
|
||||
from apps.kassa.views import (
|
||||
check_card,
|
||||
check_phone_number,
|
||||
register,
|
||||
register_card_and_membership,
|
||||
renew_membership,
|
||||
user_search,
|
||||
)
|
||||
|
||||
urlpatterns = [
|
||||
re_path(r'^$', register, name='register'),
|
||||
path('user-search/', user_search, name='user-search'),
|
||||
path('check-phone-number/', check_phone_number, name='check-phone-number'),
|
||||
path('check-card/', check_card, name='check-card'),
|
||||
path('register-card-membership/', register_card_and_membership, name='register-card-and-membership'),
|
||||
path('renew-membership/', renew_membership, name='renew-membership'),
|
||||
path("", register, name="register"),
|
||||
path("user-search/", user_search, name="user-search"),
|
||||
path("check-phone-number/", check_phone_number, name="check-phone-number"),
|
||||
path("check-card/", check_card, name="check-card"),
|
||||
path("register-card-membership/", register_card_and_membership, name="register-card-and-membership"),
|
||||
path("renew-membership/", renew_membership, name="renew-membership"),
|
||||
]
|
||||
+53
-57
@@ -1,102 +1,98 @@
|
||||
# coding: utf-8
|
||||
from django.conf import settings
|
||||
import datetime
|
||||
import logging
|
||||
|
||||
import phonenumbers
|
||||
import requests
|
||||
import logging
|
||||
from django.conf import settings
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
galtinn_auth = {
|
||||
'Authorization': 'Token {}'.format(settings.GALTINN_API_KEY)
|
||||
}
|
||||
galtinn_auth = {"Authorization": f"Token {settings.GALTINN_API_KEY}"}
|
||||
|
||||
tekstmelding_auth = {
|
||||
'Authorization': 'Token {}'.format(settings.TEKSTMELDING_API_KEY)
|
||||
}
|
||||
tekstmelding_auth = {"Authorization": f"Token {settings.TEKSTMELDING_API_KEY}"}
|
||||
|
||||
|
||||
def send_sms_activation_link(phone_number, transaction_id):
|
||||
to = phone_number.replace('+', '')
|
||||
to = phone_number.replace("+", "")
|
||||
code = transaction_id[:8]
|
||||
activation_url = settings.GALTINN_ACTIVATION_URL.format(to=to, code=code)
|
||||
message = settings.TEKSTMELDING_ACTIVATION_SMS_TEMPLATE.format(url=activation_url)
|
||||
payload = {
|
||||
'to': to,
|
||||
'message': message
|
||||
}
|
||||
payload = {"to": to, "message": message}
|
||||
if not settings.TEKSTMELDING_ENABLED:
|
||||
logger.info('Tekstmelding disabled. Payload would be: {}'.format(payload))
|
||||
return
|
||||
url = '{}send'.format(settings.TEKSTMELDING_API_URL)
|
||||
response = requests.post(url, json=payload, headers=tekstmelding_auth)
|
||||
logger.info(f"Tekstmelding disabled. Payload would be: {payload}")
|
||||
return None
|
||||
url = f"{settings.TEKSTMELDING_API_URL}send"
|
||||
response = requests.post(url, json=payload, headers=tekstmelding_auth, timeout=10)
|
||||
return response.json()
|
||||
|
||||
|
||||
def get_card(card_number):
|
||||
url = '{}cards/{}/'.format(settings.GALTINN_API_URL, card_number)
|
||||
return requests.get(url, headers=galtinn_auth)
|
||||
url = f"{settings.GALTINN_API_URL}cards/{card_number}/"
|
||||
return requests.get(url, headers=galtinn_auth, timeout=10)
|
||||
|
||||
|
||||
def get_order(order_uuid):
|
||||
url = '{}orders/{}/'.format(settings.GALTINN_API_URL, order_uuid)
|
||||
return requests.get(url, headers=galtinn_auth)
|
||||
url = f"{settings.GALTINN_API_URL}orders/{order_uuid}/"
|
||||
return requests.get(url, headers=galtinn_auth, timeout=10)
|
||||
|
||||
|
||||
def get_latest_order_by_card(card_number):
|
||||
url = '{}orders/'.format(settings.GALTINN_API_URL)
|
||||
payload = {'card_number': card_number}
|
||||
orders = requests.get(url, params=payload, headers=galtinn_auth).json()
|
||||
return orders.get('results')[0]
|
||||
url = f"{settings.GALTINN_API_URL}orders/"
|
||||
payload = {"card_number": card_number}
|
||||
orders = requests.get(url, params=payload, headers=galtinn_auth, timeout=10).json()
|
||||
return orders.get("results")[0]
|
||||
|
||||
|
||||
def get_latest_order_by_phone(phone_number):
|
||||
url = '{}orders/'.format(settings.GALTINN_API_URL)
|
||||
payload = {'phone_number': phone_number}
|
||||
orders = requests.get(url, params=payload, headers=galtinn_auth).json()
|
||||
if orders.get('count'):
|
||||
return orders.get('results')[0]
|
||||
url = f"{settings.GALTINN_API_URL}orders/"
|
||||
payload = {"phone_number": phone_number}
|
||||
orders = requests.get(url, params=payload, headers=galtinn_auth, timeout=10).json()
|
||||
if orders.get("count"):
|
||||
return orders.get("results")[0]
|
||||
return None
|
||||
|
||||
|
||||
def get_user(user_id):
|
||||
url = '{}users/{}/'.format(settings.GALTINN_API_URL, user_id)
|
||||
return requests.get(url, headers=galtinn_auth)
|
||||
url = f"{settings.GALTINN_API_URL}users/{user_id}/"
|
||||
return requests.get(url, headers=galtinn_auth, timeout=10)
|
||||
|
||||
|
||||
def get_user_by_phone(phone_number):
|
||||
url = '{}users/'.format(settings.GALTINN_API_URL)
|
||||
payload = {'phone_number': phone_number}
|
||||
users = requests.get(url, params=payload, headers=galtinn_auth).json()
|
||||
if users.get('count') == 1:
|
||||
return users.get('results')[0]
|
||||
url = f"{settings.GALTINN_API_URL}users/"
|
||||
payload = {"phone_number": phone_number}
|
||||
users = requests.get(url, params=payload, headers=galtinn_auth, timeout=10).json()
|
||||
if users.get("count") == 1:
|
||||
return users.get("results")[0]
|
||||
return None
|
||||
|
||||
|
||||
def update_card(card_number, user_id=None, order_uuid=None, transaction_id=None):
|
||||
assert not (user_id and order_uuid)
|
||||
url = '{}kassa/card/'.format(settings.GALTINN_API_URL)
|
||||
if user_id and order_uuid:
|
||||
raise ValueError("Cannot provide both user_id and order_uuid. Provide one or the other.")
|
||||
|
||||
url = f"{settings.GALTINN_API_URL}kassa/card/"
|
||||
payload = {
|
||||
'member_card': card_number,
|
||||
'user': user_id,
|
||||
'order': order_uuid,
|
||||
'transaction_id': str(transaction_id) if transaction_id else None,
|
||||
"member_card": card_number,
|
||||
"user": user_id,
|
||||
"order": order_uuid,
|
||||
"transaction_id": str(transaction_id) if transaction_id else None,
|
||||
}
|
||||
return requests.patch(url, json=payload, headers=galtinn_auth)
|
||||
return requests.patch(url, json=payload, headers=galtinn_auth, timeout=10)
|
||||
|
||||
|
||||
def update_membership(user=None, phone_number=None, card_number=None, membership_type=None,
|
||||
transaction_id=None):
|
||||
assert membership_type in ('standard', 'trial')
|
||||
url = '{}kassa/membership/'.format(settings.GALTINN_API_URL)
|
||||
def update_membership(user=None, phone_number=None, card_number=None, membership_type=None, transaction_id=None):
|
||||
if membership_type not in ("standard", "trial"):
|
||||
raise ValueError("Invalid membership type provided. Must be 'standard' or 'trial'.")
|
||||
|
||||
url = f"{settings.GALTINN_API_URL}kassa/membership/"
|
||||
payload = {
|
||||
'user': user,
|
||||
'phone_number': phone_number,
|
||||
'member_card': card_number,
|
||||
'membership_type': membership_type,
|
||||
'transaction_id': transaction_id,
|
||||
"user": user,
|
||||
"phone_number": phone_number,
|
||||
"member_card": card_number,
|
||||
"membership_type": membership_type,
|
||||
"transaction_id": transaction_id,
|
||||
}
|
||||
return requests.post(url, json=payload, headers=galtinn_auth)
|
||||
return requests.post(url, json=payload, headers=galtinn_auth, timeout=10)
|
||||
|
||||
|
||||
def format_phone_number(number):
|
||||
@@ -104,7 +100,7 @@ def format_phone_number(number):
|
||||
return number
|
||||
|
||||
try:
|
||||
p = phonenumbers.parse(number, region='NO')
|
||||
p = phonenumbers.parse(number, region="NO")
|
||||
except phonenumbers.NumberParseException:
|
||||
return number
|
||||
|
||||
@@ -115,7 +111,7 @@ def format_phone_number(number):
|
||||
|
||||
|
||||
def is_autumn():
|
||||
today = datetime.date.today()
|
||||
today = datetime.datetime.now(tz=datetime.UTC).date()
|
||||
_min = datetime.date(year=today.year, month=8, day=1)
|
||||
_max = datetime.date(year=today.year + 1, month=1, day=1)
|
||||
return _min <= today < _max
|
||||
+95
-91
@@ -1,21 +1,30 @@
|
||||
# coding: utf-8
|
||||
import json
|
||||
import logging
|
||||
import uuid
|
||||
from http import HTTPStatus
|
||||
|
||||
import phonenumbers
|
||||
import requests
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.http import JsonResponse
|
||||
from django.shortcuts import render
|
||||
from django.utils import timezone
|
||||
from itertools import groupby
|
||||
import json
|
||||
import uuid
|
||||
import logging
|
||||
import phonenumbers
|
||||
import requests
|
||||
|
||||
from apps.kassa.models import KassaEvent
|
||||
from apps.kassa.forms import AddCardForm, SearchUserForm
|
||||
from apps.kassa.utils import send_sms_activation_link, update_card, get_card, \
|
||||
get_order, get_latest_order_by_card, get_latest_order_by_phone, get_user, get_user_by_phone, \
|
||||
galtinn_auth, update_membership, format_phone_number, is_autumn
|
||||
from apps.kassa.models import KassaEvent
|
||||
from apps.kassa.utils import (
|
||||
format_phone_number,
|
||||
galtinn_auth,
|
||||
get_card,
|
||||
get_latest_order_by_card,
|
||||
get_latest_order_by_phone,
|
||||
get_user,
|
||||
get_user_by_phone,
|
||||
is_autumn,
|
||||
send_sms_activation_link,
|
||||
update_card,
|
||||
update_membership,
|
||||
)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -23,142 +32,136 @@ logger = logging.getLogger(__name__)
|
||||
@login_required
|
||||
def register(request):
|
||||
context = {
|
||||
'add_card_form': AddCardForm(),
|
||||
'search_user_form': SearchUserForm(),
|
||||
'show_trial_membership': is_autumn()
|
||||
"add_card_form": AddCardForm(),
|
||||
"search_user_form": SearchUserForm(),
|
||||
"show_trial_membership": is_autumn(),
|
||||
}
|
||||
return render(request, 'kassa/register.html', context)
|
||||
return render(request, "kassa/register.html", context)
|
||||
|
||||
|
||||
@login_required
|
||||
def user_search(request):
|
||||
payload = {
|
||||
'search': request.GET.get('search', '')
|
||||
}
|
||||
url = '{}users/'.format(settings.GALTINN_API_URL)
|
||||
data = requests.get(url, params=payload, headers=galtinn_auth).json()
|
||||
payload = {"search": request.GET.get("search", "")}
|
||||
url = f"{settings.GALTINN_API_URL}users/"
|
||||
data = requests.get(url, params=payload, headers=galtinn_auth, timeout=10).json()
|
||||
|
||||
return JsonResponse(data)
|
||||
|
||||
|
||||
@login_required
|
||||
def check_phone_number(request):
|
||||
number = request.GET.get('phone_number', '').strip()
|
||||
number = request.GET.get("phone_number", "").strip()
|
||||
|
||||
if len(number) < 2:
|
||||
return JsonResponse({'error': 'Phone number is too short'})
|
||||
min_length = 2
|
||||
if len(number) < min_length:
|
||||
return JsonResponse({"error": "Phone number is too short"})
|
||||
|
||||
# Is phone number valid?
|
||||
try:
|
||||
p = phonenumbers.parse(number, region='NO')
|
||||
p = phonenumbers.parse(number, region="NO")
|
||||
if not phonenumbers.is_valid_number(p):
|
||||
return JsonResponse({'error': "Phone number '{}' is invalid.".format(number)})
|
||||
return JsonResponse({"error": f"Phone number '{number}' is invalid."})
|
||||
|
||||
except phonenumbers.NumberParseException as e:
|
||||
return JsonResponse({'error': str(e).replace('(1) ', '')})
|
||||
return JsonResponse({"error": str(e).replace("(1) ", "")})
|
||||
|
||||
number = phonenumbers.format_number(p, phonenumbers.PhoneNumberFormat.E164)
|
||||
user = get_user_by_phone(number)
|
||||
order = get_latest_order_by_phone(number)
|
||||
|
||||
return JsonResponse({
|
||||
'user': user,
|
||||
'order': order
|
||||
})
|
||||
return JsonResponse({"user": user, "order": order})
|
||||
|
||||
|
||||
@login_required
|
||||
def check_card(request):
|
||||
if request.method != 'GET':
|
||||
return JsonResponse({'error': 'Only method GET supported'})
|
||||
if request.method != "GET":
|
||||
return JsonResponse({"error": "Only method GET supported"})
|
||||
|
||||
card_number = request.GET.get('card_number')
|
||||
card_number = request.GET.get("card_number")
|
||||
response = get_card(card_number)
|
||||
if response.status_code != 200:
|
||||
if response.status_code != HTTPStatus.OK:
|
||||
return JsonResponse(response.json(), status=response.status_code)
|
||||
|
||||
card = response.json()
|
||||
if card.get('user'):
|
||||
card['user'] = get_user(card['user']).json()
|
||||
card['order'] = None
|
||||
if card.get('orders'):
|
||||
card['order'] = get_latest_order_by_card(card_number)
|
||||
card.pop('orders')
|
||||
if card.get("user"):
|
||||
card["user"] = get_user(card["user"]).json()
|
||||
card["order"] = None
|
||||
if card.get("orders"):
|
||||
card["order"] = get_latest_order_by_card(card_number)
|
||||
card.pop("orders")
|
||||
|
||||
return JsonResponse(card, status=response.status_code)
|
||||
|
||||
|
||||
@login_required
|
||||
def register_card_and_membership(request):
|
||||
if request.method != 'POST':
|
||||
return JsonResponse({'error': 'Only method POST supported'})
|
||||
if request.method != "POST":
|
||||
return JsonResponse({"error": "Only method POST supported"})
|
||||
|
||||
post_data = json.loads(request.body.decode('utf-8'))
|
||||
post_data = json.loads(request.body.decode("utf-8"))
|
||||
# new_card_membership, update_card, add_or_renew, sms_card_notify
|
||||
# TODO: multiple actions (to allow renewal only)
|
||||
action = post_data.get('action')
|
||||
user_id = post_data.get('user_id')
|
||||
order_uuid = post_data.get('order_uuid')
|
||||
card_number = post_data.get('card_number')
|
||||
phone_number_raw = post_data.get('phone_number')
|
||||
membership_trial = post_data.get('membership_trial')
|
||||
membership_type = 'trial' if membership_trial else 'standard'
|
||||
action = post_data.get("action")
|
||||
user_id = post_data.get("user_id")
|
||||
order_uuid = post_data.get("order_uuid")
|
||||
card_number = post_data.get("card_number")
|
||||
phone_number_raw = post_data.get("phone_number")
|
||||
membership_trial = post_data.get("membership_trial")
|
||||
membership_type = "trial" if membership_trial else "standard"
|
||||
transaction_id = uuid.uuid4()
|
||||
|
||||
logger.debug('register_card_and_membership post_data %r', post_data)
|
||||
logger.debug("register_card_and_membership post_data %r", post_data)
|
||||
phone_number = format_phone_number(phone_number_raw)
|
||||
|
||||
# Update card number on user or order
|
||||
if action in ('update_card', 'sms_card_notify'):
|
||||
if action == 'update_card' and user_id:
|
||||
response = update_card(card_number,
|
||||
user_id=user_id)
|
||||
elif action == 'sms_card_notify' and order_uuid:
|
||||
response = update_card(card_number,
|
||||
order_uuid=order_uuid,
|
||||
transaction_id=transaction_id)
|
||||
else:
|
||||
assert user_id or order_uuid
|
||||
if response.status_code != 200:
|
||||
if action in ("update_card", "sms_card_notify"):
|
||||
if action == "update_card" and user_id:
|
||||
response = update_card(card_number, user_id=user_id)
|
||||
elif action == "sms_card_notify" and order_uuid:
|
||||
response = update_card(card_number, order_uuid=order_uuid, transaction_id=transaction_id)
|
||||
elif not user_id and not order_uuid:
|
||||
return JsonResponse({"error": "Either user_id or order_uuid must be set"}, status=HTTPStatus.BAD_REQUEST)
|
||||
|
||||
if response.status_code != HTTPStatus.OK:
|
||||
return JsonResponse(response.json(), status=response.status_code)
|
||||
|
||||
event_data = {
|
||||
'event': KassaEvent.UPDATE_CARD,
|
||||
'user_phone_number': phone_number,
|
||||
'card_number': card_number,
|
||||
'user_galtinn_id': user_id,
|
||||
'transaction_id': transaction_id
|
||||
"event": KassaEvent.UPDATE_CARD,
|
||||
"user_phone_number": phone_number,
|
||||
"card_number": card_number,
|
||||
"user_galtinn_id": user_id,
|
||||
"transaction_id": transaction_id,
|
||||
}
|
||||
logger.debug(event_data)
|
||||
KassaEvent.objects.create(**event_data)
|
||||
|
||||
# Add initial or renew membership for existing user or order
|
||||
if action in ('add_or_renew', 'new_card_membership'):
|
||||
if action in ("add_or_renew", "new_card_membership"):
|
||||
response = update_membership(
|
||||
user=user_id,
|
||||
phone_number=phone_number,
|
||||
card_number=card_number,
|
||||
membership_type=membership_type,
|
||||
transaction_id=str(transaction_id))
|
||||
transaction_id=str(transaction_id),
|
||||
)
|
||||
KassaEvent.objects.create(
|
||||
event=KassaEvent.ADD_OR_RENEW if membership_type != 'trial' else KassaEvent.MEMBERSHIP_TRIAL,
|
||||
event=KassaEvent.ADD_OR_RENEW if membership_type != "trial" else KassaEvent.MEMBERSHIP_TRIAL,
|
||||
user_galtinn_id=user_id,
|
||||
card_number=card_number,
|
||||
user_phone_number=phone_number,
|
||||
transaction_id=transaction_id
|
||||
transaction_id=transaction_id,
|
||||
)
|
||||
|
||||
# Send activation notification (link) to user by SMS
|
||||
if action in ('new_card_membership', 'sms_card_notify'):
|
||||
if action in ("new_card_membership", "sms_card_notify"):
|
||||
event = KassaEvent.NEW_CARD_MEMBERSHIP
|
||||
if action == 'sms_card_notify':
|
||||
if action == "sms_card_notify":
|
||||
event = KassaEvent.SMS_CARD_NOTIFY
|
||||
|
||||
# FIXME: could be async
|
||||
send_sms_activation_link(phone_number=phone_number,
|
||||
transaction_id=str(transaction_id))
|
||||
send_sms_activation_link(phone_number=phone_number, transaction_id=str(transaction_id))
|
||||
KassaEvent.objects.create(
|
||||
event=event,
|
||||
card_number=card_number,
|
||||
user_phone_number=phone_number,
|
||||
transaction_id=transaction_id
|
||||
event=event, card_number=card_number, user_phone_number=phone_number, transaction_id=transaction_id
|
||||
)
|
||||
|
||||
return JsonResponse(response.json(), status=response.status_code)
|
||||
@@ -166,18 +169,18 @@ def register_card_and_membership(request):
|
||||
|
||||
@login_required
|
||||
def renew_membership(request):
|
||||
if request.method != 'POST':
|
||||
return JsonResponse({'error': 'Only method POST supported'})
|
||||
if request.method != "POST":
|
||||
return JsonResponse({"error": "Only method POST supported"})
|
||||
|
||||
post_data = json.loads(request.body.decode('utf-8'))
|
||||
user_id = post_data.get('user_id')
|
||||
phone_number_raw = post_data.get('phone_number')
|
||||
card_number = post_data.get('card_number')
|
||||
membership_trial = post_data.get('membership_trial')
|
||||
membership_type = 'trial' if membership_trial else 'standard'
|
||||
post_data = json.loads(request.body.decode("utf-8"))
|
||||
user_id = post_data.get("user_id")
|
||||
phone_number_raw = post_data.get("phone_number")
|
||||
card_number = post_data.get("card_number")
|
||||
membership_trial = post_data.get("membership_trial")
|
||||
membership_type = "trial" if membership_trial else "standard"
|
||||
transaction_id = str(uuid.uuid4())
|
||||
|
||||
logger.debug('renew_membership post_data %r', post_data)
|
||||
logger.debug("renew_membership post_data %r", post_data)
|
||||
phone_number = format_phone_number(phone_number_raw)
|
||||
|
||||
response = update_membership(
|
||||
@@ -185,11 +188,12 @@ def renew_membership(request):
|
||||
phone_number=phone_number,
|
||||
card_number=card_number,
|
||||
membership_type=membership_type,
|
||||
transaction_id=transaction_id)
|
||||
transaction_id=transaction_id,
|
||||
)
|
||||
KassaEvent.objects.create(
|
||||
event=KassaEvent.RENEW_ONLY if membership_trial is None else KassaEvent.MEMBERSHIP_TRIAL,
|
||||
user_galtinn_id=user_id,
|
||||
transaction_id=transaction_id
|
||||
transaction_id=transaction_id,
|
||||
)
|
||||
|
||||
return JsonResponse(response.json(), status=response.status_code)
|
||||
Executable
+6
@@ -0,0 +1,6 @@
|
||||
#!/bin/bash
|
||||
set -eu
|
||||
|
||||
cd static
|
||||
npm i
|
||||
npm run build
|
||||
Executable
+24
@@ -0,0 +1,24 @@
|
||||
#!/bin/bash
|
||||
set -eu
|
||||
|
||||
source "$HOME/.local/bin/env"
|
||||
|
||||
export DJANGO_SETTINGS_MODULE=kassasite.settings
|
||||
PROJECT_PATH=/var/www/neuf.no/kassa
|
||||
PROCESS_NAME="kassa.neuf.no"
|
||||
|
||||
echo "Deploying kassa to ${PROJECT_PATH}"
|
||||
cd "$PROJECT_PATH" || exit 1
|
||||
git pull
|
||||
|
||||
# Frontend
|
||||
bin/build-frontend
|
||||
|
||||
# Backend
|
||||
uv sync
|
||||
umask 022; uv run manage.py collectstatic --noinput -i node_modules -c # Collect static (including frontend)
|
||||
uv run manage.py migrate # Run DB migrations
|
||||
|
||||
# Reload backend
|
||||
echo "Reloading supervisor processes ${PROCESS_NAME} ${PROCESS_NAME}-celery"
|
||||
sudo /usr/bin/supervisorctl pid "$PROCESS_NAME" | xargs kill -HUP
|
||||
Executable
+4
@@ -0,0 +1,4 @@
|
||||
#!/usr/bin/env bash
|
||||
set -eu
|
||||
|
||||
pre-commit run -a
|
||||
Vendored
+15
-16
@@ -7,27 +7,26 @@ from invoke import task
|
||||
@task()
|
||||
def deploy(c):
|
||||
"""Make sure proxy_user is set to your neuf username."""
|
||||
project_path = '/var/www/neuf.no/kassa'
|
||||
proxy_user = os.getenv('DEPLOY_USER', os.getenv('USER'))
|
||||
project_path = "/var/www/neuf.no/kassa"
|
||||
proxy_user = os.getenv("DEPLOY_USER", os.getenv("USER"))
|
||||
|
||||
c = Connection(host='gitdeploy@dreamcast.neuf.no', gateway=Connection('login.neuf.no', user=proxy_user))
|
||||
c = Connection(host="gitdeploy@dreamcast.neuf.no", gateway=Connection("login.neuf.no", user=proxy_user))
|
||||
|
||||
with c.cd(project_path), c.prefix('source {}/venv/bin/activate'.format(project_path)):
|
||||
c.run('git pull') # Get source
|
||||
c.run('pip install -U pip')
|
||||
c.run('pip install -r requirements.txt') # install deps in virtualenv
|
||||
with c.cd('static'): # install and compile frontend deps
|
||||
c.run('npm i')
|
||||
c.run('npm run build')
|
||||
c.run('python manage.py collectstatic --noinput -i node_modules') # Collect static
|
||||
c.run('python manage.py migrate') # Run DB migrations
|
||||
with c.cd(project_path), c.prefix(f"source {project_path}/venv/bin/activate"):
|
||||
c.run("git pull") # Get source
|
||||
c.run("uv sync") # install deps in virtualenv
|
||||
with c.cd("static"): # install and compile frontend deps
|
||||
c.run("npm i")
|
||||
c.run("npm run build")
|
||||
c.run("uv run manage.py collectstatic --noinput -i node_modules") # Collect static
|
||||
c.run("uv run manage.py migrate") # Run DB migrations
|
||||
|
||||
# Reload gunicorn
|
||||
c.sudo('/usr/bin/supervisorctl pid kassa.neuf.no | xargs kill -HUP', shell=False)
|
||||
c.sudo("/usr/bin/supervisorctl pid kassa.neuf.no | xargs kill -HUP", shell=False)
|
||||
|
||||
|
||||
@task
|
||||
def build(c):
|
||||
with c.cd('static'):
|
||||
c.run('npm i')
|
||||
c.run('npm run build')
|
||||
with c.cd("static"):
|
||||
c.run("npm i")
|
||||
c.run("npm run build")
|
||||
+67
-81
@@ -9,16 +9,17 @@ https://docs.djangoproject.com/en/1.8/ref/settings/
|
||||
"""
|
||||
|
||||
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
|
||||
import os
|
||||
import contextlib
|
||||
from pathlib import Path
|
||||
|
||||
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
BASE_DIR = Path(__file__).parent.parent
|
||||
|
||||
|
||||
# Quick-start development settings - unsuitable for production
|
||||
# See https://docs.djangoproject.com/en/1.8/howto/deployment/checklist/
|
||||
|
||||
# SECURITY WARNING: keep the secret key used in production secret!
|
||||
SECRET_KEY = '79l0@&(^ra9=s783rqeo2awhs6@c!j&wan6t-0wi8azyd@!x+7'
|
||||
SECRET_KEY = "79l0@&(^ra9=s783rqeo2awhs6@c!j&wan6t-0wi8azyd@!x+7" # noqa: S105
|
||||
|
||||
# SECURITY WARNING: don't run with debug turned on in production!
|
||||
DEBUG = True
|
||||
@@ -29,132 +30,117 @@ ALLOWED_HOSTS = []
|
||||
# Application definition
|
||||
|
||||
INSTALLED_APPS = (
|
||||
'django.contrib.admin',
|
||||
'django.contrib.auth',
|
||||
'django.contrib.contenttypes',
|
||||
'django.contrib.sessions',
|
||||
'django.contrib.messages',
|
||||
'django.contrib.staticfiles',
|
||||
|
||||
'bootstrapform',
|
||||
'django_extensions',
|
||||
'corsheaders',
|
||||
"django.contrib.admin",
|
||||
"django.contrib.auth",
|
||||
"django.contrib.contenttypes",
|
||||
"django.contrib.sessions",
|
||||
"django.contrib.messages",
|
||||
"django.contrib.staticfiles",
|
||||
"bootstrapform",
|
||||
"django_extensions",
|
||||
"corsheaders",
|
||||
)
|
||||
|
||||
LOCAL_APPS = ('apps.kassa',)
|
||||
LOCAL_APPS = ("apps.kassa",)
|
||||
|
||||
INSTALLED_APPS += LOCAL_APPS
|
||||
|
||||
MIDDLEWARE = (
|
||||
'django.contrib.sessions.middleware.SessionMiddleware',
|
||||
'corsheaders.middleware.CorsMiddleware',
|
||||
'django.middleware.common.CommonMiddleware',
|
||||
'django.middleware.csrf.CsrfViewMiddleware',
|
||||
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||
'django.contrib.messages.middleware.MessageMiddleware',
|
||||
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
||||
'django.middleware.security.SecurityMiddleware',
|
||||
"django.contrib.sessions.middleware.SessionMiddleware",
|
||||
"corsheaders.middleware.CorsMiddleware",
|
||||
"django.middleware.common.CommonMiddleware",
|
||||
"django.middleware.csrf.CsrfViewMiddleware",
|
||||
"django.contrib.auth.middleware.AuthenticationMiddleware",
|
||||
"django.contrib.messages.middleware.MessageMiddleware",
|
||||
"django.middleware.clickjacking.XFrameOptionsMiddleware",
|
||||
"django.middleware.security.SecurityMiddleware",
|
||||
)
|
||||
|
||||
ROOT_URLCONF = 'kassasite.urls'
|
||||
ROOT_URLCONF = "kassasite.urls"
|
||||
|
||||
TEMPLATES = [
|
||||
{
|
||||
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
||||
'DIRS': [
|
||||
os.path.join(BASE_DIR, 'apps/kassa/templates')
|
||||
],
|
||||
'APP_DIRS': True,
|
||||
'OPTIONS': {
|
||||
'context_processors': [
|
||||
'django.template.context_processors.debug',
|
||||
'django.template.context_processors.request',
|
||||
'django.contrib.auth.context_processors.auth',
|
||||
'django.contrib.messages.context_processors.messages',
|
||||
'apps.kassa.context_processors.sentry',
|
||||
"BACKEND": "django.template.backends.django.DjangoTemplates",
|
||||
"DIRS": [BASE_DIR.joinpath("apps/kassa/templates")],
|
||||
"APP_DIRS": True,
|
||||
"OPTIONS": {
|
||||
"context_processors": [
|
||||
"django.template.context_processors.debug",
|
||||
"django.template.context_processors.request",
|
||||
"django.contrib.auth.context_processors.auth",
|
||||
"django.contrib.messages.context_processors.messages",
|
||||
"apps.kassa.context_processors.sentry",
|
||||
],
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
WSGI_APPLICATION = 'kassasite.wsgi.application'
|
||||
WSGI_APPLICATION = "kassasite.wsgi.application"
|
||||
|
||||
|
||||
# Database
|
||||
# https://docs.djangoproject.com/en/1.8/ref/settings/#databases
|
||||
|
||||
DATABASES = {
|
||||
'default': {
|
||||
'ENGINE': 'django.db.backends.sqlite3',
|
||||
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
|
||||
"default": {
|
||||
"ENGINE": "django.db.backends.sqlite3",
|
||||
"NAME": BASE_DIR.joinpath("db.sqlite3"),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
# Internationalization
|
||||
# https://docs.djangoproject.com/en/1.8/topics/i18n/
|
||||
LANGUAGE_CODE = 'en-us'
|
||||
TIME_ZONE = 'Europe/Oslo'
|
||||
LANGUAGE_CODE = "en-us"
|
||||
TIME_ZONE = "Europe/Oslo"
|
||||
USE_I18N = True
|
||||
USE_L10N = True
|
||||
USE_TZ = True
|
||||
|
||||
|
||||
# Static files (CSS, JavaScript, Images)
|
||||
# https://docs.djangoproject.com/en/1.8/howto/static-files/
|
||||
STATIC_URL = '/static/'
|
||||
STATICFILES_DIRS = (
|
||||
os.path.join(BASE_DIR, 'static'),
|
||||
)
|
||||
STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')
|
||||
STATIC_URL = "/static/"
|
||||
STATICFILES_DIRS = (BASE_DIR.joinpath("static"),)
|
||||
STATIC_ROOT = BASE_DIR.joinpath("staticfiles")
|
||||
|
||||
LOGIN_REDIRECT_URL = '/'
|
||||
LOGIN_REDIRECT_URL = "/"
|
||||
|
||||
GALTINN_API_KEY = ''
|
||||
GALTINN_API_URL = 'https://galtinn.neuf.no/api/'
|
||||
GALTINN_ACTIVATION_URL = 'https://galtinn.neuf.no/activate/{to}/{code}'
|
||||
GALTINN_API_KEY = ""
|
||||
GALTINN_API_URL = "https://galtinn.neuf.no/api/"
|
||||
GALTINN_ACTIVATION_URL = "https://galtinn.neuf.no/activate/{to}/{code}"
|
||||
|
||||
TEKSTMELDING_API_KEY = ''
|
||||
TEKSTMELDING_API_URL = 'https://tekstmelding.neuf.no/'
|
||||
TEKSTMELDING_API_KEY = ""
|
||||
TEKSTMELDING_API_URL = "https://tekstmelding.neuf.no/"
|
||||
TEKSTMELDING_ENABLED = True
|
||||
TEKSTMELDING_ACTIVATION_SMS_TEMPLATE = 'Velkommen som medlem! Registrer deg her: {url}'
|
||||
TEKSTMELDING_ACTIVATION_SMS_TEMPLATE = "Velkommen som medlem! Registrer deg her: {url}"
|
||||
|
||||
CORS_ORIGIN_ALLOW_ALL = True
|
||||
|
||||
LOGGING = {
|
||||
'version': 1,
|
||||
'disable_existing_loggers': True,
|
||||
'formatters': {
|
||||
'verbose': {
|
||||
'format': '%(levelname)s %(asctime)s %(module)s '
|
||||
'%(process)d %(thread)d %(message)s'
|
||||
},
|
||||
"version": 1,
|
||||
"disable_existing_loggers": True,
|
||||
"formatters": {
|
||||
"verbose": {"format": "%(levelname)s %(asctime)s %(module)s %(process)d %(thread)d %(message)s"},
|
||||
},
|
||||
'handlers': {
|
||||
'console': {
|
||||
'level': 'DEBUG',
|
||||
'class': 'logging.StreamHandler',
|
||||
'formatter': 'verbose'
|
||||
}
|
||||
},
|
||||
'loggers': {
|
||||
'root': {
|
||||
'level': 'WARNING',
|
||||
'handlers': ['console'],
|
||||
"handlers": {"console": {"level": "DEBUG", "class": "logging.StreamHandler", "formatter": "verbose"}},
|
||||
"loggers": {
|
||||
"root": {
|
||||
"level": "WARNING",
|
||||
"handlers": ["console"],
|
||||
},
|
||||
'django.db.backends': {
|
||||
'level': 'ERROR',
|
||||
'handlers': ['console'],
|
||||
'propagate': False,
|
||||
"django.db.backends": {
|
||||
"level": "ERROR",
|
||||
"handlers": ["console"],
|
||||
"propagate": False,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
SENTRY_DSN = None
|
||||
SENTRY_ENVIRONMENT = 'dev'
|
||||
SENTRY_ENVIRONMENT = "dev"
|
||||
|
||||
DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
|
||||
|
||||
# Local settings
|
||||
try:
|
||||
from .local_settings import *
|
||||
except ImportError:
|
||||
pass
|
||||
with contextlib.suppress(ImportError):
|
||||
from .local_settings import * # noqa: F403
|
||||
+2
-8
@@ -1,13 +1,7 @@
|
||||
from django.conf.urls import include, url
|
||||
from django.contrib import admin
|
||||
from django.urls import path
|
||||
|
||||
from django.contrib.auth import urls as auth_urls
|
||||
from django.urls import include, path
|
||||
|
||||
from apps.kassa import urls as kassa_urls
|
||||
|
||||
urlpatterns = [
|
||||
path('admin/', admin.site.urls),
|
||||
url('^', include(kassa_urls)),
|
||||
path('accounts/', include(auth_urls))
|
||||
]
|
||||
urlpatterns = [path("admin/", admin.site.urls), path("", include(kassa_urls)), path("accounts/", include(auth_urls))]
|
||||
@@ -0,0 +1,47 @@
|
||||
[project]
|
||||
name = "kassa"
|
||||
version = "0.1.0"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.12"
|
||||
dependencies = [
|
||||
"django==4.2.*",
|
||||
"django-auth-ldap==5.*",
|
||||
"django-bootstrap-form==3.4",
|
||||
"django-cors-headers==4.*",
|
||||
"django-extensions==4.*",
|
||||
"django-phonenumber-field==8.*",
|
||||
"fabric==3.*",
|
||||
"gunicorn==23.*",
|
||||
"mysqlclient==2.*",
|
||||
"phonenumbers==9.*",
|
||||
"requests==2.*",
|
||||
"sentry-sdk==1.*",
|
||||
]
|
||||
|
||||
[tool.ruff]
|
||||
line-length = 120
|
||||
target-version = "py312"
|
||||
|
||||
[tool.ruff.lint]
|
||||
select = ["ALL"]
|
||||
ignore = [
|
||||
"D", # pydocstyle: Allow undocumented code
|
||||
"ANN", # flake8-annotations: Allow untyped code
|
||||
"TD", "FIX", # Allow TODOs and FIXMEs
|
||||
"FBT002", # Allow boolean args
|
||||
"G004", # Allow logging using f-string
|
||||
"EM101", "EM102", # Allow string in exceptions (prioritice code readability vs exception time readability)
|
||||
"TRY003", # Allow long exception messages
|
||||
"E501", # Allow long lines
|
||||
"COM812", "ISC001", # Avoid conflicting with ruff formatter
|
||||
"RUF012" # FIXME: Fixy fix
|
||||
]
|
||||
|
||||
[dependency-groups]
|
||||
dev = [
|
||||
"ruff>=0.11.12",
|
||||
]
|
||||
|
||||
# [tool.ruff.lint.pyupgrade]
|
||||
# keep-runtime-typing = true
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
django-auth-ldap==2.1.*
|
||||
django-bootstrap-form==3.4
|
||||
django-cors-headers==3.2.*
|
||||
django-extensions==2.2.*
|
||||
django-phonenumber-field==4.0.*
|
||||
django==2.2.*
|
||||
fabric==2.5.*
|
||||
gunicorn==20.0.*
|
||||
mysqlclient==1.4.6
|
||||
phonenumbers==8.11.*
|
||||
requests==2.22.*
|
||||
sentry-sdk==0.13.*
|
||||
@@ -1,4 +0,0 @@
|
||||
{
|
||||
"presets": ["@babel/preset-env"],
|
||||
"plugins": ["@babel/plugin-transform-runtime"]
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
{
|
||||
"env": {
|
||||
"browser": true
|
||||
},
|
||||
"extends": [
|
||||
"airbnb-base",
|
||||
"plugin:prettier/recommended",
|
||||
"plugin:import/errors",
|
||||
"plugin:import/warnings"
|
||||
],
|
||||
"rules": {
|
||||
"no-plusplus": "off",
|
||||
"no-console": "off",
|
||||
"no-underscore-dangle": "off",
|
||||
"no-param-reassign": "off",
|
||||
"no-restricted-syntax": "off",
|
||||
},
|
||||
"settings": {
|
||||
"import/resolver": {
|
||||
"node": {}, // https://github.com/benmosher/eslint-plugin-import/issues/1396#issuecomment-509384041
|
||||
"webpack": {
|
||||
"config": "webpack.config.js"
|
||||
}
|
||||
}
|
||||
}
|
||||
,
|
||||
"plugins": ["prettier"],
|
||||
"parser": "babel-eslint"
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
module.exports = {
|
||||
env: {
|
||||
browser: true,
|
||||
},
|
||||
parserOptions: {
|
||||
ecmaVersion: 2020,
|
||||
sourceType: 'module',
|
||||
},
|
||||
extends: ['plugin:prettier/recommended', 'plugin:import/errors', 'plugin:import/warnings'],
|
||||
rules: {
|
||||
'no-console': 'off',
|
||||
'prefer-destructuring': 'off',
|
||||
'import/no-extraneous-dependencies': [
|
||||
'error',
|
||||
{
|
||||
devDependencies: ['**/*.test.js', '**/*.spec.js', '**/__tests__/**'],
|
||||
},
|
||||
],
|
||||
'import/prefer-default-export': 'off',
|
||||
'import/order': [
|
||||
'error',
|
||||
{
|
||||
groups: ['builtin', 'external', 'internal', ['parent', 'sibling', 'index']],
|
||||
'newlines-between': 'always',
|
||||
alphabetize: { order: 'asc' },
|
||||
},
|
||||
],
|
||||
},
|
||||
plugins: ['prettier'],
|
||||
};
|
||||
@@ -0,0 +1 @@
|
||||
v20
|
||||
+1
-3
@@ -1,5 +1,3 @@
|
||||
{
|
||||
"singleQuote": true,
|
||||
"printWidth": 120,
|
||||
"arrowParens": "always"
|
||||
"printWidth": 120
|
||||
}
|
||||
+165
-166
@@ -1,15 +1,16 @@
|
||||
import * as $ from 'jquery';
|
||||
import moment from 'moment';
|
||||
import nunjucks from 'nunjucks';
|
||||
import debounce from 'lodash.debounce';
|
||||
import sortBy from 'lodash.sortby';
|
||||
import fromPairs from 'lodash.frompairs';
|
||||
import $ from "jquery";
|
||||
window.$ = window.jQuery = $; // Make sure jQuery is available globally
|
||||
import debounce from "lodash.debounce";
|
||||
import fromPairs from "lodash.frompairs";
|
||||
import sortBy from "lodash.sortby";
|
||||
import moment from "moment";
|
||||
import nunjucks from "nunjucks";
|
||||
|
||||
import 'bootstrap-sass/assets/javascripts/bootstrap/tooltip';
|
||||
import 'bootstrap-sass/assets/javascripts/bootstrap/popover';
|
||||
import "bootstrap-sass/assets/javascripts/bootstrap/tooltip";
|
||||
import "bootstrap-sass/assets/javascripts/bootstrap/popover";
|
||||
|
||||
import './templates';
|
||||
import './index.scss';
|
||||
import "./templates";
|
||||
import "./index.scss";
|
||||
|
||||
/* Init and global vars */
|
||||
let _dom;
|
||||
@@ -20,29 +21,29 @@ let membershipOrder = null; /* selected order object */
|
||||
const cardForm = {
|
||||
fields: {
|
||||
cardNumber: false,
|
||||
phoneNumber: false
|
||||
}
|
||||
phoneNumber: false,
|
||||
},
|
||||
};
|
||||
const STATE_ICON = {
|
||||
success: 'ok',
|
||||
warning: 'warning-sign',
|
||||
error: 'remove'
|
||||
success: "ok",
|
||||
warning: "warning-sign",
|
||||
error: "remove",
|
||||
};
|
||||
// TODO: replace with lodash template
|
||||
const nunjucksEnv = new nunjucks.Environment();
|
||||
|
||||
const urls = {
|
||||
userSearch: '/user-search/',
|
||||
checkPhoneNumber: '/check-phone-number/',
|
||||
checkCard: '/check-card/',
|
||||
registerCardAndMembership: '/register-card-membership/',
|
||||
renewMembership: '/renew-membership/'
|
||||
userSearch: "/user-search/",
|
||||
checkPhoneNumber: "/check-phone-number/",
|
||||
checkCard: "/check-card/",
|
||||
registerCardAndMembership: "/register-card-membership/",
|
||||
renewMembership: "/renew-membership/",
|
||||
};
|
||||
|
||||
function getCookie(name) {
|
||||
let cookieValue = null;
|
||||
if (document.cookie && document.cookie !== '') {
|
||||
const cookies = document.cookie.split(';');
|
||||
if (document.cookie && document.cookie !== "") {
|
||||
const cookies = document.cookie.split(";");
|
||||
for (let i = 0; i < cookies.length; i++) {
|
||||
const cookie = $.trim(cookies[i]);
|
||||
// Does this cookie string begin with the name we want?
|
||||
@@ -56,12 +57,12 @@ function getCookie(name) {
|
||||
}
|
||||
|
||||
function getParameterByName(name) {
|
||||
return new URLSearchParams(window.location.search).get(name) || '';
|
||||
return new URLSearchParams(window.location.search).get(name) || "";
|
||||
}
|
||||
|
||||
function formatPhoneNumber(str) {
|
||||
if (!str || str.length <= 1) {
|
||||
return 'No phone number';
|
||||
return "No phone number";
|
||||
}
|
||||
if (str.length === 11) {
|
||||
return `${str.slice(0, 3)} ${str.slice(3, 6)} ${str.slice(6, 8)} ${str.slice(8)}`;
|
||||
@@ -83,7 +84,7 @@ function sortSearchResults(results) {
|
||||
}
|
||||
|
||||
function formatResults(results) {
|
||||
return nunjucksEnv.render('search_results.html', { results });
|
||||
return nunjucksEnv.render("search_results.html", { results });
|
||||
}
|
||||
|
||||
function renderSelectedUser(userData) {
|
||||
@@ -93,7 +94,7 @@ function renderSelectedUser(userData) {
|
||||
} else {
|
||||
context = { res: userData, checked: true, no_user_actions: true };
|
||||
}
|
||||
const selectedUserHtml = nunjucksEnv.render('search_result.html', context);
|
||||
const selectedUserHtml = nunjucksEnv.render("search_result.html", context);
|
||||
_dom.selectedEntityWrap.html(selectedUserHtml);
|
||||
}
|
||||
|
||||
@@ -102,7 +103,7 @@ function renderSelectedOrder(order) {
|
||||
return;
|
||||
}
|
||||
const context = { order, checked: true, no_user_actions: true };
|
||||
const selectedOrderHtml = nunjucksEnv.render('selected_order.html', context);
|
||||
const selectedOrderHtml = nunjucksEnv.render("selected_order.html", context);
|
||||
_dom.selectedEntityWrap.html(selectedOrderHtml);
|
||||
}
|
||||
|
||||
@@ -110,31 +111,29 @@ function setToast(message, messageType) {
|
||||
const context = {
|
||||
message,
|
||||
message_type: messageType,
|
||||
icon: STATE_ICON[messageType]
|
||||
icon: STATE_ICON[messageType],
|
||||
};
|
||||
const html = nunjucksEnv.render('toast.html', context);
|
||||
const html = nunjucksEnv.render("toast.html", context);
|
||||
_dom.toastWrap.html(html);
|
||||
_dom.toastWrap.addClass('visible');
|
||||
_dom.toastWrap.addClass("visible");
|
||||
setTimeout(() => {
|
||||
_dom.toastWrap.removeClass('visible');
|
||||
_dom.toastWrap.removeClass("visible");
|
||||
}, 5000);
|
||||
}
|
||||
|
||||
function updateMemberShipButton(user, order) {
|
||||
const todayPlusThirtyDays = moment()
|
||||
.add(30, 'days')
|
||||
.format('YYYY-MM-DD');
|
||||
// const todayPlusThirtyDays = moment().add(30, 'days').format('YYYY-MM-DD');
|
||||
/* User with valid membership */
|
||||
if (user && user.is_member) {
|
||||
/* ...which is lifelong cannot renew */
|
||||
if (user.last_membership.membership_type === 'lifelong') {
|
||||
_dom.membershipSubmitButton.prop('disabled', true);
|
||||
if (user.last_membership.membership_type === "lifelong") {
|
||||
_dom.membershipSubmitButton.prop("disabled", true);
|
||||
}
|
||||
/* ...or expiring soon and the user has a card */
|
||||
// else if (user.last_membership.end_date <= todayPlusThirtyDays && user.active_member_card) {
|
||||
|
||||
// FIXME: ponas workaround 2019-12-17, block below commented out
|
||||
_dom.membershipSubmitButton.prop('disabled', false);
|
||||
_dom.membershipSubmitButton.prop("disabled", false);
|
||||
|
||||
/* ...with a membership that expires soon can renew */
|
||||
// else if (user.last_membership.end_date <= todayPlusThirtyDays) {
|
||||
@@ -144,10 +143,10 @@ function updateMemberShipButton(user, order) {
|
||||
/* User with expired membership can renew */
|
||||
// else if (user && !user.is_member && user.active_member_card) {
|
||||
else if (user && !user.is_member) {
|
||||
_dom.membershipSubmitButton.prop('disabled', false);
|
||||
_dom.membershipSubmitButton.prop("disabled", false);
|
||||
} else if (order) {
|
||||
// FIXME: ponas workaround 2019-12-17, block below commented out
|
||||
_dom.membershipSubmitButton.prop('disabled', false);
|
||||
_dom.membershipSubmitButton.prop("disabled", false);
|
||||
|
||||
/* Order */
|
||||
/* If the membership has expired it can be renewed */
|
||||
@@ -155,7 +154,7 @@ function updateMemberShipButton(user, order) {
|
||||
// _dom.membershipSubmitButton.prop('disabled', false);
|
||||
// }
|
||||
} else {
|
||||
_dom.membershipSubmitButton.prop('disabled', true);
|
||||
_dom.membershipSubmitButton.prop("disabled", true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -163,14 +162,14 @@ function setSelectedUser(user, updateSearchResult) {
|
||||
selectedUser = user;
|
||||
updateMemberShipButton(user, null);
|
||||
|
||||
_dom.userIdField.val((user && user.id) || '').trigger('change');
|
||||
_dom.userIdField.val((user && user.id) || "").trigger("change");
|
||||
if (updateSearchResult) {
|
||||
const staleSearchResult = _dom.results.find('.search-result').removeClass('selected');
|
||||
staleSearchResult.find('input').prop('checked', false);
|
||||
const staleSearchResult = _dom.results.find(".search-result").removeClass("selected");
|
||||
staleSearchResult.find("input").prop("checked", false);
|
||||
/* User is still in search result, mark selected */
|
||||
if (user && users && users.find(({ id }) => id === user.id)) {
|
||||
const searchResult = _dom.results.find(`[data-user-id="${user.id}"]`).toggleClass('selected');
|
||||
searchResult.find('input').prop('checked', true);
|
||||
const searchResult = _dom.results.find(`[data-user-id="${user.id}"]`).toggleClass("selected");
|
||||
searchResult.find("input").prop("checked", true);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -180,40 +179,40 @@ function setSelectedOrder(order) {
|
||||
if (order) {
|
||||
updateMemberShipButton(null, order);
|
||||
}
|
||||
_dom.orderUuidField.val((order && order.uuid) || '').trigger('change');
|
||||
_dom.orderUuidField.val((order && order.uuid) || "").trigger("change");
|
||||
}
|
||||
|
||||
function setFieldState(fieldElement, state, helpText) {
|
||||
if (!helpText) {
|
||||
helpText = '';
|
||||
helpText = "";
|
||||
}
|
||||
const helpSpan = `<span class="help-block ">${helpText}</span>`;
|
||||
const feedbackSpan = `<span class="glyphicon glyphicon-${STATE_ICON[state]} form-control-feedback" aria-hidden="true"></span>`;
|
||||
const cssClass = `has-${state}`;
|
||||
const formGroup = fieldElement.parent().parent();
|
||||
formGroup.removeClass('has-success has-warning has-error has-feedback'); // reset
|
||||
if (['success', 'warning', 'error'].indexOf(state) !== -1) {
|
||||
formGroup.removeClass("has-success has-warning has-error has-feedback"); // reset
|
||||
if (["success", "warning", "error"].indexOf(state) !== -1) {
|
||||
formGroup.addClass(`${cssClass} has-feedback`);
|
||||
/* Feedback icon */
|
||||
const feedbackEl = formGroup.find('.form-control-feedback');
|
||||
const feedbackEl = formGroup.find(".form-control-feedback");
|
||||
if (!feedbackEl.length) {
|
||||
// add
|
||||
formGroup.append(feedbackSpan);
|
||||
} else {
|
||||
// Update
|
||||
feedbackEl.removeClass('glyphicon-ok glypicon-warning-sign glyphicon-remove');
|
||||
feedbackEl.removeClass("glyphicon-ok glypicon-warning-sign glyphicon-remove");
|
||||
feedbackEl.addClass(`glyphicon-${STATE_ICON[state]}`);
|
||||
}
|
||||
/* Help text */
|
||||
const helpTextEl = formGroup.find('.help-block');
|
||||
const helpTextEl = formGroup.find(".help-block");
|
||||
if (!helpTextEl.length) {
|
||||
formGroup.append(helpSpan);
|
||||
} else {
|
||||
helpTextEl.text(helpText);
|
||||
}
|
||||
} else {
|
||||
formGroup.find('.form-control-feedback').remove();
|
||||
formGroup.find('.help-block').remove();
|
||||
formGroup.find(".form-control-feedback").remove();
|
||||
formGroup.find(".help-block").remove();
|
||||
}
|
||||
}
|
||||
function resetCardForm(resetNative) {
|
||||
@@ -221,18 +220,18 @@ function resetCardForm(resetNative) {
|
||||
_dom.registerCardForm.get(0).reset();
|
||||
}
|
||||
/* Clear field states */
|
||||
setFieldState(_dom.phoneNumberField, '');
|
||||
setFieldState(_dom.phoneNumberField, "");
|
||||
cardForm.fields.phoneNumber = false;
|
||||
setFieldState(_dom.cardNumberField, '');
|
||||
setFieldState(_dom.cardNumberField, "");
|
||||
cardForm.fields.cardNumber = false;
|
||||
selectedUser = null;
|
||||
membershipOrder = null;
|
||||
_dom.userIdField.val('').trigger('change');
|
||||
_dom.membershipTrialCheckBox.prop('checked', false);
|
||||
_dom.userIdField.val("").trigger("change");
|
||||
_dom.membershipTrialCheckBox.prop("checked", false);
|
||||
|
||||
/* Disable submit buttons */
|
||||
_dom.registerSubmitButton.prop('disabled', true);
|
||||
_dom.membershipSubmitButton.prop('disabled', true);
|
||||
_dom.registerSubmitButton.prop("disabled", true);
|
||||
_dom.membershipSubmitButton.prop("disabled", true);
|
||||
}
|
||||
function resetSearchForm(resetNative) {
|
||||
if (resetNative) {
|
||||
@@ -240,7 +239,7 @@ function resetSearchForm(resetNative) {
|
||||
}
|
||||
/* Clear search results */
|
||||
users = null;
|
||||
_dom.results.html('');
|
||||
_dom.results.html("");
|
||||
}
|
||||
|
||||
function cardFormIsValid() {
|
||||
@@ -249,41 +248,41 @@ function cardFormIsValid() {
|
||||
|
||||
function updateSubmitButton() {
|
||||
if (cardFormIsValid()) {
|
||||
_dom.registerSubmitButton.prop('disabled', false);
|
||||
_dom.registerSubmitButton.prop("disabled", false);
|
||||
return;
|
||||
}
|
||||
_dom.registerSubmitButton.prop('disabled', true);
|
||||
_dom.registerSubmitButton.prop("disabled", true);
|
||||
}
|
||||
function validatePhoneNumber(val) {
|
||||
if (val.length === 0) {
|
||||
cardForm.fields.phoneNumber = false;
|
||||
updateSubmitButton();
|
||||
return 'Phone number should not be empty.';
|
||||
return "Phone number should not be empty.";
|
||||
}
|
||||
cardForm.fields.phoneNumber = true;
|
||||
updateSubmitButton();
|
||||
return '';
|
||||
return "";
|
||||
}
|
||||
|
||||
function validateCardNumber(val) {
|
||||
if (val.length === 0) {
|
||||
cardForm.fields.cardNumber = false;
|
||||
updateSubmitButton();
|
||||
return 'Card number should not be empty.';
|
||||
return "Card number should not be empty.";
|
||||
}
|
||||
if (val.length > 0 && val[0] !== '1') {
|
||||
if (val.length > 0 && val[0] !== "1") {
|
||||
cardForm.fields.cardNumber = false;
|
||||
updateSubmitButton();
|
||||
return 'Card number should start with 1.';
|
||||
return "Card number should start with 1.";
|
||||
}
|
||||
if (val.length !== 9) {
|
||||
cardForm.fields.cardNumber = false;
|
||||
updateSubmitButton();
|
||||
return 'Card number should be 9 digits long.';
|
||||
return "Card number should be 9 digits long.";
|
||||
}
|
||||
cardForm.fields.cardNumber = true;
|
||||
updateSubmitButton();
|
||||
return '';
|
||||
return "";
|
||||
}
|
||||
|
||||
function getFormData(formElement) {
|
||||
@@ -307,7 +306,7 @@ function checkPhoneNumber() {
|
||||
if (data.error) {
|
||||
setSelectedUser(null, true);
|
||||
setSelectedOrder(null);
|
||||
setFieldState(_dom.phoneNumberField, 'error', data.error);
|
||||
setFieldState(_dom.phoneNumberField, "error", data.error);
|
||||
cardForm.fields.phoneNumber = false;
|
||||
updateSubmitButton();
|
||||
return;
|
||||
@@ -315,12 +314,12 @@ function checkPhoneNumber() {
|
||||
|
||||
const { user } = data;
|
||||
const { order } = data;
|
||||
let message = '';
|
||||
let message = "";
|
||||
|
||||
/* Has valid card membership */
|
||||
if (!user && order && order.member_card && order.product.is_valid) {
|
||||
message = `Phone number is already tied to card ${order.member_card} and is valid until ${order.product.end_date}.`;
|
||||
setFieldState(_dom.phoneNumberField, 'error', message);
|
||||
setFieldState(_dom.phoneNumberField, "error", message);
|
||||
cardForm.fields.phoneNumber = false;
|
||||
setSelectedOrder(order);
|
||||
updateSubmitButton();
|
||||
@@ -329,8 +328,8 @@ function checkPhoneNumber() {
|
||||
|
||||
if (user) {
|
||||
/* Existing user */
|
||||
message = 'Phone number belongs to existing user.';
|
||||
setFieldState(_dom.phoneNumberField, 'success', message);
|
||||
message = "Phone number belongs to existing user.";
|
||||
setFieldState(_dom.phoneNumberField, "success", message);
|
||||
setSelectedUser(user, true);
|
||||
setSelectedOrder(null);
|
||||
cardForm.fields.phoneNumber = true;
|
||||
@@ -339,9 +338,9 @@ function checkPhoneNumber() {
|
||||
setSelectedOrder(order);
|
||||
/* Valid membership, no card associated */
|
||||
if (order.product.is_valid) {
|
||||
message = 'Phone number has a valid membership.';
|
||||
message = "Phone number has a valid membership.";
|
||||
} else {
|
||||
message = 'Phone number has an expired membership.';
|
||||
message = "Phone number has an expired membership.";
|
||||
}
|
||||
} else {
|
||||
/* Valid phone number associated user or order */
|
||||
@@ -349,61 +348,61 @@ function checkPhoneNumber() {
|
||||
setSelectedUser(null);
|
||||
}
|
||||
|
||||
setFieldState(_dom.phoneNumberField, 'success', message);
|
||||
setFieldState(_dom.phoneNumberField, "success", message);
|
||||
cardForm.fields.phoneNumber = true;
|
||||
updateSubmitButton();
|
||||
});
|
||||
}
|
||||
|
||||
/* Add filter |phoneNumber */
|
||||
nunjucksEnv.addFilter('phoneNumber', formatPhoneNumber);
|
||||
nunjucksEnv.addFilter("phoneNumber", formatPhoneNumber);
|
||||
|
||||
/* On document ready */
|
||||
$(document).ready(function() {
|
||||
$(function () {
|
||||
_dom = {
|
||||
results: $('.results-wrap'),
|
||||
query: $('#id_query'),
|
||||
phoneNumberLabel: $('[for=id_phone_number]'),
|
||||
phoneNumberField: $('#id_phone_number'),
|
||||
cardNumberField: $('#id_card_number'),
|
||||
registerCardForm: $('.register-card-form'),
|
||||
registerSubmitButton: $('#register-submit-btn'),
|
||||
usernameField: $('#id_username'),
|
||||
userIdField: $('#id_user_id'),
|
||||
orderUuidField: $('#id_order_uuid'),
|
||||
selectedEntityWrap: $('.register-card-form--selected-entity-wrap'),
|
||||
toastWrap: $('.toast-wrap'),
|
||||
registerResetButton: $('.register-reset-btn'),
|
||||
searchResetButton: $('.search-reset-btn'),
|
||||
membershipSubmitButton: $('#membership-submit-btn'),
|
||||
membershipTrialCheckBox: $('#id_membership_trial')
|
||||
results: $(".results-wrap"),
|
||||
query: $("#id_query"),
|
||||
phoneNumberLabel: $("[for=id_phone_number]"),
|
||||
phoneNumberField: $("#id_phone_number"),
|
||||
cardNumberField: $("#id_card_number"),
|
||||
registerCardForm: $(".register-card-form"),
|
||||
registerSubmitButton: $("#register-submit-btn"),
|
||||
usernameField: $("#id_username"),
|
||||
userIdField: $("#id_user_id"),
|
||||
orderUuidField: $("#id_order_uuid"),
|
||||
selectedEntityWrap: $(".register-card-form--selected-entity-wrap"),
|
||||
toastWrap: $(".toast-wrap"),
|
||||
registerResetButton: $(".register-reset-btn"),
|
||||
searchResetButton: $(".search-reset-btn"),
|
||||
membershipSubmitButton: $("#membership-submit-btn"),
|
||||
membershipTrialCheckBox: $("#id_membership_trial"),
|
||||
};
|
||||
|
||||
const lazyCheckPhoneNumber = debounce(checkPhoneNumber, 250);
|
||||
_dom.phoneNumberField.on('input', (e) => {
|
||||
_dom.phoneNumberField.on("input", (e) => {
|
||||
const val = e.target.value.trim();
|
||||
const validationMsg = validatePhoneNumber(val);
|
||||
if (validationMsg !== '') {
|
||||
if (validationMsg !== "") {
|
||||
setSelectedUser(null, true);
|
||||
setFieldState(_dom.phoneNumberField, 'error', validationMsg);
|
||||
setFieldState(_dom.phoneNumberField, "error", validationMsg);
|
||||
return;
|
||||
}
|
||||
lazyCheckPhoneNumber();
|
||||
});
|
||||
/* Card number as you type */
|
||||
_dom.cardNumberField.on('input', (e) => {
|
||||
_dom.cardNumberField.on("input", (e) => {
|
||||
/* Card number should exist in the database and not tied to existing user */
|
||||
const val = e.target.value.trim();
|
||||
const validationMsg = validateCardNumber(val);
|
||||
if (validationMsg !== '') {
|
||||
setFieldState(_dom.cardNumberField, 'error', validationMsg);
|
||||
if (validationMsg !== "") {
|
||||
setFieldState(_dom.cardNumberField, "error", validationMsg);
|
||||
return;
|
||||
}
|
||||
$.getJSON(urls.checkCard, { card_number: val }, (data) => {
|
||||
if (data.error) {
|
||||
updateSubmitButton();
|
||||
cardForm.fields.cardNumber = false;
|
||||
setFieldState(_dom.cardNumberField, 'error', data.error);
|
||||
setFieldState(_dom.cardNumberField, "error", data.error);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -414,18 +413,18 @@ $(document).ready(function() {
|
||||
|
||||
if (!card) {
|
||||
cardForm.fields.cardNumber = false;
|
||||
setFieldState(_dom.cardNumberField, 'error', 'Cannot find card number in database.');
|
||||
setFieldState(_dom.cardNumberField, "error", "Cannot find card number in database.");
|
||||
} else if (card && card.registered !== null && user === null && order && !order.product.is_valid) {
|
||||
/* An already registered card (with no user) can repurchase membership if expired */
|
||||
setSelectedOrder(order);
|
||||
cardForm.fields.cardNumber = true;
|
||||
setFieldState(
|
||||
_dom.cardNumberField,
|
||||
'success',
|
||||
"success",
|
||||
`Card number is in use (not activated) and belongs to ${formatPhoneNumber(order.phone_number)}.`
|
||||
);
|
||||
} else if (card && card.registered !== null) {
|
||||
let ownerString = '???';
|
||||
let ownerString = "???";
|
||||
if (user) {
|
||||
ownerString = `existing user: ${user.first_name} ${user.last_name} (${formatPhoneNumber(user.phone_number)})`;
|
||||
} else {
|
||||
@@ -433,26 +432,26 @@ $(document).ready(function() {
|
||||
ownerString = `phone number: ${formatPhoneNumber(order.phone_number)}`;
|
||||
}
|
||||
cardForm.fields.cardNumber = false;
|
||||
setFieldState(_dom.cardNumberField, 'error', `Card number is in use and belongs to ${ownerString}.`);
|
||||
setFieldState(_dom.cardNumberField, "error", `Card number is in use and belongs to ${ownerString}.`);
|
||||
} else if (card && user === null) {
|
||||
cardForm.fields.cardNumber = true;
|
||||
setFieldState(_dom.cardNumberField, 'success');
|
||||
setFieldState(_dom.cardNumberField, "success");
|
||||
} else {
|
||||
cardForm.fields.cardNumber = true;
|
||||
setFieldState(_dom.cardNumberField, '');
|
||||
setFieldState(_dom.cardNumberField, "");
|
||||
}
|
||||
updateSubmitButton();
|
||||
}).fail(() => {
|
||||
cardForm.fields.cardNumber = false;
|
||||
setFieldState(_dom.cardNumberField, 'error', 'Cannot find card number in database.');
|
||||
setFieldState(_dom.cardNumberField, "error", "Cannot find card number in database.");
|
||||
});
|
||||
});
|
||||
|
||||
/* User search as you type */
|
||||
_dom.query.on('input', () => {
|
||||
_dom.query.on("input", () => {
|
||||
const val = _dom.query.val().trim();
|
||||
if (val.length <= 2) {
|
||||
_dom.results.html('');
|
||||
_dom.results.html("");
|
||||
return;
|
||||
}
|
||||
if (searching) {
|
||||
@@ -465,38 +464,38 @@ $(document).ready(function() {
|
||||
users = data.results;
|
||||
} else if (data.error) {
|
||||
_dom.results.html(data.error);
|
||||
} else if (val !== '') {
|
||||
} else if (val !== "") {
|
||||
_dom.results.html(`Found no user matching: '${val}'`);
|
||||
} else {
|
||||
_dom.results.html('');
|
||||
_dom.results.html("");
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
/* On search result click */
|
||||
_dom.results.on('click', '.search-result input', () => {
|
||||
_dom.results.on("click", ".search-result input", () => {
|
||||
const label = $(this).parent();
|
||||
let number = label.attr('data-phone-number');
|
||||
const userId = label.attr('data-user-id');
|
||||
let number = label.attr("data-phone-number");
|
||||
const userId = label.attr("data-user-id");
|
||||
|
||||
/* Update selected state */
|
||||
$('.search-result').removeClass('selected');
|
||||
label.toggleClass('selected');
|
||||
$(".search-result").removeClass("selected");
|
||||
label.toggleClass("selected");
|
||||
|
||||
setSelectedUser(users.find(({ id }) => id === userId));
|
||||
|
||||
/* Update phone number field */
|
||||
if (number === '-') {
|
||||
number = '';
|
||||
if (number === "-") {
|
||||
number = "";
|
||||
}
|
||||
_dom.phoneNumberField.val(number).trigger('input');
|
||||
_dom.phoneNumberField.val(number).trigger("input");
|
||||
});
|
||||
/* On submit button click */
|
||||
_dom.registerSubmitButton.on('click', (e) => {
|
||||
_dom.registerSubmitButton.on("click", (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
if (!cardFormIsValid()) {
|
||||
setToast('Either phone number or card number is not valid', 'error');
|
||||
setToast("Either phone number or card number is not valid", "error");
|
||||
return;
|
||||
}
|
||||
const payload = getFormData(_dom.registerCardForm);
|
||||
@@ -506,66 +505,66 @@ $(document).ready(function() {
|
||||
// action: 'new_card_membership', 'update_card', 'add_or_renew', 'sms_card_notify'
|
||||
if (selectedUser === null && membershipOrder === null) {
|
||||
// Unknown phone / card, new membership
|
||||
payload.action = 'new_card_membership';
|
||||
payload.action = "new_card_membership";
|
||||
} else if (selectedUser === null && membershipOrder !== null) {
|
||||
if (membershipOrder.member_card === null && membershipOrder.product.is_valid) {
|
||||
// Associate member card with a valid membership order
|
||||
payload.action = 'sms_card_notify';
|
||||
payload.action = "sms_card_notify";
|
||||
payload.order_uuid = membershipOrder.uuid;
|
||||
} else {
|
||||
// New card for expired order -- treat as new card membership
|
||||
payload.action = 'new_card_membership';
|
||||
payload.action = "new_card_membership";
|
||||
}
|
||||
} else if (selectedUser !== null && selectedUser.is_member) {
|
||||
// Add member card to a user
|
||||
payload.action = 'update_card';
|
||||
payload.action = "update_card";
|
||||
} else {
|
||||
// New order for user
|
||||
payload.action = 'add_or_renew'; // Note: could also update card number
|
||||
payload.action = "add_or_renew"; // Note: could also update card number
|
||||
}
|
||||
$.ajax(urls.registerCardAndMembership, {
|
||||
data: JSON.stringify(payload),
|
||||
contentType: 'application/json',
|
||||
dataType: 'json',
|
||||
type: 'post',
|
||||
headers: { 'X-CSRFToken': getCookie('csrftoken') }
|
||||
contentType: "application/json",
|
||||
dataType: "json",
|
||||
type: "post",
|
||||
headers: { "X-CSRFToken": getCookie("csrftoken") },
|
||||
})
|
||||
.done(() => {
|
||||
let successMessage = `New card registered ${payload.card_number}`;
|
||||
if (payload.action === 'add_or_renew') {
|
||||
if (payload.action === "add_or_renew") {
|
||||
const fullName = `${selectedUser.first_name} ${selectedUser.last_name}`;
|
||||
successMessage = `New membership and card number ${payload.card_number} registered to ${fullName} `;
|
||||
} else if (payload.action === 'new_card_membership') {
|
||||
} else if (payload.action === "new_card_membership") {
|
||||
successMessage = `New membership and card registered to ${formatPhoneNumber(
|
||||
payload.phone_number
|
||||
)}. Activation SMS sent `;
|
||||
} else if (payload.action === 'sms_card_notify') {
|
||||
} else if (payload.action === "sms_card_notify") {
|
||||
successMessage = `New card registered to ${formatPhoneNumber(
|
||||
payload.phone_number
|
||||
)}. Membership already paid ${membershipOrder.product.start_date}. Activation SMS sent `;
|
||||
}
|
||||
setToast(`${successMessage} :-)`, 'success');
|
||||
setToast(`${successMessage} :-)`, "success");
|
||||
resetCardForm(true);
|
||||
})
|
||||
.fail((data) => {
|
||||
console.log('failed', data);
|
||||
console.log("failed", data);
|
||||
let errorText = data.responseText;
|
||||
if (data.responseJSON) {
|
||||
if (data.responseJSON.non_field_errors) {
|
||||
errorText = data.responseJSON.non_field_errors.join('<br>');
|
||||
errorText = data.responseJSON.non_field_errors.join("<br>");
|
||||
} else {
|
||||
errorText = 'Not sure why. :-(';
|
||||
errorText = "Not sure why. :-(";
|
||||
}
|
||||
}
|
||||
setToast(`Failed! ${errorText}`, 'error');
|
||||
setToast(`Failed! ${errorText}`, "error");
|
||||
});
|
||||
});
|
||||
|
||||
/* Membership renewal button */
|
||||
_dom.membershipSubmitButton.on('click', (e) => {
|
||||
_dom.membershipSubmitButton.on("click", (e) => {
|
||||
e.preventDefault();
|
||||
if (!selectedUser && !membershipOrder) {
|
||||
setToast('Computer says no.', 'error');
|
||||
setToast("Computer says no.", "error");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -579,62 +578,62 @@ $(document).ready(function() {
|
||||
|
||||
$.ajax(urls.renewMembership, {
|
||||
data: JSON.stringify(payload),
|
||||
contentType: 'application/json',
|
||||
dataType: 'json',
|
||||
type: 'post',
|
||||
headers: { 'X-CSRFToken': getCookie('csrftoken') }
|
||||
contentType: "application/json",
|
||||
dataType: "json",
|
||||
type: "post",
|
||||
headers: { "X-CSRFToken": getCookie("csrftoken") },
|
||||
})
|
||||
.done((data) => {
|
||||
let name = 'unknown';
|
||||
let name = "unknown";
|
||||
if (data.user) {
|
||||
name = `${selectedUser.first_name} ${selectedUser.last_name}`;
|
||||
} else {
|
||||
name = `phone number ${data.phone_number}`;
|
||||
}
|
||||
const successMessage = `Membership renewed for ${name} :-)`;
|
||||
setToast(successMessage, 'success');
|
||||
setToast(successMessage, "success");
|
||||
resetCardForm(true);
|
||||
})
|
||||
.fail((data) => {
|
||||
console.log('failed', data);
|
||||
console.log("failed", data);
|
||||
let errorText = data.responseText;
|
||||
if (data.responseJSON) {
|
||||
if (data.responseJSON.non_field_errors) {
|
||||
errorText = data.responseJSON.non_field_errors.join('<br>');
|
||||
errorText = data.responseJSON.non_field_errors.join("<br>");
|
||||
} else {
|
||||
errorText = 'Not sure why. :-(';
|
||||
errorText = "Not sure why. :-(";
|
||||
}
|
||||
}
|
||||
setToast(`Failed! ${errorText}`, 'error');
|
||||
setToast(`Failed! ${errorText}`, "error");
|
||||
});
|
||||
});
|
||||
|
||||
/* On user id change */
|
||||
_dom.userIdField.on('change', function() {
|
||||
_dom.userIdField.on("change", function () {
|
||||
/* Render selected user in search form */
|
||||
renderSelectedUser(selectedUser);
|
||||
});
|
||||
/* On order uuid change */
|
||||
_dom.orderUuidField.on('change', function() {
|
||||
_dom.orderUuidField.on("change", function () {
|
||||
/* Render selected order */
|
||||
renderSelectedOrder(membershipOrder);
|
||||
});
|
||||
/* Reset buttons */
|
||||
_dom.registerResetButton.on('click', function() {
|
||||
_dom.registerResetButton.on("click", function () {
|
||||
resetCardForm();
|
||||
});
|
||||
_dom.searchResetButton.on('click', function() {
|
||||
_dom.searchResetButton.on("click", function () {
|
||||
resetSearchForm();
|
||||
});
|
||||
|
||||
/* Ninja add some icons to bootstrap form */
|
||||
const iconMobile =
|
||||
'<span class="input-group-addon"><span class="glyphicon glyphicon-phone glyphicon-phone-large"></span></span>';
|
||||
_dom.phoneNumberField.parent().addClass('input-group');
|
||||
_dom.phoneNumberField.parent().addClass("input-group");
|
||||
_dom.phoneNumberField.before(iconMobile);
|
||||
|
||||
/* Render initial placeholder user */
|
||||
const selectedUserHtml = nunjucksEnv.render('search_result.html', { placeholder: true });
|
||||
const selectedUserHtml = nunjucksEnv.render("search_result.html", { placeholder: true });
|
||||
_dom.selectedEntityWrap.html(selectedUserHtml);
|
||||
|
||||
/* Initial focus */
|
||||
@@ -643,18 +642,18 @@ $(document).ready(function() {
|
||||
}
|
||||
|
||||
/* Load query params */
|
||||
if (getParameterByName('user_id')) {
|
||||
_dom.userIdField.val(getParameterByName('user_id')).trigger('change');
|
||||
if (getParameterByName("user_id")) {
|
||||
_dom.userIdField.val(getParameterByName("user_id")).trigger("change");
|
||||
}
|
||||
if (getParameterByName('phone_number')) {
|
||||
_dom.phoneNumberField.val(getParameterByName('phone_number')).trigger('input');
|
||||
if (getParameterByName("phone_number")) {
|
||||
_dom.phoneNumberField.val(getParameterByName("phone_number")).trigger("input");
|
||||
}
|
||||
if (getParameterByName('card_number')) {
|
||||
_dom.cardNumberField.val(getParameterByName('card_number')).trigger('input');
|
||||
if (getParameterByName("card_number")) {
|
||||
_dom.cardNumberField.val(getParameterByName("card_number")).trigger("input");
|
||||
}
|
||||
|
||||
$('[data-toggle="popover"]').popover({
|
||||
html: true,
|
||||
trigger: 'click'
|
||||
trigger: "click",
|
||||
});
|
||||
});
|
||||
@@ -1,134 +0,0 @@
|
||||
/* global require */
|
||||
'use strict';
|
||||
var gulp = require('gulp');
|
||||
|
||||
// load plugins
|
||||
var $ = require('gulp-load-plugins')();
|
||||
var bowerFiles = require('main-bower-files');
|
||||
var del = require('del');
|
||||
var browserSync = require('browser-sync').create();
|
||||
var reload = browserSync.reload;
|
||||
|
||||
gulp.task('styles', function () {
|
||||
gulp.src('app/styles/*.scss')
|
||||
.pipe($.sass({
|
||||
includePaths: ['bower_components/foundation/scss'],
|
||||
sourceComments: 'map' // TODO does this work?
|
||||
}).on('error', $.sass.logError))
|
||||
.pipe($.autoprefixer('last 1 version'))
|
||||
.pipe(gulp.dest('dist/styles'))
|
||||
.pipe(browserSync.stream())
|
||||
.pipe($.size());
|
||||
});
|
||||
|
||||
gulp.task('diststyles', function () {
|
||||
return gulp.src('app/styles/*.scss')
|
||||
.pipe($.sass({
|
||||
includePaths: ['bower_components/foundation/scss']
|
||||
}))
|
||||
.pipe($.autoprefixer('last 1 version'))
|
||||
.pipe($.csso())
|
||||
.pipe(gulp.dest('dist/styles'))
|
||||
.pipe(browserSync.stream())
|
||||
.pipe($.size());
|
||||
});
|
||||
|
||||
gulp.task('vendorscripts', function () {
|
||||
return gulp.src(bowerFiles())
|
||||
.pipe($.filter('**/*.js'))
|
||||
//.pipe($.uglify())
|
||||
.pipe($.concat('vendor.js'))
|
||||
.pipe(gulp.dest('dist/scripts'))
|
||||
.pipe($.size());
|
||||
});
|
||||
|
||||
gulp.task('vendorstyles', function () {
|
||||
// TODO: copy styles to scss-files instead?
|
||||
return gulp.src(bowerFiles())
|
||||
.pipe($.filter('**/*.css'))
|
||||
.pipe($.concat('vendor.css'))
|
||||
//.pipe($.csso())
|
||||
.pipe(gulp.dest('dist/styles'))
|
||||
.pipe($.size());
|
||||
});
|
||||
|
||||
gulp.task('scripts', function () {
|
||||
return gulp.src('app/scripts/**/*.js')
|
||||
.pipe($.jshint())
|
||||
.pipe($.jshint.reporter(require('jshint-stylish')))
|
||||
.pipe(gulp.dest('dist/scripts'))
|
||||
.pipe($.size());
|
||||
});
|
||||
|
||||
gulp.task('distscripts', function () {
|
||||
return gulp.src('app/scripts/**/*.js')
|
||||
.pipe($.jshint())
|
||||
.pipe($.jshint.reporter(require('jshint-stylish')))
|
||||
//.pipe($.uglify())
|
||||
.pipe(gulp.dest('dist/scripts'))
|
||||
.pipe($.size());
|
||||
});
|
||||
|
||||
gulp.task('images', function () {
|
||||
return gulp.src('app/images/**/*')
|
||||
.pipe($.imagemin({
|
||||
optimizationLevel: 3,
|
||||
progressive: true,
|
||||
interlaced: true
|
||||
}))
|
||||
.pipe(gulp.dest('dist/images'))
|
||||
.pipe($.size());
|
||||
});
|
||||
|
||||
gulp.task('fonts', function () {
|
||||
return gulp.src(bowerFiles().concat(['app/fonts/**/*.*']))
|
||||
.pipe($.filter('**/*.{eot,svg,ttf,woff,woff2}'))
|
||||
.pipe($.flatten())
|
||||
.pipe(gulp.dest('dist/fonts'))
|
||||
.pipe($.size());
|
||||
});
|
||||
|
||||
gulp.task('extras', function () {
|
||||
return gulp.src(['app/*.*', '!app/*.php'], { dot: true })
|
||||
.pipe(gulp.dest('dist'));
|
||||
});
|
||||
|
||||
gulp.task('templates', function () {
|
||||
return gulp.src('app/templates/*.html')
|
||||
.pipe($.nunjucks())
|
||||
.pipe($.concat('templates.js'))
|
||||
.pipe(gulp.dest('dist/scripts'))
|
||||
.pipe($.size());
|
||||
});
|
||||
|
||||
gulp.task('clean', function (cb) {
|
||||
del(['dist'], cb);
|
||||
});
|
||||
|
||||
gulp.task('build', ['vendorscripts', 'vendorstyles', 'distscripts', 'diststyles', 'images', 'fonts', 'extras', 'templates']);
|
||||
|
||||
gulp.task('default', ['clean'], function () {
|
||||
gulp.start('build');
|
||||
});
|
||||
|
||||
gulp.task('serve', ['styles'], function () {
|
||||
});
|
||||
|
||||
gulp.task('watch', ['serve'], function () {
|
||||
browserSync.init({
|
||||
proxy: 'localhost:8001'
|
||||
});
|
||||
|
||||
// watch for changes
|
||||
gulp.watch([
|
||||
'../apps/kassa/templates/**/*.html',
|
||||
'dist/scripts/**/*.js',
|
||||
'dist/images/**/*'
|
||||
]).on('change', reload);
|
||||
|
||||
gulp.watch('app/styles/**/*.scss', ['styles']);
|
||||
gulp.watch('app/scripts/**/*.js', ['scripts']);
|
||||
gulp.watch('app/templates/**/*.html', ['templates']);
|
||||
gulp.watch('app/images/**/*', ['images']);
|
||||
gulp.watch('bower.json', ['vendorscripts']);
|
||||
});
|
||||
Generated
+6733
-9908
File diff suppressed because it is too large.
Load diff
+19
-33
@@ -2,51 +2,37 @@
|
||||
"name": "kassa",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"build": "webpack -p --env production --display-error-details --mode production",
|
||||
"build": "rspack build --mode production",
|
||||
"build:templates": "echo '/* eslint-disable */' > app/templates.js && nunjucks-precompile app/templates >> app/templates.js",
|
||||
"start": "webpack-dev-server --hot --env development --mode development",
|
||||
"start": "rspack serve --mode development",
|
||||
"format": "eslint --fix \"app/**/*.js\"",
|
||||
"lint": "eslint --quiet \"app/**/*.js\""
|
||||
},
|
||||
"dependencies": {
|
||||
"bootstrap-sass": "^3.4.1",
|
||||
"jquery": "^3.4.1",
|
||||
"bootstrap-sass": "^3.4.3",
|
||||
"jquery": "^3.7.1",
|
||||
"list.js": "^1.5.0",
|
||||
"lodash.debounce": "^4.0.8",
|
||||
"lodash.frompairs": "^4.0.1",
|
||||
"lodash.sortby": "^4.7.0",
|
||||
"moment": "^2.24.0",
|
||||
"nunjucks": "^3.2.0"
|
||||
"moment": "^2.30.1",
|
||||
"nunjucks": "^3.2.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.7.7",
|
||||
"@babel/plugin-syntax-dynamic-import": "^7.7.4",
|
||||
"@babel/plugin-transform-runtime": "^7.7.6",
|
||||
"@babel/preset-env": "^7.7.7",
|
||||
"@babel/preset-react": "^7.7.4",
|
||||
"babel-eslint": "^10.0.3",
|
||||
"babel-loader": "^8.0.6",
|
||||
"clean-webpack-plugin": "^3.0.0",
|
||||
"css-loader": "^3.4.0",
|
||||
"eslint": "^6.8.0",
|
||||
"eslint-config-airbnb-base": "^14.0.0",
|
||||
"eslint-config-prettier": "^6.7.0",
|
||||
"eslint-import-resolver-babel-module": "^5.1.0",
|
||||
"eslint-plugin-import": "^2.19.1",
|
||||
"eslint-plugin-prettier": "^3.1.2",
|
||||
"file-loader": "^5.0.2",
|
||||
"html-webpack-plugin": "^3.2.0",
|
||||
"mini-css-extract-plugin": "^0.9.0",
|
||||
"node-sass": "^4.13.0",
|
||||
"prettier": "^1.19.1",
|
||||
"sass-loader": "^8.0.0",
|
||||
"style-loader": "^1.1.2",
|
||||
"url-loader": "^3.0.0",
|
||||
"webpack": "^4.41.5",
|
||||
"webpack-cli": "^3.3.10",
|
||||
"webpack-dev-server": "^3.10.1"
|
||||
"@rspack/cli": "^1.3.13",
|
||||
"@rspack/core": "^1.3.13",
|
||||
"css-loader": "^7.1.2",
|
||||
"eslint": "^8.13.0",
|
||||
"eslint-config-prettier": "^8.5.0",
|
||||
"eslint-plugin-import": "^2.26.0",
|
||||
"eslint-plugin-prettier": "^4.0.0",
|
||||
"file-loader": "^6.2.0",
|
||||
"prettier": "^2.6.2",
|
||||
"sass-embedded": "^1.89.1",
|
||||
"sass-loader": "^16.0.5",
|
||||
"style-loader": "^4.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "12"
|
||||
"node": "20"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
const path = require('path');
|
||||
|
||||
const { defineConfig } = require('@rspack/cli');
|
||||
const { rspack } = require('@rspack/core');
|
||||
|
||||
module.exports = defineConfig({
|
||||
entry: './app/index.js',
|
||||
output: {
|
||||
path: path.resolve(__dirname, 'dist'),
|
||||
filename: '[name].js',
|
||||
},
|
||||
resolve: {
|
||||
extensions: ['.jsx', '.js', '.json', '.scss', '.css'],
|
||||
modules: ['node_modules'],
|
||||
},
|
||||
plugins: [
|
||||
new rspack.CopyRspackPlugin({
|
||||
patterns: [
|
||||
{
|
||||
from: path.resolve(__dirname, 'app', 'images'),
|
||||
to: path.resolve(__dirname, 'dist', 'images'),
|
||||
},
|
||||
],
|
||||
}),
|
||||
new rspack.CssExtractRspackPlugin(),
|
||||
new rspack.ProvidePlugin({
|
||||
jQuery: 'jquery',
|
||||
}),
|
||||
],
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.(png|jpg|gif|svg)$/,
|
||||
use: [
|
||||
{
|
||||
loader: require.resolve('file-loader'),
|
||||
options: {},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
test: /\.(woff|woff2|eot|ttf|otf)$/,
|
||||
type: 'asset',
|
||||
},
|
||||
{
|
||||
test: /\.css$/,
|
||||
use: [rspack.CssExtractRspackPlugin.loader, require.resolve('css-loader')],
|
||||
},
|
||||
{
|
||||
test: /\.scss$/,
|
||||
use: [
|
||||
rspack.CssExtractRspackPlugin.loader,
|
||||
{
|
||||
loader: require.resolve('css-loader'),
|
||||
options: {
|
||||
importLoaders: 1,
|
||||
modules: false,
|
||||
sourceMap: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
loader: require.resolve('sass-loader'),
|
||||
options: {
|
||||
api: 'modern-compiler',
|
||||
implementation: require.resolve('sass-embedded'),
|
||||
sassOptions: {
|
||||
silenceDeprecations: ['global-builtin', 'legacy-js-api', 'mixed-decls', 'color-functions', 'import'],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
// FIXME(nikolark): This workflow does not work
|
||||
devServer: {
|
||||
port: 8001,
|
||||
proxy: [
|
||||
{
|
||||
context: '/api',
|
||||
target: 'http://localhost:8000',
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
@@ -1,96 +0,0 @@
|
||||
const path = require('path');
|
||||
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
|
||||
const webpack = require('webpack');
|
||||
const HtmlWebpackPlugin = require('html-webpack-plugin');
|
||||
|
||||
const baseStyleLoader = (env) => (env === 'production' ? MiniCssExtractPlugin.loader : require.resolve('style-loader'));
|
||||
|
||||
module.exports = (env, argv) => {
|
||||
return {
|
||||
entry: './app/index.js',
|
||||
output: {
|
||||
filename: '[name].js',
|
||||
path: path.resolve(__dirname, 'dist')
|
||||
},
|
||||
resolve: {
|
||||
extensions: ['.jsx', '.js', '.json', '.scss', '.css'],
|
||||
modules: ['node_modules']
|
||||
},
|
||||
plugins: [
|
||||
new MiniCssExtractPlugin(),
|
||||
env === 'production' ? new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/) : null,
|
||||
env !== 'production' ? new HtmlWebpackPlugin({ template: './app/index.html' }) : null,
|
||||
new webpack.ProvidePlugin({
|
||||
jQuery: 'jquery'
|
||||
})
|
||||
].filter(Boolean),
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.(png|jpg|gif|svg)$/,
|
||||
use: [
|
||||
{
|
||||
loader: require.resolve('file-loader'),
|
||||
options: {}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
test: /\.(woff|woff2|eot|ttf|otf)$/,
|
||||
use: require.resolve('file-loader')
|
||||
},
|
||||
{
|
||||
test: /\.(js|jsx)$/,
|
||||
exclude: /node_modules/,
|
||||
loader: require.resolve('babel-loader')
|
||||
},
|
||||
{
|
||||
test: /\.css$/,
|
||||
loaders: [baseStyleLoader(env), require.resolve('css-loader')]
|
||||
},
|
||||
{
|
||||
test: /\.scss$/,
|
||||
loaders: [
|
||||
baseStyleLoader(env),
|
||||
{
|
||||
loader: require.resolve('css-loader'),
|
||||
options: {
|
||||
importLoaders: 1,
|
||||
modules: false,
|
||||
sourceMap: true
|
||||
}
|
||||
},
|
||||
{
|
||||
loader: require.resolve('sass-loader'),
|
||||
options: {
|
||||
sassOptions: {
|
||||
precision: 8
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
devServer: {
|
||||
contentBase: path.resolve(__dirname, 'dist'),
|
||||
historyApiFallback: true,
|
||||
overlay: {
|
||||
errors: true,
|
||||
warnings: false
|
||||
},
|
||||
port: 8001,
|
||||
publicPath: '/',
|
||||
proxy: {
|
||||
'/api': 'http://localhost:8000'
|
||||
// TODO: get urls
|
||||
},
|
||||
stats: {
|
||||
assets: false,
|
||||
modules: false,
|
||||
chunks: false
|
||||
}
|
||||
},
|
||||
devtool: env === 'production' ? 'source-map' : 'cheap-module-eval-source-map'
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,580 @@
|
||||
version = 1
|
||||
revision = 2
|
||||
requires-python = ">=3.12"
|
||||
|
||||
[[package]]
|
||||
name = "asgiref"
|
||||
version = "3.8.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/29/38/b3395cc9ad1b56d2ddac9970bc8f4141312dbaec28bc7c218b0dfafd0f42/asgiref-3.8.1.tar.gz", hash = "sha256:c343bd80a0bec947a9860adb4c432ffa7db769836c64238fc34bdc3fec84d590", size = 35186, upload-time = "2024-03-22T14:39:36.863Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/39/e3/893e8757be2612e6c266d9bb58ad2e3651524b5b40cf56761e985a28b13e/asgiref-3.8.1-py3-none-any.whl", hash = "sha256:3e1e3ecc849832fe52ccf2cb6686b7a55f82bb1d6aee72a58826471390335e47", size = 23828, upload-time = "2024-03-22T14:39:34.521Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bcrypt"
|
||||
version = "4.3.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/bb/5d/6d7433e0f3cd46ce0b43cd65e1db465ea024dbb8216fb2404e919c2ad77b/bcrypt-4.3.0.tar.gz", hash = "sha256:3a3fd2204178b6d2adcf09cb4f6426ffef54762577a7c9b54c159008cb288c18", size = 25697, upload-time = "2025-02-28T01:24:09.174Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/bf/2c/3d44e853d1fe969d229bd58d39ae6902b3d924af0e2b5a60d17d4b809ded/bcrypt-4.3.0-cp313-cp313t-macosx_10_12_universal2.whl", hash = "sha256:f01e060f14b6b57bbb72fc5b4a83ac21c443c9a2ee708e04a10e9192f90a6281", size = 483719, upload-time = "2025-02-28T01:22:34.539Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a1/e2/58ff6e2a22eca2e2cff5370ae56dba29d70b1ea6fc08ee9115c3ae367795/bcrypt-4.3.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c5eeac541cefd0bb887a371ef73c62c3cd78535e4887b310626036a7c0a817bb", size = 272001, upload-time = "2025-02-28T01:22:38.078Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/37/1f/c55ed8dbe994b1d088309e366749633c9eb90d139af3c0a50c102ba68a1a/bcrypt-4.3.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:59e1aa0e2cd871b08ca146ed08445038f42ff75968c7ae50d2fdd7860ade2180", size = 277451, upload-time = "2025-02-28T01:22:40.787Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d7/1c/794feb2ecf22fe73dcfb697ea7057f632061faceb7dcf0f155f3443b4d79/bcrypt-4.3.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:0042b2e342e9ae3d2ed22727c1262f76cc4f345683b5c1715f0250cf4277294f", size = 272792, upload-time = "2025-02-28T01:22:43.144Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/13/b7/0b289506a3f3598c2ae2bdfa0ea66969812ed200264e3f61df77753eee6d/bcrypt-4.3.0-cp313-cp313t-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:74a8d21a09f5e025a9a23e7c0fd2c7fe8e7503e4d356c0a2c1486ba010619f09", size = 289752, upload-time = "2025-02-28T01:22:45.56Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/dc/24/d0fb023788afe9e83cc118895a9f6c57e1044e7e1672f045e46733421fe6/bcrypt-4.3.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:0142b2cb84a009f8452c8c5a33ace5e3dfec4159e7735f5afe9a4d50a8ea722d", size = 277762, upload-time = "2025-02-28T01:22:47.023Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e4/38/cde58089492e55ac4ef6c49fea7027600c84fd23f7520c62118c03b4625e/bcrypt-4.3.0-cp313-cp313t-manylinux_2_34_aarch64.whl", hash = "sha256:12fa6ce40cde3f0b899729dbd7d5e8811cb892d31b6f7d0334a1f37748b789fd", size = 272384, upload-time = "2025-02-28T01:22:49.221Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/de/6a/d5026520843490cfc8135d03012a413e4532a400e471e6188b01b2de853f/bcrypt-4.3.0-cp313-cp313t-manylinux_2_34_x86_64.whl", hash = "sha256:5bd3cca1f2aa5dbcf39e2aa13dd094ea181f48959e1071265de49cc2b82525af", size = 277329, upload-time = "2025-02-28T01:22:51.603Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b3/a3/4fc5255e60486466c389e28c12579d2829b28a527360e9430b4041df4cf9/bcrypt-4.3.0-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:335a420cfd63fc5bc27308e929bee231c15c85cc4c496610ffb17923abf7f231", size = 305241, upload-time = "2025-02-28T01:22:53.283Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c7/15/2b37bc07d6ce27cc94e5b10fd5058900eb8fb11642300e932c8c82e25c4a/bcrypt-4.3.0-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:0e30e5e67aed0187a1764911af023043b4542e70a7461ad20e837e94d23e1d6c", size = 309617, upload-time = "2025-02-28T01:22:55.461Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5f/1f/99f65edb09e6c935232ba0430c8c13bb98cb3194b6d636e61d93fe60ac59/bcrypt-4.3.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:3b8d62290ebefd49ee0b3ce7500f5dbdcf13b81402c05f6dafab9a1e1b27212f", size = 335751, upload-time = "2025-02-28T01:22:57.81Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/00/1b/b324030c706711c99769988fcb694b3cb23f247ad39a7823a78e361bdbb8/bcrypt-4.3.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:2ef6630e0ec01376f59a006dc72918b1bf436c3b571b80fa1968d775fa02fe7d", size = 355965, upload-time = "2025-02-28T01:22:59.181Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/aa/dd/20372a0579dd915dfc3b1cd4943b3bca431866fcb1dfdfd7518c3caddea6/bcrypt-4.3.0-cp313-cp313t-win32.whl", hash = "sha256:7a4be4cbf241afee43f1c3969b9103a41b40bcb3a3f467ab19f891d9bc4642e4", size = 155316, upload-time = "2025-02-28T01:23:00.763Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6d/52/45d969fcff6b5577c2bf17098dc36269b4c02197d551371c023130c0f890/bcrypt-4.3.0-cp313-cp313t-win_amd64.whl", hash = "sha256:5c1949bf259a388863ced887c7861da1df681cb2388645766c89fdfd9004c669", size = 147752, upload-time = "2025-02-28T01:23:02.908Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/11/22/5ada0b9af72b60cbc4c9a399fdde4af0feaa609d27eb0adc61607997a3fa/bcrypt-4.3.0-cp38-abi3-macosx_10_12_universal2.whl", hash = "sha256:f81b0ed2639568bf14749112298f9e4e2b28853dab50a8b357e31798686a036d", size = 498019, upload-time = "2025-02-28T01:23:05.838Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b8/8c/252a1edc598dc1ce57905be173328eda073083826955ee3c97c7ff5ba584/bcrypt-4.3.0-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:864f8f19adbe13b7de11ba15d85d4a428c7e2f344bac110f667676a0ff84924b", size = 279174, upload-time = "2025-02-28T01:23:07.274Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/29/5b/4547d5c49b85f0337c13929f2ccbe08b7283069eea3550a457914fc078aa/bcrypt-4.3.0-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3e36506d001e93bffe59754397572f21bb5dc7c83f54454c990c74a468cd589e", size = 283870, upload-time = "2025-02-28T01:23:09.151Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/be/21/7dbaf3fa1745cb63f776bb046e481fbababd7d344c5324eab47f5ca92dd2/bcrypt-4.3.0-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:842d08d75d9fe9fb94b18b071090220697f9f184d4547179b60734846461ed59", size = 279601, upload-time = "2025-02-28T01:23:11.461Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6d/64/e042fc8262e971347d9230d9abbe70d68b0a549acd8611c83cebd3eaec67/bcrypt-4.3.0-cp38-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:7c03296b85cb87db865d91da79bf63d5609284fc0cab9472fdd8367bbd830753", size = 297660, upload-time = "2025-02-28T01:23:12.989Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/50/b8/6294eb84a3fef3b67c69b4470fcdd5326676806bf2519cda79331ab3c3a9/bcrypt-4.3.0-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:62f26585e8b219cdc909b6a0069efc5e4267e25d4a3770a364ac58024f62a761", size = 284083, upload-time = "2025-02-28T01:23:14.5Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/62/e6/baff635a4f2c42e8788fe1b1633911c38551ecca9a749d1052d296329da6/bcrypt-4.3.0-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:beeefe437218a65322fbd0069eb437e7c98137e08f22c4660ac2dc795c31f8bb", size = 279237, upload-time = "2025-02-28T01:23:16.686Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/39/48/46f623f1b0c7dc2e5de0b8af5e6f5ac4cc26408ac33f3d424e5ad8da4a90/bcrypt-4.3.0-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:97eea7408db3a5bcce4a55d13245ab3fa566e23b4c67cd227062bb49e26c585d", size = 283737, upload-time = "2025-02-28T01:23:18.897Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/49/8b/70671c3ce9c0fca4a6cc3cc6ccbaa7e948875a2e62cbd146e04a4011899c/bcrypt-4.3.0-cp38-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:191354ebfe305e84f344c5964c7cd5f924a3bfc5d405c75ad07f232b6dffb49f", size = 312741, upload-time = "2025-02-28T01:23:21.041Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/27/fb/910d3a1caa2d249b6040a5caf9f9866c52114d51523ac2fb47578a27faee/bcrypt-4.3.0-cp38-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:41261d64150858eeb5ff43c753c4b216991e0ae16614a308a15d909503617732", size = 316472, upload-time = "2025-02-28T01:23:23.183Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/dc/cf/7cf3a05b66ce466cfb575dbbda39718d45a609daa78500f57fa9f36fa3c0/bcrypt-4.3.0-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:33752b1ba962ee793fa2b6321404bf20011fe45b9afd2a842139de3011898fef", size = 343606, upload-time = "2025-02-28T01:23:25.361Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e3/b8/e970ecc6d7e355c0d892b7f733480f4aa8509f99b33e71550242cf0b7e63/bcrypt-4.3.0-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:50e6e80a4bfd23a25f5c05b90167c19030cf9f87930f7cb2eacb99f45d1c3304", size = 362867, upload-time = "2025-02-28T01:23:26.875Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a9/97/8d3118efd8354c555a3422d544163f40d9f236be5b96c714086463f11699/bcrypt-4.3.0-cp38-abi3-win32.whl", hash = "sha256:67a561c4d9fb9465ec866177e7aebcad08fe23aaf6fbd692a6fab69088abfc51", size = 160589, upload-time = "2025-02-28T01:23:28.381Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/29/07/416f0b99f7f3997c69815365babbc2e8754181a4b1899d921b3c7d5b6f12/bcrypt-4.3.0-cp38-abi3-win_amd64.whl", hash = "sha256:584027857bc2843772114717a7490a37f68da563b3620f78a849bcb54dc11e62", size = 152794, upload-time = "2025-02-28T01:23:30.187Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6e/c1/3fa0e9e4e0bfd3fd77eb8b52ec198fd6e1fd7e9402052e43f23483f956dd/bcrypt-4.3.0-cp39-abi3-macosx_10_12_universal2.whl", hash = "sha256:0d3efb1157edebfd9128e4e46e2ac1a64e0c1fe46fb023158a407c7892b0f8c3", size = 498969, upload-time = "2025-02-28T01:23:31.945Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ce/d4/755ce19b6743394787fbd7dff6bf271b27ee9b5912a97242e3caf125885b/bcrypt-4.3.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:08bacc884fd302b611226c01014eca277d48f0a05187666bca23aac0dad6fe24", size = 279158, upload-time = "2025-02-28T01:23:34.161Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9b/5d/805ef1a749c965c46b28285dfb5cd272a7ed9fa971f970435a5133250182/bcrypt-4.3.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f6746e6fec103fcd509b96bacdfdaa2fbde9a553245dbada284435173a6f1aef", size = 284285, upload-time = "2025-02-28T01:23:35.765Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ab/2b/698580547a4a4988e415721b71eb45e80c879f0fb04a62da131f45987b96/bcrypt-4.3.0-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:afe327968aaf13fc143a56a3360cb27d4ad0345e34da12c7290f1b00b8fe9a8b", size = 279583, upload-time = "2025-02-28T01:23:38.021Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f2/87/62e1e426418204db520f955ffd06f1efd389feca893dad7095bf35612eec/bcrypt-4.3.0-cp39-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:d9af79d322e735b1fc33404b5765108ae0ff232d4b54666d46730f8ac1a43676", size = 297896, upload-time = "2025-02-28T01:23:39.575Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/cb/c6/8fedca4c2ada1b6e889c52d2943b2f968d3427e5d65f595620ec4c06fa2f/bcrypt-4.3.0-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:f1e3ffa1365e8702dc48c8b360fef8d7afeca482809c5e45e653af82ccd088c1", size = 284492, upload-time = "2025-02-28T01:23:40.901Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/4d/4d/c43332dcaaddb7710a8ff5269fcccba97ed3c85987ddaa808db084267b9a/bcrypt-4.3.0-cp39-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:3004df1b323d10021fda07a813fd33e0fd57bef0e9a480bb143877f6cba996fe", size = 279213, upload-time = "2025-02-28T01:23:42.653Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/dc/7f/1e36379e169a7df3a14a1c160a49b7b918600a6008de43ff20d479e6f4b5/bcrypt-4.3.0-cp39-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:531457e5c839d8caea9b589a1bcfe3756b0547d7814e9ce3d437f17da75c32b0", size = 284162, upload-time = "2025-02-28T01:23:43.964Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/1c/0a/644b2731194b0d7646f3210dc4d80c7fee3ecb3a1f791a6e0ae6bb8684e3/bcrypt-4.3.0-cp39-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:17a854d9a7a476a89dcef6c8bd119ad23e0f82557afbd2c442777a16408e614f", size = 312856, upload-time = "2025-02-28T01:23:46.011Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/dc/62/2a871837c0bb6ab0c9a88bf54de0fc021a6a08832d4ea313ed92a669d437/bcrypt-4.3.0-cp39-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:6fb1fd3ab08c0cbc6826a2e0447610c6f09e983a281b919ed721ad32236b8b23", size = 316726, upload-time = "2025-02-28T01:23:47.575Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0c/a1/9898ea3faac0b156d457fd73a3cb9c2855c6fd063e44b8522925cdd8ce46/bcrypt-4.3.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:e965a9c1e9a393b8005031ff52583cedc15b7884fce7deb8b0346388837d6cfe", size = 343664, upload-time = "2025-02-28T01:23:49.059Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/40/f2/71b4ed65ce38982ecdda0ff20c3ad1b15e71949c78b2c053df53629ce940/bcrypt-4.3.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:79e70b8342a33b52b55d93b3a59223a844962bef479f6a0ea318ebbcadf71505", size = 363128, upload-time = "2025-02-28T01:23:50.399Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/11/99/12f6a58eca6dea4be992d6c681b7ec9410a1d9f5cf368c61437e31daa879/bcrypt-4.3.0-cp39-abi3-win32.whl", hash = "sha256:b4d4e57f0a63fd0b358eb765063ff661328f69a04494427265950c71b992a39a", size = 160598, upload-time = "2025-02-28T01:23:51.775Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a9/cf/45fb5261ece3e6b9817d3d82b2f343a505fd58674a92577923bc500bd1aa/bcrypt-4.3.0-cp39-abi3-win_amd64.whl", hash = "sha256:e53e074b120f2877a35cc6c736b8eb161377caae8925c17688bd46ba56daaa5b", size = 152799, upload-time = "2025-02-28T01:23:53.139Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "certifi"
|
||||
version = "2025.4.26"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/e8/9e/c05b3920a3b7d20d3d3310465f50348e5b3694f4f88c6daf736eef3024c4/certifi-2025.4.26.tar.gz", hash = "sha256:0a816057ea3cdefcef70270d2c515e4506bbc954f417fa5ade2021213bb8f0c6", size = 160705, upload-time = "2025-04-26T02:12:29.51Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/4a/7e/3db2bd1b1f9e95f7cddca6d6e75e2f2bd9f51b1246e546d88addca0106bd/certifi-2025.4.26-py3-none-any.whl", hash = "sha256:30350364dfe371162649852c63336a15c70c6510c2ad5015b21c2345311805f3", size = 159618, upload-time = "2025-04-26T02:12:27.662Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cffi"
|
||||
version = "1.17.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "pycparser" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/fc/97/c783634659c2920c3fc70419e3af40972dbaf758daa229a7d6ea6135c90d/cffi-1.17.1.tar.gz", hash = "sha256:1c39c6016c32bc48dd54561950ebd6836e1670f2ae46128f67cf49e789c52824", size = 516621, upload-time = "2024-09-04T20:45:21.852Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/5a/84/e94227139ee5fb4d600a7a4927f322e1d4aea6fdc50bd3fca8493caba23f/cffi-1.17.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:805b4371bf7197c329fcb3ead37e710d1bca9da5d583f5073b799d5c5bd1eee4", size = 183178, upload-time = "2024-09-04T20:44:12.232Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/da/ee/fb72c2b48656111c4ef27f0f91da355e130a923473bf5ee75c5643d00cca/cffi-1.17.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:733e99bc2df47476e3848417c5a4540522f234dfd4ef3ab7fafdf555b082ec0c", size = 178840, upload-time = "2024-09-04T20:44:13.739Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/cc/b6/db007700f67d151abadf508cbfd6a1884f57eab90b1bb985c4c8c02b0f28/cffi-1.17.1-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1257bdabf294dceb59f5e70c64a3e2f462c30c7ad68092d01bbbfb1c16b1ba36", size = 454803, upload-time = "2024-09-04T20:44:15.231Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/1a/df/f8d151540d8c200eb1c6fba8cd0dfd40904f1b0682ea705c36e6c2e97ab3/cffi-1.17.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da95af8214998d77a98cc14e3a3bd00aa191526343078b530ceb0bd710fb48a5", size = 478850, upload-time = "2024-09-04T20:44:17.188Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/28/c0/b31116332a547fd2677ae5b78a2ef662dfc8023d67f41b2a83f7c2aa78b1/cffi-1.17.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d63afe322132c194cf832bfec0dc69a99fb9bb6bbd550f161a49e9e855cc78ff", size = 485729, upload-time = "2024-09-04T20:44:18.688Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/91/2b/9a1ddfa5c7f13cab007a2c9cc295b70fbbda7cb10a286aa6810338e60ea1/cffi-1.17.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f79fc4fc25f1c8698ff97788206bb3c2598949bfe0fef03d299eb1b5356ada99", size = 471256, upload-time = "2024-09-04T20:44:20.248Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b2/d5/da47df7004cb17e4955df6a43d14b3b4ae77737dff8bf7f8f333196717bf/cffi-1.17.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b62ce867176a75d03a665bad002af8e6d54644fad99a3c70905c543130e39d93", size = 479424, upload-time = "2024-09-04T20:44:21.673Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0b/ac/2a28bcf513e93a219c8a4e8e125534f4f6db03e3179ba1c45e949b76212c/cffi-1.17.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:386c8bf53c502fff58903061338ce4f4950cbdcb23e2902d86c0f722b786bbe3", size = 484568, upload-time = "2024-09-04T20:44:23.245Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d4/38/ca8a4f639065f14ae0f1d9751e70447a261f1a30fa7547a828ae08142465/cffi-1.17.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4ceb10419a9adf4460ea14cfd6bc43d08701f0835e979bf821052f1805850fe8", size = 488736, upload-time = "2024-09-04T20:44:24.757Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/86/c5/28b2d6f799ec0bdecf44dced2ec5ed43e0eb63097b0f58c293583b406582/cffi-1.17.1-cp312-cp312-win32.whl", hash = "sha256:a08d7e755f8ed21095a310a693525137cfe756ce62d066e53f502a83dc550f65", size = 172448, upload-time = "2024-09-04T20:44:26.208Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/50/b9/db34c4755a7bd1cb2d1603ac3863f22bcecbd1ba29e5ee841a4bc510b294/cffi-1.17.1-cp312-cp312-win_amd64.whl", hash = "sha256:51392eae71afec0d0c8fb1a53b204dbb3bcabcb3c9b807eedf3e1e6ccf2de903", size = 181976, upload-time = "2024-09-04T20:44:27.578Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/8d/f8/dd6c246b148639254dad4d6803eb6a54e8c85c6e11ec9df2cffa87571dbe/cffi-1.17.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f3a2b4222ce6b60e2e8b337bb9596923045681d71e5a082783484d845390938e", size = 182989, upload-time = "2024-09-04T20:44:28.956Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/8b/f1/672d303ddf17c24fc83afd712316fda78dc6fce1cd53011b839483e1ecc8/cffi-1.17.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0984a4925a435b1da406122d4d7968dd861c1385afe3b45ba82b750f229811e2", size = 178802, upload-time = "2024-09-04T20:44:30.289Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0e/2d/eab2e858a91fdff70533cab61dcff4a1f55ec60425832ddfdc9cd36bc8af/cffi-1.17.1-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d01b12eeeb4427d3110de311e1774046ad344f5b1a7403101878976ecd7a10f3", size = 454792, upload-time = "2024-09-04T20:44:32.01Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/75/b2/fbaec7c4455c604e29388d55599b99ebcc250a60050610fadde58932b7ee/cffi-1.17.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:706510fe141c86a69c8ddc029c7910003a17353970cff3b904ff0686a5927683", size = 478893, upload-time = "2024-09-04T20:44:33.606Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/4f/b7/6e4a2162178bf1935c336d4da8a9352cccab4d3a5d7914065490f08c0690/cffi-1.17.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de55b766c7aa2e2a3092c51e0483d700341182f08e67c63630d5b6f200bb28e5", size = 485810, upload-time = "2024-09-04T20:44:35.191Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c7/8a/1d0e4a9c26e54746dc08c2c6c037889124d4f59dffd853a659fa545f1b40/cffi-1.17.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c59d6e989d07460165cc5ad3c61f9fd8f1b4796eacbd81cee78957842b834af4", size = 471200, upload-time = "2024-09-04T20:44:36.743Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/26/9f/1aab65a6c0db35f43c4d1b4f580e8df53914310afc10ae0397d29d697af4/cffi-1.17.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd398dbc6773384a17fe0d3e7eeb8d1a21c2200473ee6806bb5e6a8e62bb73dd", size = 479447, upload-time = "2024-09-04T20:44:38.492Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5f/e4/fb8b3dd8dc0e98edf1135ff067ae070bb32ef9d509d6cb0f538cd6f7483f/cffi-1.17.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:3edc8d958eb099c634dace3c7e16560ae474aa3803a5df240542b305d14e14ed", size = 484358, upload-time = "2024-09-04T20:44:40.046Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f1/47/d7145bf2dc04684935d57d67dff9d6d795b2ba2796806bb109864be3a151/cffi-1.17.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:72e72408cad3d5419375fc87d289076ee319835bdfa2caad331e377589aebba9", size = 488469, upload-time = "2024-09-04T20:44:41.616Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/bf/ee/f94057fa6426481d663b88637a9a10e859e492c73d0384514a17d78ee205/cffi-1.17.1-cp313-cp313-win32.whl", hash = "sha256:e03eab0a8677fa80d646b5ddece1cbeaf556c313dcfac435ba11f107ba117b5d", size = 172475, upload-time = "2024-09-04T20:44:43.733Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7c/fc/6a8cb64e5f0324877d503c854da15d76c1e50eb722e320b15345c4d0c6de/cffi-1.17.1-cp313-cp313-win_amd64.whl", hash = "sha256:f6a16c31041f09ead72d69f583767292f750d24913dadacf5756b966aacb3f1a", size = 182009, upload-time = "2024-09-04T20:44:45.309Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "charset-normalizer"
|
||||
version = "3.4.2"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/e4/33/89c2ced2b67d1c2a61c19c6751aa8902d46ce3dacb23600a283619f5a12d/charset_normalizer-3.4.2.tar.gz", hash = "sha256:5baececa9ecba31eff645232d59845c07aa030f0c81ee70184a90d35099a0e63", size = 126367, upload-time = "2025-05-02T08:34:42.01Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/d7/a4/37f4d6035c89cac7930395a35cc0f1b872e652eaafb76a6075943754f095/charset_normalizer-3.4.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0c29de6a1a95f24b9a1aa7aefd27d2487263f00dfd55a77719b530788f75cff7", size = 199936, upload-time = "2025-05-02T08:32:33.712Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ee/8a/1a5e33b73e0d9287274f899d967907cd0bf9c343e651755d9307e0dbf2b3/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cddf7bd982eaa998934a91f69d182aec997c6c468898efe6679af88283b498d3", size = 143790, upload-time = "2025-05-02T08:32:35.768Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/66/52/59521f1d8e6ab1482164fa21409c5ef44da3e9f653c13ba71becdd98dec3/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fcbe676a55d7445b22c10967bceaaf0ee69407fbe0ece4d032b6eb8d4565982a", size = 153924, upload-time = "2025-05-02T08:32:37.284Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/86/2d/fb55fdf41964ec782febbf33cb64be480a6b8f16ded2dbe8db27a405c09f/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d41c4d287cfc69060fa91cae9683eacffad989f1a10811995fa309df656ec214", size = 146626, upload-time = "2025-05-02T08:32:38.803Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/8c/73/6ede2ec59bce19b3edf4209d70004253ec5f4e319f9a2e3f2f15601ed5f7/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4e594135de17ab3866138f496755f302b72157d115086d100c3f19370839dd3a", size = 148567, upload-time = "2025-05-02T08:32:40.251Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/09/14/957d03c6dc343c04904530b6bef4e5efae5ec7d7990a7cbb868e4595ee30/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cf713fe9a71ef6fd5adf7a79670135081cd4431c2943864757f0fa3a65b1fafd", size = 150957, upload-time = "2025-05-02T08:32:41.705Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0d/c8/8174d0e5c10ccebdcb1b53cc959591c4c722a3ad92461a273e86b9f5a302/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a370b3e078e418187da8c3674eddb9d983ec09445c99a3a263c2011993522981", size = 145408, upload-time = "2025-05-02T08:32:43.709Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/58/aa/8904b84bc8084ac19dc52feb4f5952c6df03ffb460a887b42615ee1382e8/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a955b438e62efdf7e0b7b52a64dc5c3396e2634baa62471768a64bc2adb73d5c", size = 153399, upload-time = "2025-05-02T08:32:46.197Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c2/26/89ee1f0e264d201cb65cf054aca6038c03b1a0c6b4ae998070392a3ce605/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:7222ffd5e4de8e57e03ce2cef95a4c43c98fcb72ad86909abdfc2c17d227fc1b", size = 156815, upload-time = "2025-05-02T08:32:48.105Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/fd/07/68e95b4b345bad3dbbd3a8681737b4338ff2c9df29856a6d6d23ac4c73cb/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:bee093bf902e1d8fc0ac143c88902c3dfc8941f7ea1d6a8dd2bcb786d33db03d", size = 154537, upload-time = "2025-05-02T08:32:49.719Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/77/1a/5eefc0ce04affb98af07bc05f3bac9094513c0e23b0562d64af46a06aae4/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:dedb8adb91d11846ee08bec4c8236c8549ac721c245678282dcb06b221aab59f", size = 149565, upload-time = "2025-05-02T08:32:51.404Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/37/a0/2410e5e6032a174c95e0806b1a6585eb21e12f445ebe239fac441995226a/charset_normalizer-3.4.2-cp312-cp312-win32.whl", hash = "sha256:db4c7bf0e07fc3b7d89ac2a5880a6a8062056801b83ff56d8464b70f65482b6c", size = 98357, upload-time = "2025-05-02T08:32:53.079Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6c/4f/c02d5c493967af3eda9c771ad4d2bbc8df6f99ddbeb37ceea6e8716a32bc/charset_normalizer-3.4.2-cp312-cp312-win_amd64.whl", hash = "sha256:5a9979887252a82fefd3d3ed2a8e3b937a7a809f65dcb1e068b090e165bbe99e", size = 105776, upload-time = "2025-05-02T08:32:54.573Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ea/12/a93df3366ed32db1d907d7593a94f1fe6293903e3e92967bebd6950ed12c/charset_normalizer-3.4.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:926ca93accd5d36ccdabd803392ddc3e03e6d4cd1cf17deff3b989ab8e9dbcf0", size = 199622, upload-time = "2025-05-02T08:32:56.363Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/04/93/bf204e6f344c39d9937d3c13c8cd5bbfc266472e51fc8c07cb7f64fcd2de/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eba9904b0f38a143592d9fc0e19e2df0fa2e41c3c3745554761c5f6447eedabf", size = 143435, upload-time = "2025-05-02T08:32:58.551Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/22/2a/ea8a2095b0bafa6c5b5a55ffdc2f924455233ee7b91c69b7edfcc9e02284/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3fddb7e2c84ac87ac3a947cb4e66d143ca5863ef48e4a5ecb83bd48619e4634e", size = 153653, upload-time = "2025-05-02T08:33:00.342Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b6/57/1b090ff183d13cef485dfbe272e2fe57622a76694061353c59da52c9a659/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98f862da73774290f251b9df8d11161b6cf25b599a66baf087c1ffe340e9bfd1", size = 146231, upload-time = "2025-05-02T08:33:02.081Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e2/28/ffc026b26f441fc67bd21ab7f03b313ab3fe46714a14b516f931abe1a2d8/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c9379d65defcab82d07b2a9dfbfc2e95bc8fe0ebb1b176a3190230a3ef0e07c", size = 148243, upload-time = "2025-05-02T08:33:04.063Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c0/0f/9abe9bd191629c33e69e47c6ef45ef99773320e9ad8e9cb08b8ab4a8d4cb/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e635b87f01ebc977342e2697d05b56632f5f879a4f15955dfe8cef2448b51691", size = 150442, upload-time = "2025-05-02T08:33:06.418Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/67/7c/a123bbcedca91d5916c056407f89a7f5e8fdfce12ba825d7d6b9954a1a3c/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1c95a1e2902a8b722868587c0e1184ad5c55631de5afc0eb96bc4b0d738092c0", size = 145147, upload-time = "2025-05-02T08:33:08.183Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ec/fe/1ac556fa4899d967b83e9893788e86b6af4d83e4726511eaaad035e36595/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ef8de666d6179b009dce7bcb2ad4c4a779f113f12caf8dc77f0162c29d20490b", size = 153057, upload-time = "2025-05-02T08:33:09.986Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2b/ff/acfc0b0a70b19e3e54febdd5301a98b72fa07635e56f24f60502e954c461/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:32fc0341d72e0f73f80acb0a2c94216bd704f4f0bce10aedea38f30502b271ff", size = 156454, upload-time = "2025-05-02T08:33:11.814Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/92/08/95b458ce9c740d0645feb0e96cea1f5ec946ea9c580a94adfe0b617f3573/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:289200a18fa698949d2b39c671c2cc7a24d44096784e76614899a7ccf2574b7b", size = 154174, upload-time = "2025-05-02T08:33:13.707Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/78/be/8392efc43487ac051eee6c36d5fbd63032d78f7728cb37aebcc98191f1ff/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4a476b06fbcf359ad25d34a057b7219281286ae2477cc5ff5e3f70a246971148", size = 149166, upload-time = "2025-05-02T08:33:15.458Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/44/96/392abd49b094d30b91d9fbda6a69519e95802250b777841cf3bda8fe136c/charset_normalizer-3.4.2-cp313-cp313-win32.whl", hash = "sha256:aaeeb6a479c7667fbe1099af9617c83aaca22182d6cf8c53966491a0f1b7ffb7", size = 98064, upload-time = "2025-05-02T08:33:17.06Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e9/b0/0200da600134e001d91851ddc797809e2fe0ea72de90e09bec5a2fbdaccb/charset_normalizer-3.4.2-cp313-cp313-win_amd64.whl", hash = "sha256:aa6af9e7d59f9c12b33ae4e9450619cf2488e2bbe9b44030905877f0b2324980", size = 105641, upload-time = "2025-05-02T08:33:18.753Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/20/94/c5790835a017658cbfabd07f3bfb549140c3ac458cfc196323996b10095a/charset_normalizer-3.4.2-py3-none-any.whl", hash = "sha256:7f56930ab0abd1c45cd15be65cc741c28b1c9a34876ce8c17a2fa107810c0af0", size = 52626, upload-time = "2025-05-02T08:34:40.053Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cryptography"
|
||||
version = "45.0.3"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "cffi", marker = "platform_python_implementation != 'PyPy'" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/13/1f/9fa001e74a1993a9cadd2333bb889e50c66327b8594ac538ab8a04f915b7/cryptography-45.0.3.tar.gz", hash = "sha256:ec21313dd335c51d7877baf2972569f40a4291b76a0ce51391523ae358d05899", size = 744738, upload-time = "2025-05-25T14:17:24.777Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/82/b2/2345dc595998caa6f68adf84e8f8b50d18e9fc4638d32b22ea8daedd4b7a/cryptography-45.0.3-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:7573d9eebaeceeb55285205dbbb8753ac1e962af3d9640791d12b36864065e71", size = 7056239, upload-time = "2025-05-25T14:16:12.22Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/71/3d/ac361649a0bfffc105e2298b720d8b862330a767dab27c06adc2ddbef96a/cryptography-45.0.3-cp311-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d377dde61c5d67eb4311eace661c3efda46c62113ff56bf05e2d679e02aebb5b", size = 4205541, upload-time = "2025-05-25T14:16:14.333Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/70/3e/c02a043750494d5c445f769e9c9f67e550d65060e0bfce52d91c1362693d/cryptography-45.0.3-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fae1e637f527750811588e4582988932c222f8251f7b7ea93739acb624e1487f", size = 4433275, upload-time = "2025-05-25T14:16:16.421Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/40/7a/9af0bfd48784e80eef3eb6fd6fde96fe706b4fc156751ce1b2b965dada70/cryptography-45.0.3-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:ca932e11218bcc9ef812aa497cdf669484870ecbcf2d99b765d6c27a86000942", size = 4209173, upload-time = "2025-05-25T14:16:18.163Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/31/5f/d6f8753c8708912df52e67969e80ef70b8e8897306cd9eb8b98201f8c184/cryptography-45.0.3-cp311-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:af3f92b1dc25621f5fad065288a44ac790c5798e986a34d393ab27d2b27fcff9", size = 3898150, upload-time = "2025-05-25T14:16:20.34Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/8b/50/f256ab79c671fb066e47336706dc398c3b1e125f952e07d54ce82cf4011a/cryptography-45.0.3-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:2f8f8f0b73b885ddd7f3d8c2b2234a7d3ba49002b0223f58cfde1bedd9563c56", size = 4466473, upload-time = "2025-05-25T14:16:22.605Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/62/e7/312428336bb2df0848d0768ab5a062e11a32d18139447a76dfc19ada8eed/cryptography-45.0.3-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:9cc80ce69032ffa528b5e16d217fa4d8d4bb7d6ba8659c1b4d74a1b0f4235fca", size = 4211890, upload-time = "2025-05-25T14:16:24.738Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e7/53/8a130e22c1e432b3c14896ec5eb7ac01fb53c6737e1d705df7e0efb647c6/cryptography-45.0.3-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:c824c9281cb628015bfc3c59335163d4ca0540d49de4582d6c2637312907e4b1", size = 4466300, upload-time = "2025-05-25T14:16:26.768Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ba/75/6bb6579688ef805fd16a053005fce93944cdade465fc92ef32bbc5c40681/cryptography-45.0.3-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:5833bb4355cb377ebd880457663a972cd044e7f49585aee39245c0d592904578", size = 4332483, upload-time = "2025-05-25T14:16:28.316Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2f/11/2538f4e1ce05c6c4f81f43c1ef2bd6de7ae5e24ee284460ff6c77e42ca77/cryptography-45.0.3-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:9bb5bf55dcb69f7067d80354d0a348368da907345a2c448b0babc4215ccd3497", size = 4573714, upload-time = "2025-05-25T14:16:30.474Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f5/bb/e86e9cf07f73a98d84a4084e8fd420b0e82330a901d9cac8149f994c3417/cryptography-45.0.3-cp311-abi3-win32.whl", hash = "sha256:3ad69eeb92a9de9421e1f6685e85a10fbcfb75c833b42cc9bc2ba9fb00da4710", size = 2934752, upload-time = "2025-05-25T14:16:32.204Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c7/75/063bc9ddc3d1c73e959054f1fc091b79572e716ef74d6caaa56e945b4af9/cryptography-45.0.3-cp311-abi3-win_amd64.whl", hash = "sha256:97787952246a77d77934d41b62fb1b6f3581d83f71b44796a4158d93b8f5c490", size = 3412465, upload-time = "2025-05-25T14:16:33.888Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/71/9b/04ead6015229a9396890d7654ee35ef630860fb42dc9ff9ec27f72157952/cryptography-45.0.3-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:c92519d242703b675ccefd0f0562eb45e74d438e001f8ab52d628e885751fb06", size = 7031892, upload-time = "2025-05-25T14:16:36.214Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/46/c7/c7d05d0e133a09fc677b8a87953815c522697bdf025e5cac13ba419e7240/cryptography-45.0.3-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c5edcb90da1843df85292ef3a313513766a78fbbb83f584a5a58fb001a5a9d57", size = 4196181, upload-time = "2025-05-25T14:16:37.934Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/08/7a/6ad3aa796b18a683657cef930a986fac0045417e2dc428fd336cfc45ba52/cryptography-45.0.3-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:38deed72285c7ed699864f964a3f4cf11ab3fb38e8d39cfcd96710cd2b5bb716", size = 4423370, upload-time = "2025-05-25T14:16:39.502Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/4f/58/ec1461bfcb393525f597ac6a10a63938d18775b7803324072974b41a926b/cryptography-45.0.3-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:5555365a50efe1f486eed6ac7062c33b97ccef409f5970a0b6f205a7cfab59c8", size = 4197839, upload-time = "2025-05-25T14:16:41.322Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d4/3d/5185b117c32ad4f40846f579369a80e710d6146c2baa8ce09d01612750db/cryptography-45.0.3-cp37-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:9e4253ed8f5948a3589b3caee7ad9a5bf218ffd16869c516535325fece163dcc", size = 3886324, upload-time = "2025-05-25T14:16:43.041Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/67/85/caba91a57d291a2ad46e74016d1f83ac294f08128b26e2a81e9b4f2d2555/cryptography-45.0.3-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:cfd84777b4b6684955ce86156cfb5e08d75e80dc2585e10d69e47f014f0a5342", size = 4450447, upload-time = "2025-05-25T14:16:44.759Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ae/d1/164e3c9d559133a38279215c712b8ba38e77735d3412f37711b9f8f6f7e0/cryptography-45.0.3-cp37-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:a2b56de3417fd5f48773ad8e91abaa700b678dc7fe1e0c757e1ae340779acf7b", size = 4200576, upload-time = "2025-05-25T14:16:46.438Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/71/7a/e002d5ce624ed46dfc32abe1deff32190f3ac47ede911789ee936f5a4255/cryptography-45.0.3-cp37-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:57a6500d459e8035e813bd8b51b671977fb149a8c95ed814989da682314d0782", size = 4450308, upload-time = "2025-05-25T14:16:48.228Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/87/ad/3fbff9c28cf09b0a71e98af57d74f3662dea4a174b12acc493de00ea3f28/cryptography-45.0.3-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:f22af3c78abfbc7cbcdf2c55d23c3e022e1a462ee2481011d518c7fb9c9f3d65", size = 4325125, upload-time = "2025-05-25T14:16:49.844Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f5/b4/51417d0cc01802304c1984d76e9592f15e4801abd44ef7ba657060520bf0/cryptography-45.0.3-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:232954730c362638544758a8160c4ee1b832dc011d2c41a306ad8f7cccc5bb0b", size = 4560038, upload-time = "2025-05-25T14:16:51.398Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/80/38/d572f6482d45789a7202fb87d052deb7a7b136bf17473ebff33536727a2c/cryptography-45.0.3-cp37-abi3-win32.whl", hash = "sha256:cb6ab89421bc90e0422aca911c69044c2912fc3debb19bb3c1bfe28ee3dff6ab", size = 2924070, upload-time = "2025-05-25T14:16:53.472Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/91/5a/61f39c0ff4443651cc64e626fa97ad3099249152039952be8f344d6b0c86/cryptography-45.0.3-cp37-abi3-win_amd64.whl", hash = "sha256:d54ae41e6bd70ea23707843021c778f151ca258081586f0cfa31d936ae43d1b2", size = 3395005, upload-time = "2025-05-25T14:16:55.134Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "decorator"
|
||||
version = "5.2.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/43/fa/6d96a0978d19e17b68d634497769987b16c8f4cd0a7a05048bec693caa6b/decorator-5.2.1.tar.gz", hash = "sha256:65f266143752f734b0a7cc83c46f4618af75b8c5911b00ccb61d0ac9b6da0360", size = 56711, upload-time = "2025-02-24T04:41:34.073Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/4e/8c/f3147f5c4b73e7550fe5f9352eaa956ae838d5c51eb58e7a25b9f3e2643b/decorator-5.2.1-py3-none-any.whl", hash = "sha256:d316bb415a2d9e2d2b3abcc4084c6502fc09240e292cd76a76afc106a1c8e04a", size = 9190, upload-time = "2025-02-24T04:41:32.565Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "deprecated"
|
||||
version = "1.2.18"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "wrapt" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/98/97/06afe62762c9a8a86af0cfb7bfdab22a43ad17138b07af5b1a58442690a2/deprecated-1.2.18.tar.gz", hash = "sha256:422b6f6d859da6f2ef57857761bfb392480502a64c3028ca9bbe86085d72115d", size = 2928744, upload-time = "2025-01-27T10:46:25.7Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/6e/c6/ac0b6c1e2d138f1002bcf799d330bd6d85084fece321e662a14223794041/Deprecated-1.2.18-py2.py3-none-any.whl", hash = "sha256:bd5011788200372a32418f888e326a09ff80d0214bd961147cfed01b5c018eec", size = 9998, upload-time = "2025-01-27T10:46:09.186Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "django"
|
||||
version = "4.2.21"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "asgiref" },
|
||||
{ name = "sqlparse" },
|
||||
{ name = "tzdata", marker = "sys_platform == 'win32'" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/c1/bb/2fad5edc1af2945cb499a2e322ac28e4714fc310bd5201ed1f5a9f73a342/django-4.2.21.tar.gz", hash = "sha256:b54ac28d6aa964fc7c2f7335138a54d78980232011e0cd2231d04eed393dcb0d", size = 10424638, upload-time = "2025-05-07T14:07:07.992Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/2c/4f/aeaa3098da18b625ed672f3da6d1cd94e188d1b2cc27c2c841b2f9666282/django-4.2.21-py3-none-any.whl", hash = "sha256:1d658c7bf5d31c7d0cac1cab58bc1f822df89255080fec81909256c30e6180b3", size = 7993839, upload-time = "2025-05-07T14:07:01.318Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "django-auth-ldap"
|
||||
version = "5.2.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "django" },
|
||||
{ name = "python-ldap" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/88/70/6f6a89474667376080f8362f7c17c744d1c52720f0eb085cf74182149efe/django_auth_ldap-5.2.0.tar.gz", hash = "sha256:08ba6efc0340d9874725a962311b14991e29a33593eb150a8fb640709dbfa80f", size = 55287, upload-time = "2025-05-07T12:15:56.774Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/a1/65/0d26a8b5c19039305d7ae0e8e702613a9a1fe1ef3ebbd6206b9e104b7c43/django_auth_ldap-5.2.0-py3-none-any.whl", hash = "sha256:7dc6eb576ba36051850b580e4bdf4464e04bbe7367c3827a3121b4d7c51fb175", size = 20913, upload-time = "2025-05-07T12:15:54.962Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "django-bootstrap-form"
|
||||
version = "3.4"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "django" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/c7/3d/4ec51e3816d3bd5959058e210598e819fb86d492d16a19d195a025a64758/django-bootstrap-form-3.4.tar.gz", hash = "sha256:de3f7893e515352834d446c441c0cb861637f92cebbe59d8229469c6cd3dc640", size = 4422, upload-time = "2018-03-15T07:04:42.039Z" }
|
||||
|
||||
[[package]]
|
||||
name = "django-cors-headers"
|
||||
version = "4.7.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "asgiref" },
|
||||
{ name = "django" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/93/6c/16f6cb6064c63074fd5b2bd494eb319afd846236d9c1a6c765946df2c289/django_cors_headers-4.7.0.tar.gz", hash = "sha256:6fdf31bf9c6d6448ba09ef57157db2268d515d94fc5c89a0a1028e1fc03ee52b", size = 21037, upload-time = "2025-02-06T22:15:28.924Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/7e/a2/7bcfff86314bd9dd698180e31ba00604001606efb518a06cca6833a54285/django_cors_headers-4.7.0-py3-none-any.whl", hash = "sha256:f1c125dcd58479fe7a67fe2499c16ee38b81b397463cf025f0e2c42937421070", size = 12794, upload-time = "2025-02-06T22:15:24.341Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "django-extensions"
|
||||
version = "4.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "django" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/6d/b3/ed0f54ed706ec0b54fd251cc0364a249c6cd6c6ec97f04dc34be5e929eac/django_extensions-4.1.tar.gz", hash = "sha256:7b70a4d28e9b840f44694e3f7feb54f55d495f8b3fa6c5c0e5e12bcb2aa3cdeb", size = 283078, upload-time = "2025-04-11T01:15:39.617Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/64/96/d967ca440d6a8e3861120f51985d8e5aec79b9a8bdda16041206adfe7adc/django_extensions-4.1-py3-none-any.whl", hash = "sha256:0699a7af28f2523bf8db309a80278519362cd4b6e1fd0a8cd4bf063e1e023336", size = 232980, upload-time = "2025-04-11T01:15:37.701Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "django-phonenumber-field"
|
||||
version = "8.1.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "django" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/95/4b/edec2f3e4b8f8048540d7d1844bc576a0bdb711e1f6a4cde364c505c865d/django_phonenumber_field-8.1.0.tar.gz", hash = "sha256:f19450625574ad3a2de047c409527d0e57699496849e71e53b53e8679ff67b27", size = 43854, upload-time = "2025-04-10T16:09:29.363Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/89/98/0413e47d7d1fab0e5a81eda6caacdcba69d50f74f64b65febf943cbd1f1e/django_phonenumber_field-8.1.0-py3-none-any.whl", hash = "sha256:9ba66873bf47401b13fc57797fbf85e785864a6bfdc8b9daa3a9012886567045", size = 65208, upload-time = "2025-04-10T16:09:27.843Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fabric"
|
||||
version = "3.2.2"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "decorator" },
|
||||
{ name = "deprecated" },
|
||||
{ name = "invoke" },
|
||||
{ name = "paramiko" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/0d/3f/337f278b70ba339c618a490f6b8033b7006c583bd197a897f12fbc468c51/fabric-3.2.2.tar.gz", hash = "sha256:8783ca42e3b0076f08b26901aac6b9d9b1f19c410074e7accfab902c184ff4a3", size = 183215, upload-time = "2023-08-31T01:42:05.55Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/d6/1f/e99e23ee01847147fa194e8d41cfcf2535a2dbfcb51414c541cadb15c5d7/fabric-3.2.2-py3-none-any.whl", hash = "sha256:91c47c0be68b14936c88b34da8a1f55e5710fd28397dac5d4ff2e21558113a6f", size = 59417, upload-time = "2023-08-31T01:42:03.917Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gunicorn"
|
||||
version = "23.0.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "packaging" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/34/72/9614c465dc206155d93eff0ca20d42e1e35afc533971379482de953521a4/gunicorn-23.0.0.tar.gz", hash = "sha256:f014447a0101dc57e294f6c18ca6b40227a4c90e9bdb586042628030cba004ec", size = 375031, upload-time = "2024-08-10T20:25:27.378Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/cb/7d/6dac2a6e1eba33ee43f318edbed4ff29151a49b5d37f080aad1e6469bca4/gunicorn-23.0.0-py3-none-any.whl", hash = "sha256:ec400d38950de4dfd418cff8328b2c8faed0edb0d517d3394e457c317908ca4d", size = 85029, upload-time = "2024-08-10T20:25:24.996Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "idna"
|
||||
version = "3.10"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490, upload-time = "2024-09-15T18:07:39.745Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442, upload-time = "2024-09-15T18:07:37.964Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "invoke"
|
||||
version = "2.2.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/f9/42/127e6d792884ab860defc3f4d80a8f9812e48ace584ffc5a346de58cdc6c/invoke-2.2.0.tar.gz", hash = "sha256:ee6cbb101af1a859c7fe84f2a264c059020b0cb7fe3535f9424300ab568f6bd5", size = 299835, upload-time = "2023-07-12T18:05:17.998Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/0a/66/7f8c48009c72d73bc6bbe6eb87ac838d6a526146f7dab14af671121eb379/invoke-2.2.0-py3-none-any.whl", hash = "sha256:6ea924cc53d4f78e3d98bc436b08069a03077e6f85ad1ddaa8a116d7dad15820", size = 160274, upload-time = "2023-07-12T18:05:16.294Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "kassa"
|
||||
version = "0.1.0"
|
||||
source = { virtual = "." }
|
||||
dependencies = [
|
||||
{ name = "django" },
|
||||
{ name = "django-auth-ldap" },
|
||||
{ name = "django-bootstrap-form" },
|
||||
{ name = "django-cors-headers" },
|
||||
{ name = "django-extensions" },
|
||||
{ name = "django-phonenumber-field" },
|
||||
{ name = "fabric" },
|
||||
{ name = "gunicorn" },
|
||||
{ name = "mysqlclient" },
|
||||
{ name = "phonenumbers" },
|
||||
{ name = "requests" },
|
||||
{ name = "sentry-sdk" },
|
||||
]
|
||||
|
||||
[package.dev-dependencies]
|
||||
dev = [
|
||||
{ name = "ruff" },
|
||||
]
|
||||
|
||||
[package.metadata]
|
||||
requires-dist = [
|
||||
{ name = "django", specifier = "==4.2.*" },
|
||||
{ name = "django-auth-ldap", specifier = "==5.*" },
|
||||
{ name = "django-bootstrap-form", specifier = "==3.4" },
|
||||
{ name = "django-cors-headers", specifier = "==4.*" },
|
||||
{ name = "django-extensions", specifier = "==4.*" },
|
||||
{ name = "django-phonenumber-field", specifier = "==8.*" },
|
||||
{ name = "fabric", specifier = "==3.*" },
|
||||
{ name = "gunicorn", specifier = "==23.*" },
|
||||
{ name = "mysqlclient", specifier = "==2.*" },
|
||||
{ name = "phonenumbers", specifier = "==9.*" },
|
||||
{ name = "requests", specifier = "==2.*" },
|
||||
{ name = "sentry-sdk", specifier = "==1.*" },
|
||||
]
|
||||
|
||||
[package.metadata.requires-dev]
|
||||
dev = [{ name = "ruff", specifier = ">=0.11.12" }]
|
||||
|
||||
[[package]]
|
||||
name = "mysqlclient"
|
||||
version = "2.2.7"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/61/68/810093cb579daae426794bbd9d88aa830fae296e85172d18cb0f0e5dd4bc/mysqlclient-2.2.7.tar.gz", hash = "sha256:24ae22b59416d5fcce7e99c9d37548350b4565baac82f95e149cac6ce4163845", size = 91383, upload-time = "2025-01-10T12:06:00.763Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/bb/b5/2a8a4bcba3440550f358b839638fe8ec9146fa3c9194890b4998a530c926/mysqlclient-2.2.7-cp312-cp312-win_amd64.whl", hash = "sha256:4b4c0200890837fc64014cc938ef2273252ab544c1b12a6c1d674c23943f3f2e", size = 208032, upload-time = "2025-01-10T11:56:29.879Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/29/01/e80141f1cd0459e4c9a5dd309dee135bbae41d6c6c121252fdd853001a8a/mysqlclient-2.2.7-cp313-cp313-win_amd64.whl", hash = "sha256:201a6faa301011dd07bca6b651fe5aaa546d7c9a5426835a06c3172e1056a3c5", size = 208000, upload-time = "2025-01-10T11:56:32.293Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "packaging"
|
||||
version = "25.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/a1/d4/1fc4078c65507b51b96ca8f8c3ba19e6a61c8253c72794544580a7b6c24d/packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f", size = 165727, upload-time = "2025-04-19T11:48:59.673Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", size = 66469, upload-time = "2025-04-19T11:48:57.875Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "paramiko"
|
||||
version = "3.5.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "bcrypt" },
|
||||
{ name = "cryptography" },
|
||||
{ name = "pynacl" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/7d/15/ad6ce226e8138315f2451c2aeea985bf35ee910afb477bae7477dc3a8f3b/paramiko-3.5.1.tar.gz", hash = "sha256:b2c665bc45b2b215bd7d7f039901b14b067da00f3a11e6640995fd58f2664822", size = 1566110, upload-time = "2025-02-04T02:37:59.783Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/15/f8/c7bd0ef12954a81a1d3cea60a13946bd9a49a0036a5927770c461eade7ae/paramiko-3.5.1-py3-none-any.whl", hash = "sha256:43b9a0501fc2b5e70680388d9346cf252cfb7d00b0667c39e80eb43a408b8f61", size = 227298, upload-time = "2025-02-04T02:37:57.672Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phonenumbers"
|
||||
version = "9.0.6"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/40/57/2923088eb7fe397a544dcf31f31705ddd3d2d6f965d68c8d7ce2afe70856/phonenumbers-9.0.6.tar.gz", hash = "sha256:bfb228cfc4a5644f7c1a32ded1bd2ee6f8e3749e422fbdabd57661e86ad8872d", size = 2297098, upload-time = "2025-05-28T05:45:48.251Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/1a/65/b760522ce1b080e1d8af50d63e537beb5642ff043d789e4d7f5e3b4c3b30/phonenumbers-9.0.6-py2.py3-none-any.whl", hash = "sha256:b4a0f5a0dc30235b273d06acfe9afc2160cce4f1c8220067985ed81d72bb29a9", size = 2582823, upload-time = "2025-05-28T05:45:44.078Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pyasn1"
|
||||
version = "0.6.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/ba/e9/01f1a64245b89f039897cb0130016d79f77d52669aae6ee7b159a6c4c018/pyasn1-0.6.1.tar.gz", hash = "sha256:6f580d2bdd84365380830acf45550f2511469f673cb4a5ae3857a3170128b034", size = 145322, upload-time = "2024-09-10T22:41:42.55Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/c8/f1/d6a797abb14f6283c0ddff96bbdd46937f64122b8c925cab503dd37f8214/pyasn1-0.6.1-py3-none-any.whl", hash = "sha256:0d632f46f2ba09143da3a8afe9e33fb6f92fa2320ab7e886e2d0f7672af84629", size = 83135, upload-time = "2024-09-11T16:00:36.122Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pyasn1-modules"
|
||||
version = "0.4.2"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "pyasn1" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/e9/e6/78ebbb10a8c8e4b61a59249394a4a594c1a7af95593dc933a349c8d00964/pyasn1_modules-0.4.2.tar.gz", hash = "sha256:677091de870a80aae844b1ca6134f54652fa2c8c5a52aa396440ac3106e941e6", size = 307892, upload-time = "2025-03-28T02:41:22.17Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/47/8d/d529b5d697919ba8c11ad626e835d4039be708a35b0d22de83a269a6682c/pyasn1_modules-0.4.2-py3-none-any.whl", hash = "sha256:29253a9207ce32b64c3ac6600edc75368f98473906e8fd1043bd6b5b1de2c14a", size = 181259, upload-time = "2025-03-28T02:41:19.028Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pycparser"
|
||||
version = "2.22"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/1d/b2/31537cf4b1ca988837256c910a668b553fceb8f069bedc4b1c826024b52c/pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6", size = 172736, upload-time = "2024-03-30T13:22:22.564Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/13/a3/a812df4e2dd5696d1f351d58b8fe16a405b234ad2886a0dab9183fb78109/pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc", size = 117552, upload-time = "2024-03-30T13:22:20.476Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pynacl"
|
||||
version = "1.5.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "cffi" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/a7/22/27582568be639dfe22ddb3902225f91f2f17ceff88ce80e4db396c8986da/PyNaCl-1.5.0.tar.gz", hash = "sha256:8ac7448f09ab85811607bdd21ec2464495ac8b7c66d146bf545b0f08fb9220ba", size = 3392854, upload-time = "2022-01-07T22:05:41.134Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/ce/75/0b8ede18506041c0bf23ac4d8e2971b4161cd6ce630b177d0a08eb0d8857/PyNaCl-1.5.0-cp36-abi3-macosx_10_10_universal2.whl", hash = "sha256:401002a4aaa07c9414132aaed7f6836ff98f59277a234704ff66878c2ee4a0d1", size = 349920, upload-time = "2022-01-07T22:05:49.156Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/59/bb/fddf10acd09637327a97ef89d2a9d621328850a72f1fdc8c08bdf72e385f/PyNaCl-1.5.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:52cb72a79269189d4e0dc537556f4740f7f0a9ec41c1322598799b0bdad4ef92", size = 601722, upload-time = "2022-01-07T22:05:50.989Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5d/70/87a065c37cca41a75f2ce113a5a2c2aa7533be648b184ade58971b5f7ccc/PyNaCl-1.5.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a36d4a9dda1f19ce6e03c9a784a2921a4b726b02e1c736600ca9c22029474394", size = 680087, upload-time = "2022-01-07T22:05:52.539Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ee/87/f1bb6a595f14a327e8285b9eb54d41fef76c585a0edef0a45f6fc95de125/PyNaCl-1.5.0-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:0c84947a22519e013607c9be43706dd42513f9e6ae5d39d3613ca1e142fba44d", size = 856678, upload-time = "2022-01-07T22:05:54.251Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/66/28/ca86676b69bf9f90e710571b67450508484388bfce09acf8a46f0b8c785f/PyNaCl-1.5.0-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:06b8f6fa7f5de8d5d2f7573fe8c863c051225a27b61e6860fd047b1775807858", size = 1133660, upload-time = "2022-01-07T22:05:56.056Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3d/85/c262db650e86812585e2bc59e497a8f59948a005325a11bbbc9ecd3fe26b/PyNaCl-1.5.0-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:a422368fc821589c228f4c49438a368831cb5bbc0eab5ebe1d7fac9dded6567b", size = 663824, upload-time = "2022-01-07T22:05:57.434Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/fd/1a/cc308a884bd299b651f1633acb978e8596c71c33ca85e9dc9fa33a5399b9/PyNaCl-1.5.0-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:61f642bf2378713e2c2e1de73444a3778e5f0a38be6fee0fe532fe30060282ff", size = 1117912, upload-time = "2022-01-07T22:05:58.665Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/25/2d/b7df6ddb0c2a33afdb358f8af6ea3b8c4d1196ca45497dd37a56f0c122be/PyNaCl-1.5.0-cp36-abi3-win32.whl", hash = "sha256:e46dae94e34b085175f8abb3b0aaa7da40767865ac82c928eeb9e57e1ea8a543", size = 204624, upload-time = "2022-01-07T22:06:00.085Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5e/22/d3db169895faaf3e2eda892f005f433a62db2decbcfbc2f61e6517adfa87/PyNaCl-1.5.0-cp36-abi3-win_amd64.whl", hash = "sha256:20f42270d27e1b6a29f54032090b972d97f0a1b0948cc52392041ef7831fee93", size = 212141, upload-time = "2022-01-07T22:06:01.861Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "python-ldap"
|
||||
version = "3.4.4"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "pyasn1" },
|
||||
{ name = "pyasn1-modules" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/fd/8b/1eeb4025dc1d3ac2f16678f38dec9ebdde6271c74955b72db5ce7a4dbfbd/python-ldap-3.4.4.tar.gz", hash = "sha256:7edb0accec4e037797705f3a05cbf36a9fde50d08c8f67f2aef99a2628fab828", size = 377889, upload-time = "2023-11-17T21:14:16.32Z" }
|
||||
|
||||
[[package]]
|
||||
name = "requests"
|
||||
version = "2.32.3"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "certifi" },
|
||||
{ name = "charset-normalizer" },
|
||||
{ name = "idna" },
|
||||
{ name = "urllib3" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/63/70/2bf7780ad2d390a8d301ad0b550f1581eadbd9a20f896afe06353c2a2913/requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760", size = 131218, upload-time = "2024-05-29T15:37:49.536Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/f9/9b/335f9764261e915ed497fcdeb11df5dfd6f7bf257d4a6a2a686d80da4d54/requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6", size = 64928, upload-time = "2024-05-29T15:37:47.027Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ruff"
|
||||
version = "0.11.12"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/15/0a/92416b159ec00cdf11e5882a9d80d29bf84bba3dbebc51c4898bfbca1da6/ruff-0.11.12.tar.gz", hash = "sha256:43cf7f69c7d7c7d7513b9d59c5d8cafd704e05944f978614aa9faff6ac202603", size = 4202289, upload-time = "2025-05-29T13:31:40.037Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/60/cc/53eb79f012d15e136d40a8e8fc519ba8f55a057f60b29c2df34efd47c6e3/ruff-0.11.12-py3-none-linux_armv6l.whl", hash = "sha256:c7680aa2f0d4c4f43353d1e72123955c7a2159b8646cd43402de6d4a3a25d7cc", size = 10285597, upload-time = "2025-05-29T13:30:57.539Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e7/d7/73386e9fb0232b015a23f62fea7503f96e29c29e6c45461d4a73bac74df9/ruff-0.11.12-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:2cad64843da9f134565c20bcc430642de897b8ea02e2e79e6e02a76b8dcad7c3", size = 11053154, upload-time = "2025-05-29T13:31:00.865Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/4e/eb/3eae144c5114e92deb65a0cb2c72326c8469e14991e9bc3ec0349da1331c/ruff-0.11.12-py3-none-macosx_11_0_arm64.whl", hash = "sha256:9b6886b524a1c659cee1758140138455d3c029783d1b9e643f3624a5ee0cb0aa", size = 10403048, upload-time = "2025-05-29T13:31:03.413Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/29/64/20c54b20e58b1058db6689e94731f2a22e9f7abab74e1a758dfba058b6ca/ruff-0.11.12-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3cc3a3690aad6e86c1958d3ec3c38c4594b6ecec75c1f531e84160bd827b2012", size = 10597062, upload-time = "2025-05-29T13:31:05.539Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/29/3a/79fa6a9a39422a400564ca7233a689a151f1039110f0bbbabcb38106883a/ruff-0.11.12-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f97fdbc2549f456c65b3b0048560d44ddd540db1f27c778a938371424b49fe4a", size = 10155152, upload-time = "2025-05-29T13:31:07.986Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e5/a4/22c2c97b2340aa968af3a39bc38045e78d36abd4ed3fa2bde91c31e712e3/ruff-0.11.12-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:74adf84960236961090e2d1348c1a67d940fd12e811a33fb3d107df61eef8fc7", size = 11723067, upload-time = "2025-05-29T13:31:10.57Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/bc/cf/3e452fbd9597bcd8058856ecd42b22751749d07935793a1856d988154151/ruff-0.11.12-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:b56697e5b8bcf1d61293ccfe63873aba08fdbcbbba839fc046ec5926bdb25a3a", size = 12460807, upload-time = "2025-05-29T13:31:12.88Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2f/ec/8f170381a15e1eb7d93cb4feef8d17334d5a1eb33fee273aee5d1f8241a3/ruff-0.11.12-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4d47afa45e7b0eaf5e5969c6b39cbd108be83910b5c74626247e366fd7a36a13", size = 12063261, upload-time = "2025-05-29T13:31:15.236Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0d/bf/57208f8c0a8153a14652a85f4116c0002148e83770d7a41f2e90b52d2b4e/ruff-0.11.12-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:692bf9603fe1bf949de8b09a2da896f05c01ed7a187f4a386cdba6760e7f61be", size = 11329601, upload-time = "2025-05-29T13:31:18.68Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c3/56/edf942f7fdac5888094d9ffa303f12096f1a93eb46570bcf5f14c0c70880/ruff-0.11.12-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:08033320e979df3b20dba567c62f69c45e01df708b0f9c83912d7abd3e0801cd", size = 11522186, upload-time = "2025-05-29T13:31:21.216Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ed/63/79ffef65246911ed7e2290aeece48739d9603b3a35f9529fec0fc6c26400/ruff-0.11.12-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:929b7706584f5bfd61d67d5070f399057d07c70585fa8c4491d78ada452d3bef", size = 10449032, upload-time = "2025-05-29T13:31:23.417Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/88/19/8c9d4d8a1c2a3f5a1ea45a64b42593d50e28b8e038f1aafd65d6b43647f3/ruff-0.11.12-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:7de4a73205dc5756b8e09ee3ed67c38312dce1aa28972b93150f5751199981b5", size = 10129370, upload-time = "2025-05-29T13:31:25.777Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/bc/0f/2d15533eaa18f460530a857e1778900cd867ded67f16c85723569d54e410/ruff-0.11.12-py3-none-musllinux_1_2_i686.whl", hash = "sha256:2635c2a90ac1b8ca9e93b70af59dfd1dd2026a40e2d6eebaa3efb0465dd9cf02", size = 11123529, upload-time = "2025-05-29T13:31:28.396Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/4f/e2/4c2ac669534bdded835356813f48ea33cfb3a947dc47f270038364587088/ruff-0.11.12-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:d05d6a78a89166f03f03a198ecc9d18779076ad0eec476819467acb401028c0c", size = 11577642, upload-time = "2025-05-29T13:31:30.647Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a7/9b/c9ddf7f924d5617a1c94a93ba595f4b24cb5bc50e98b94433ab3f7ad27e5/ruff-0.11.12-py3-none-win32.whl", hash = "sha256:f5a07f49767c4be4772d161bfc049c1f242db0cfe1bd976e0f0886732a4765d6", size = 10475511, upload-time = "2025-05-29T13:31:32.917Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/fd/d6/74fb6d3470c1aada019ffff33c0f9210af746cca0a4de19a1f10ce54968a/ruff-0.11.12-py3-none-win_amd64.whl", hash = "sha256:5a4d9f8030d8c3a45df201d7fb3ed38d0219bccd7955268e863ee4a115fa0832", size = 11523573, upload-time = "2025-05-29T13:31:35.782Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/44/42/d58086ec20f52d2b0140752ae54b355ea2be2ed46f914231136dd1effcc7/ruff-0.11.12-py3-none-win_arm64.whl", hash = "sha256:65194e37853158d368e333ba282217941029a28ea90913c67e558c611d04daa5", size = 10697770, upload-time = "2025-05-29T13:31:38.009Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sentry-sdk"
|
||||
version = "1.45.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "certifi" },
|
||||
{ name = "urllib3" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/c8/28/02c0cd9184f9108e3c52519f9628b215077a3854240e0b17ae845e664855/sentry_sdk-1.45.1.tar.gz", hash = "sha256:a16c997c0f4e3df63c0fc5e4207ccb1ab37900433e0f72fef88315d317829a26", size = 244774, upload-time = "2024-07-26T13:48:32.375Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/fe/9f/105366a122efa93f0cb1914f841747d160788e4d022d0488d2d44c2ba26c/sentry_sdk-1.45.1-py2.py3-none-any.whl", hash = "sha256:608887855ccfe39032bfd03936e3a1c4f4fc99b3a4ac49ced54a4220de61c9c1", size = 267163, upload-time = "2024-07-26T13:48:29.38Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sqlparse"
|
||||
version = "0.5.3"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/e5/40/edede8dd6977b0d3da179a342c198ed100dd2aba4be081861ee5911e4da4/sqlparse-0.5.3.tar.gz", hash = "sha256:09f67787f56a0b16ecdbde1bfc7f5d9c3371ca683cfeaa8e6ff60b4807ec9272", size = 84999, upload-time = "2024-12-10T12:05:30.728Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/a9/5c/bfd6bd0bf979426d405cc6e71eceb8701b148b16c21d2dc3c261efc61c7b/sqlparse-0.5.3-py3-none-any.whl", hash = "sha256:cf2196ed3418f3ba5de6af7e82c694a9fbdbfecccdfc72e281548517081f16ca", size = 44415, upload-time = "2024-12-10T12:05:27.824Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tzdata"
|
||||
version = "2025.2"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/95/32/1a225d6164441be760d75c2c42e2780dc0873fe382da3e98a2e1e48361e5/tzdata-2025.2.tar.gz", hash = "sha256:b60a638fcc0daffadf82fe0f57e53d06bdec2f36c4df66280ae79bce6bd6f2b9", size = 196380, upload-time = "2025-03-23T13:54:43.652Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/5c/23/c7abc0ca0a1526a0774eca151daeb8de62ec457e77262b66b359c3c7679e/tzdata-2025.2-py2.py3-none-any.whl", hash = "sha256:1a403fada01ff9221ca8044d701868fa132215d84beb92242d9acd2147f667a8", size = 347839, upload-time = "2025-03-23T13:54:41.845Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "urllib3"
|
||||
version = "2.4.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/8a/78/16493d9c386d8e60e442a35feac5e00f0913c0f4b7c217c11e8ec2ff53e0/urllib3-2.4.0.tar.gz", hash = "sha256:414bc6535b787febd7567804cc015fee39daab8ad86268f1310a9250697de466", size = 390672, upload-time = "2025-04-10T15:23:39.232Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/6b/11/cc635220681e93a0183390e26485430ca2c7b5f9d33b15c74c2861cb8091/urllib3-2.4.0-py3-none-any.whl", hash = "sha256:4e16665048960a0900c702d4a66415956a584919c03361cac9f1df5c5dd7e813", size = 128680, upload-time = "2025-04-10T15:23:37.377Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wrapt"
|
||||
version = "1.17.2"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/c3/fc/e91cc220803d7bc4db93fb02facd8461c37364151b8494762cc88b0fbcef/wrapt-1.17.2.tar.gz", hash = "sha256:41388e9d4d1522446fe79d3213196bd9e3b301a336965b9e27ca2788ebd122f3", size = 55531, upload-time = "2025-01-14T10:35:45.465Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/a1/bd/ab55f849fd1f9a58ed7ea47f5559ff09741b25f00c191231f9f059c83949/wrapt-1.17.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:d5e2439eecc762cd85e7bd37161d4714aa03a33c5ba884e26c81559817ca0925", size = 53799, upload-time = "2025-01-14T10:33:57.4Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/53/18/75ddc64c3f63988f5a1d7e10fb204ffe5762bc663f8023f18ecaf31a332e/wrapt-1.17.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:3fc7cb4c1c744f8c05cd5f9438a3caa6ab94ce8344e952d7c45a8ed59dd88392", size = 38821, upload-time = "2025-01-14T10:33:59.334Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/48/2a/97928387d6ed1c1ebbfd4efc4133a0633546bec8481a2dd5ec961313a1c7/wrapt-1.17.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8fdbdb757d5390f7c675e558fd3186d590973244fab0c5fe63d373ade3e99d40", size = 38919, upload-time = "2025-01-14T10:34:04.093Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/73/54/3bfe5a1febbbccb7a2f77de47b989c0b85ed3a6a41614b104204a788c20e/wrapt-1.17.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5bb1d0dbf99411f3d871deb6faa9aabb9d4e744d67dcaaa05399af89d847a91d", size = 88721, upload-time = "2025-01-14T10:34:07.163Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/25/cb/7262bc1b0300b4b64af50c2720ef958c2c1917525238d661c3e9a2b71b7b/wrapt-1.17.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d18a4865f46b8579d44e4fe1e2bcbc6472ad83d98e22a26c963d46e4c125ef0b", size = 80899, upload-time = "2025-01-14T10:34:09.82Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2a/5a/04cde32b07a7431d4ed0553a76fdb7a61270e78c5fd5a603e190ac389f14/wrapt-1.17.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc570b5f14a79734437cb7b0500376b6b791153314986074486e0b0fa8d71d98", size = 89222, upload-time = "2025-01-14T10:34:11.258Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/09/28/2e45a4f4771fcfb109e244d5dbe54259e970362a311b67a965555ba65026/wrapt-1.17.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6d9187b01bebc3875bac9b087948a2bccefe464a7d8f627cf6e48b1bbae30f82", size = 86707, upload-time = "2025-01-14T10:34:12.49Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c6/d2/dcb56bf5f32fcd4bd9aacc77b50a539abdd5b6536872413fd3f428b21bed/wrapt-1.17.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:9e8659775f1adf02eb1e6f109751268e493c73716ca5761f8acb695e52a756ae", size = 79685, upload-time = "2025-01-14T10:34:15.043Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/80/4e/eb8b353e36711347893f502ce91c770b0b0929f8f0bed2670a6856e667a9/wrapt-1.17.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e8b2816ebef96d83657b56306152a93909a83f23994f4b30ad4573b00bd11bb9", size = 87567, upload-time = "2025-01-14T10:34:16.563Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/17/27/4fe749a54e7fae6e7146f1c7d914d28ef599dacd4416566c055564080fe2/wrapt-1.17.2-cp312-cp312-win32.whl", hash = "sha256:468090021f391fe0056ad3e807e3d9034e0fd01adcd3bdfba977b6fdf4213ea9", size = 36672, upload-time = "2025-01-14T10:34:17.727Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/15/06/1dbf478ea45c03e78a6a8c4be4fdc3c3bddea5c8de8a93bc971415e47f0f/wrapt-1.17.2-cp312-cp312-win_amd64.whl", hash = "sha256:ec89ed91f2fa8e3f52ae53cd3cf640d6feff92ba90d62236a81e4e563ac0e991", size = 38865, upload-time = "2025-01-14T10:34:19.577Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ce/b9/0ffd557a92f3b11d4c5d5e0c5e4ad057bd9eb8586615cdaf901409920b14/wrapt-1.17.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:6ed6ffac43aecfe6d86ec5b74b06a5be33d5bb9243d055141e8cabb12aa08125", size = 53800, upload-time = "2025-01-14T10:34:21.571Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c0/ef/8be90a0b7e73c32e550c73cfb2fa09db62234227ece47b0e80a05073b375/wrapt-1.17.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:35621ae4c00e056adb0009f8e86e28eb4a41a4bfa8f9bfa9fca7d343fe94f998", size = 38824, upload-time = "2025-01-14T10:34:22.999Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/36/89/0aae34c10fe524cce30fe5fc433210376bce94cf74d05b0d68344c8ba46e/wrapt-1.17.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a604bf7a053f8362d27eb9fefd2097f82600b856d5abe996d623babd067b1ab5", size = 38920, upload-time = "2025-01-14T10:34:25.386Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3b/24/11c4510de906d77e0cfb5197f1b1445d4fec42c9a39ea853d482698ac681/wrapt-1.17.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5cbabee4f083b6b4cd282f5b817a867cf0b1028c54d445b7ec7cfe6505057cf8", size = 88690, upload-time = "2025-01-14T10:34:28.058Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/71/d7/cfcf842291267bf455b3e266c0c29dcb675b5540ee8b50ba1699abf3af45/wrapt-1.17.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:49703ce2ddc220df165bd2962f8e03b84c89fee2d65e1c24a7defff6f988f4d6", size = 80861, upload-time = "2025-01-14T10:34:29.167Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d5/66/5d973e9f3e7370fd686fb47a9af3319418ed925c27d72ce16b791231576d/wrapt-1.17.2-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8112e52c5822fc4253f3901b676c55ddf288614dc7011634e2719718eaa187dc", size = 89174, upload-time = "2025-01-14T10:34:31.702Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a7/d3/8e17bb70f6ae25dabc1aaf990f86824e4fd98ee9cadf197054e068500d27/wrapt-1.17.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:9fee687dce376205d9a494e9c121e27183b2a3df18037f89d69bd7b35bcf59e2", size = 86721, upload-time = "2025-01-14T10:34:32.91Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6f/54/f170dfb278fe1c30d0ff864513cff526d624ab8de3254b20abb9cffedc24/wrapt-1.17.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:18983c537e04d11cf027fbb60a1e8dfd5190e2b60cc27bc0808e653e7b218d1b", size = 79763, upload-time = "2025-01-14T10:34:34.903Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/4a/98/de07243751f1c4a9b15c76019250210dd3486ce098c3d80d5f729cba029c/wrapt-1.17.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:703919b1633412ab54bcf920ab388735832fdcb9f9a00ae49387f0fe67dad504", size = 87585, upload-time = "2025-01-14T10:34:36.13Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f9/f0/13925f4bd6548013038cdeb11ee2cbd4e37c30f8bfd5db9e5a2a370d6e20/wrapt-1.17.2-cp313-cp313-win32.whl", hash = "sha256:abbb9e76177c35d4e8568e58650aa6926040d6a9f6f03435b7a522bf1c487f9a", size = 36676, upload-time = "2025-01-14T10:34:37.962Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/bf/ae/743f16ef8c2e3628df3ddfd652b7d4c555d12c84b53f3d8218498f4ade9b/wrapt-1.17.2-cp313-cp313-win_amd64.whl", hash = "sha256:69606d7bb691b50a4240ce6b22ebb319c1cfb164e5f6569835058196e0f3a845", size = 38871, upload-time = "2025-01-14T10:34:39.13Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3d/bc/30f903f891a82d402ffb5fda27ec1d621cc97cb74c16fea0b6141f1d4e87/wrapt-1.17.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:4a721d3c943dae44f8e243b380cb645a709ba5bd35d3ad27bc2ed947e9c68192", size = 56312, upload-time = "2025-01-14T10:34:40.604Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/8a/04/c97273eb491b5f1c918857cd26f314b74fc9b29224521f5b83f872253725/wrapt-1.17.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:766d8bbefcb9e00c3ac3b000d9acc51f1b399513f44d77dfe0eb026ad7c9a19b", size = 40062, upload-time = "2025-01-14T10:34:45.011Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/4e/ca/3b7afa1eae3a9e7fefe499db9b96813f41828b9fdb016ee836c4c379dadb/wrapt-1.17.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:e496a8ce2c256da1eb98bd15803a79bee00fc351f5dfb9ea82594a3f058309e0", size = 40155, upload-time = "2025-01-14T10:34:47.25Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/89/be/7c1baed43290775cb9030c774bc53c860db140397047cc49aedaf0a15477/wrapt-1.17.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40d615e4fe22f4ad3528448c193b218e077656ca9ccb22ce2cb20db730f8d306", size = 113471, upload-time = "2025-01-14T10:34:50.934Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/32/98/4ed894cf012b6d6aae5f5cc974006bdeb92f0241775addad3f8cd6ab71c8/wrapt-1.17.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a5aaeff38654462bc4b09023918b7f21790efb807f54c000a39d41d69cf552cb", size = 101208, upload-time = "2025-01-14T10:34:52.297Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ea/fd/0c30f2301ca94e655e5e057012e83284ce8c545df7661a78d8bfca2fac7a/wrapt-1.17.2-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9a7d15bbd2bc99e92e39f49a04653062ee6085c0e18b3b7512a4f2fe91f2d681", size = 109339, upload-time = "2025-01-14T10:34:53.489Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/75/56/05d000de894c4cfcb84bcd6b1df6214297b8089a7bd324c21a4765e49b14/wrapt-1.17.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:e3890b508a23299083e065f435a492b5435eba6e304a7114d2f919d400888cc6", size = 110232, upload-time = "2025-01-14T10:34:55.327Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/53/f8/c3f6b2cf9b9277fb0813418e1503e68414cd036b3b099c823379c9575e6d/wrapt-1.17.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:8c8b293cd65ad716d13d8dd3624e42e5a19cc2a2f1acc74b30c2c13f15cb61a6", size = 100476, upload-time = "2025-01-14T10:34:58.055Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a7/b1/0bb11e29aa5139d90b770ebbfa167267b1fc548d2302c30c8f7572851738/wrapt-1.17.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4c82b8785d98cdd9fed4cac84d765d234ed3251bd6afe34cb7ac523cb93e8b4f", size = 106377, upload-time = "2025-01-14T10:34:59.3Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6a/e1/0122853035b40b3f333bbb25f1939fc1045e21dd518f7f0922b60c156f7c/wrapt-1.17.2-cp313-cp313t-win32.whl", hash = "sha256:13e6afb7fe71fe7485a4550a8844cc9ffbe263c0f1a1eea569bc7091d4898555", size = 37986, upload-time = "2025-01-14T10:35:00.498Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/09/5e/1655cf481e079c1f22d0cabdd4e51733679932718dc23bf2db175f329b76/wrapt-1.17.2-cp313-cp313t-win_amd64.whl", hash = "sha256:eaf675418ed6b3b31c7a989fd007fa7c3be66ce14e5c3b27336383604c9da85c", size = 40750, upload-time = "2025-01-14T10:35:03.378Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2d/82/f56956041adef78f849db6b289b282e72b55ab8045a75abad81898c28d19/wrapt-1.17.2-py3-none-any.whl", hash = "sha256:b18f2d1533a71f069c7f82d524a52599053d4c7166e9dd374ae2136b7f40f7c8", size = 23594, upload-time = "2025-01-14T10:35:44.018Z" },
|
||||
]
|
||||
Reference in new issue
Block a user