From de329a5340b7f077a516787d5967655f0885c1d3 Mon Sep 17 00:00:00 2001 From: Nathan Chapman Date: Fri, 14 Jul 2023 21:04:20 -0600 Subject: [PATCH 1/4] Move address to the User, Order, and Subscription models respectively --- ...city_user_shipping_postal_code_and_more.py | 38 +++++++++++ ...004_transfer_address_data_to_user_model.py | 27 ++++++++ accounts/models.py | 11 ++++ ...ity_order_shipping_postal_code_and_more.py | 63 +++++++++++++++++++ ...s_data_to_order_and_subscription_models.py | 41 ++++++++++++ core/models.py | 21 +++++++ docker-compose.yml | 2 +- 7 files changed, 202 insertions(+), 1 deletion(-) create mode 100644 accounts/migrations/0003_user_shipping_city_user_shipping_postal_code_and_more.py create mode 100644 accounts/migrations/0004_transfer_address_data_to_user_model.py create mode 100644 core/migrations/0004_order_shipping_city_order_shipping_postal_code_and_more.py create mode 100644 core/migrations/0005_transfer_address_data_to_order_and_subscription_models.py diff --git a/accounts/migrations/0003_user_shipping_city_user_shipping_postal_code_and_more.py b/accounts/migrations/0003_user_shipping_city_user_shipping_postal_code_and_more.py new file mode 100644 index 0000000..19f2c8f --- /dev/null +++ b/accounts/migrations/0003_user_shipping_city_user_shipping_postal_code_and_more.py @@ -0,0 +1,38 @@ +# Generated by Django 4.1.6 on 2023-07-15 02:46 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('accounts', '0002_address_accounts_address_all_key'), + ] + + operations = [ + migrations.AddField( + model_name='user', + name='shipping_city', + field=models.CharField(blank=True, max_length=256), + ), + migrations.AddField( + model_name='user', + name='shipping_postal_code', + field=models.CharField(blank=True, max_length=20), + ), + migrations.AddField( + model_name='user', + name='shipping_state', + field=models.CharField(blank=True, choices=[('AL', 'Alabama'), ('AK', 'Alaska'), ('AS', 'American Samoa'), ('AZ', 'Arizona'), ('AR', 'Arkansas'), ('AA', 'Armed Forces Americas'), ('AE', 'Armed Forces Europe'), ('AP', 'Armed Forces Pacific'), ('CA', 'California'), ('CO', 'Colorado'), ('CT', 'Connecticut'), ('DE', 'Delaware'), ('DC', 'District of Columbia'), ('FM', 'Federated States of Micronesia'), ('FL', 'Florida'), ('GA', 'Georgia'), ('GU', 'Guam'), ('HI', 'Hawaii'), ('ID', 'Idaho'), ('IL', 'Illinois'), ('IN', 'Indiana'), ('IA', 'Iowa'), ('KS', 'Kansas'), ('KY', 'Kentucky'), ('LA', 'Louisiana'), ('ME', 'Maine'), ('MH', 'Marshall Islands'), ('MD', 'Maryland'), ('MA', 'Massachusetts'), ('MI', 'Michigan'), ('MN', 'Minnesota'), ('MS', 'Mississippi'), ('MO', 'Missouri'), ('MT', 'Montana'), ('NE', 'Nebraska'), ('NV', 'Nevada'), ('NH', 'New Hampshire'), ('NJ', 'New Jersey'), ('NM', 'New Mexico'), ('NY', 'New York'), ('NC', 'North Carolina'), ('ND', 'North Dakota'), ('MP', 'Northern Mariana Islands'), ('OH', 'Ohio'), ('OK', 'Oklahoma'), ('OR', 'Oregon'), ('PW', 'Palau'), ('PA', 'Pennsylvania'), ('PR', 'Puerto Rico'), ('RI', 'Rhode Island'), ('SC', 'South Carolina'), ('SD', 'South Dakota'), ('TN', 'Tennessee'), ('TX', 'Texas'), ('UT', 'Utah'), ('VT', 'Vermont'), ('VI', 'Virgin Islands'), ('VA', 'Virginia'), ('WA', 'Washington'), ('WV', 'West Virginia'), ('WI', 'Wisconsin'), ('WY', 'Wyoming')], max_length=2), + ), + migrations.AddField( + model_name='user', + name='shipping_street_address_1', + field=models.CharField(blank=True, max_length=256), + ), + migrations.AddField( + model_name='user', + name='shipping_street_address_2', + field=models.CharField(blank=True, max_length=256), + ), + ] diff --git a/accounts/migrations/0004_transfer_address_data_to_user_model.py b/accounts/migrations/0004_transfer_address_data_to_user_model.py new file mode 100644 index 0000000..3200916 --- /dev/null +++ b/accounts/migrations/0004_transfer_address_data_to_user_model.py @@ -0,0 +1,27 @@ +# Generated by Django 4.1.6 on 2023-07-15 02:56 + +from django.db import migrations + + +def copy_default_address_to_user(apps, schema_editor): + User = apps.get_model("accounts", "User") + + for user in User.objects.all(): + if user.default_shipping_address: + user.shipping_street_address_1 = user.default_shipping_address.street_address_1 + user.shipping_street_address_2 = user.default_shipping_address.street_address_2 + user.shipping_city = user.default_shipping_address.city + user.shipping_state = user.default_shipping_address.state + user.shipping_postal_code = user.default_shipping_address.postal_code + user.save() + + +class Migration(migrations.Migration): + + dependencies = [ + ('accounts', '0003_user_shipping_city_user_shipping_postal_code_and_more'), + ] + + operations = [ + migrations.RunPython(copy_default_address_to_user) + ] diff --git a/accounts/models.py b/accounts/models.py index 63ef944..0911905 100644 --- a/accounts/models.py +++ b/accounts/models.py @@ -76,6 +76,17 @@ class User(AbstractUser): ) stripe_id = models.CharField(max_length=255, blank=True) + # Shipping address + shipping_street_address_1 = models.CharField(max_length=256, blank=True) + shipping_street_address_2 = models.CharField(max_length=256, blank=True) + shipping_city = models.CharField(max_length=256, blank=True) + shipping_state = models.CharField( + max_length=2, + choices=USPS_CHOICES, + blank=True + ) + shipping_postal_code = models.CharField(max_length=20, blank=True) + def get_or_create_stripe_id(self): if not self.stripe_id: response = stripe.Customer.create( diff --git a/core/migrations/0004_order_shipping_city_order_shipping_postal_code_and_more.py b/core/migrations/0004_order_shipping_city_order_shipping_postal_code_and_more.py new file mode 100644 index 0000000..0dd0860 --- /dev/null +++ b/core/migrations/0004_order_shipping_city_order_shipping_postal_code_and_more.py @@ -0,0 +1,63 @@ +# Generated by Django 4.1.6 on 2023-07-15 02:46 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0003_rename_max_order_per_customer_productvariant_order_limit'), + ] + + operations = [ + migrations.AddField( + model_name='order', + name='shipping_city', + field=models.CharField(blank=True, max_length=256), + ), + migrations.AddField( + model_name='order', + name='shipping_postal_code', + field=models.CharField(blank=True, max_length=20), + ), + migrations.AddField( + model_name='order', + name='shipping_state', + field=models.CharField(blank=True, choices=[('AL', 'Alabama'), ('AK', 'Alaska'), ('AS', 'American Samoa'), ('AZ', 'Arizona'), ('AR', 'Arkansas'), ('AA', 'Armed Forces Americas'), ('AE', 'Armed Forces Europe'), ('AP', 'Armed Forces Pacific'), ('CA', 'California'), ('CO', 'Colorado'), ('CT', 'Connecticut'), ('DE', 'Delaware'), ('DC', 'District of Columbia'), ('FM', 'Federated States of Micronesia'), ('FL', 'Florida'), ('GA', 'Georgia'), ('GU', 'Guam'), ('HI', 'Hawaii'), ('ID', 'Idaho'), ('IL', 'Illinois'), ('IN', 'Indiana'), ('IA', 'Iowa'), ('KS', 'Kansas'), ('KY', 'Kentucky'), ('LA', 'Louisiana'), ('ME', 'Maine'), ('MH', 'Marshall Islands'), ('MD', 'Maryland'), ('MA', 'Massachusetts'), ('MI', 'Michigan'), ('MN', 'Minnesota'), ('MS', 'Mississippi'), ('MO', 'Missouri'), ('MT', 'Montana'), ('NE', 'Nebraska'), ('NV', 'Nevada'), ('NH', 'New Hampshire'), ('NJ', 'New Jersey'), ('NM', 'New Mexico'), ('NY', 'New York'), ('NC', 'North Carolina'), ('ND', 'North Dakota'), ('MP', 'Northern Mariana Islands'), ('OH', 'Ohio'), ('OK', 'Oklahoma'), ('OR', 'Oregon'), ('PW', 'Palau'), ('PA', 'Pennsylvania'), ('PR', 'Puerto Rico'), ('RI', 'Rhode Island'), ('SC', 'South Carolina'), ('SD', 'South Dakota'), ('TN', 'Tennessee'), ('TX', 'Texas'), ('UT', 'Utah'), ('VT', 'Vermont'), ('VI', 'Virgin Islands'), ('VA', 'Virginia'), ('WA', 'Washington'), ('WV', 'West Virginia'), ('WI', 'Wisconsin'), ('WY', 'Wyoming')], max_length=2), + ), + migrations.AddField( + model_name='order', + name='shipping_street_address_1', + field=models.CharField(blank=True, max_length=256), + ), + migrations.AddField( + model_name='order', + name='shipping_street_address_2', + field=models.CharField(blank=True, max_length=256), + ), + migrations.AddField( + model_name='subscription', + name='shipping_city', + field=models.CharField(blank=True, max_length=256), + ), + migrations.AddField( + model_name='subscription', + name='shipping_postal_code', + field=models.CharField(blank=True, max_length=20), + ), + migrations.AddField( + model_name='subscription', + name='shipping_state', + field=models.CharField(blank=True, choices=[('AL', 'Alabama'), ('AK', 'Alaska'), ('AS', 'American Samoa'), ('AZ', 'Arizona'), ('AR', 'Arkansas'), ('AA', 'Armed Forces Americas'), ('AE', 'Armed Forces Europe'), ('AP', 'Armed Forces Pacific'), ('CA', 'California'), ('CO', 'Colorado'), ('CT', 'Connecticut'), ('DE', 'Delaware'), ('DC', 'District of Columbia'), ('FM', 'Federated States of Micronesia'), ('FL', 'Florida'), ('GA', 'Georgia'), ('GU', 'Guam'), ('HI', 'Hawaii'), ('ID', 'Idaho'), ('IL', 'Illinois'), ('IN', 'Indiana'), ('IA', 'Iowa'), ('KS', 'Kansas'), ('KY', 'Kentucky'), ('LA', 'Louisiana'), ('ME', 'Maine'), ('MH', 'Marshall Islands'), ('MD', 'Maryland'), ('MA', 'Massachusetts'), ('MI', 'Michigan'), ('MN', 'Minnesota'), ('MS', 'Mississippi'), ('MO', 'Missouri'), ('MT', 'Montana'), ('NE', 'Nebraska'), ('NV', 'Nevada'), ('NH', 'New Hampshire'), ('NJ', 'New Jersey'), ('NM', 'New Mexico'), ('NY', 'New York'), ('NC', 'North Carolina'), ('ND', 'North Dakota'), ('MP', 'Northern Mariana Islands'), ('OH', 'Ohio'), ('OK', 'Oklahoma'), ('OR', 'Oregon'), ('PW', 'Palau'), ('PA', 'Pennsylvania'), ('PR', 'Puerto Rico'), ('RI', 'Rhode Island'), ('SC', 'South Carolina'), ('SD', 'South Dakota'), ('TN', 'Tennessee'), ('TX', 'Texas'), ('UT', 'Utah'), ('VT', 'Vermont'), ('VI', 'Virgin Islands'), ('VA', 'Virginia'), ('WA', 'Washington'), ('WV', 'West Virginia'), ('WI', 'Wisconsin'), ('WY', 'Wyoming')], max_length=2), + ), + migrations.AddField( + model_name='subscription', + name='shipping_street_address_1', + field=models.CharField(blank=True, max_length=256), + ), + migrations.AddField( + model_name='subscription', + name='shipping_street_address_2', + field=models.CharField(blank=True, max_length=256), + ), + ] diff --git a/core/migrations/0005_transfer_address_data_to_order_and_subscription_models.py b/core/migrations/0005_transfer_address_data_to_order_and_subscription_models.py new file mode 100644 index 0000000..939b3dd --- /dev/null +++ b/core/migrations/0005_transfer_address_data_to_order_and_subscription_models.py @@ -0,0 +1,41 @@ +# Generated by Django 4.1.6 on 2023-07-15 02:59 + +from django.db import migrations + + +def copy_address_to_order(apps, schema_editor): + Order = apps.get_model("core", "Order") + + for order in Order.objects.all(): + if order.shipping_address: + order.shipping_street_address_1 = order.shipping_address.street_address_1 + order.shipping_street_address_2 = order.shipping_address.street_address_2 + order.shipping_city = order.shipping_address.city + order.shipping_state = order.shipping_address.state + order.shipping_postal_code = order.shipping_address.postal_code + order.save() + + +def copy_address_to_subscription(apps, schema_editor): + Subscription = apps.get_model("core", "Subscription") + + for subscription in Subscription.objects.all(): + if subscription.shipping_address: + subscription.shipping_street_address_1 = subscription.shipping_address.street_address_1 + subscription.shipping_street_address_2 = subscription.shipping_address.street_address_2 + subscription.shipping_city = subscription.shipping_address.city + subscription.shipping_state = subscription.shipping_address.state + subscription.shipping_postal_code = subscription.shipping_address.postal_code + subscription.save() + + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0004_order_shipping_city_order_shipping_postal_code_and_more'), + ] + + operations = [ + migrations.RunPython(copy_address_to_order), + migrations.RunPython(copy_address_to_subscription) + ] diff --git a/core/models.py b/core/models.py index 7116ea8..ad5c5ae 100644 --- a/core/models.py +++ b/core/models.py @@ -16,6 +16,7 @@ from django.contrib.postgres.fields import ArrayField, HStoreField from django.forms.models import model_to_dict from django_measurement.models import MeasurementField +from localflavor.us.us_states import USPS_CHOICES from accounts.models import User, Address @@ -344,6 +345,16 @@ class Order(models.Model): null=True, on_delete=models.SET_NULL ) + # Shipping address + shipping_street_address_1 = models.CharField(max_length=256, blank=True) + shipping_street_address_2 = models.CharField(max_length=256, blank=True) + shipping_city = models.CharField(max_length=256, blank=True) + shipping_state = models.CharField( + max_length=2, + choices=USPS_CHOICES, + blank=True + ) + shipping_postal_code = models.CharField(max_length=20, blank=True) shipping_address = models.ForeignKey( Address, related_name="+", @@ -544,6 +555,16 @@ class Subscription(models.Model): on_delete=models.SET_NULL, null=True ) + # Shipping address + shipping_street_address_1 = models.CharField(max_length=256, blank=True) + shipping_street_address_2 = models.CharField(max_length=256, blank=True) + shipping_city = models.CharField(max_length=256, blank=True) + shipping_state = models.CharField( + max_length=2, + choices=USPS_CHOICES, + blank=True + ) + shipping_postal_code = models.CharField(max_length=20, blank=True) shipping_address = models.ForeignKey( Address, related_name='+', diff --git a/docker-compose.yml b/docker-compose.yml index d87d188..9544583 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -25,7 +25,7 @@ services: - redis web: build: . - command: sh -c "python manage.py migrate && python manage.py collectstatic --no-input && gunicorn --bind :8000 --reload ptcoffee.wsgi:application" + command: gunicorn --bind :8000 --reload ptcoffee.wsgi:application volumes: - .:/app - ./static/:/var/www/static From faf9b5b734429599184b94eb02082c260dca27d7 Mon Sep 17 00:00:00 2001 From: Nathan Chapman Date: Fri, 14 Jul 2023 22:59:03 -0600 Subject: [PATCH 2/4] Remove address model and unnecessary apps --- accounts/admin.py | 3 +- accounts/apps.py | 1 + accounts/forms.py | 34 +- .../0005_remove_user_addresses_and_more.py | 29 + accounts/models.py | 78 +- accounts/signals.py | 3 +- accounts/utils.py | 47 +- ..._name_order_shipping_last_name_and_more.py | 33 + ...d_last_names_to_order_and_subscriptions.py | 35 + ...8_remove_order_billing_address_and_more.py | 25 + core/models.py | 27 +- core/signals.py | 14 +- .../templates/dashboard/customer/detail.html | 11 +- .../templates/dashboard/order/detail.html | 2 +- .../dashboard/order/tracking_form.html | 2 +- .../dashboard/partials/_address.html | 10 +- dashboard/views.py | 11 +- poetry.lock | 2109 ++++++++--------- ptcoffee/settings.py | 3 - pyproject.toml | 3 - .../templates/storefront/customer_detail.html | 44 +- storefront/urls.py | 15 - storefront/views.py | 105 +- 23 files changed, 1195 insertions(+), 1449 deletions(-) create mode 100644 accounts/migrations/0005_remove_user_addresses_and_more.py create mode 100644 core/migrations/0006_order_shipping_first_name_order_shipping_last_name_and_more.py create mode 100644 core/migrations/0007_transfer_address_first_and_last_names_to_order_and_subscriptions.py create mode 100644 core/migrations/0008_remove_order_billing_address_and_more.py diff --git a/accounts/admin.py b/accounts/admin.py index 20b4b3f..12b4437 100644 --- a/accounts/admin.py +++ b/accounts/admin.py @@ -2,7 +2,7 @@ from django.contrib import admin from django.contrib.auth import get_user_model from django.contrib.auth.admin import UserAdmin -from .models import Address, User +from .models import User from .forms import AccountCreateForm, AccountUpdateForm class UserAdmin(UserAdmin): @@ -12,5 +12,4 @@ class UserAdmin(UserAdmin): list_display = ['email', 'username',] -admin.site.register(Address) admin.site.register(User, UserAdmin) diff --git a/accounts/apps.py b/accounts/apps.py index 734d6a1..2606800 100644 --- a/accounts/apps.py +++ b/accounts/apps.py @@ -9,3 +9,4 @@ class AccountsConfig(AppConfig): from .signals import ( user_saved ) + diff --git a/accounts/forms.py b/accounts/forms.py index 84852f6..9762acc 100644 --- a/accounts/forms.py +++ b/accounts/forms.py @@ -2,21 +2,7 @@ from django import forms from django.contrib.auth.forms import UserCreationForm, UserChangeForm from allauth.account.forms import SignupForm from captcha.fields import CaptchaField -from .models import Address, User - - -class AddressForm(forms.ModelForm): - class Meta: - model = Address - fields = ( - 'first_name', - 'last_name', - 'street_address_1', - 'street_address_2', - 'city', - 'state', - 'postal_code', - ) +from .models import User class AccountCreateForm(UserCreationForm): @@ -32,23 +18,26 @@ class AccountUpdateForm(UserChangeForm): 'first_name', 'last_name', 'email', - 'default_shipping_address', - 'addresses', + 'shipping_street_address_1', + 'shipping_street_address_2', + 'shipping_city', + 'shipping_state', + 'shipping_postal_code', ) class CustomerUpdateForm(forms.ModelForm): - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - self.fields['default_shipping_address'].queryset = kwargs['instance'].addresses - class Meta: model = User fields = ( 'first_name', 'last_name', 'email', - 'default_shipping_address', + 'shipping_street_address_1', + 'shipping_street_address_2', + 'shipping_city', + 'shipping_state', + 'shipping_postal_code', ) @@ -62,3 +51,4 @@ class UserSignupForm(SignupForm): widget=forms.TextInput(attrs={'placeholder': 'Last name'}) ) captcha = CaptchaField() + diff --git a/accounts/migrations/0005_remove_user_addresses_and_more.py b/accounts/migrations/0005_remove_user_addresses_and_more.py new file mode 100644 index 0000000..3411df5 --- /dev/null +++ b/accounts/migrations/0005_remove_user_addresses_and_more.py @@ -0,0 +1,29 @@ +# Generated by Django 4.1.6 on 2023-07-15 03:27 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0008_remove_order_billing_address_and_more'), + ('accounts', '0004_transfer_address_data_to_user_model'), + ] + + operations = [ + migrations.RemoveField( + model_name='user', + name='addresses', + ), + migrations.RemoveField( + model_name='user', + name='default_billing_address', + ), + migrations.RemoveField( + model_name='user', + name='default_shipping_address', + ), + migrations.DeleteModel( + name='Address', + ), + ] diff --git a/accounts/models.py b/accounts/models.py index 0911905..8716633 100644 --- a/accounts/models.py +++ b/accounts/models.py @@ -5,75 +5,7 @@ from django.contrib.auth.models import AbstractUser from localflavor.us.us_states import USPS_CHOICES -class Address(models.Model): - first_name = models.CharField(max_length=256, blank=True) - last_name = models.CharField(max_length=256, blank=True) - street_address_1 = models.CharField(max_length=256, blank=True) - street_address_2 = models.CharField(max_length=256, blank=True) - city = models.CharField(max_length=256, blank=True) - state = models.CharField( - max_length=2, - choices=USPS_CHOICES, - blank=True - ) - postal_code = models.CharField(max_length=20, blank=True) - - def as_stripe_dict(self): - return { - 'name': f'{self.first_name} {self.last_name}', - 'address': { - 'line1': self.street_address_1, - 'line2': self.street_address_2, - 'city': self.city, - 'state': self.state, - 'postal_code': self.postal_code - } - } - - def __str__(self): - return f""" - {self.first_name} {self.last_name} - {self.street_address_1} - {self.street_address_2} - {self.city}, {self.state}, {self.postal_code} - """ - - def __iter__(self): - yield ('address_line_1', self.street_address_1), - yield ('address_line_2', self.street_address_2), - yield ('admin_area_2', self.city), - yield ('admin_area_1', self.state), - yield ('postal_code', self.postal_code), - yield ('country_code', 'US') - - class Meta: - constraints = [ - models.UniqueConstraint( - name='accounts_address_all_key', - fields=[ - 'first_name', - 'last_name', - 'street_address_1', - 'street_address_2', - 'city', - 'state', - 'postal_code' - ], - violation_error_message='Duplicate: Address already exists.' - ) - ] - - class User(AbstractUser): - addresses = models.ManyToManyField( - Address, blank=True, related_name="user_addresses" - ) - default_shipping_address = models.ForeignKey( - Address, related_name="+", null=True, blank=True, on_delete=models.SET_NULL - ) - default_billing_address = models.ForeignKey( - Address, related_name="+", null=True, blank=True, on_delete=models.SET_NULL - ) stripe_id = models.CharField(max_length=255, blank=True) # Shipping address @@ -87,6 +19,16 @@ class User(AbstractUser): ) shipping_postal_code = models.CharField(max_length=20, blank=True) + @property + def has_shipping_address(self): + if (self.shipping_street_address_1 != '' + and self.shipping_street_address_2 != '' + and self.shipping_city != '' + and self.shipping_state != '' + and self.shipping_postal_code != ''): + return True + return False + def get_or_create_stripe_id(self): if not self.stripe_id: response = stripe.Customer.create( diff --git a/accounts/signals.py b/accounts/signals.py index 02a14db..be22b39 100644 --- a/accounts/signals.py +++ b/accounts/signals.py @@ -5,7 +5,7 @@ from django.dispatch import receiver from django.db import models from django.conf import settings -from .models import Address, User +from .models import User logger = logging.getLogger(__name__) stripe.api_key = settings.STRIPE_API_KEY @@ -16,3 +16,4 @@ def user_saved(sender, instance, created, **kwargs): logger.info('User was saved') if created or not instance.stripe_id: instance.get_or_create_stripe_id() + diff --git a/accounts/utils.py b/accounts/utils.py index 8154408..f41c16c 100644 --- a/accounts/utils.py +++ b/accounts/utils.py @@ -1,26 +1,9 @@ -from allauth.account.models import EmailAddress - -from .models import Address, User -from .tasks import send_account_created_email +from .models import User def get_or_create_customer(request, shipping_address): - address, a_created = Address.objects.get_or_create( - first_name=shipping_address['first_name'], - last_name=shipping_address['last_name'], - street_address_1=shipping_address['street_address_1'], - street_address_2=shipping_address['street_address_2'], - city=shipping_address['city'], - state=shipping_address['state'], - postal_code=shipping_address['postal_code'] - ) - if request.user.is_authenticated: user = request.user - user.addresses.add(address) - if not user.default_shipping_address: - user.default_shipping_address = address - user.save() else: user, u_created = User.objects.get_or_create( email=shipping_address['email'].lower(), @@ -29,27 +12,17 @@ def get_or_create_customer(request, shipping_address): 'is_staff': False, 'is_active': True, 'is_superuser': False, - 'first_name': address.first_name, - 'last_name': address.last_name, - 'default_shipping_address': address, + 'first_name': shipping_address['first_name'], + 'last_name': shipping_address['last_name'], + 'shipping_street_address_1': shipping_address['street_address_1'], + 'shipping_street_address_2': shipping_address['street_address_2'], + 'shipping_city': shipping_address['city'], + 'shipping_state': shipping_address['state'], + 'shipping_postal_code': shipping_address['postal_code'] } ) if u_created: - password = User.objects.make_random_password() - user.set_password(password) - user.addresses.add(address) + user.make_random_password() user.save() - EmailAddress.objects.create( - user=user, email=user.email, primary=True, verified=False - ) - - u = { - 'full_name': user.get_full_name(), - 'email': user.email, - 'password': password - } - - send_account_created_email.delay(u) - - return user, address + return user diff --git a/core/migrations/0006_order_shipping_first_name_order_shipping_last_name_and_more.py b/core/migrations/0006_order_shipping_first_name_order_shipping_last_name_and_more.py new file mode 100644 index 0000000..0acb697 --- /dev/null +++ b/core/migrations/0006_order_shipping_first_name_order_shipping_last_name_and_more.py @@ -0,0 +1,33 @@ +# Generated by Django 4.1.6 on 2023-07-15 03:20 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0005_transfer_address_data_to_order_and_subscription_models'), + ] + + operations = [ + migrations.AddField( + model_name='order', + name='shipping_first_name', + field=models.CharField(blank=True, max_length=256), + ), + migrations.AddField( + model_name='order', + name='shipping_last_name', + field=models.CharField(blank=True, max_length=256), + ), + migrations.AddField( + model_name='subscription', + name='shipping_first_name', + field=models.CharField(blank=True, max_length=256), + ), + migrations.AddField( + model_name='subscription', + name='shipping_last_name', + field=models.CharField(blank=True, max_length=256), + ), + ] diff --git a/core/migrations/0007_transfer_address_first_and_last_names_to_order_and_subscriptions.py b/core/migrations/0007_transfer_address_first_and_last_names_to_order_and_subscriptions.py new file mode 100644 index 0000000..3db25d8 --- /dev/null +++ b/core/migrations/0007_transfer_address_first_and_last_names_to_order_and_subscriptions.py @@ -0,0 +1,35 @@ +# Generated by Django 4.1.6 on 2023-07-15 03:20 + +from django.db import migrations + + +def copy_address_to_order(apps, schema_editor): + Order = apps.get_model("core", "Order") + + for order in Order.objects.all(): + if order.shipping_address: + order.shipping_first_name = order.shipping_address.first_name + order.shipping_last_name = order.shipping_address.last_name + order.save() + + +def copy_address_to_subscription(apps, schema_editor): + Subscription = apps.get_model("core", "Subscription") + + for subscription in Subscription.objects.all(): + if subscription.shipping_address: + subscription.shipping_first_name = subscription.shipping_address.first_name + subscription.shipping_last_name = subscription.shipping_address.last_name + subscription.save() + + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0006_order_shipping_first_name_order_shipping_last_name_and_more'), + ] + + operations = [ + migrations.RunPython(copy_address_to_order), + migrations.RunPython(copy_address_to_subscription) + ] diff --git a/core/migrations/0008_remove_order_billing_address_and_more.py b/core/migrations/0008_remove_order_billing_address_and_more.py new file mode 100644 index 0000000..5cf6af4 --- /dev/null +++ b/core/migrations/0008_remove_order_billing_address_and_more.py @@ -0,0 +1,25 @@ +# Generated by Django 4.1.6 on 2023-07-15 03:27 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0007_transfer_address_first_and_last_names_to_order_and_subscriptions'), + ] + + operations = [ + migrations.RemoveField( + model_name='order', + name='billing_address', + ), + migrations.RemoveField( + model_name='order', + name='shipping_address', + ), + migrations.RemoveField( + model_name='subscription', + name='shipping_address', + ), + ] diff --git a/core/models.py b/core/models.py index ad5c5ae..8b9a480 100644 --- a/core/models.py +++ b/core/models.py @@ -18,7 +18,7 @@ from django.forms.models import model_to_dict from django_measurement.models import MeasurementField from localflavor.us.us_states import USPS_CHOICES -from accounts.models import User, Address +from accounts.models import User from . import ( DiscountValueType, @@ -338,14 +338,9 @@ class Order(models.Model): default=OrderStatus.UNFULFILLED, choices=OrderStatus.CHOICES ) - billing_address = models.ForeignKey( - Address, - related_name="+", - editable=False, - null=True, - on_delete=models.SET_NULL - ) # Shipping address + shipping_first_name = models.CharField(max_length=256, blank=True) + shipping_last_name = models.CharField(max_length=256, blank=True) shipping_street_address_1 = models.CharField(max_length=256, blank=True) shipping_street_address_2 = models.CharField(max_length=256, blank=True) shipping_city = models.CharField(max_length=256, blank=True) @@ -355,13 +350,6 @@ class Order(models.Model): blank=True ) shipping_postal_code = models.CharField(max_length=20, blank=True) - shipping_address = models.ForeignKey( - Address, - related_name="+", - editable=False, - null=True, - on_delete=models.SET_NULL - ) coupon = models.ForeignKey( Coupon, related_name='orders', @@ -556,6 +544,8 @@ class Subscription(models.Model): null=True ) # Shipping address + shipping_first_name = models.CharField(max_length=256, blank=True) + shipping_last_name = models.CharField(max_length=256, blank=True) shipping_street_address_1 = models.CharField(max_length=256, blank=True) shipping_street_address_2 = models.CharField(max_length=256, blank=True) shipping_city = models.CharField(max_length=256, blank=True) @@ -565,13 +555,6 @@ class Subscription(models.Model): blank=True ) shipping_postal_code = models.CharField(max_length=20, blank=True) - shipping_address = models.ForeignKey( - Address, - related_name='+', - editable=False, - null=True, - on_delete=models.SET_NULL - ) items = ArrayField( models.JSONField(blank=True, null=True), default=list diff --git a/core/signals.py b/core/signals.py index 14bac23..c740788 100644 --- a/core/signals.py +++ b/core/signals.py @@ -53,13 +53,13 @@ def transaction_created(sender, instance, created, **kwargs): 'shipping_total': str(instance.order.shipping_total), 'total_amount': str(instance.order.total_amount), 'shipping_address': { - 'first_name': instance.order.shipping_address.first_name, - 'last_name': instance.order.shipping_address.last_name, - 'street_address_1': instance.order.shipping_address.street_address_1, - 'street_address_2': instance.order.shipping_address.street_address_2, - 'city': instance.order.shipping_address.city, - 'state': instance.order.shipping_address.state, - 'postal_code': instance.order.shipping_address.postal_code + 'first_name': instance.order.shipping_first_name, + 'last_name': instance.order.shipping_last_name, + 'street_address_1': instance.order.shipping_street_address_1, + 'street_address_2': instance.order.shipping_street_address_2, + 'city': instance.order.shipping_city, + 'state': instance.order.shipping_state, + 'postal_code': instance.order.shipping_postal_code }, 'line_items': list( format_order_lines(instance.order.lines.all()) diff --git a/dashboard/templates/dashboard/customer/detail.html b/dashboard/templates/dashboard/customer/detail.html index c0828fc..fe2a932 100644 --- a/dashboard/templates/dashboard/customer/detail.html +++ b/dashboard/templates/dashboard/customer/detail.html @@ -26,18 +26,11 @@
Default shipping address
- {% include 'dashboard/partials/_address.html' with address=customer.default_shipping_address %} -
- -
All addresses
-
- {% for address in customer.addresses.all %} - {% include 'dashboard/partials/_address.html' with address=address %} - {% endfor %} + {% include 'dashboard/partials/_address.html' with address=customer %}
- +

Orders

diff --git a/dashboard/templates/dashboard/order/detail.html b/dashboard/templates/dashboard/order/detail.html index 297b7c3..4709e1e 100644 --- a/dashboard/templates/dashboard/order/detail.html +++ b/dashboard/templates/dashboard/order/detail.html @@ -133,7 +133,7 @@
Shipping address - {% include 'dashboard/partials/_address.html' with address=order.shipping_address %} + {% include 'dashboard/partials/_address.html' with address=order %}
diff --git a/dashboard/templates/dashboard/order/tracking_form.html b/dashboard/templates/dashboard/order/tracking_form.html index 05e9b5e..f8d45b3 100644 --- a/dashboard/templates/dashboard/order/tracking_form.html +++ b/dashboard/templates/dashboard/order/tracking_form.html @@ -17,7 +17,7 @@
Shipping address - {% include 'dashboard/partials/_address.html' with address=order.shipping_address %} + {% include 'dashboard/partials/_address.html' with address=order %}
{% csrf_token %} diff --git a/dashboard/templates/dashboard/partials/_address.html b/dashboard/templates/dashboard/partials/_address.html index 1867d62..7c3de9e 100644 --- a/dashboard/templates/dashboard/partials/_address.html +++ b/dashboard/templates/dashboard/partials/_address.html @@ -1,9 +1,7 @@
- {{address.first_name}} - {{address.last_name}}
- {{address.street_address_1}}
- {% if address.street_address_2 %} - {{address.street_address_2}}
+ {{address.shipping_street_address_1}}
+ {% if address.shipping_street_address_2 %} + {{address.shipping_street_address_2}}
{% endif %} - {{address.city}}, {{address.state}}, {{address.postal_code}} + {{address.shipping_city}}, {{address.shipping_state}}, {{address.shipping_postal_code}}
diff --git a/dashboard/views.py b/dashboard/views.py index fa7d136..ffffee5 100644 --- a/dashboard/views.py +++ b/dashboard/views.py @@ -31,7 +31,6 @@ from django.db.models.functions import Coalesce from accounts.models import User from accounts.utils import get_or_create_customer -from accounts.forms import AddressForm from core.models import ( ProductCategory, Product, @@ -259,8 +258,6 @@ class OrderDetailView(LoginRequiredMixin, DetailView): self.kwargs.get(self.pk_url_kwarg) ).select_related( 'customer', - 'billing_address', - 'shipping_address' ).prefetch_related( 'lines__variant__product__productphoto_set' ) @@ -717,9 +714,11 @@ class CustomerUpdateView( 'first_name', 'last_name', 'email', - 'is_staff', - 'addresses', - 'default_shipping_address' + 'shipping_street_address_1', + 'shipping_street_address_2', + 'shipping_city', + 'shipping_state', + 'shipping_postal_code', ) def get_success_url(self): diff --git a/poetry.lock b/poetry.lock index 4ab2721..f15aa37 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,10 +1,15 @@ +# This file is automatically @generated by Poetry 1.5.1 and should not be changed by hand. + [[package]] name = "amqp" version = "5.1.1" description = "Low-level AMQP client for Python (fork of amqplib)." -category = "main" optional = false python-versions = ">=3.6" +files = [ + {file = "amqp-5.1.1-py3-none-any.whl", hash = "sha256:6f0956d2c23d8fa6e7691934d8c3930eadb44972cbbd1a7ae3a520f735d43359"}, + {file = "amqp-5.1.1.tar.gz", hash = "sha256:2c1b13fecc0893e946c65cbd5f36427861cffa4ea2201d8f6fca22e2a373b5e2"}, +] [package.dependencies] vine = ">=5.0.0" @@ -13,9 +18,12 @@ vine = ">=5.0.0" name = "asgiref" version = "3.6.0" description = "ASGI specs, helper code, and adapters" -category = "main" optional = false python-versions = ">=3.7" +files = [ + {file = "asgiref-3.6.0-py3-none-any.whl", hash = "sha256:71e68008da809b957b7ee4b43dbccff33d1b23519fb8344e33f049897077afac"}, + {file = "asgiref-3.6.0.tar.gz", hash = "sha256:9567dfe7bd8d3c8c892227827c41cce860b368104c3431da67a0c5a65a949506"}, +] [package.extras] tests = ["mypy (>=0.800)", "pytest", "pytest-asyncio"] @@ -24,25 +32,34 @@ tests = ["mypy (>=0.800)", "pytest", "pytest-asyncio"] name = "async-generator" version = "1.10" description = "Async generators and context managers for Python 3.5+" -category = "main" optional = false python-versions = ">=3.5" +files = [ + {file = "async_generator-1.10-py3-none-any.whl", hash = "sha256:01c7bf666359b4967d2cda0000cc2e4af16a0ae098cbffcb8472fb9e8ad6585b"}, + {file = "async_generator-1.10.tar.gz", hash = "sha256:6ebb3d106c12920aaae42ccb6f787ef5eefdcdd166ea3d628fa8476abe712144"}, +] [[package]] name = "async-timeout" version = "4.0.2" description = "Timeout context manager for asyncio programs" -category = "main" optional = false python-versions = ">=3.6" +files = [ + {file = "async-timeout-4.0.2.tar.gz", hash = "sha256:2163e1640ddb52b7a8c80d0a67a08587e5d245cc9c553a74a847056bc2976b15"}, + {file = "async_timeout-4.0.2-py3-none-any.whl", hash = "sha256:8ca1e4fcf50d07413d66d1a5e416e42cfdf5851c981d679a09851a6853383b3c"}, +] [[package]] name = "attrs" version = "22.2.0" description = "Classes Without Boilerplate" -category = "main" optional = false python-versions = ">=3.6" +files = [ + {file = "attrs-22.2.0-py3-none-any.whl", hash = "sha256:29e95c7f6778868dbd49170f98f8818f78f3dc5e0e37c0b1f474e3561b240836"}, + {file = "attrs-22.2.0.tar.gz", hash = "sha256:c9227bfc2f01993c03f68db37d1d15c9690188323c067c641f1a35ca58185f99"}, +] [package.extras] cov = ["attrs[tests]", "coverage-enable-subprocess", "coverage[toml] (>=5.3)"] @@ -55,9 +72,12 @@ tests-no-zope = ["cloudpickle", "cloudpickle", "hypothesis", "hypothesis", "mypy name = "babel" version = "2.11.0" description = "Internationalization utilities" -category = "main" optional = false python-versions = ">=3.6" +files = [ + {file = "Babel-2.11.0-py3-none-any.whl", hash = "sha256:1ad3eca1c885218f6dce2ab67291178944f810a10a9b5f3cb8382a5a232b64fe"}, + {file = "Babel-2.11.0.tar.gz", hash = "sha256:5ef4b3226b0180dedded4229651c8b0e1a3a6a2837d45a073272f313e4cf97f6"}, +] [package.dependencies] pytz = ">=2015.7" @@ -66,17 +86,23 @@ pytz = ">=2015.7" name = "billiard" version = "3.6.4.0" description = "Python multiprocessing fork with improvements and bugfixes" -category = "main" optional = false python-versions = "*" +files = [ + {file = "billiard-3.6.4.0-py3-none-any.whl", hash = "sha256:87103ea78fa6ab4d5c751c4909bcff74617d985de7fa8b672cf8618afd5a875b"}, + {file = "billiard-3.6.4.0.tar.gz", hash = "sha256:299de5a8da28a783d51b197d496bef4f1595dd023a93a4f59dde1886ae905547"}, +] [[package]] name = "celery" version = "5.2.7" description = "Distributed Task Queue." -category = "main" optional = false python-versions = ">=3.7" +files = [ + {file = "celery-5.2.7-py3-none-any.whl", hash = "sha256:138420c020cd58d6707e6257b6beda91fd39af7afde5d36c6334d175302c0e14"}, + {file = "celery-5.2.7.tar.gz", hash = "sha256:fafbd82934d30f8a004f81e8f7a062e31413a23d444be8ee3326553915958c6d"}, +] [package.dependencies] billiard = ">=3.6.4.0,<4.0" @@ -126,1052 +152,20 @@ zstd = ["zstandard"] name = "certifi" version = "2022.12.7" description = "Python package for providing Mozilla's CA Bundle." -category = "main" optional = false python-versions = ">=3.6" +files = [ + {file = "certifi-2022.12.7-py3-none-any.whl", hash = "sha256:4ad3232f5e926d6718ec31cfc1fcadfde020920e278684144551c91769c7bc18"}, + {file = "certifi-2022.12.7.tar.gz", hash = "sha256:35824b4c3a97115964b408844d64aa14db1cc518f6562e8d7261699d1350a9e3"}, +] [[package]] name = "cffi" version = "1.15.1" description = "Foreign Function Interface for Python calling C code." -category = "main" optional = false python-versions = "*" - -[package.dependencies] -pycparser = "*" - -[[package]] -name = "charset-normalizer" -version = "3.0.1" -description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." -category = "main" -optional = false -python-versions = "*" - -[[package]] -name = "click" -version = "8.1.3" -description = "Composable command line interface toolkit" -category = "main" -optional = false -python-versions = ">=3.7" - -[package.dependencies] -colorama = {version = "*", markers = "platform_system == \"Windows\""} - -[[package]] -name = "click-didyoumean" -version = "0.3.0" -description = "Enables git-like *did-you-mean* feature in click" -category = "main" -optional = false -python-versions = ">=3.6.2,<4.0.0" - -[package.dependencies] -click = ">=7" - -[[package]] -name = "click-plugins" -version = "1.1.1" -description = "An extension module for click to enable registering CLI commands via setuptools entry-points." -category = "main" -optional = false -python-versions = "*" - -[package.dependencies] -click = ">=4.0" - -[package.extras] -dev = ["coveralls", "pytest (>=3.6)", "pytest-cov", "wheel"] - -[[package]] -name = "click-repl" -version = "0.2.0" -description = "REPL plugin for Click" -category = "main" -optional = false -python-versions = "*" - -[package.dependencies] -click = "*" -prompt-toolkit = "*" -six = "*" - -[[package]] -name = "colorama" -version = "0.4.6" -description = "Cross-platform colored terminal text." -category = "main" -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" - -[[package]] -name = "cryptography" -version = "39.0.0" -description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." -category = "main" -optional = false -python-versions = ">=3.6" - -[package.dependencies] -cffi = ">=1.12" - -[package.extras] -docs = ["sphinx (>=1.6.5,!=1.8.0,!=3.1.0,!=3.1.1,!=5.2.0,!=5.2.0.post0)", "sphinx-rtd-theme"] -docstest = ["pyenchant (>=1.6.11)", "sphinxcontrib-spelling (>=4.0.1)", "twine (>=1.12.0)"] -pep8test = ["black", "ruff"] -sdist = ["setuptools-rust (>=0.11.4)"] -ssh = ["bcrypt (>=3.1.5)"] -test = ["hypothesis (>=1.11.4,!=3.79.2)", "iso8601", "pretend", "pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-subtests", "pytest-xdist", "pytz"] - -[[package]] -name = "defusedxml" -version = "0.7.1" -description = "XML bomb protection for Python stdlib modules" -category = "main" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" - -[[package]] -name = "django" -version = "4.1.6" -description = "A high-level Python web framework that encourages rapid development and clean, pragmatic design." -category = "main" -optional = false -python-versions = ">=3.8" - -[package.dependencies] -asgiref = ">=3.5.2,<4" -sqlparse = ">=0.2.2" -tzdata = {version = "*", markers = "sys_platform == \"win32\""} - -[package.extras] -argon2 = ["argon2-cffi (>=19.1.0)"] -bcrypt = ["bcrypt"] - -[[package]] -name = "django-allauth" -version = "0.52.0" -description = "Integrated set of Django applications addressing authentication, registration, account management as well as 3rd party (social) account authentication." -category = "main" -optional = false -python-versions = ">=3.5" - -[package.dependencies] -Django = ">=2.0" -pyjwt = {version = ">=1.7", extras = ["crypto"]} -python3-openid = ">=3.0.8" -requests = "*" -requests-oauthlib = ">=0.3.0" - -[[package]] -name = "django-analytical" -version = "3.1.0" -description = "Analytics service integration for Django projects" -category = "main" -optional = false -python-versions = ">=3.6" - -[[package]] -name = "django-anymail" -version = "9.0" -description = "Django email backends and webhooks for Amazon SES, Mailgun, Mailjet, Mandrill, Postal, Postmark, SendGrid, SendinBlue, and SparkPost" -category = "main" -optional = false -python-versions = ">=3.6" - -[package.dependencies] -django = ">=2.0" -requests = ">=2.4.3" - -[package.extras] -amazon-ses = ["boto3"] -dev = ["flake8", "sphinx", "sphinx-rtd-theme", "tox", "twine", "wheel"] -postal = ["cryptography"] - -[[package]] -name = "django-appconf" -version = "1.0.5" -description = "A helper class for handling configuration defaults of packaged apps gracefully." -category = "main" -optional = false -python-versions = ">=3.6" - -[package.dependencies] -django = "*" - -[[package]] -name = "django-celery-beat" -version = "2.4.0" -description = "Database-backed Periodic Tasks." -category = "main" -optional = false -python-versions = "*" - -[package.dependencies] -celery = ">=5.2.3,<6.0" -Django = ">=2.2,<4.2" -django-timezone-field = ">=5.0" -python-crontab = ">=2.3.4" -tzdata = "*" - -[[package]] -name = "django-celery-results" -version = "2.4.0" -description = "Celery result backends for Django." -category = "main" -optional = false -python-versions = "*" - -[package.dependencies] -celery = ">=5.2.3,<6.0" - -[[package]] -name = "django-compressor" -version = "4.3.1" -description = "('Compresses linked and inline JavaScript or CSS into single cached files.',)" -category = "main" -optional = false -python-versions = "*" - -[package.dependencies] -django-appconf = ">=1.0.3" -rcssmin = "1.1.1" -rjsmin = "1.2.1" - -[[package]] -name = "django-debug-toolbar" -version = "3.8.1" -description = "A configurable set of panels that display various debug information about the current request/response." -category = "main" -optional = false -python-versions = ">=3.7" - -[package.dependencies] -django = ">=3.2.4" -sqlparse = ">=0.2" - -[[package]] -name = "django-filter" -version = "22.1" -description = "Django-filter is a reusable Django application for allowing users to filter querysets dynamically." -category = "main" -optional = false -python-versions = ">=3.7" - -[package.dependencies] -Django = ">=3.2" - -[[package]] -name = "django-localflavor" -version = "3.1" -description = "Country-specific Django helpers" -category = "main" -optional = false -python-versions = "*" - -[package.dependencies] -django = ">=2.2" -python-stdnum = ">=1.6" - -[[package]] -name = "django-measurement" -version = "3.2.4" -description = "Convenient fields and classes for handling measurements" -category = "main" -optional = false -python-versions = "*" - -[package.dependencies] -django = ">=2.2" -django-appconf = ">=1.0.2" -measurement = ">=1.6,<4.0" - -[[package]] -name = "django-ranged-response" -version = "0.2.0" -description = "Modified Django FileResponse that adds Content-Range headers." -category = "main" -optional = false -python-versions = "*" - -[package.dependencies] -django = "*" - -[[package]] -name = "django-render-block" -version = "0.9.2" -description = "Render a particular block from a template to a string." -category = "main" -optional = false -python-versions = ">=3.7" - -[package.dependencies] -django = ">=2.2" - -[package.extras] -dev = ["Jinja2 (>=2.8)"] - -[[package]] -name = "django-simple-captcha" -version = "0.5.17" -description = "A very simple, yet powerful, Django captcha application" -category = "main" -optional = false -python-versions = "*" - -[package.dependencies] -Django = ">=2.2" -django-ranged-response = "0.2.0" -Pillow = ">=6.2.0" - -[package.extras] -test = ["testfixtures"] - -[[package]] -name = "django-storages" -version = "1.13.2" -description = "Support for many storage backends in Django" -category = "main" -optional = false -python-versions = ">=3.7" - -[package.dependencies] -Django = ">=3.2" - -[package.extras] -azure = ["azure-storage-blob (>=12.0.0)"] -boto3 = ["boto3 (>=1.4.4)"] -dropbox = ["dropbox (>=7.2.1)"] -google = ["google-cloud-storage (>=1.27.0)"] -libcloud = ["apache-libcloud"] -sftp = ["paramiko (>=1.10.0)"] - -[[package]] -name = "django-templated-email" -version = "3.0.1" -description = "A Django oriented templated / transaction email abstraction" -category = "main" -optional = false -python-versions = "*" - -[package.dependencies] -django-render-block = ">=0.5" - -[[package]] -name = "django-timezone-field" -version = "5.0" -description = "A Django app providing DB, form, and REST framework fields for zoneinfo and pytz timezone objects." -category = "main" -optional = false -python-versions = ">=3.7,<4.0" - -[package.dependencies] -pytz = "*" - -[[package]] -name = "environs" -version = "9.5.0" -description = "simplified environment variable parsing" -category = "main" -optional = false -python-versions = ">=3.6" - -[package.dependencies] -marshmallow = ">=3.0.0" -python-dotenv = "*" - -[package.extras] -dev = ["dj-database-url", "dj-email-url", "django-cache-url", "flake8 (==4.0.1)", "flake8-bugbear (==21.9.2)", "mypy (==0.910)", "pre-commit (>=2.4,<3.0)", "pytest", "tox"] -django = ["dj-database-url", "dj-email-url", "django-cache-url"] -lint = ["flake8 (==4.0.1)", "flake8-bugbear (==21.9.2)", "mypy (==0.910)", "pre-commit (>=2.4,<3.0)"] -tests = ["dj-database-url", "dj-email-url", "django-cache-url", "pytest"] - -[[package]] -name = "gunicorn" -version = "20.1.0" -description = "WSGI HTTP Server for UNIX" -category = "main" -optional = false -python-versions = ">=3.5" - -[package.dependencies] -setuptools = ">=3.0" - -[package.extras] -eventlet = ["eventlet (>=0.24.1)"] -gevent = ["gevent (>=1.4.0)"] -setproctitle = ["setproctitle"] -tornado = ["tornado (>=0.2)"] - -[[package]] -name = "h11" -version = "0.14.0" -description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" -category = "main" -optional = false -python-versions = ">=3.7" - -[[package]] -name = "idna" -version = "3.4" -description = "Internationalized Domain Names in Applications (IDNA)" -category = "main" -optional = false -python-versions = ">=3.5" - -[[package]] -name = "kombu" -version = "5.2.4" -description = "Messaging library for Python." -category = "main" -optional = false -python-versions = ">=3.7" - -[package.dependencies] -amqp = ">=5.0.9,<6.0.0" -vine = "*" - -[package.extras] -azureservicebus = ["azure-servicebus (>=7.0.0)"] -azurestoragequeues = ["azure-storage-queue"] -consul = ["python-consul (>=0.6.0)"] -librabbitmq = ["librabbitmq (>=2.0.0)"] -mongodb = ["pymongo (>=3.3.0,<3.12.1)"] -msgpack = ["msgpack"] -pyro = ["pyro4"] -qpid = ["qpid-python (>=0.26)", "qpid-tools (>=0.26)"] -redis = ["redis (>=3.4.1,!=4.0.0,!=4.0.1)"] -slmq = ["softlayer-messaging (>=1.0.3)"] -sqlalchemy = ["sqlalchemy"] -sqs = ["boto3 (>=1.9.12)", "pycurl (>=7.44.1,<7.45.0)", "urllib3 (>=1.26.7)"] -yaml = ["PyYAML (>=3.10)"] -zookeeper = ["kazoo (>=1.3.1)"] - -[[package]] -name = "lxml" -version = "4.9.2" -description = "Powerful and Pythonic XML processing library combining libxml2/libxslt with the ElementTree API." -category = "main" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, != 3.4.*" - -[package.extras] -cssselect = ["cssselect (>=0.7)"] -html5 = ["html5lib"] -htmlsoup = ["BeautifulSoup4"] -source = ["Cython (>=0.29.7)"] - -[[package]] -name = "marshmallow" -version = "3.19.0" -description = "A lightweight library for converting complex datatypes to and from native Python datatypes." -category = "main" -optional = false -python-versions = ">=3.7" - -[package.dependencies] -packaging = ">=17.0" - -[package.extras] -dev = ["flake8 (==5.0.4)", "flake8-bugbear (==22.10.25)", "mypy (==0.990)", "pre-commit (>=2.4,<3.0)", "pytest", "pytz", "simplejson", "tox"] -docs = ["alabaster (==0.7.12)", "autodocsumm (==0.2.9)", "sphinx (==5.3.0)", "sphinx-issues (==3.0.1)", "sphinx-version-warning (==1.1.2)"] -lint = ["flake8 (==5.0.4)", "flake8-bugbear (==22.10.25)", "mypy (==0.990)", "pre-commit (>=2.4,<3.0)"] -tests = ["pytest", "pytz", "simplejson"] - -[[package]] -name = "measurement" -version = "3.2.2" -description = "Easily use and manipulate unit-aware measurements in Python." -category = "main" -optional = false -python-versions = ">=3.7" - -[package.dependencies] -sympy = "*" - -[package.extras] -docs = ["python-docs-theme", "sphinx"] -test = ["pytest", "pytest-cov"] - -[[package]] -name = "mpmath" -version = "1.2.1" -description = "Python library for arbitrary-precision floating-point arithmetic" -category = "main" -optional = false -python-versions = "*" - -[package.extras] -develop = ["codecov", "pycodestyle", "pytest (>=4.6)", "pytest-cov", "wheel"] -tests = ["pytest (>=4.6)"] - -[[package]] -name = "oauthlib" -version = "3.2.2" -description = "A generic, spec-compliant, thorough implementation of the OAuth request-signing logic" -category = "main" -optional = false -python-versions = ">=3.6" - -[package.extras] -rsa = ["cryptography (>=3.0.0)"] -signals = ["blinker (>=1.4.0)"] -signedtoken = ["cryptography (>=3.0.0)", "pyjwt (>=2.0.0,<3)"] - -[[package]] -name = "outcome" -version = "1.2.0" -description = "Capture the outcome of Python function calls." -category = "main" -optional = false -python-versions = ">=3.7" - -[package.dependencies] -attrs = ">=19.2.0" - -[[package]] -name = "packaging" -version = "23.0" -description = "Core utilities for Python packages" -category = "main" -optional = false -python-versions = ">=3.7" - -[[package]] -name = "paypal-checkout-serversdk" -version = "1.0.1" -description = "Python Rest SDK for PayPal Checkout" -category = "main" -optional = false -python-versions = "*" - -[package.dependencies] -paypalhttp = "*" - -[[package]] -name = "paypalhttp" -version = "1.0.1" -description = "" -category = "main" -optional = false -python-versions = "*" - -[package.dependencies] -pyopenssl = ">=0.15" -requests = ">=2.0.0" -six = ">=1.0.0" - -[[package]] -name = "pillow" -version = "9.4.0" -description = "Python Imaging Library (Fork)" -category = "main" -optional = false -python-versions = ">=3.7" - -[package.extras] -docs = ["furo", "olefile", "sphinx (>=2.4)", "sphinx-copybutton", "sphinx-inline-tabs", "sphinx-issues (>=3.0.1)", "sphinx-removed-in", "sphinxext-opengraph"] -tests = ["check-manifest", "coverage", "defusedxml", "markdown2", "olefile", "packaging", "pyroma", "pytest", "pytest-cov", "pytest-timeout"] - -[[package]] -name = "prompt-toolkit" -version = "3.0.36" -description = "Library for building powerful interactive command lines in Python" -category = "main" -optional = false -python-versions = ">=3.6.2" - -[package.dependencies] -wcwidth = "*" - -[[package]] -name = "psycopg2-binary" -version = "2.9.5" -description = "psycopg2 - Python-PostgreSQL Database Adapter" -category = "main" -optional = false -python-versions = ">=3.6" - -[[package]] -name = "py-moneyed" -version = "3.0" -description = "Provides Currency and Money classes for use in your Python code." -category = "main" -optional = false -python-versions = ">=3.7" - -[package.dependencies] -babel = ">=2.8.0" -typing-extensions = ">=3.7.4.3" - -[package.extras] -tests = ["pytest (>=2.3.0)", "tox (>=1.6.0)"] -type-tests = ["mypy (>=0.812)", "pytest (>=2.3.0)", "pytest-mypy-plugins"] - -[[package]] -name = "pycodestyle" -version = "2.10.0" -description = "Python style guide checker" -category = "main" -optional = false -python-versions = ">=3.6" - -[[package]] -name = "pycparser" -version = "2.21" -description = "C parser in Python" -category = "main" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" - -[[package]] -name = "pyjwt" -version = "2.6.0" -description = "JSON Web Token implementation in Python" -category = "main" -optional = false -python-versions = ">=3.7" - -[package.dependencies] -cryptography = {version = ">=3.4.0", optional = true, markers = "extra == \"crypto\""} - -[package.extras] -crypto = ["cryptography (>=3.4.0)"] -dev = ["coverage[toml] (==5.0.4)", "cryptography (>=3.4.0)", "pre-commit", "pytest (>=6.0.0,<7.0.0)", "sphinx (>=4.5.0,<5.0.0)", "sphinx-rtd-theme", "zope.interface"] -docs = ["sphinx (>=4.5.0,<5.0.0)", "sphinx-rtd-theme", "zope.interface"] -tests = ["coverage[toml] (==5.0.4)", "pytest (>=6.0.0,<7.0.0)"] - -[[package]] -name = "pyopenssl" -version = "23.0.0" -description = "Python wrapper module around the OpenSSL library" -category = "main" -optional = false -python-versions = ">=3.6" - -[package.dependencies] -cryptography = ">=38.0.0,<40" - -[package.extras] -docs = ["sphinx (!=5.2.0,!=5.2.0.post0)", "sphinx-rtd-theme"] -test = ["flaky", "pretend", "pytest (>=3.0.1)"] - -[[package]] -name = "pysocks" -version = "1.7.1" -description = "A Python SOCKS client module. See https://github.com/Anorov/PySocks for more information." -category = "main" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" - -[[package]] -name = "python-crontab" -version = "2.7.1" -description = "Python Crontab API" -category = "main" -optional = false -python-versions = "*" - -[package.dependencies] -python-dateutil = "*" - -[package.extras] -cron-description = ["cron-descriptor"] -cron-schedule = ["croniter"] - -[[package]] -name = "python-dateutil" -version = "2.8.2" -description = "Extensions to the standard Python datetime module" -category = "main" -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" - -[package.dependencies] -six = ">=1.5" - -[[package]] -name = "python-dotenv" -version = "0.21.1" -description = "Read key-value pairs from a .env file and set them as environment variables" -category = "main" -optional = false -python-versions = ">=3.7" - -[package.extras] -cli = ["click (>=5.0)"] - -[[package]] -name = "python-stdnum" -version = "1.18" -description = "Python module to handle standardized numbers and codes" -category = "main" -optional = false -python-versions = "*" - -[package.extras] -soap = ["zeep"] -soap-alt = ["suds"] -soap-fallback = ["PySimpleSOAP"] - -[[package]] -name = "python3-openid" -version = "3.2.0" -description = "OpenID support for modern servers and consumers." -category = "main" -optional = false -python-versions = "*" - -[package.dependencies] -defusedxml = "*" - -[package.extras] -mysql = ["mysql-connector-python"] -postgresql = ["psycopg2"] - -[[package]] -name = "pytz" -version = "2022.7.1" -description = "World timezone definitions, modern and historical" -category = "main" -optional = false -python-versions = "*" - -[[package]] -name = "rcssmin" -version = "1.1.1" -description = "CSS Minifier" -category = "main" -optional = false -python-versions = "*" - -[[package]] -name = "redis" -version = "4.4.2" -description = "Python client for Redis database and key-value store" -category = "main" -optional = false -python-versions = ">=3.7" - -[package.dependencies] -async-timeout = ">=4.0.2" - -[package.extras] -hiredis = ["hiredis (>=1.0.0)"] -ocsp = ["cryptography (>=36.0.1)", "pyopenssl (==20.0.1)", "requests (>=2.26.0)"] - -[[package]] -name = "requests" -version = "2.28.2" -description = "Python HTTP for Humans." -category = "main" -optional = false -python-versions = ">=3.7, <4" - -[package.dependencies] -certifi = ">=2017.4.17" -charset-normalizer = ">=2,<4" -idna = ">=2.5,<4" -urllib3 = ">=1.21.1,<1.27" - -[package.extras] -socks = ["PySocks (>=1.5.6,!=1.5.7)"] -use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] - -[[package]] -name = "requests-oauthlib" -version = "1.3.1" -description = "OAuthlib authentication support for Requests." -category = "main" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" - -[package.dependencies] -oauthlib = ">=3.0.0" -requests = ">=2.0.0" - -[package.extras] -rsa = ["oauthlib[signedtoken] (>=3.0.0)"] - -[[package]] -name = "rjsmin" -version = "1.2.1" -description = "Javascript Minifier" -category = "main" -optional = false -python-versions = "*" - -[[package]] -name = "selenium" -version = "4.8.0" -description = "" -category = "main" -optional = false -python-versions = ">=3.7" - -[package.dependencies] -certifi = ">=2021.10.8" -trio = ">=0.17,<1.0" -trio-websocket = ">=0.9,<1.0" -urllib3 = {version = ">=1.26,<2.0", extras = ["socks"]} - -[[package]] -name = "sentry-sdk" -version = "1.14.0" -description = "Python client for Sentry (https://sentry.io)" -category = "main" -optional = false -python-versions = "*" - -[package.dependencies] -certifi = "*" -urllib3 = {version = ">=1.26.11", markers = "python_version >= \"3.6\""} - -[package.extras] -aiohttp = ["aiohttp (>=3.5)"] -beam = ["apache-beam (>=2.12)"] -bottle = ["bottle (>=0.12.13)"] -celery = ["celery (>=3)"] -chalice = ["chalice (>=1.16.0)"] -django = ["django (>=1.8)"] -falcon = ["falcon (>=1.4)"] -fastapi = ["fastapi (>=0.79.0)"] -flask = ["blinker (>=1.1)", "flask (>=0.11)"] -httpx = ["httpx (>=0.16.0)"] -opentelemetry = ["opentelemetry-distro (>=0.35b0)"] -pure-eval = ["asttokens", "executing", "pure-eval"] -pymongo = ["pymongo (>=3.1)"] -pyspark = ["pyspark (>=2.4.4)"] -quart = ["blinker (>=1.1)", "quart (>=0.16.1)"] -rq = ["rq (>=0.6)"] -sanic = ["sanic (>=0.8)"] -sqlalchemy = ["sqlalchemy (>=1.2)"] -starlette = ["starlette (>=0.19.1)"] -starlite = ["starlite (>=1.48)"] -tornado = ["tornado (>=5)"] - -[[package]] -name = "setuptools" -version = "67.1.0" -description = "Easily download, build, install, upgrade, and uninstall Python packages" -category = "main" -optional = false -python-versions = ">=3.7" - -[package.extras] -docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (==0.8.3)", "sphinx-reredirects", "sphinxcontrib-towncrier"] -testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8 (<5)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pip-run (>=8.8)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] -testing-integration = ["build[virtualenv]", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] - -[[package]] -name = "six" -version = "1.16.0" -description = "Python 2 and 3 compatibility utilities" -category = "main" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" - -[[package]] -name = "sniffio" -version = "1.3.0" -description = "Sniff out which async library your code is running under" -category = "main" -optional = false -python-versions = ">=3.7" - -[[package]] -name = "sortedcontainers" -version = "2.4.0" -description = "Sorted Containers -- Sorted List, Sorted Dict, Sorted Set" -category = "main" -optional = false -python-versions = "*" - -[[package]] -name = "sqlparse" -version = "0.4.3" -description = "A non-validating SQL parser." -category = "main" -optional = false -python-versions = ">=3.5" - -[[package]] -name = "stripe" -version = "5.1.0" -description = "Python bindings for the Stripe API" -category = "main" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" - -[package.dependencies] -requests = {version = ">=2.20", markers = "python_version >= \"3.0\""} - -[[package]] -name = "sympy" -version = "1.11.1" -description = "Computer algebra system (CAS) in Python" -category = "main" -optional = false -python-versions = ">=3.8" - -[package.dependencies] -mpmath = ">=0.19" - -[[package]] -name = "trio" -version = "0.22.0" -description = "A friendly Python library for async concurrency and I/O" -category = "main" -optional = false -python-versions = ">=3.7" - -[package.dependencies] -async-generator = ">=1.9" -attrs = ">=19.2.0" -cffi = {version = ">=1.14", markers = "os_name == \"nt\" and implementation_name != \"pypy\""} -idna = "*" -outcome = "*" -sniffio = "*" -sortedcontainers = "*" - -[[package]] -name = "trio-websocket" -version = "0.9.2" -description = "WebSocket library for Trio" -category = "main" -optional = false -python-versions = ">=3.5" - -[package.dependencies] -async-generator = ">=1.10" -trio = ">=0.11" -wsproto = ">=0.14" - -[[package]] -name = "typing-extensions" -version = "4.4.0" -description = "Backported and Experimental Type Hints for Python 3.7+" -category = "main" -optional = false -python-versions = ">=3.7" - -[[package]] -name = "tzdata" -version = "2022.7" -description = "Provider of IANA time zone data" -category = "main" -optional = false -python-versions = ">=2" - -[[package]] -name = "urllib3" -version = "1.26.14" -description = "HTTP library with thread-safe connection pooling, file post, and more." -category = "main" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" - -[package.dependencies] -PySocks = {version = ">=1.5.6,<1.5.7 || >1.5.7,<2.0", optional = true, markers = "extra == \"socks\""} - -[package.extras] -brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)", "brotlipy (>=0.6.0)"] -secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "ipaddress", "pyOpenSSL (>=0.14)", "urllib3-secure-extra"] -socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] - -[[package]] -name = "usps-api" -version = "0.5" -description = "Python wrapper for the USPS API" -category = "main" -optional = false -python-versions = "*" - -[package.dependencies] -lxml = "*" -requests = "*" -xmltodict = "*" - -[[package]] -name = "vine" -version = "5.0.0" -description = "Promises, promises, promises." -category = "main" -optional = false -python-versions = ">=3.6" - -[[package]] -name = "wcwidth" -version = "0.2.6" -description = "Measures the displayed width of unicode strings in a terminal" -category = "main" -optional = false -python-versions = "*" - -[[package]] -name = "wsproto" -version = "1.2.0" -description = "WebSockets state-machine based protocol implementation" -category = "main" -optional = false -python-versions = ">=3.7.0" - -[package.dependencies] -h11 = ">=0.9.0,<1" - -[[package]] -name = "xmltodict" -version = "0.13.0" -description = "Makes working with XML feel like you are working with JSON" -category = "main" -optional = false -python-versions = ">=3.4" - -[metadata] -lock-version = "1.1" -python-versions = "^3.11" -content-hash = "6624d86a540ead66402daae3c3353c3a26f2f8bc9293676b68e1c2e8199e385a" - -[metadata.files] -amqp = [ - {file = "amqp-5.1.1-py3-none-any.whl", hash = "sha256:6f0956d2c23d8fa6e7691934d8c3930eadb44972cbbd1a7ae3a520f735d43359"}, - {file = "amqp-5.1.1.tar.gz", hash = "sha256:2c1b13fecc0893e946c65cbd5f36427861cffa4ea2201d8f6fca22e2a373b5e2"}, -] -asgiref = [ - {file = "asgiref-3.6.0-py3-none-any.whl", hash = "sha256:71e68008da809b957b7ee4b43dbccff33d1b23519fb8344e33f049897077afac"}, - {file = "asgiref-3.6.0.tar.gz", hash = "sha256:9567dfe7bd8d3c8c892227827c41cce860b368104c3431da67a0c5a65a949506"}, -] -async-generator = [ - {file = "async_generator-1.10-py3-none-any.whl", hash = "sha256:01c7bf666359b4967d2cda0000cc2e4af16a0ae098cbffcb8472fb9e8ad6585b"}, - {file = "async_generator-1.10.tar.gz", hash = "sha256:6ebb3d106c12920aaae42ccb6f787ef5eefdcdd166ea3d628fa8476abe712144"}, -] -async-timeout = [ - {file = "async-timeout-4.0.2.tar.gz", hash = "sha256:2163e1640ddb52b7a8c80d0a67a08587e5d245cc9c553a74a847056bc2976b15"}, - {file = "async_timeout-4.0.2-py3-none-any.whl", hash = "sha256:8ca1e4fcf50d07413d66d1a5e416e42cfdf5851c981d679a09851a6853383b3c"}, -] -attrs = [ - {file = "attrs-22.2.0-py3-none-any.whl", hash = "sha256:29e95c7f6778868dbd49170f98f8818f78f3dc5e0e37c0b1f474e3561b240836"}, - {file = "attrs-22.2.0.tar.gz", hash = "sha256:c9227bfc2f01993c03f68db37d1d15c9690188323c067c641f1a35ca58185f99"}, -] -babel = [ - {file = "Babel-2.11.0-py3-none-any.whl", hash = "sha256:1ad3eca1c885218f6dce2ab67291178944f810a10a9b5f3cb8382a5a232b64fe"}, - {file = "Babel-2.11.0.tar.gz", hash = "sha256:5ef4b3226b0180dedded4229651c8b0e1a3a6a2837d45a073272f313e4cf97f6"}, -] -billiard = [ - {file = "billiard-3.6.4.0-py3-none-any.whl", hash = "sha256:87103ea78fa6ab4d5c751c4909bcff74617d985de7fa8b672cf8618afd5a875b"}, - {file = "billiard-3.6.4.0.tar.gz", hash = "sha256:299de5a8da28a783d51b197d496bef4f1595dd023a93a4f59dde1886ae905547"}, -] -celery = [ - {file = "celery-5.2.7-py3-none-any.whl", hash = "sha256:138420c020cd58d6707e6257b6beda91fd39af7afde5d36c6334d175302c0e14"}, - {file = "celery-5.2.7.tar.gz", hash = "sha256:fafbd82934d30f8a004f81e8f7a062e31413a23d444be8ee3326553915958c6d"}, -] -certifi = [ - {file = "certifi-2022.12.7-py3-none-any.whl", hash = "sha256:4ad3232f5e926d6718ec31cfc1fcadfde020920e278684144551c91769c7bc18"}, - {file = "certifi-2022.12.7.tar.gz", hash = "sha256:35824b4c3a97115964b408844d64aa14db1cc518f6562e8d7261699d1350a9e3"}, -] -cffi = [ +files = [ {file = "cffi-1.15.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:a66d3508133af6e8548451b25058d5812812ec3798c886bf38ed24a98216fab2"}, {file = "cffi-1.15.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:470c103ae716238bbe698d67ad020e1db9d9dba34fa5a899b5e21577e6d52ed2"}, {file = "cffi-1.15.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:9ad5db27f9cabae298d151c85cf2bad1d359a1b9c686a275df03385758e2f914"}, @@ -1237,7 +231,17 @@ cffi = [ {file = "cffi-1.15.1-cp39-cp39-win_amd64.whl", hash = "sha256:70df4e3b545a17496c9b3f41f5115e69a4f2e77e94e1d2a8e1070bc0c38c8a3c"}, {file = "cffi-1.15.1.tar.gz", hash = "sha256:d400bfb9a37b1351253cb402671cea7e89bdecc294e8016a707f6d1d8ac934f9"}, ] -charset-normalizer = [ + +[package.dependencies] +pycparser = "*" + +[[package]] +name = "charset-normalizer" +version = "3.0.1" +description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." +optional = false +python-versions = "*" +files = [ {file = "charset-normalizer-3.0.1.tar.gz", hash = "sha256:ebea339af930f8ca5d7a699b921106c6e29c617fe9606fa7baa043c1cdae326f"}, {file = "charset_normalizer-3.0.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:88600c72ef7587fe1708fd242b385b6ed4b8904976d5da0893e31df8b3480cb6"}, {file = "charset_normalizer-3.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c75ffc45f25324e68ab238cb4b5c0a38cd1c3d7f1fb1f72b5541de469e2247db"}, @@ -1327,27 +331,86 @@ charset-normalizer = [ {file = "charset_normalizer-3.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:0a11e971ed097d24c534c037d298ad32c6ce81a45736d31e0ff0ad37ab437d59"}, {file = "charset_normalizer-3.0.1-py3-none-any.whl", hash = "sha256:7e189e2e1d3ed2f4aebabd2d5b0f931e883676e51c7624826e0a4e5fe8a0bf24"}, ] -click = [ + +[[package]] +name = "click" +version = "8.1.3" +description = "Composable command line interface toolkit" +optional = false +python-versions = ">=3.7" +files = [ {file = "click-8.1.3-py3-none-any.whl", hash = "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"}, {file = "click-8.1.3.tar.gz", hash = "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e"}, ] -click-didyoumean = [ + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + +[[package]] +name = "click-didyoumean" +version = "0.3.0" +description = "Enables git-like *did-you-mean* feature in click" +optional = false +python-versions = ">=3.6.2,<4.0.0" +files = [ {file = "click-didyoumean-0.3.0.tar.gz", hash = "sha256:f184f0d851d96b6d29297354ed981b7dd71df7ff500d82fa6d11f0856bee8035"}, {file = "click_didyoumean-0.3.0-py3-none-any.whl", hash = "sha256:a0713dc7a1de3f06bc0df5a9567ad19ead2d3d5689b434768a6145bff77c0667"}, ] -click-plugins = [ + +[package.dependencies] +click = ">=7" + +[[package]] +name = "click-plugins" +version = "1.1.1" +description = "An extension module for click to enable registering CLI commands via setuptools entry-points." +optional = false +python-versions = "*" +files = [ {file = "click-plugins-1.1.1.tar.gz", hash = "sha256:46ab999744a9d831159c3411bb0c79346d94a444df9a3a3742e9ed63645f264b"}, {file = "click_plugins-1.1.1-py2.py3-none-any.whl", hash = "sha256:5d262006d3222f5057fd81e1623d4443e41dcda5dc815c06b442aa3c02889fc8"}, ] -click-repl = [ + +[package.dependencies] +click = ">=4.0" + +[package.extras] +dev = ["coveralls", "pytest (>=3.6)", "pytest-cov", "wheel"] + +[[package]] +name = "click-repl" +version = "0.2.0" +description = "REPL plugin for Click" +optional = false +python-versions = "*" +files = [ {file = "click-repl-0.2.0.tar.gz", hash = "sha256:cd12f68d745bf6151210790540b4cb064c7b13e571bc64b6957d98d120dacfd8"}, {file = "click_repl-0.2.0-py3-none-any.whl", hash = "sha256:94b3fbbc9406a236f176e0506524b2937e4b23b6f4c0c0b2a0a83f8a64e9194b"}, ] -colorama = [ + +[package.dependencies] +click = "*" +prompt-toolkit = "*" +six = "*" + +[[package]] +name = "colorama" +version = "0.4.6" +description = "Cross-platform colored terminal text." +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +files = [ {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, ] -cryptography = [ + +[[package]] +name = "cryptography" +version = "39.0.0" +description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." +optional = false +python-versions = ">=3.6" +files = [ {file = "cryptography-39.0.0-cp36-abi3-macosx_10_12_universal2.whl", hash = "sha256:c52a1a6f81e738d07f43dab57831c29e57d21c81a942f4602fac7ee21b27f288"}, {file = "cryptography-39.0.0-cp36-abi3-macosx_10_12_x86_64.whl", hash = "sha256:80ee674c08aaef194bc4627b7f2956e5ba7ef29c3cc3ca488cf15854838a8f72"}, {file = "cryptography-39.0.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:887cbc1ea60786e534b00ba8b04d1095f4272d380ebd5f7a7eb4cc274710fad9"}, @@ -1372,100 +435,350 @@ cryptography = [ {file = "cryptography-39.0.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:e0a05aee6a82d944f9b4edd6a001178787d1546ec7c6223ee9a848a7ade92e39"}, {file = "cryptography-39.0.0.tar.gz", hash = "sha256:f964c7dcf7802d133e8dbd1565914fa0194f9d683d82411989889ecd701e8adf"}, ] -defusedxml = [ + +[package.dependencies] +cffi = ">=1.12" + +[package.extras] +docs = ["sphinx (>=1.6.5,!=1.8.0,!=3.1.0,!=3.1.1,!=5.2.0,!=5.2.0.post0)", "sphinx-rtd-theme"] +docstest = ["pyenchant (>=1.6.11)", "sphinxcontrib-spelling (>=4.0.1)", "twine (>=1.12.0)"] +pep8test = ["black", "ruff"] +sdist = ["setuptools-rust (>=0.11.4)"] +ssh = ["bcrypt (>=3.1.5)"] +test = ["hypothesis (>=1.11.4,!=3.79.2)", "iso8601", "pretend", "pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-subtests", "pytest-xdist", "pytz"] + +[[package]] +name = "defusedxml" +version = "0.7.1" +description = "XML bomb protection for Python stdlib modules" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +files = [ {file = "defusedxml-0.7.1-py2.py3-none-any.whl", hash = "sha256:a352e7e428770286cc899e2542b6cdaedb2b4953ff269a210103ec58f6198a61"}, {file = "defusedxml-0.7.1.tar.gz", hash = "sha256:1bb3032db185915b62d7c6209c5a8792be6a32ab2fedacc84e01b52c51aa3e69"}, ] -django = [ + +[[package]] +name = "django" +version = "4.1.6" +description = "A high-level Python web framework that encourages rapid development and clean, pragmatic design." +optional = false +python-versions = ">=3.8" +files = [ {file = "Django-4.1.6-py3-none-any.whl", hash = "sha256:c6fe7ebe7c017fe59f1029821dae0acb5a2ddcd6c9a0138fd20a8bfefac914bc"}, {file = "Django-4.1.6.tar.gz", hash = "sha256:bceb0fe1a386781af0788cae4108622756cd05e7775448deec04a71ddf87685d"}, ] -django-allauth = [ + +[package.dependencies] +asgiref = ">=3.5.2,<4" +sqlparse = ">=0.2.2" +tzdata = {version = "*", markers = "sys_platform == \"win32\""} + +[package.extras] +argon2 = ["argon2-cffi (>=19.1.0)"] +bcrypt = ["bcrypt"] + +[[package]] +name = "django-allauth" +version = "0.52.0" +description = "Integrated set of Django applications addressing authentication, registration, account management as well as 3rd party (social) account authentication." +optional = false +python-versions = ">=3.5" +files = [ {file = "django-allauth-0.52.0.tar.gz", hash = "sha256:e380661ceafe55734c40102819ae720403027036f28e9f9827f0faeddc24ed5f"}, ] -django-analytical = [ + +[package.dependencies] +Django = ">=2.0" +pyjwt = {version = ">=1.7", extras = ["crypto"]} +python3-openid = ">=3.0.8" +requests = "*" +requests-oauthlib = ">=0.3.0" + +[[package]] +name = "django-analytical" +version = "3.1.0" +description = "Analytics service integration for Django projects" +optional = false +python-versions = ">=3.6" +files = [ {file = "django-analytical-3.1.0.tar.gz", hash = "sha256:6127c9196c8de3bcb4626f420d2ae670a7703152b1841b1b3e852b31a9a9d44b"}, {file = "django_analytical-3.1.0-py3-none-any.whl", hash = "sha256:43de3d8ef7734732f58eba4e5e7df0dea37512dbd89727efdfb30c27a96d4ea9"}, ] -django-anymail = [ + +[[package]] +name = "django-anymail" +version = "9.0" +description = "Django email backends and webhooks for Amazon SES, Mailgun, Mailjet, Mandrill, Postal, Postmark, SendGrid, SendinBlue, and SparkPost" +optional = false +python-versions = ">=3.6" +files = [ {file = "django-anymail-9.0.tar.gz", hash = "sha256:4239f7c61fb77b6eb8c8591a317a84a2a78f6bce1f8f42847921de74194b5d8a"}, {file = "django_anymail-9.0-py3-none-any.whl", hash = "sha256:c21d94ffdbada613a85c22a7bf32e37447d2811e1688cd001d3cad3c7f1ff289"}, ] -django-appconf = [ + +[package.dependencies] +django = ">=2.0" +requests = ">=2.4.3" + +[package.extras] +amazon-ses = ["boto3"] +dev = ["flake8", "sphinx", "sphinx-rtd-theme", "tox", "twine", "wheel"] +postal = ["cryptography"] + +[[package]] +name = "django-appconf" +version = "1.0.5" +description = "A helper class for handling configuration defaults of packaged apps gracefully." +optional = false +python-versions = ">=3.6" +files = [ {file = "django-appconf-1.0.5.tar.gz", hash = "sha256:be3db0be6c81fa84742000b89a81c016d70ae66a7ccb620cdef592b1f1a6aaa4"}, {file = "django_appconf-1.0.5-py3-none-any.whl", hash = "sha256:ae9f864ee1958c815a965ed63b3fba4874eec13de10236ba063a788f9a17389d"}, ] -django-celery-beat = [ - {file = "django-celery-beat-2.4.0.tar.gz", hash = "sha256:58efe9460e4373a241c2b3d839518f29a28ae19bc80a8dba20da204c7ea50613"}, - {file = "django_celery_beat-2.4.0-py3-none-any.whl", hash = "sha256:a20b7a7857daaa2eea1f4cfefb2a965456800216f5763c6cf918c2e21372b3c8"}, -] -django-celery-results = [ - {file = "django_celery_results-2.4.0-py3-none-any.whl", hash = "sha256:be91307c02fbbf0dda21993c3001c60edb74595444ccd6ad696552fe3689e85b"}, - {file = "django_celery_results-2.4.0.tar.gz", hash = "sha256:75aa51970db5691cbf242c6a0ff50c8cdf419e265cd0e9b772335d06436c4b99"}, -] -django-compressor = [ + +[package.dependencies] +django = "*" + +[[package]] +name = "django-compressor" +version = "4.3.1" +description = "('Compresses linked and inline JavaScript or CSS into single cached files.',)" +optional = false +python-versions = "*" +files = [ {file = "django_compressor-4.3.1-py2.py3-none-any.whl", hash = "sha256:2c451174acb6f083054af7c8089376599b22d6380bd60311f78ec3fed79acc8e"}, {file = "django_compressor-4.3.1.tar.gz", hash = "sha256:68858c0da6cc099cc29a022d86c3ba8aed114da9d709eeceb0d7b8181b5f8942"}, ] -django-debug-toolbar = [ + +[package.dependencies] +django-appconf = ">=1.0.3" +rcssmin = "1.1.1" +rjsmin = "1.2.1" + +[[package]] +name = "django-debug-toolbar" +version = "3.8.1" +description = "A configurable set of panels that display various debug information about the current request/response." +optional = false +python-versions = ">=3.7" +files = [ {file = "django_debug_toolbar-3.8.1-py3-none-any.whl", hash = "sha256:879f8a4672d41621c06a4d322dcffa630fc4df056cada6e417ed01db0e5e0478"}, {file = "django_debug_toolbar-3.8.1.tar.gz", hash = "sha256:24ef1a7d44d25e60d7951e378454c6509bf536dce7e7d9d36e7c387db499bc27"}, ] -django-filter = [ + +[package.dependencies] +django = ">=3.2.4" +sqlparse = ">=0.2" + +[[package]] +name = "django-filter" +version = "22.1" +description = "Django-filter is a reusable Django application for allowing users to filter querysets dynamically." +optional = false +python-versions = ">=3.7" +files = [ {file = "django-filter-22.1.tar.gz", hash = "sha256:ed473b76e84f7e83b2511bb2050c3efb36d135207d0128dfe3ae4b36e3594ba5"}, {file = "django_filter-22.1-py3-none-any.whl", hash = "sha256:ed429e34760127e3520a67f415bec4c905d4649fbe45d0d6da37e6ff5e0287eb"}, ] -django-localflavor = [ + +[package.dependencies] +Django = ">=3.2" + +[[package]] +name = "django-localflavor" +version = "3.1" +description = "Country-specific Django helpers" +optional = false +python-versions = "*" +files = [ {file = "django-localflavor-3.1.tar.gz", hash = "sha256:ac2fa377bbcba4cae95e97077d9e77c7f22b3d93e4845e2e133ba7e664043a44"}, {file = "django_localflavor-3.1-py3-none-any.whl", hash = "sha256:6593865dc671333b3edc88e729e6d384d00b6db7891ef5d3a65db831a40050d2"}, ] -django-measurement = [ + +[package.dependencies] +django = ">=2.2" +python-stdnum = ">=1.6" + +[[package]] +name = "django-measurement" +version = "3.2.4" +description = "Convenient fields and classes for handling measurements" +optional = false +python-versions = "*" +files = [ {file = "django-measurement-3.2.4.tar.gz", hash = "sha256:db1279b04bacf3b48259312adaaefcfe55ca30b1e0933fa37d6c387c156834d5"}, {file = "django_measurement-3.2.4-py2.py3-none-any.whl", hash = "sha256:b2d40b8b56b4e8277130a2a8cbc1f01f597589a636e0ea7dfbc4e4c05d458cef"}, ] -django-ranged-response = [ + +[package.dependencies] +django = ">=2.2" +django-appconf = ">=1.0.2" +measurement = ">=1.6,<4.0" + +[[package]] +name = "django-ranged-response" +version = "0.2.0" +description = "Modified Django FileResponse that adds Content-Range headers." +optional = false +python-versions = "*" +files = [ {file = "django-ranged-response-0.2.0.tar.gz", hash = "sha256:f71fff352a37316b9bead717fc76e4ddd6c9b99c4680cdf4783b9755af1cf985"}, ] -django-render-block = [ + +[package.dependencies] +django = "*" + +[[package]] +name = "django-render-block" +version = "0.9.2" +description = "Render a particular block from a template to a string." +optional = false +python-versions = ">=3.7" +files = [ {file = "django-render-block-0.9.2.tar.gz", hash = "sha256:3dde0d2abde9381685659d2ed0761b8bddf7741c48eaadec9e5b50a6c7850247"}, {file = "django_render_block-0.9.2-py3-none-any.whl", hash = "sha256:1768832be78476c36627b3e3a8e7d9eb0e2bc46b84daf2c51958e2f674173c40"}, ] -django-simple-captcha = [ + +[package.dependencies] +django = ">=2.2" + +[package.extras] +dev = ["Jinja2 (>=2.8)"] + +[[package]] +name = "django-simple-captcha" +version = "0.5.17" +description = "A very simple, yet powerful, Django captcha application" +optional = false +python-versions = "*" +files = [ {file = "django-simple-captcha-0.5.17.tar.gz", hash = "sha256:9649e66dab4e71efacbfef02f48b83b91684898352a1ab56f1686ce71033b328"}, {file = "django_simple_captcha-0.5.17-py2.py3-none-any.whl", hash = "sha256:f9a07e5e9de264ba4039c9eaad66bc48188a21ceda5fcdc2fa13c5512141c2c9"}, ] -django-storages = [ - {file = "django-storages-1.13.2.tar.gz", hash = "sha256:cbadd15c909ceb7247d4ffc503f12a9bec36999df8d0bef7c31e57177d512688"}, - {file = "django_storages-1.13.2-py3-none-any.whl", hash = "sha256:31dc5a992520be571908c4c40d55d292660ece3a55b8141462b4e719aa38eab3"}, -] -django-templated-email = [ + +[package.dependencies] +Django = ">=2.2" +django-ranged-response = "0.2.0" +Pillow = ">=6.2.0" + +[package.extras] +test = ["testfixtures"] + +[[package]] +name = "django-templated-email" +version = "3.0.1" +description = "A Django oriented templated / transaction email abstraction" +optional = false +python-versions = "*" +files = [ {file = "django-templated-email-3.0.1.tar.gz", hash = "sha256:432430293b0be35495faa23fcd8d60b33ff59d9a10b3366d7e01f75d9ff843f2"}, ] -django-timezone-field = [ - {file = "django-timezone-field-5.0.tar.gz", hash = "sha256:15746ed367a5a32eda76cfa2886eeec1de8cda79f519b7c5e12f87ed7cdbd663"}, - {file = "django_timezone_field-5.0-py3-none-any.whl", hash = "sha256:199f211082eeac7e83563929b8ce41399c1c0f00dfc2f36bc00bea381027eaaa"}, -] -environs = [ + +[package.dependencies] +django-render-block = ">=0.5" + +[[package]] +name = "environs" +version = "9.5.0" +description = "simplified environment variable parsing" +optional = false +python-versions = ">=3.6" +files = [ {file = "environs-9.5.0-py2.py3-none-any.whl", hash = "sha256:1e549569a3de49c05f856f40bce86979e7d5ffbbc4398e7f338574c220189124"}, {file = "environs-9.5.0.tar.gz", hash = "sha256:a76307b36fbe856bdca7ee9161e6c466fd7fcffc297109a118c59b54e27e30c9"}, ] -gunicorn = [ + +[package.dependencies] +marshmallow = ">=3.0.0" +python-dotenv = "*" + +[package.extras] +dev = ["dj-database-url", "dj-email-url", "django-cache-url", "flake8 (==4.0.1)", "flake8-bugbear (==21.9.2)", "mypy (==0.910)", "pre-commit (>=2.4,<3.0)", "pytest", "tox"] +django = ["dj-database-url", "dj-email-url", "django-cache-url"] +lint = ["flake8 (==4.0.1)", "flake8-bugbear (==21.9.2)", "mypy (==0.910)", "pre-commit (>=2.4,<3.0)"] +tests = ["dj-database-url", "dj-email-url", "django-cache-url", "pytest"] + +[[package]] +name = "gunicorn" +version = "20.1.0" +description = "WSGI HTTP Server for UNIX" +optional = false +python-versions = ">=3.5" +files = [ {file = "gunicorn-20.1.0-py3-none-any.whl", hash = "sha256:9dcc4547dbb1cb284accfb15ab5667a0e5d1881cc443e0677b4882a4067a807e"}, {file = "gunicorn-20.1.0.tar.gz", hash = "sha256:e0a968b5ba15f8a328fdfd7ab1fcb5af4470c28aaf7e55df02a99bc13138e6e8"}, ] -h11 = [ + +[package.dependencies] +setuptools = ">=3.0" + +[package.extras] +eventlet = ["eventlet (>=0.24.1)"] +gevent = ["gevent (>=1.4.0)"] +setproctitle = ["setproctitle"] +tornado = ["tornado (>=0.2)"] + +[[package]] +name = "h11" +version = "0.14.0" +description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" +optional = false +python-versions = ">=3.7" +files = [ {file = "h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761"}, {file = "h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"}, ] -idna = [ + +[[package]] +name = "idna" +version = "3.4" +description = "Internationalized Domain Names in Applications (IDNA)" +optional = false +python-versions = ">=3.5" +files = [ {file = "idna-3.4-py3-none-any.whl", hash = "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2"}, {file = "idna-3.4.tar.gz", hash = "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4"}, ] -kombu = [ + +[[package]] +name = "kombu" +version = "5.2.4" +description = "Messaging library for Python." +optional = false +python-versions = ">=3.7" +files = [ {file = "kombu-5.2.4-py3-none-any.whl", hash = "sha256:8b213b24293d3417bcf0d2f5537b7f756079e3ea232a8386dcc89a59fd2361a4"}, {file = "kombu-5.2.4.tar.gz", hash = "sha256:37cee3ee725f94ea8bb173eaab7c1760203ea53bbebae226328600f9d2799610"}, ] -lxml = [ + +[package.dependencies] +amqp = ">=5.0.9,<6.0.0" +vine = "*" + +[package.extras] +azureservicebus = ["azure-servicebus (>=7.0.0)"] +azurestoragequeues = ["azure-storage-queue"] +consul = ["python-consul (>=0.6.0)"] +librabbitmq = ["librabbitmq (>=2.0.0)"] +mongodb = ["pymongo (>=3.3.0,<3.12.1)"] +msgpack = ["msgpack"] +pyro = ["pyro4"] +qpid = ["qpid-python (>=0.26)", "qpid-tools (>=0.26)"] +redis = ["redis (>=3.4.1,!=4.0.0,!=4.0.1)"] +slmq = ["softlayer-messaging (>=1.0.3)"] +sqlalchemy = ["sqlalchemy"] +sqs = ["boto3 (>=1.9.12)", "pycurl (>=7.44.1,<7.45.0)", "urllib3 (>=1.26.7)"] +yaml = ["PyYAML (>=3.10)"] +zookeeper = ["kazoo (>=1.3.1)"] + +[[package]] +name = "lxml" +version = "4.9.2" +description = "Powerful and Pythonic XML processing library combining libxml2/libxslt with the ElementTree API." +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, != 3.4.*" +files = [ {file = "lxml-4.9.2-cp27-cp27m-macosx_10_15_x86_64.whl", hash = "sha256:76cf573e5a365e790396a5cc2b909812633409306c6531a6877c59061e42c4f2"}, {file = "lxml-4.9.2-cp27-cp27m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b1f42b6921d0e81b1bcb5e395bc091a70f41c4d4e55ba99c6da2b31626c44892"}, {file = "lxml-4.9.2-cp27-cp27m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:9f102706d0ca011de571de32c3247c6476b55bb6bc65a20f682f000b07a4852a"}, @@ -1544,39 +857,158 @@ lxml = [ {file = "lxml-4.9.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:7b515674acfdcadb0eb5d00d8a709868173acece5cb0be3dd165950cbfdf5409"}, {file = "lxml-4.9.2.tar.gz", hash = "sha256:2455cfaeb7ac70338b3257f41e21f0724f4b5b0c0e7702da67ee6c3640835b67"}, ] -marshmallow = [ + +[package.extras] +cssselect = ["cssselect (>=0.7)"] +html5 = ["html5lib"] +htmlsoup = ["BeautifulSoup4"] +source = ["Cython (>=0.29.7)"] + +[[package]] +name = "marshmallow" +version = "3.19.0" +description = "A lightweight library for converting complex datatypes to and from native Python datatypes." +optional = false +python-versions = ">=3.7" +files = [ {file = "marshmallow-3.19.0-py3-none-any.whl", hash = "sha256:93f0958568da045b0021ec6aeb7ac37c81bfcccbb9a0e7ed8559885070b3a19b"}, {file = "marshmallow-3.19.0.tar.gz", hash = "sha256:90032c0fd650ce94b6ec6dc8dfeb0e3ff50c144586462c389b81a07205bedb78"}, ] -measurement = [ + +[package.dependencies] +packaging = ">=17.0" + +[package.extras] +dev = ["flake8 (==5.0.4)", "flake8-bugbear (==22.10.25)", "mypy (==0.990)", "pre-commit (>=2.4,<3.0)", "pytest", "pytz", "simplejson", "tox"] +docs = ["alabaster (==0.7.12)", "autodocsumm (==0.2.9)", "sphinx (==5.3.0)", "sphinx-issues (==3.0.1)", "sphinx-version-warning (==1.1.2)"] +lint = ["flake8 (==5.0.4)", "flake8-bugbear (==22.10.25)", "mypy (==0.990)", "pre-commit (>=2.4,<3.0)"] +tests = ["pytest", "pytz", "simplejson"] + +[[package]] +name = "measurement" +version = "3.2.2" +description = "Easily use and manipulate unit-aware measurements in Python." +optional = false +python-versions = ">=3.7" +files = [ {file = "measurement-3.2.2-py3-none-any.whl", hash = "sha256:9e51ed130bd077f3bfe563959e1b1d7d5f2f4a51055c94625418db47464ffd78"}, {file = "measurement-3.2.2.tar.gz", hash = "sha256:a16fe5a8bd72f3682285e87a0abe880d647bcf6e3bf9cc727eeb261d6163c07e"}, ] -mpmath = [ + +[package.dependencies] +sympy = "*" + +[package.extras] +docs = ["python-docs-theme", "sphinx"] +test = ["pytest", "pytest-cov"] + +[[package]] +name = "mpmath" +version = "1.2.1" +description = "Python library for arbitrary-precision floating-point arithmetic" +optional = false +python-versions = "*" +files = [ {file = "mpmath-1.2.1-py3-none-any.whl", hash = "sha256:604bc21bd22d2322a177c73bdb573994ef76e62edd595d17e00aff24b0667e5c"}, {file = "mpmath-1.2.1.tar.gz", hash = "sha256:79ffb45cf9f4b101a807595bcb3e72e0396202e0b1d25d689134b48c4216a81a"}, ] -oauthlib = [ + +[package.extras] +develop = ["codecov", "pycodestyle", "pytest (>=4.6)", "pytest-cov", "wheel"] +tests = ["pytest (>=4.6)"] + +[[package]] +name = "oauthlib" +version = "3.2.2" +description = "A generic, spec-compliant, thorough implementation of the OAuth request-signing logic" +optional = false +python-versions = ">=3.6" +files = [ {file = "oauthlib-3.2.2-py3-none-any.whl", hash = "sha256:8139f29aac13e25d502680e9e19963e83f16838d48a0d71c287fe40e7067fbca"}, {file = "oauthlib-3.2.2.tar.gz", hash = "sha256:9859c40929662bec5d64f34d01c99e093149682a3f38915dc0655d5a633dd918"}, ] -outcome = [ + +[package.extras] +rsa = ["cryptography (>=3.0.0)"] +signals = ["blinker (>=1.4.0)"] +signedtoken = ["cryptography (>=3.0.0)", "pyjwt (>=2.0.0,<3)"] + +[[package]] +name = "outcome" +version = "1.2.0" +description = "Capture the outcome of Python function calls." +optional = false +python-versions = ">=3.7" +files = [ {file = "outcome-1.2.0-py2.py3-none-any.whl", hash = "sha256:c4ab89a56575d6d38a05aa16daeaa333109c1f96167aba8901ab18b6b5e0f7f5"}, {file = "outcome-1.2.0.tar.gz", hash = "sha256:6f82bd3de45da303cf1f771ecafa1633750a358436a8bb60e06a1ceb745d2672"}, ] -packaging = [ + +[package.dependencies] +attrs = ">=19.2.0" + +[[package]] +name = "packaging" +version = "23.0" +description = "Core utilities for Python packages" +optional = false +python-versions = ">=3.7" +files = [ {file = "packaging-23.0-py3-none-any.whl", hash = "sha256:714ac14496c3e68c99c29b00845f7a2b85f3bb6f1078fd9f72fd20f0570002b2"}, {file = "packaging-23.0.tar.gz", hash = "sha256:b6ad297f8907de0fa2fe1ccbd26fdaf387f5f47c7275fedf8cce89f99446cf97"}, ] -paypal-checkout-serversdk = [ + +[[package]] +name = "paypal-checkout-serversdk" +version = "1.0.1" +description = "Python Rest SDK for PayPal Checkout" +optional = false +python-versions = "*" +files = [ {file = "paypal-checkout-serversdk-1.0.1.tar.gz", hash = "sha256:80f62ba2d9fe22b58c2ce1f310146acf6037088493398dba8b1bb67b493aee5e"}, {file = "paypal_checkout_serversdk-1.0.1-py2-none-any.whl", hash = "sha256:e82bf50c249d7383cb4f68d8562a68dde3e7089389f286c111b6689bd3fdad36"}, ] -paypalhttp = [ + +[package.dependencies] +paypalhttp = "*" + +[[package]] +name = "paypalhttp" +version = "1.0.1" +description = "" +optional = false +python-versions = "*" +files = [ {file = "paypalhttp-1.0.1-py3-none-any.whl", hash = "sha256:251a6e72e2c5140c5372ee6351b64f7af61b5aad9c554618db5782a06205989a"}, {file = "paypalhttp-1.0.1.tar.gz", hash = "sha256:20e00f95ea052f59145b65bc2baf3b8720f449460c96bf7d32f191c8e293d16d"}, ] -pillow = [ + +[package.dependencies] +pyopenssl = ">=0.15" +requests = ">=2.0.0" +six = ">=1.0.0" + +[[package]] +name = "pillow" +version = "9.4.0" +description = "Python Imaging Library (Fork)" +optional = false +python-versions = ">=3.7" +files = [ + {file = "Pillow-9.4.0-1-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:1b4b4e9dda4f4e4c4e6896f93e84a8f0bcca3b059de9ddf67dac3c334b1195e1"}, + {file = "Pillow-9.4.0-1-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:fb5c1ad6bad98c57482236a21bf985ab0ef42bd51f7ad4e4538e89a997624e12"}, + {file = "Pillow-9.4.0-1-cp37-cp37m-macosx_10_10_x86_64.whl", hash = "sha256:f0caf4a5dcf610d96c3bd32932bfac8aee61c96e60481c2a0ea58da435e25acd"}, + {file = "Pillow-9.4.0-1-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:3f4cc516e0b264c8d4ccd6b6cbc69a07c6d582d8337df79be1e15a5056b258c9"}, + {file = "Pillow-9.4.0-1-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:b8c2f6eb0df979ee99433d8b3f6d193d9590f735cf12274c108bd954e30ca858"}, + {file = "Pillow-9.4.0-1-pp38-pypy38_pp73-macosx_10_10_x86_64.whl", hash = "sha256:b70756ec9417c34e097f987b4d8c510975216ad26ba6e57ccb53bc758f490dab"}, + {file = "Pillow-9.4.0-1-pp39-pypy39_pp73-macosx_10_10_x86_64.whl", hash = "sha256:43521ce2c4b865d385e78579a082b6ad1166ebed2b1a2293c3be1d68dd7ca3b9"}, + {file = "Pillow-9.4.0-2-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:9d9a62576b68cd90f7075876f4e8444487db5eeea0e4df3ba298ee38a8d067b0"}, + {file = "Pillow-9.4.0-2-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:87708d78a14d56a990fbf4f9cb350b7d89ee8988705e58e39bdf4d82c149210f"}, + {file = "Pillow-9.4.0-2-cp37-cp37m-macosx_10_10_x86_64.whl", hash = "sha256:8a2b5874d17e72dfb80d917213abd55d7e1ed2479f38f001f264f7ce7bae757c"}, + {file = "Pillow-9.4.0-2-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:83125753a60cfc8c412de5896d10a0a405e0bd88d0470ad82e0869ddf0cb3848"}, + {file = "Pillow-9.4.0-2-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:9e5f94742033898bfe84c93c831a6f552bb629448d4072dd312306bab3bd96f1"}, + {file = "Pillow-9.4.0-2-pp38-pypy38_pp73-macosx_10_10_x86_64.whl", hash = "sha256:013016af6b3a12a2f40b704677f8b51f72cb007dac785a9933d5c86a72a7fe33"}, + {file = "Pillow-9.4.0-2-pp39-pypy39_pp73-macosx_10_10_x86_64.whl", hash = "sha256:99d92d148dd03fd19d16175b6d355cc1b01faf80dae93c6c3eb4163709edc0a9"}, {file = "Pillow-9.4.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:2968c58feca624bb6c8502f9564dd187d0e1389964898f5e9e1fbc8533169157"}, {file = "Pillow-9.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c5c1362c14aee73f50143d74389b2c158707b4abce2cb055b7ad37ce60738d47"}, {file = "Pillow-9.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bd752c5ff1b4a870b7661234694f24b1d2b9076b8bf337321a814c612665f343"}, @@ -1641,11 +1073,32 @@ pillow = [ {file = "Pillow-9.4.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:8f127e7b028900421cad64f51f75c051b628db17fb00e099eb148761eed598c9"}, {file = "Pillow-9.4.0.tar.gz", hash = "sha256:a1c2d7780448eb93fbcc3789bf3916aa5720d942e37945f4056680317f1cd23e"}, ] -prompt-toolkit = [ + +[package.extras] +docs = ["furo", "olefile", "sphinx (>=2.4)", "sphinx-copybutton", "sphinx-inline-tabs", "sphinx-issues (>=3.0.1)", "sphinx-removed-in", "sphinxext-opengraph"] +tests = ["check-manifest", "coverage", "defusedxml", "markdown2", "olefile", "packaging", "pyroma", "pytest", "pytest-cov", "pytest-timeout"] + +[[package]] +name = "prompt-toolkit" +version = "3.0.36" +description = "Library for building powerful interactive command lines in Python" +optional = false +python-versions = ">=3.6.2" +files = [ {file = "prompt_toolkit-3.0.36-py3-none-any.whl", hash = "sha256:aa64ad242a462c5ff0363a7b9cfe696c20d55d9fc60c11fd8e632d064804d305"}, {file = "prompt_toolkit-3.0.36.tar.gz", hash = "sha256:3e163f254bef5a03b146397d7c1963bd3e2812f0964bb9a24e6ec761fd28db63"}, ] -psycopg2-binary = [ + +[package.dependencies] +wcwidth = "*" + +[[package]] +name = "psycopg2-binary" +version = "2.9.5" +description = "psycopg2 - Python-PostgreSQL Database Adapter" +optional = false +python-versions = ">=3.6" +files = [ {file = "psycopg2-binary-2.9.5.tar.gz", hash = "sha256:33e632d0885b95a8b97165899006c40e9ecdc634a529dca7b991eb7de4ece41c"}, {file = "psycopg2_binary-2.9.5-cp310-cp310-macosx_10_15_x86_64.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:0775d6252ccb22b15da3b5d7adbbf8cfe284916b14b6dc0ff503a23edb01ee85"}, {file = "psycopg2_binary-2.9.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2ec46ed947801652c9643e0b1dc334cfb2781232e375ba97312c2fc256597632"}, @@ -1718,56 +1171,164 @@ psycopg2-binary = [ {file = "psycopg2_binary-2.9.5-cp39-cp39-win32.whl", hash = "sha256:937880290775033a743f4836aa253087b85e62784b63fd099ee725d567a48aa1"}, {file = "psycopg2_binary-2.9.5-cp39-cp39-win_amd64.whl", hash = "sha256:484405b883630f3e74ed32041a87456c5e0e63a8e3429aa93e8714c366d62bd1"}, ] -py-moneyed = [ + +[[package]] +name = "py-moneyed" +version = "3.0" +description = "Provides Currency and Money classes for use in your Python code." +optional = false +python-versions = ">=3.7" +files = [ {file = "py-moneyed-3.0.tar.gz", hash = "sha256:4906f0f02cf2b91edba2e156f2d4e9a78f224059ab8c8fa2ff26230c75d894e8"}, {file = "py_moneyed-3.0-py3-none-any.whl", hash = "sha256:9583a14f99c05b46196193d8185206e9b73c8439fc8a5eee9cfc7e733676d9bb"}, ] -pycodestyle = [ + +[package.dependencies] +babel = ">=2.8.0" +typing-extensions = ">=3.7.4.3" + +[package.extras] +tests = ["pytest (>=2.3.0)", "tox (>=1.6.0)"] +type-tests = ["mypy (>=0.812)", "pytest (>=2.3.0)", "pytest-mypy-plugins"] + +[[package]] +name = "pycodestyle" +version = "2.10.0" +description = "Python style guide checker" +optional = false +python-versions = ">=3.6" +files = [ {file = "pycodestyle-2.10.0-py2.py3-none-any.whl", hash = "sha256:8a4eaf0d0495c7395bdab3589ac2db602797d76207242c17d470186815706610"}, {file = "pycodestyle-2.10.0.tar.gz", hash = "sha256:347187bdb476329d98f695c213d7295a846d1152ff4fe9bacb8a9590b8ee7053"}, ] -pycparser = [ + +[[package]] +name = "pycparser" +version = "2.21" +description = "C parser in Python" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ {file = "pycparser-2.21-py2.py3-none-any.whl", hash = "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9"}, {file = "pycparser-2.21.tar.gz", hash = "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"}, ] -pyjwt = [ + +[[package]] +name = "pyjwt" +version = "2.6.0" +description = "JSON Web Token implementation in Python" +optional = false +python-versions = ">=3.7" +files = [ {file = "PyJWT-2.6.0-py3-none-any.whl", hash = "sha256:d83c3d892a77bbb74d3e1a2cfa90afaadb60945205d1095d9221f04466f64c14"}, {file = "PyJWT-2.6.0.tar.gz", hash = "sha256:69285c7e31fc44f68a1feb309e948e0df53259d579295e6cfe2b1792329f05fd"}, ] -pyopenssl = [ + +[package.dependencies] +cryptography = {version = ">=3.4.0", optional = true, markers = "extra == \"crypto\""} + +[package.extras] +crypto = ["cryptography (>=3.4.0)"] +dev = ["coverage[toml] (==5.0.4)", "cryptography (>=3.4.0)", "pre-commit", "pytest (>=6.0.0,<7.0.0)", "sphinx (>=4.5.0,<5.0.0)", "sphinx-rtd-theme", "zope.interface"] +docs = ["sphinx (>=4.5.0,<5.0.0)", "sphinx-rtd-theme", "zope.interface"] +tests = ["coverage[toml] (==5.0.4)", "pytest (>=6.0.0,<7.0.0)"] + +[[package]] +name = "pyopenssl" +version = "23.0.0" +description = "Python wrapper module around the OpenSSL library" +optional = false +python-versions = ">=3.6" +files = [ {file = "pyOpenSSL-23.0.0-py3-none-any.whl", hash = "sha256:df5fc28af899e74e19fccb5510df423581047e10ab6f1f4ba1763ff5fde844c0"}, {file = "pyOpenSSL-23.0.0.tar.gz", hash = "sha256:c1cc5f86bcacefc84dada7d31175cae1b1518d5f60d3d0bb595a67822a868a6f"}, ] -pysocks = [ + +[package.dependencies] +cryptography = ">=38.0.0,<40" + +[package.extras] +docs = ["sphinx (!=5.2.0,!=5.2.0.post0)", "sphinx-rtd-theme"] +test = ["flaky", "pretend", "pytest (>=3.0.1)"] + +[[package]] +name = "pysocks" +version = "1.7.1" +description = "A Python SOCKS client module. See https://github.com/Anorov/PySocks for more information." +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ {file = "PySocks-1.7.1-py27-none-any.whl", hash = "sha256:08e69f092cc6dbe92a0fdd16eeb9b9ffbc13cadfe5ca4c7bd92ffb078b293299"}, {file = "PySocks-1.7.1-py3-none-any.whl", hash = "sha256:2725bd0a9925919b9b51739eea5f9e2bae91e83288108a9ad338b2e3a4435ee5"}, {file = "PySocks-1.7.1.tar.gz", hash = "sha256:3f8804571ebe159c380ac6de37643bb4685970655d3bba243530d6558b799aa0"}, ] -python-crontab = [ - {file = "python-crontab-2.7.1.tar.gz", hash = "sha256:b21af4647c7bbb848fef2f020616c6b0289dcb9f94b4f991a55310ff9bec5749"}, - {file = "python_crontab-2.7.1-py3-none-any.whl", hash = "sha256:9c374d1c9d401afdd8dd958f20077f74c158ab3fffb9604296802715e887fe48"}, -] -python-dateutil = [ - {file = "python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"}, - {file = "python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"}, -] -python-dotenv = [ + +[[package]] +name = "python-dotenv" +version = "0.21.1" +description = "Read key-value pairs from a .env file and set them as environment variables" +optional = false +python-versions = ">=3.7" +files = [ {file = "python-dotenv-0.21.1.tar.gz", hash = "sha256:1c93de8f636cde3ce377292818d0e440b6e45a82f215c3744979151fa8151c49"}, {file = "python_dotenv-0.21.1-py3-none-any.whl", hash = "sha256:41e12e0318bebc859fcc4d97d4db8d20ad21721a6aa5047dd59f090391cb549a"}, ] -python-stdnum = [ + +[package.extras] +cli = ["click (>=5.0)"] + +[[package]] +name = "python-stdnum" +version = "1.18" +description = "Python module to handle standardized numbers and codes" +optional = false +python-versions = "*" +files = [ {file = "python-stdnum-1.18.tar.gz", hash = "sha256:bcc763d9c49ae23da5d2b7a686d5fd1deec9d9051341160a10d1ac723a26bec0"}, {file = "python_stdnum-1.18-py2.py3-none-any.whl", hash = "sha256:d7f2a3c7ef4635c957b9cbdd9b1993d1f6ee3a2959f03e172c45440d99f296eb"}, ] -python3-openid = [ + +[package.extras] +soap = ["zeep"] +soap-alt = ["suds"] +soap-fallback = ["PySimpleSOAP"] + +[[package]] +name = "python3-openid" +version = "3.2.0" +description = "OpenID support for modern servers and consumers." +optional = false +python-versions = "*" +files = [ {file = "python3-openid-3.2.0.tar.gz", hash = "sha256:33fbf6928f401e0b790151ed2b5290b02545e8775f982485205a066f874aaeaf"}, {file = "python3_openid-3.2.0-py3-none-any.whl", hash = "sha256:6626f771e0417486701e0b4daff762e7212e820ca5b29fcc0d05f6f8736dfa6b"}, ] -pytz = [ + +[package.dependencies] +defusedxml = "*" + +[package.extras] +mysql = ["mysql-connector-python"] +postgresql = ["psycopg2"] + +[[package]] +name = "pytz" +version = "2022.7.1" +description = "World timezone definitions, modern and historical" +optional = false +python-versions = "*" +files = [ {file = "pytz-2022.7.1-py2.py3-none-any.whl", hash = "sha256:78f4f37d8198e0627c5f1143240bb0206b8691d8d7ac6d78fee88b78733f8c4a"}, {file = "pytz-2022.7.1.tar.gz", hash = "sha256:01a0681c4b9684a28304615eba55d1ab31ae00bf68ec157ec3708a8182dbbcd0"}, ] -rcssmin = [ + +[[package]] +name = "rcssmin" +version = "1.1.1" +description = "CSS Minifier" +optional = false +python-versions = "*" +files = [ {file = "rcssmin-1.1.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:d4e263fa9428704fd94c2cb565c7519ca1d225217943f71caffe6741ab5b9df1"}, {file = "rcssmin-1.1.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:c7278c1c25bb90d8e554df92cfb3b6a1195004ead50f764653d3093933ee0877"}, {file = "rcssmin-1.1.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:f15673e97f0a68b4c378c4d15b088fe96d60bc106d278c88829923118833c20f"}, @@ -1792,19 +1353,71 @@ rcssmin = [ {file = "rcssmin-1.1.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:a417735d4023d47d048a6288c88dbceadd20abaaf65a11bb4fda1e8458057019"}, {file = "rcssmin-1.1.1.tar.gz", hash = "sha256:4f9400b4366d29f5f5446f58e78549afa8338e6a59740c73115e9f6ac413dc64"}, ] -redis = [ + +[[package]] +name = "redis" +version = "4.4.2" +description = "Python client for Redis database and key-value store" +optional = false +python-versions = ">=3.7" +files = [ {file = "redis-4.4.2-py3-none-any.whl", hash = "sha256:e6206448e2f8a432871d07d432c13ed6c2abcf6b74edb436c99752b1371be387"}, {file = "redis-4.4.2.tar.gz", hash = "sha256:a010f6cb7378065040a02839c3f75c7e0fb37a87116fb4a95be82a95552776c7"}, ] -requests = [ + +[package.dependencies] +async-timeout = ">=4.0.2" + +[package.extras] +hiredis = ["hiredis (>=1.0.0)"] +ocsp = ["cryptography (>=36.0.1)", "pyopenssl (==20.0.1)", "requests (>=2.26.0)"] + +[[package]] +name = "requests" +version = "2.28.2" +description = "Python HTTP for Humans." +optional = false +python-versions = ">=3.7, <4" +files = [ {file = "requests-2.28.2-py3-none-any.whl", hash = "sha256:64299f4909223da747622c030b781c0d7811e359c37124b4bd368fb8c6518baa"}, {file = "requests-2.28.2.tar.gz", hash = "sha256:98b1b2782e3c6c4904938b84c0eb932721069dfdb9134313beff7c83c2df24bf"}, ] -requests-oauthlib = [ + +[package.dependencies] +certifi = ">=2017.4.17" +charset-normalizer = ">=2,<4" +idna = ">=2.5,<4" +urllib3 = ">=1.21.1,<1.27" + +[package.extras] +socks = ["PySocks (>=1.5.6,!=1.5.7)"] +use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] + +[[package]] +name = "requests-oauthlib" +version = "1.3.1" +description = "OAuthlib authentication support for Requests." +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ {file = "requests-oauthlib-1.3.1.tar.gz", hash = "sha256:75beac4a47881eeb94d5ea5d6ad31ef88856affe2332b9aafb52c6452ccf0d7a"}, {file = "requests_oauthlib-1.3.1-py2.py3-none-any.whl", hash = "sha256:2577c501a2fb8d05a304c09d090d6e47c306fef15809d102b327cf8364bddab5"}, ] -rjsmin = [ + +[package.dependencies] +oauthlib = ">=3.0.0" +requests = ">=2.0.0" + +[package.extras] +rsa = ["oauthlib[signedtoken] (>=3.0.0)"] + +[[package]] +name = "rjsmin" +version = "1.2.1" +description = "Javascript Minifier" +optional = false +python-versions = "*" +files = [ {file = "rjsmin-1.2.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:35827844d2085bd59d34214dfba6f1fc42a215c455887437b07dbf9c73019cc1"}, {file = "rjsmin-1.2.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:812af25c08d6a5ae98019a2e1b47ebb47f7469abd351670c353d619eaeae4064"}, {file = "rjsmin-1.2.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:b8464629a18fe69f70677854c93a3707976024b226a0ce62707c618f923e1346"}, @@ -1829,78 +1442,290 @@ rjsmin = [ {file = "rjsmin-1.2.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:8a6710e358c661dcdcfd027e67de3afd72a6af4c88101dcf110de39e9bbded39"}, {file = "rjsmin-1.2.1.tar.gz", hash = "sha256:1f982be8e011438777a94307279b40134a3935fc0f079312ee299725b8af5411"}, ] -selenium = [ + +[[package]] +name = "selenium" +version = "4.8.0" +description = "" +optional = false +python-versions = ">=3.7" +files = [ {file = "selenium-4.8.0-py3-none-any.whl", hash = "sha256:20f28ee4ea9b273b4112a7df5276ebb3052f79ff6eff42a564db6143e5926683"}, {file = "selenium-4.8.0.tar.gz", hash = "sha256:fee36724d6cf0b18c73781bb8ec7be4a35ab1e2564e64e64e64da75e50e052af"}, ] -sentry-sdk = [ + +[package.dependencies] +certifi = ">=2021.10.8" +trio = ">=0.17,<1.0" +trio-websocket = ">=0.9,<1.0" +urllib3 = {version = ">=1.26,<2.0", extras = ["socks"]} + +[[package]] +name = "sentry-sdk" +version = "1.14.0" +description = "Python client for Sentry (https://sentry.io)" +optional = false +python-versions = "*" +files = [ {file = "sentry-sdk-1.14.0.tar.gz", hash = "sha256:273fe05adf052b40fd19f6d4b9a5556316807246bd817e5e3482930730726bb0"}, {file = "sentry_sdk-1.14.0-py2.py3-none-any.whl", hash = "sha256:72c00322217d813cf493fe76590b23a757e063ff62fec59299f4af7201dd4448"}, ] -setuptools = [ + +[package.dependencies] +certifi = "*" +urllib3 = {version = ">=1.26.11", markers = "python_version >= \"3.6\""} + +[package.extras] +aiohttp = ["aiohttp (>=3.5)"] +beam = ["apache-beam (>=2.12)"] +bottle = ["bottle (>=0.12.13)"] +celery = ["celery (>=3)"] +chalice = ["chalice (>=1.16.0)"] +django = ["django (>=1.8)"] +falcon = ["falcon (>=1.4)"] +fastapi = ["fastapi (>=0.79.0)"] +flask = ["blinker (>=1.1)", "flask (>=0.11)"] +httpx = ["httpx (>=0.16.0)"] +opentelemetry = ["opentelemetry-distro (>=0.35b0)"] +pure-eval = ["asttokens", "executing", "pure-eval"] +pymongo = ["pymongo (>=3.1)"] +pyspark = ["pyspark (>=2.4.4)"] +quart = ["blinker (>=1.1)", "quart (>=0.16.1)"] +rq = ["rq (>=0.6)"] +sanic = ["sanic (>=0.8)"] +sqlalchemy = ["sqlalchemy (>=1.2)"] +starlette = ["starlette (>=0.19.1)"] +starlite = ["starlite (>=1.48)"] +tornado = ["tornado (>=5)"] + +[[package]] +name = "setuptools" +version = "67.1.0" +description = "Easily download, build, install, upgrade, and uninstall Python packages" +optional = false +python-versions = ">=3.7" +files = [ {file = "setuptools-67.1.0-py3-none-any.whl", hash = "sha256:a7687c12b444eaac951ea87a9627c4f904ac757e7abdc5aac32833234af90378"}, {file = "setuptools-67.1.0.tar.gz", hash = "sha256:e261cdf010c11a41cb5cb5f1bf3338a7433832029f559a6a7614bd42a967c300"}, ] -six = [ + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (==0.8.3)", "sphinx-reredirects", "sphinxcontrib-towncrier"] +testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8 (<5)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pip-run (>=8.8)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] +testing-integration = ["build[virtualenv]", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] + +[[package]] +name = "six" +version = "1.16.0" +description = "Python 2 and 3 compatibility utilities" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" +files = [ {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, ] -sniffio = [ + +[[package]] +name = "sniffio" +version = "1.3.0" +description = "Sniff out which async library your code is running under" +optional = false +python-versions = ">=3.7" +files = [ {file = "sniffio-1.3.0-py3-none-any.whl", hash = "sha256:eecefdce1e5bbfb7ad2eeaabf7c1eeb404d7757c379bd1f7e5cce9d8bf425384"}, {file = "sniffio-1.3.0.tar.gz", hash = "sha256:e60305c5e5d314f5389259b7f22aaa33d8f7dee49763119234af3755c55b9101"}, ] -sortedcontainers = [ + +[[package]] +name = "sortedcontainers" +version = "2.4.0" +description = "Sorted Containers -- Sorted List, Sorted Dict, Sorted Set" +optional = false +python-versions = "*" +files = [ {file = "sortedcontainers-2.4.0-py2.py3-none-any.whl", hash = "sha256:a163dcaede0f1c021485e957a39245190e74249897e2ae4b2aa38595db237ee0"}, {file = "sortedcontainers-2.4.0.tar.gz", hash = "sha256:25caa5a06cc30b6b83d11423433f65d1f9d76c4c6a0c90e3379eaa43b9bfdb88"}, ] -sqlparse = [ + +[[package]] +name = "sqlparse" +version = "0.4.3" +description = "A non-validating SQL parser." +optional = false +python-versions = ">=3.5" +files = [ {file = "sqlparse-0.4.3-py3-none-any.whl", hash = "sha256:0323c0ec29cd52bceabc1b4d9d579e311f3e4961b98d174201d5622a23b85e34"}, {file = "sqlparse-0.4.3.tar.gz", hash = "sha256:69ca804846bb114d2ec380e4360a8a340db83f0ccf3afceeb1404df028f57268"}, ] -stripe = [ + +[[package]] +name = "stripe" +version = "5.1.0" +description = "Python bindings for the Stripe API" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ {file = "stripe-5.1.0-py2.py3-none-any.whl", hash = "sha256:e25e5fe34afa9adda1b64be1852c3081d6b3f198fccd718fc9936dbcd2c175a7"}, {file = "stripe-5.1.0.tar.gz", hash = "sha256:f2d91d8e3daa4f385403c6cdbb6b38f5482b2de82b6e834a300b36352380e68e"}, ] -sympy = [ + +[package.dependencies] +requests = {version = ">=2.20", markers = "python_version >= \"3.0\""} + +[[package]] +name = "sympy" +version = "1.11.1" +description = "Computer algebra system (CAS) in Python" +optional = false +python-versions = ">=3.8" +files = [ {file = "sympy-1.11.1-py3-none-any.whl", hash = "sha256:938f984ee2b1e8eae8a07b884c8b7a1146010040fccddc6539c54f401c8f6fcf"}, {file = "sympy-1.11.1.tar.gz", hash = "sha256:e32380dce63cb7c0108ed525570092fd45168bdae2faa17e528221ef72e88658"}, ] -trio = [ + +[package.dependencies] +mpmath = ">=0.19" + +[[package]] +name = "trio" +version = "0.22.0" +description = "A friendly Python library for async concurrency and I/O" +optional = false +python-versions = ">=3.7" +files = [ {file = "trio-0.22.0-py3-none-any.whl", hash = "sha256:f1dd0780a89bfc880c7c7994519cb53f62aacb2c25ff487001c0052bd721cdf0"}, {file = "trio-0.22.0.tar.gz", hash = "sha256:ce68f1c5400a47b137c5a4de72c7c901bd4e7a24fbdebfe9b41de8c6c04eaacf"}, ] -trio-websocket = [ + +[package.dependencies] +async-generator = ">=1.9" +attrs = ">=19.2.0" +cffi = {version = ">=1.14", markers = "os_name == \"nt\" and implementation_name != \"pypy\""} +idna = "*" +outcome = "*" +sniffio = "*" +sortedcontainers = "*" + +[[package]] +name = "trio-websocket" +version = "0.9.2" +description = "WebSocket library for Trio" +optional = false +python-versions = ">=3.5" +files = [ {file = "trio-websocket-0.9.2.tar.gz", hash = "sha256:a3d34de8fac26023eee701ed1e7bf4da9a8326b61a62934ec9e53b64970fd8fe"}, {file = "trio_websocket-0.9.2-py3-none-any.whl", hash = "sha256:5b558f6e83cc20a37c3b61202476c5295d1addf57bd65543364e0337e37ed2bc"}, ] -typing-extensions = [ + +[package.dependencies] +async-generator = ">=1.10" +trio = ">=0.11" +wsproto = ">=0.14" + +[[package]] +name = "typing-extensions" +version = "4.4.0" +description = "Backported and Experimental Type Hints for Python 3.7+" +optional = false +python-versions = ">=3.7" +files = [ {file = "typing_extensions-4.4.0-py3-none-any.whl", hash = "sha256:16fa4864408f655d35ec496218b85f79b3437c829e93320c7c9215ccfd92489e"}, {file = "typing_extensions-4.4.0.tar.gz", hash = "sha256:1511434bb92bf8dd198c12b1cc812e800d4181cfcb867674e0f8279cc93087aa"}, ] -tzdata = [ + +[[package]] +name = "tzdata" +version = "2022.7" +description = "Provider of IANA time zone data" +optional = false +python-versions = ">=2" +files = [ {file = "tzdata-2022.7-py2.py3-none-any.whl", hash = "sha256:2b88858b0e3120792a3c0635c23daf36a7d7eeeca657c323da299d2094402a0d"}, {file = "tzdata-2022.7.tar.gz", hash = "sha256:fe5f866eddd8b96e9fcba978f8e503c909b19ea7efda11e52e39494bad3a7bfa"}, ] -urllib3 = [ + +[[package]] +name = "urllib3" +version = "1.26.14" +description = "HTTP library with thread-safe connection pooling, file post, and more." +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" +files = [ {file = "urllib3-1.26.14-py2.py3-none-any.whl", hash = "sha256:75edcdc2f7d85b137124a6c3c9fc3933cdeaa12ecb9a6a959f22797a0feca7e1"}, {file = "urllib3-1.26.14.tar.gz", hash = "sha256:076907bf8fd355cde77728471316625a4d2f7e713c125f51953bb5b3eecf4f72"}, ] -usps-api = [ + +[package.dependencies] +PySocks = {version = ">=1.5.6,<1.5.7 || >1.5.7,<2.0", optional = true, markers = "extra == \"socks\""} + +[package.extras] +brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)", "brotlipy (>=0.6.0)"] +secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "ipaddress", "pyOpenSSL (>=0.14)", "urllib3-secure-extra"] +socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] + +[[package]] +name = "usps-api" +version = "0.5" +description = "Python wrapper for the USPS API" +optional = false +python-versions = "*" +files = [ {file = "usps-api-0.5.tar.gz", hash = "sha256:20c2f19e02dde3eac01b794d6a4c1bcad03ab681d67a8579d090dbda5e5247a3"}, ] -vine = [ + +[package.dependencies] +lxml = "*" +requests = "*" +xmltodict = "*" + +[[package]] +name = "vine" +version = "5.0.0" +description = "Promises, promises, promises." +optional = false +python-versions = ">=3.6" +files = [ {file = "vine-5.0.0-py2.py3-none-any.whl", hash = "sha256:4c9dceab6f76ed92105027c49c823800dd33cacce13bdedc5b914e3514b7fb30"}, {file = "vine-5.0.0.tar.gz", hash = "sha256:7d3b1624a953da82ef63462013bbd271d3eb75751489f9807598e8f340bd637e"}, ] -wcwidth = [ + +[[package]] +name = "wcwidth" +version = "0.2.6" +description = "Measures the displayed width of unicode strings in a terminal" +optional = false +python-versions = "*" +files = [ {file = "wcwidth-0.2.6-py2.py3-none-any.whl", hash = "sha256:795b138f6875577cd91bba52baf9e445cd5118fd32723b460e30a0af30ea230e"}, {file = "wcwidth-0.2.6.tar.gz", hash = "sha256:a5220780a404dbe3353789870978e472cfe477761f06ee55077256e509b156d0"}, ] -wsproto = [ + +[[package]] +name = "wsproto" +version = "1.2.0" +description = "WebSockets state-machine based protocol implementation" +optional = false +python-versions = ">=3.7.0" +files = [ {file = "wsproto-1.2.0-py3-none-any.whl", hash = "sha256:b9acddd652b585d75b20477888c56642fdade28bdfd3579aa24a4d2c037dd736"}, {file = "wsproto-1.2.0.tar.gz", hash = "sha256:ad565f26ecb92588a3e43bc3d96164de84cd9902482b130d0ddbaa9664a85065"}, ] -xmltodict = [ + +[package.dependencies] +h11 = ">=0.9.0,<1" + +[[package]] +name = "xmltodict" +version = "0.13.0" +description = "Makes working with XML feel like you are working with JSON" +optional = false +python-versions = ">=3.4" +files = [ {file = "xmltodict-0.13.0-py2.py3-none-any.whl", hash = "sha256:aa89e8fd76320154a40d19a0df04a4695fb9dc5ba977cbb68ab3e4eb225e7852"}, {file = "xmltodict-0.13.0.tar.gz", hash = "sha256:341595a488e3e01a85a9d8911d8912fd922ede5fecc4dce437eb4b6c8d037e56"}, ] + +[metadata] +lock-version = "2.0" +python-versions = "^3.11" +content-hash = "fc52ce089cd76070e64e3f927f18a8f3a96e0b27e5377a9453d400520250f3f2" diff --git a/ptcoffee/settings.py b/ptcoffee/settings.py index b2ff1e6..b47b55d 100644 --- a/ptcoffee/settings.py +++ b/ptcoffee/settings.py @@ -72,10 +72,7 @@ INSTALLED_APPS = [ # 3rd Party 'django_filters', - 'storages', 'localflavor', - 'django_celery_beat', - 'django_celery_results', 'anymail', 'compressor', 'allauth', diff --git a/pyproject.toml b/pyproject.toml index 6293432..e296956 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -11,12 +11,9 @@ django = "^4.1.5" celery = {extras = ["redis"], version = "^5.2.7"} django-allauth = "^0.52.0" django-anymail = {extras = ["mailgun"], version = "^9.0"} -django-celery-beat = "^2.4.0" -django-celery-results = "^2.4.0" django-compressor = "^4.1" django-filter = "^22.1" django-measurement = "^3.2.4" -django-storages = "^1.13.2" django-templated-email = "^3.0.1" paypal-checkout-serversdk = "^1.0.1" pillow = "^9.4.0" diff --git a/storefront/templates/storefront/customer_detail.html b/storefront/templates/storefront/customer_detail.html index 38f433b..5bdc0b4 100644 --- a/storefront/templates/storefront/customer_detail.html +++ b/storefront/templates/storefront/customer_detail.html @@ -18,45 +18,17 @@ Manage
- Default shipping address - {% with shipping_address=customer.default_shipping_address %} -
- {{shipping_address.first_name}} - {{shipping_address.last_name}}
- {{shipping_address.street_address_1}}
- {% if shipping_address.street_address_2 %} - {{shipping_address.street_address_2}}
- {% endif %} - {{shipping_address.city}}, {{shipping_address.state}}, {{shipping_address.postal_code}} -
- {% endwith %} + Shipping address +
+ {{customer.shipping_street_address_1}}
+ {% if shipping_street_address_2 %} + {{customer.shipping_street_address_2}}
+ {% endif %} + {{customer.shipping_city}}, {{customer.shipping_state}}, {{customer.shipping_postal_code}} +
-
-

Your addresses

-

- + New address -

-
- {% for address in customer.addresses.all %} -

-

- {{address.first_name}} - {{address.last_name}}
- {{address.street_address_1}}
- {% if address.street_address_2 %} - {{address.street_address_2}}
- {% endif %} - {{address.city}}, {{address.state}}, {{address.postal_code}} -
- Edit -

- {% empty %} -

No other addresses.

- {% endfor %} -
-
{% if customer.subscriptions.count > 0 %}

Your subscriptions

diff --git a/storefront/urls.py b/storefront/urls.py index 6db96e5..920d2a4 100644 --- a/storefront/urls.py +++ b/storefront/urls.py @@ -89,26 +89,11 @@ urlpatterns = [ views.CustomerUpdateView.as_view(), name='customer-update', ), - # path( - # 'delete/', - # views.CustomerDeleteView.as_view(), - # name='customer-delete' - # ), path( 'orders//', views.OrderDetailView.as_view(), name='order-detail', ), - path( - 'addresses/new/', - views.CustomerAddressCreateView.as_view(), - name='customer-address-create', - ), - path( - 'addresses//update/', - views.CustomerAddressUpdateView.as_view(), - name='address-update', - ) ])), path( diff --git a/storefront/views.py b/storefront/views.py index c33b516..5bb02e2 100644 --- a/storefront/views.py +++ b/storefront/views.py @@ -34,11 +34,9 @@ from paypalcheckoutsdk.orders import OrdersCreateRequest, OrdersCaptureRequest from paypalcheckoutsdk.core import PayPalHttpClient, SandboxEnvironment from moneyed import Money, USD -from accounts.models import User, Address +from accounts.models import User from accounts.utils import get_or_create_customer -from accounts.forms import ( - AddressForm as AccountAddressForm, CustomerUpdateForm -) +from accounts.forms import CustomerUpdateForm from core.models import ( ProductCategory, Product, ProductVariant, ProductOption, Order, Transaction, OrderLine, Coupon, ShippingRate, @@ -252,16 +250,15 @@ class CheckoutAddressView(FormView): def get_initial(self): user = self.request.user initial = None - if user.is_authenticated and user.default_shipping_address: - address = user.default_shipping_address + if user.is_authenticated: initial = { - 'full_name': address.first_name + ' ' + address.last_name, + 'full_name': user.first_name + ' ' + user.last_name, 'email': user.email, - 'street_address_1': address.street_address_1, - 'street_address_2': address.street_address_2, - 'city': address.city, - 'state': address.state, - 'postal_code': address.postal_code + 'street_address_1': user.shipping_street_address_1, + 'street_address_2': user.shipping_street_address_2, + 'city': user.shipping_city, + 'state': user.shipping_state, + 'postal_code': user.shipping_postal_code } elif self.request.session.get('shipping_address'): address = self.request.session.get('shipping_address') @@ -413,7 +410,14 @@ class OrderCreateView(CreateView): shipping_container = cart.get_shipping_container() form.instance.shipping_total = cart.get_shipping_price(shipping_container) shipping_address = self.request.session.get('shipping_address') - form.instance.customer, form.instance.shipping_address = get_or_create_customer(self.request, shipping_address) + form.instance.shipping_first_name = shipping_address['first_name'] + form.instance.shipping_last_name = shipping_address['last_name'] + form.instance.shipping_street_address_1 = shipping_address['street_address_1'] + form.instance.shipping_street_address_2 = shipping_address['street_address_2'] + form.instance.shipping_city = shipping_address['city'] + form.instance.shipping_state = shipping_address['state'] + form.instance.shipping_postal_code = shipping_address['postal_code'] + form.instance.customer = get_or_create_customer(self.request, shipping_address) form.instance.status = OrderStatus.DRAFT self.object = form.save() bulk_list = cart.build_bulk_list(self.object) @@ -443,7 +447,14 @@ class FreeOrderCreateView(CreateView): shipping_container = cart.get_shipping_container() form.instance.shipping_total = cart.get_shipping_price(shipping_container) shipping_address = self.request.session.get('shipping_address') - form.instance.customer, form.instance.shipping_address = get_or_create_customer(self.request, shipping_address) + form.instance.shipping_first_name = shipping_address['first_name'] + form.instance.shipping_last_name = shipping_address['last_name'] + form.instance.shipping_street_address_1 = shipping_address['street_address_1'] + form.instance.shipping_street_address_2 = shipping_address['street_address_2'] + form.instance.shipping_city = shipping_address['city'] + form.instance.shipping_state = shipping_address['state'] + form.instance.shipping_postal_code = shipping_address['postal_code'] + form.instance.customer = get_or_create_customer(self.request, shipping_address) form.instance.status = OrderStatus.UNFULFILLED self.object = form.save() bulk_list = cart.build_bulk_list(self.object) @@ -562,58 +573,6 @@ class OrderDetailView(UserPassesTestMixin, LoginRequiredMixin, DetailView): return context -class CustomerAddressCreateView( - UserPassesTestMixin, LoginRequiredMixin, CreateView -): - model = Address - template_name = 'storefront/address_create_form.html' - form_class = AccountAddressForm - permission_denied_message = 'Not authorized.' - raise_exception = True - - def test_func(self): - return self.request.user.pk == self.kwargs['pk'] - - def get_context_data(self, **kwargs): - context = super().get_context_data(**kwargs) - context['customer'] = User.objects.get(pk=self.kwargs['pk']) - return context - - def form_valid(self, form): - customer = User.objects.get(pk=self.kwargs['pk']) - self.object = form.save() - customer.addresses.add(self.object) - - return super().form_valid(form) - - def get_success_url(self): - return reverse( - 'storefront:customer-detail', kwargs={'pk': self.kwargs['pk']} - ) - - -class CustomerAddressUpdateView( - UserPassesTestMixin, LoginRequiredMixin, UpdateView -): - model = Address - pk_url_kwarg = 'address_pk' - template_name = 'storefront/address_form.html' - form_class = AccountAddressForm - permission_denied_message = 'Not authorized.' - raise_exception = True - - def test_func(self): - return self.request.user.pk == self.kwargs['pk'] - - def get_context_data(self, **kwargs): - context = super().get_context_data(**kwargs) - context['customer'] = User.objects.get(pk=self.kwargs['pk']) - return context - - def get_success_url(self): - return reverse('storefront:customer-detail', kwargs={'pk': self.kwargs['pk']}) - - class AboutView(TemplateView): template_name = 'storefront/about.html' @@ -754,7 +713,7 @@ class SubscriptionCreateView(SuccessMessageMixin, CreateView): ).get('recurring') shipping_cost = get_shipping_cost( self.object.total_weight, - self.object.shipping_address.postal_code + self.object.shipping_postal_code ) * 100 line_items.append({ 'price_data': { @@ -789,7 +748,16 @@ class SubscriptionCreateView(SuccessMessageMixin, CreateView): def form_valid(self, form): shipping_address = self.request.session.get('shipping_address') subscription = self.request.session.get('subscription') - form.instance.customer, form.instance.shipping_address = get_or_create_customer(self.request, shipping_address) + form.instance.shipping_first_name = shipping_address['first_name'] + form.instance.shipping_last_name = shipping_address['last_name'] + form.instance.shipping_street_address_1 = shipping_address['street_address_1'] + form.instance.shipping_street_address_2 = shipping_address['street_address_2'] + form.instance.shipping_city = shipping_address['city'] + form.instance.shipping_state = shipping_address['state'] + form.instance.shipping_postal_code = shipping_address['postal_code'] + form.instance.customer = get_or_create_customer( + self.request, shipping_address + ) weight, unit = subscription['metadata']['total_weight'].split(':') form.instance.total_weight = guess( float(weight), unit, measures=[Weight] @@ -855,3 +823,4 @@ def stripe_webhook(request): subscription.create_order(event.data.object) return JsonResponse({'status': 'success'}) + From cf4a1e5ec754b50cfade86217774d861f6552959 Mon Sep 17 00:00:00 2001 From: Nathan Chapman Date: Fri, 14 Jul 2023 23:04:41 -0600 Subject: [PATCH 3/4] Add host name check in nginx conf --- nginx/prod.conf | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/nginx/prod.conf b/nginx/prod.conf index fbd942b..d04f0d8 100644 --- a/nginx/prod.conf +++ b/nginx/prod.conf @@ -19,6 +19,10 @@ server { listen [::]:443 ssl http2; server_name ptcoffee.com www.ptcoffee.com; + if ($http_host !~* ^(ptcoffee.com|www.ptcoffee.com)$ ) { + return 444; + } + # SSL ssl_certificate /etc/letsencrypt/live/ptcoffee.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/ptcoffee.com/privkey.pem; From e75f1dc1bcc3bc736ac2b346d118c8b891528244 Mon Sep 17 00:00:00 2001 From: Nathan Chapman Date: Sun, 16 Jul 2023 12:42:58 -0600 Subject: [PATCH 4/4] Finish address template changes --- accounts/forms.py | 16 ++++++++++- .../templates/dashboard/customer/detail.html | 6 ++--- .../templates/dashboard/order/detail.html | 2 +- .../dashboard/order/tracking_form.html | 2 +- .../dashboard/partials/_address.html | 7 ++--- static/styles/main.css | 15 +++++++++++ storefront/cart.py | 8 +++--- .../templates/storefront/customer_detail.html | 21 +++++++-------- .../templates/storefront/customer_form.html | 19 ++++++++----- .../customer_shipping_address_form.html | 27 +++++++++++++++++++ storefront/urls.py | 5 ++++ storefront/views.py | 20 +++++++++++++- templates/account/password_change.html | 16 +++++++++-- 13 files changed, 130 insertions(+), 34 deletions(-) create mode 100644 storefront/templates/storefront/customer_shipping_address_form.html diff --git a/accounts/forms.py b/accounts/forms.py index 9762acc..a61a599 100644 --- a/accounts/forms.py +++ b/accounts/forms.py @@ -27,18 +27,32 @@ class AccountUpdateForm(UserChangeForm): class CustomerUpdateForm(forms.ModelForm): + class Meta: + model = User + fields = ( + 'email', + ) + + +class CustomerShippingAddressUpdateForm(forms.ModelForm): class Meta: model = User fields = ( 'first_name', 'last_name', - 'email', 'shipping_street_address_1', 'shipping_street_address_2', 'shipping_city', 'shipping_state', 'shipping_postal_code', ) + labels = { + 'shipping_street_address_1': 'Street line 1', + 'shipping_street_address_2': 'Street line 2', + 'shipping_city': 'City', + 'shipping_state': 'State', + 'shipping_postal_code': 'ZIP code', + } class UserSignupForm(SignupForm): diff --git a/dashboard/templates/dashboard/customer/detail.html b/dashboard/templates/dashboard/customer/detail.html index fe2a932..4edbad1 100644 --- a/dashboard/templates/dashboard/customer/detail.html +++ b/dashboard/templates/dashboard/customer/detail.html @@ -19,14 +19,14 @@

Details

-
Primary email address
+
Email address
{{ customer.email }} ↗
-
Default shipping address
+
Shipping address
- {% include 'dashboard/partials/_address.html' with address=customer %} + {% include 'dashboard/partials/_address.html' with full_name=customer.get_full_name address=customer %}
diff --git a/dashboard/templates/dashboard/order/detail.html b/dashboard/templates/dashboard/order/detail.html index 4709e1e..b95f6a9 100644 --- a/dashboard/templates/dashboard/order/detail.html +++ b/dashboard/templates/dashboard/order/detail.html @@ -133,7 +133,7 @@
Shipping address - {% include 'dashboard/partials/_address.html' with address=order %} + {% include 'dashboard/partials/_address.html' with full_name=order.customer.get_full_name address=order %}
diff --git a/dashboard/templates/dashboard/order/tracking_form.html b/dashboard/templates/dashboard/order/tracking_form.html index f8d45b3..33fa38e 100644 --- a/dashboard/templates/dashboard/order/tracking_form.html +++ b/dashboard/templates/dashboard/order/tracking_form.html @@ -17,7 +17,7 @@
Shipping address - {% include 'dashboard/partials/_address.html' with address=order %} + {% include 'dashboard/partials/_address.html' with full_name=order.customer.get_full_name address=order %}
{% csrf_token %} diff --git a/dashboard/templates/dashboard/partials/_address.html b/dashboard/templates/dashboard/partials/_address.html index 7c3de9e..707ae64 100644 --- a/dashboard/templates/dashboard/partials/_address.html +++ b/dashboard/templates/dashboard/partials/_address.html @@ -1,7 +1,8 @@
- {{address.shipping_street_address_1}}
+ {{ full_name }}
+ {{ address.shipping_street_address_1 }}
{% if address.shipping_street_address_2 %} - {{address.shipping_street_address_2}}
+ {{ address.shipping_street_address_2 }}
{% endif %} - {{address.shipping_city}}, {{address.shipping_state}}, {{address.shipping_postal_code}} + {{ address.shipping_city }}, {{ address.shipping_state }}, {{ address.shipping_postal_code }}
diff --git a/static/styles/main.css b/static/styles/main.css index a098f1a..7406904 100644 --- a/static/styles/main.css +++ b/static/styles/main.css @@ -126,6 +126,21 @@ table a { white-space: normal; } +.form-table { + width: unset; + border: none; +} +.form-table th, +.form-table td { + border: none; +} +.form-table th { + padding: 0 1rem 1rem 0; +} +.form-table td { + padding: 0 0 1rem 0; +} + /* ========================================================================== Forms diff --git a/storefront/cart.py b/storefront/cart.py index 32cfb90..d4cbb40 100644 --- a/storefront/cart.py +++ b/storefront/cart.py @@ -255,7 +255,7 @@ class Cart: if container is None: container = self.get_shipping_container() - if not self.total_weight > Weight(lb=0): + if self.total_weight <= Weight(lb=0): return Decimal('0.00') if len(self) > 0 and self.session.get('shipping_address'): @@ -286,11 +286,11 @@ class Cart: ) if usps_rate_request['service'] == ShippingContainer.PRIORITY: - shipping_cost = Decimal(postage['Rate']) + shipping_price = Decimal(postage['Rate']) elif usps_rate_request['service'] == ShippingContainer.PRIORITY_COMMERCIAL: - shipping_cost = Decimal(postage['CommercialRate']) + shipping_price = Decimal(postage['CommercialRate']) - return shipping_cost + return shipping_price else: raise ShippingAddressError( 'Could not retrieve shipping address.' diff --git a/storefront/templates/storefront/customer_detail.html b/storefront/templates/storefront/customer_detail.html index 5bdc0b4..c31dfa4 100644 --- a/storefront/templates/storefront/customer_detail.html +++ b/storefront/templates/storefront/customer_detail.html @@ -5,21 +5,20 @@ {% block content %}
-
-

{{customer.get_full_name}}

- Edit profile -
-

Info

+

My Account

+

+ Change password +

- Email address
- {{customer.email}}
- Manage + Email address Change
+ {{customer.email}}
- Shipping address + Shipping address Change
+ {{ customer.get_full_name }}
{{customer.shipping_street_address_1}}
{% if shipping_street_address_2 %} {{customer.shipping_street_address_2}}
@@ -31,7 +30,7 @@
{% if customer.subscriptions.count > 0 %}
-

Your subscriptions

+

My subscriptions

@@ -53,7 +52,7 @@ {% endif %}
-

Your orders

+

My orders

diff --git a/storefront/templates/storefront/customer_form.html b/storefront/templates/storefront/customer_form.html index b63ba12..a7d7911 100644 --- a/storefront/templates/storefront/customer_form.html +++ b/storefront/templates/storefront/customer_form.html @@ -5,17 +5,22 @@

← Back

Update your profile

-

- Change password -

{% csrf_token %} - {{form.as_p}} -

- -

+
+ {{form.as_table}} + + + + + + +
+ + +
diff --git a/storefront/templates/storefront/customer_shipping_address_form.html b/storefront/templates/storefront/customer_shipping_address_form.html new file mode 100644 index 0000000..9f8c336 --- /dev/null +++ b/storefront/templates/storefront/customer_shipping_address_form.html @@ -0,0 +1,27 @@ +{% extends "base.html" %} + +{% block content %} +
+
+

← Back

+

Update your shipping address

+
+
+
+ {% csrf_token %} + + {{form.as_table}} + + + + + + +
+ + +
+
+
+
+{% endblock %} diff --git a/storefront/urls.py b/storefront/urls.py index 920d2a4..bf34d62 100644 --- a/storefront/urls.py +++ b/storefront/urls.py @@ -89,6 +89,11 @@ urlpatterns = [ views.CustomerUpdateView.as_view(), name='customer-update', ), + path( + 'shipping-address/update/', + views.CustomerShippingAddressUpdateView.as_view(), + name='customer-shipping-address-update', + ), path( 'orders//', views.OrderDetailView.as_view(), diff --git a/storefront/views.py b/storefront/views.py index 5bb02e2..cd541c3 100644 --- a/storefront/views.py +++ b/storefront/views.py @@ -36,7 +36,7 @@ from moneyed import Money, USD from accounts.models import User from accounts.utils import get_or_create_customer -from accounts.forms import CustomerUpdateForm +from accounts.forms import CustomerUpdateForm, CustomerShippingAddressUpdateForm from core.models import ( ProductCategory, Product, ProductVariant, ProductOption, Order, Transaction, OrderLine, Coupon, ShippingRate, @@ -557,6 +557,24 @@ class CustomerUpdateView(UserPassesTestMixin, LoginRequiredMixin, UpdateView): ) +class CustomerShippingAddressUpdateView(UserPassesTestMixin, LoginRequiredMixin, UpdateView): + model = User + template_name = 'storefront/customer_shipping_address_form.html' + context_object_name = 'customer' + form_class = CustomerShippingAddressUpdateForm + permission_denied_message = 'Not authorized.' + raise_exception = True + + def test_func(self): + return self.request.user.pk == self.get_object().pk + + def get_success_url(self): + return reverse( + 'storefront:customer-detail', kwargs={'pk': self.object.pk} + ) + + + class OrderDetailView(UserPassesTestMixin, LoginRequiredMixin, DetailView): model = Order template_name = 'storefront/order_detail.html' diff --git a/templates/account/password_change.html b/templates/account/password_change.html index 466b1fd..0a23b3a 100644 --- a/templates/account/password_change.html +++ b/templates/account/password_change.html @@ -14,8 +14,20 @@
{% csrf_token %} - {{ form.as_p }} - + + + {{ form.as_table }} + + + + + + + +
+ + +
{% endblock %}