aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatthew Lemon <y@yulqen.org>2024-12-31 16:01:12 +0000
committerMatthew Lemon <y@yulqen.org>2024-12-31 16:01:12 +0000
commitc74ea9e6b4af97be26029334868fa3264032c2d3 (patch)
tree6d0dcc825f9c4d70f543eeb1817d664e5e358320
parentb00665b30423e4818afafdec305202797638e145 (diff)
Major refactoring and removal of junk
This commit includes significant refactoring, cleanup, and updates across various components of the Django project. The following changes were made: - **Database Migration Updates:** - Removed unnecessary migrations in the `alphabetlearning.contrib.sites` and `alphabetlearning.payments` apps, consolidating them into cleaner initial migration files. - Altered the `Site` model in `alphabetlearning.contrib.sites` to streamline its fields and default settings. - Introduced new models in `alphabetlearning.payments` related to email signups and verification, reflecting a shift in focus and better alignment with current business logic. - **Django Settings Changes:** - Updated the database settings to use SQLite for local development, while commenting out production-specific configurations. - Managed third-party dependencies within `requirements.txt` and `pyproject.toml`, ensuring alignment with the latest Django version (5.1.4) and removing obsolete dependencies (e.g., `celery`, `django-celery-beat`, `flower`, etc.). - **Docker and Compose Clean-up:** - Removed old Dockerfiles and unnecessary services from the `docker-compose` configuration, streamlining the local and production setups. - Updated the Dockerfile configuration for local development, focusing on essential services only. - **General Code Clean-up:** - Removed unused tasks, views, tests, and files related to the Celery and Redis frameworks. - Cleaned up various model definitions and their related migrations for consistency and clarity. - **Documentation and Comments:** - Updated comments and code documentation where necessary to reflect the changes made during this cleanup process. This major refactor aims to enhance project maintainability and streamline the development experience while preparing for future feature expansions and improvements.
-rw-r--r--.gitignore1
-rw-r--r--alphabetlearning/contrib/sites/migrations/0001_initial.py47
-rw-r--r--alphabetlearning/contrib/sites/migrations/0002_alter_domain_unique.py21
-rw-r--r--alphabetlearning/contrib/sites/migrations/0003_set_site_domain_and_name.py63
-rw-r--r--alphabetlearning/contrib/sites/migrations/0004_alter_options_ordering_domain.py21
-rw-r--r--alphabetlearning/payments/migrations/0001_initial.py138
-rw-r--r--alphabetlearning/payments/migrations/0002_initial.py46
-rw-r--r--alphabetlearning/payments/migrations/0002_subscriptionplan_and_more.py36
-rw-r--r--alphabetlearning/payments/migrations/0003_product_price.py53
-rw-r--r--alphabetlearning/payments/migrations/0004_rename_stripe_product_id_price_stripe_price_id.py18
-rw-r--r--alphabetlearning/payments/migrations/0005_remove_subscriptionplan_stripe_plan_id.py17
-rw-r--r--alphabetlearning/payments/migrations/0006_subscription_plan.py24
-rw-r--r--alphabetlearning/payments/migrations/0007_remove_cartitem_quantity_and_more.py23
-rw-r--r--alphabetlearning/payments/migrations/0008_remove_price_product_price_resource.py25
-rw-r--r--alphabetlearning/payments/migrations/0009_emailsignup_alter_price_resource.py28
-rw-r--r--alphabetlearning/payments/migrations/0010_pendingemailverification.py26
-rw-r--r--alphabetlearning/payments/migrations/0011_remove_pendingemailverification_first_name_and_more.py21
-rw-r--r--alphabetlearning/payments/migrations/0012_rename_pendingemailverification_emailverification.py17
-rw-r--r--alphabetlearning/payments/migrations/0013_alter_emailverification_email.py18
-rw-r--r--alphabetlearning/payments/views.py4
-rw-r--r--alphabetlearning/resources/migrations/0001_initial.py40
-rw-r--r--alphabetlearning/resources/migrations/0002_alter_resource_additional_resource_category_and_more.py24
-rw-r--r--alphabetlearning/resources/migrations/0003_alter_resource_description.py18
-rw-r--r--alphabetlearning/resources/migrations/0004_resource_feature_slot_1_resource_feature_slot_2_and_more.py28
-rw-r--r--alphabetlearning/resources/migrations/0005_rename_feature_slot_1_resource_feature_slot_and_more.py26
-rw-r--r--alphabetlearning/resources/migrations/0006_resource_card_description_and_more.py23
-rw-r--r--alphabetlearning/resources/migrations/0007_alter_resource_feature_slot.py18
-rw-r--r--alphabetlearning/resources/migrations/0008_alter_resource_card_description.py18
-rw-r--r--alphabetlearning/resources/migrations/0009_alter_resource_feature_slot.py18
-rw-r--r--alphabetlearning/resources/migrations/0010_alter_resource_age_range.py18
-rw-r--r--alphabetlearning/resources/migrations/0011_alter_resource_curriculum.py18
-rw-r--r--alphabetlearning/resources/migrations/0012_resourcecategory_colour_css_class.py18
-rw-r--r--alphabetlearning/resources/migrations/0013_resourcecategory_badge_foreground_colour.py18
-rw-r--r--alphabetlearning/resources/migrations/0014_remove_resource_additional_resource_category_and_more.py27
-rw-r--r--alphabetlearning/resources/migrations/0015_resourcesubcategory.py30
-rw-r--r--alphabetlearning/resources/migrations/0016_alter_resourcesubcategory_options_and_more.py31
-rw-r--r--alphabetlearning/resources/migrations/0017_rename_additional_resource_category_resource_subcategories.py18
-rw-r--r--alphabetlearning/resources/migrations/0018_alter_resource_subcategories.py25
-rw-r--r--alphabetlearning/resources/migrations/0019_alter_pdfpagesnapshot_options_and_more.py26
-rw-r--r--alphabetlearning/resources/migrations/0020_remove_resource_price_resource_stripe_product_id.py23
-rw-r--r--alphabetlearning/users/migrations/0001_initial.py121
-rw-r--r--alphabetlearning/users/tasks.py9
-rw-r--r--alphabetlearning/users/tests/test_tasks.py17
-rw-r--r--compose/local/django/Dockerfile91
-rw-r--r--compose/local/django/celery/beat/start8
-rw-r--r--compose/local/django/celery/flower/start8
-rw-r--r--compose/local/django/celery/worker/start7
-rw-r--r--compose/local/django/start9
-rw-r--r--compose/local/docs/Dockerfile62
-rw-r--r--compose/local/docs/start7
-rw-r--r--compose/production/aws/Dockerfile9
-rw-r--r--compose/production/aws/maintenance/download23
-rw-r--r--compose/production/aws/maintenance/upload29
-rw-r--r--compose/production/django/Dockerfile91
-rw-r--r--compose/production/django/celery/beat/start8
-rw-r--r--compose/production/django/celery/flower/start11
-rw-r--r--compose/production/django/celery/worker/start8
-rw-r--r--compose/production/django/entrypoint49
-rw-r--r--compose/production/django/start10
-rw-r--r--compose/production/nginx/Dockerfile2
-rw-r--r--compose/production/nginx/default.conf7
-rw-r--r--compose/production/postgres/Dockerfile6
-rw-r--r--compose/production/postgres/maintenance/_sourced/constants.sh5
-rw-r--r--compose/production/postgres/maintenance/_sourced/countdown.sh12
-rw-r--r--compose/production/postgres/maintenance/_sourced/messages.sh41
-rw-r--r--compose/production/postgres/maintenance/_sourced/yes_no.sh16
-rw-r--r--compose/production/postgres/maintenance/backup38
-rw-r--r--compose/production/postgres/maintenance/backups22
-rw-r--r--compose/production/postgres/maintenance/restore55
-rw-r--r--compose/production/postgres/maintenance/rmbackup36
-rw-r--r--compose/production/traefik/Dockerfile5
-rw-r--r--compose/production/traefik/traefik.yml75
-rw-r--r--config/__init__.py5
-rw-r--r--config/celery_app.py17
-rw-r--r--config/settings/base.py76
-rw-r--r--config/settings/local.py4
-rw-r--r--config/settings/production.py3
-rw-r--r--local.yml75
-rw-r--r--pyproject.toml14
-rw-r--r--requirements.txt164
-rw-r--r--requirements/base.txt30
-rw-r--r--requirements/local.txt207
-rw-r--r--requirements/production.txt12
-rw-r--r--uv.lock308
84 files changed, 269 insertions, 2655 deletions
diff --git a/.gitignore b/.gitignore
index c3beae3..7b1ee5a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -343,3 +343,4 @@ pyblackbird_cc/media/
/pyblackbird_cc/static/scss/custom.css
/pyblackbird_cc/static/bootstrap/scss/
.vscode
+db.sqlite3
diff --git a/alphabetlearning/contrib/sites/migrations/0001_initial.py b/alphabetlearning/contrib/sites/migrations/0001_initial.py
index fd76afb..d6bb9e1 100644
--- a/alphabetlearning/contrib/sites/migrations/0001_initial.py
+++ b/alphabetlearning/contrib/sites/migrations/0001_initial.py
@@ -1,43 +1,32 @@
+# Generated by Django 5.1.4 on 2024-12-31 15:26
+
import django.contrib.sites.models
-from django.contrib.sites.models import _simple_domain_name_validator
-from django.db import migrations
-from django.db import models
+from django.db import migrations, models
class Migration(migrations.Migration):
- dependencies = []
+ initial = True
+
+ dependencies = [
+ ]
operations = [
migrations.CreateModel(
- name="Site",
+ name='Site',
fields=[
- (
- "id",
- models.AutoField(
- verbose_name="ID",
- serialize=False,
- auto_created=True,
- primary_key=True,
- ),
- ),
- (
- "domain",
- models.CharField(
- max_length=100,
- verbose_name="domain name",
- validators=[_simple_domain_name_validator],
- ),
- ),
- ("name", models.CharField(max_length=50, verbose_name="display name")),
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('domain', models.CharField(max_length=100, unique=True, validators=[django.contrib.sites.models._simple_domain_name_validator], verbose_name='domain name')),
+ ('name', models.CharField(max_length=50, verbose_name='display name')),
],
options={
- "ordering": ("domain",),
- "db_table": "django_site",
- "verbose_name": "site",
- "verbose_name_plural": "sites",
+ 'verbose_name': 'site',
+ 'verbose_name_plural': 'sites',
+ 'db_table': 'django_site',
+ 'ordering': ['domain'],
},
- bases=(models.Model,),
- managers=[("objects", django.contrib.sites.models.SiteManager())],
+ managers=[
+ ('objects', django.contrib.sites.models.SiteManager()),
+ ],
),
]
diff --git a/alphabetlearning/contrib/sites/migrations/0002_alter_domain_unique.py b/alphabetlearning/contrib/sites/migrations/0002_alter_domain_unique.py
deleted file mode 100644
index 4a44a6a..0000000
--- a/alphabetlearning/contrib/sites/migrations/0002_alter_domain_unique.py
+++ /dev/null
@@ -1,21 +0,0 @@
-import django.contrib.sites.models
-from django.db import migrations
-from django.db import models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [("sites", "0001_initial")]
-
- operations = [
- migrations.AlterField(
- model_name="site",
- name="domain",
- field=models.CharField(
- max_length=100,
- unique=True,
- validators=[django.contrib.sites.models._simple_domain_name_validator],
- verbose_name="domain name",
- ),
- )
- ]
diff --git a/alphabetlearning/contrib/sites/migrations/0003_set_site_domain_and_name.py b/alphabetlearning/contrib/sites/migrations/0003_set_site_domain_and_name.py
deleted file mode 100644
index f4b1cbb..0000000
--- a/alphabetlearning/contrib/sites/migrations/0003_set_site_domain_and_name.py
+++ /dev/null
@@ -1,63 +0,0 @@
-"""
-To understand why this file is here, please read:
-
-http://cookiecutter-django.readthedocs.io/en/latest/faq.html#why-is-there-a-django-contrib-sites-directory-in-cookiecutter-django
-"""
-from django.conf import settings
-from django.db import migrations
-
-
-def _update_or_create_site_with_sequence(site_model, connection, domain, name):
- """Update or create the site with default ID and keep the DB sequence in sync."""
- site, created = site_model.objects.update_or_create(
- id=settings.SITE_ID,
- defaults={
- "domain": domain,
- "name": name,
- },
- )
- if created:
- # We provided the ID explicitly when creating the Site entry, therefore the DB
- # sequence to auto-generate them wasn't used and is now out of sync. If we
- # don't do anything, we'll get a unique constraint violation the next time a
- # site is created.
- # To avoid this, we need to manually update DB sequence and make sure it's
- # greater than the maximum value.
- max_id = site_model.objects.order_by("-id").first().id
- with connection.cursor() as cursor:
- cursor.execute("SELECT last_value from django_site_id_seq")
- (current_id,) = cursor.fetchone()
- if current_id <= max_id:
- cursor.execute(
- "alter sequence django_site_id_seq restart with %s",
- [max_id + 1],
- )
-
-
-def update_site_forward(apps, schema_editor):
- """Set site domain and name."""
- Site = apps.get_model("sites", "Site")
- _update_or_create_site_with_sequence(
- Site,
- schema_editor.connection,
- "resources.joannalemon.com",
- "pyblackbird-cc",
- )
-
-
-def update_site_backward(apps, schema_editor):
- """Revert site domain and name to default."""
- Site = apps.get_model("sites", "Site")
- _update_or_create_site_with_sequence(
- Site,
- schema_editor.connection,
- "example.com",
- "example.com",
- )
-
-
-class Migration(migrations.Migration):
-
- dependencies = [("sites", "0002_alter_domain_unique")]
-
- operations = [migrations.RunPython(update_site_forward, update_site_backward)]
diff --git a/alphabetlearning/contrib/sites/migrations/0004_alter_options_ordering_domain.py b/alphabetlearning/contrib/sites/migrations/0004_alter_options_ordering_domain.py
deleted file mode 100644
index f7118ca..0000000
--- a/alphabetlearning/contrib/sites/migrations/0004_alter_options_ordering_domain.py
+++ /dev/null
@@ -1,21 +0,0 @@
-# Generated by Django 3.1.7 on 2021-02-04 14:49
-
-from django.db import migrations
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ("sites", "0003_set_site_domain_and_name"),
- ]
-
- operations = [
- migrations.AlterModelOptions(
- name="site",
- options={
- "ordering": ["domain"],
- "verbose_name": "site",
- "verbose_name_plural": "sites",
- },
- ),
- ]
diff --git a/alphabetlearning/payments/migrations/0001_initial.py b/alphabetlearning/payments/migrations/0001_initial.py
index 33b7602..136aef1 100644
--- a/alphabetlearning/payments/migrations/0001_initial.py
+++ b/alphabetlearning/payments/migrations/0001_initial.py
@@ -1,7 +1,7 @@
-# Generated by Django 5.0.4 on 2024-09-03 19:21
+# Generated by Django 5.1.4 on 2024-12-31 15:26
import django.db.models.deletion
-from django.conf import settings
+import uuid
from django.db import migrations, models
@@ -10,94 +10,78 @@ class Migration(migrations.Migration):
initial = True
dependencies = [
- ("resources", "0019_alter_pdfpagesnapshot_options_and_more"),
- migrations.swappable_dependency(settings.AUTH_USER_MODEL),
+ ('resources', '0001_initial'),
]
operations = [
migrations.CreateModel(
- name="ShoppingCart",
+ name='EmailSignup',
fields=[
- (
- "id",
- models.BigAutoField(
- auto_created=True,
- primary_key=True,
- serialize=False,
- verbose_name="ID",
- ),
- ),
- ("created_at", models.DateTimeField(auto_now_add=True)),
- ("updated_at", models.DateTimeField(auto_now=True)),
- (
- "user",
- models.OneToOneField(
- on_delete=django.db.models.deletion.CASCADE,
- to=settings.AUTH_USER_MODEL,
- ),
- ),
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('email', models.EmailField(max_length=254, unique=True)),
+ ('date_added', models.DateTimeField(auto_now_add=True)),
],
),
migrations.CreateModel(
- name="Subscription",
+ name='EmailVerification',
fields=[
- (
- "id",
- models.BigAutoField(
- auto_created=True,
- primary_key=True,
- serialize=False,
- verbose_name="ID",
- ),
- ),
- ("is_active", models.BooleanField(default=False)),
- ("start_date", models.DateTimeField(blank=True, null=True)),
- ("end_date", models.DateTimeField(blank=True, null=True)),
- (
- "stripe_subscription_id",
- models.CharField(blank=True, max_length=255, null=True),
- ),
- (
- "user",
- models.OneToOneField(
- on_delete=django.db.models.deletion.CASCADE,
- to=settings.AUTH_USER_MODEL,
- ),
- ),
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('email', models.EmailField(max_length=254, unique=True)),
+ ('verification_token', models.UUIDField(default=uuid.uuid4, editable=False)),
+ ('created_at', models.DateTimeField(auto_now_add=True)),
+ ('is_verified', models.BooleanField(default=False)),
],
),
migrations.CreateModel(
- name="CartItem",
+ name='Product',
fields=[
- (
- "id",
- models.BigAutoField(
- auto_created=True,
- primary_key=True,
- serialize=False,
- verbose_name="ID",
- ),
- ),
- ("quantity", models.PositiveIntegerField(default=1)),
- ("added_at", models.DateTimeField(auto_now_add=True)),
- (
- "resource",
- models.ForeignKey(
- on_delete=django.db.models.deletion.CASCADE,
- to="resources.resource",
- ),
- ),
- (
- "cart",
- models.ForeignKey(
- on_delete=django.db.models.deletion.CASCADE,
- related_name="items",
- to="payments.shoppingcart",
- ),
- ),
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('name', models.CharField(max_length=255)),
+ ('stripe_product_id', models.CharField(max_length=100)),
+ ],
+ ),
+ migrations.CreateModel(
+ name='ShoppingCart',
+ fields=[
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('created_at', models.DateTimeField(auto_now_add=True)),
+ ('updated_at', models.DateTimeField(auto_now=True)),
+ ],
+ ),
+ migrations.CreateModel(
+ name='Subscription',
+ fields=[
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('is_active', models.BooleanField(default=False)),
+ ('start_date', models.DateTimeField(blank=True, null=True)),
+ ('end_date', models.DateTimeField(blank=True, null=True)),
+ ],
+ ),
+ migrations.CreateModel(
+ name='SubscriptionPlan',
+ fields=[
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('name', models.CharField(max_length=255)),
+ ('price', models.DecimalField(decimal_places=2, max_digits=6)),
+ ('description', models.TextField()),
+ ('allowed_downloads', models.PositiveIntegerField()),
+ ],
+ ),
+ migrations.CreateModel(
+ name='CartItem',
+ fields=[
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('added_at', models.DateTimeField(auto_now_add=True)),
+ ('resource', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='resources.resource')),
+ ],
+ ),
+ migrations.CreateModel(
+ name='Price',
+ fields=[
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('price', models.IntegerField(default=0)),
+ ('stripe_price_id', models.CharField(max_length=100)),
+ ('resource', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='price_obj', to='resources.resource')),
],
- options={
- "unique_together": {("cart", "resource")},
- },
),
]
diff --git a/alphabetlearning/payments/migrations/0002_initial.py b/alphabetlearning/payments/migrations/0002_initial.py
new file mode 100644
index 0000000..04df889
--- /dev/null
+++ b/alphabetlearning/payments/migrations/0002_initial.py
@@ -0,0 +1,46 @@
+# Generated by Django 5.1.4 on 2024-12-31 15:26
+
+import django.db.models.deletion
+from django.conf import settings
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ initial = True
+
+ dependencies = [
+ ('payments', '0001_initial'),
+ migrations.swappable_dependency(settings.AUTH_USER_MODEL),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='shoppingcart',
+ name='user',
+ field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL),
+ ),
+ migrations.AddField(
+ model_name='cartitem',
+ name='cart',
+ field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='items', to='payments.shoppingcart'),
+ ),
+ migrations.AddField(
+ model_name='subscription',
+ name='user',
+ field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL),
+ ),
+ migrations.AddField(
+ model_name='subscription',
+ name='plan',
+ field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='payments.subscriptionplan'),
+ ),
+ migrations.AlterUniqueTogether(
+ name='cartitem',
+ unique_together={('cart', 'resource')},
+ ),
+ migrations.AddConstraint(
+ model_name='subscription',
+ constraint=models.UniqueConstraint(fields=('user', 'plan'), name='unique_user_plan'),
+ ),
+ ]
diff --git a/alphabetlearning/payments/migrations/0002_subscriptionplan_and_more.py b/alphabetlearning/payments/migrations/0002_subscriptionplan_and_more.py
deleted file mode 100644
index cab49b5..0000000
--- a/alphabetlearning/payments/migrations/0002_subscriptionplan_and_more.py
+++ /dev/null
@@ -1,36 +0,0 @@
-# Generated by Django 5.0.4 on 2024-09-03 19:32
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ("payments", "0001_initial"),
- ]
-
- operations = [
- migrations.CreateModel(
- name="SubscriptionPlan",
- fields=[
- (
- "id",
- models.BigAutoField(
- auto_created=True,
- primary_key=True,
- serialize=False,
- verbose_name="ID",
- ),
- ),
- ("name", models.CharField(max_length=255)),
- ("price", models.DecimalField(decimal_places=2, max_digits=6)),
- ("description", models.TextField()),
- ("allowed_downloads", models.PositiveIntegerField()),
- ("stripe_plan_id", models.CharField(max_length=255)),
- ],
- ),
- migrations.RemoveField(
- model_name="subscription",
- name="stripe_subscription_id",
- ),
- ]
diff --git a/alphabetlearning/payments/migrations/0003_product_price.py b/alphabetlearning/payments/migrations/0003_product_price.py
deleted file mode 100644
index b12d5dc..0000000
--- a/alphabetlearning/payments/migrations/0003_product_price.py
+++ /dev/null
@@ -1,53 +0,0 @@
-# Generated by Django 5.0.4 on 2024-09-04 19:01
-
-import django.db.models.deletion
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ("payments", "0002_subscriptionplan_and_more"),
- ]
-
- operations = [
- migrations.CreateModel(
- name="Product",
- fields=[
- (
- "id",
- models.BigAutoField(
- auto_created=True,
- primary_key=True,
- serialize=False,
- verbose_name="ID",
- ),
- ),
- ("name", models.CharField(max_length=255)),
- ("stripe_product_id", models.CharField(max_length=100)),
- ],
- ),
- migrations.CreateModel(
- name="Price",
- fields=[
- (
- "id",
- models.BigAutoField(
- auto_created=True,
- primary_key=True,
- serialize=False,
- verbose_name="ID",
- ),
- ),
- ("price", models.IntegerField(default=0)),
- ("stripe_product_id", models.CharField(max_length=100)),
- (
- "product",
- models.ForeignKey(
- on_delete=django.db.models.deletion.CASCADE,
- to="payments.product",
- ),
- ),
- ],
- ),
- ]
diff --git a/alphabetlearning/payments/migrations/0004_rename_stripe_product_id_price_stripe_price_id.py b/alphabetlearning/payments/migrations/0004_rename_stripe_product_id_price_stripe_price_id.py
deleted file mode 100644
index e5a339f..0000000
--- a/alphabetlearning/payments/migrations/0004_rename_stripe_product_id_price_stripe_price_id.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# Generated by Django 5.0.4 on 2024-09-04 19:17
-
-from django.db import migrations
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ("payments", "0003_product_price"),
- ]
-
- operations = [
- migrations.RenameField(
- model_name="price",
- old_name="stripe_product_id",
- new_name="stripe_price_id",
- ),
- ]
diff --git a/alphabetlearning/payments/migrations/0005_remove_subscriptionplan_stripe_plan_id.py b/alphabetlearning/payments/migrations/0005_remove_subscriptionplan_stripe_plan_id.py
deleted file mode 100644
index 1e642b4..0000000
--- a/alphabetlearning/payments/migrations/0005_remove_subscriptionplan_stripe_plan_id.py
+++ /dev/null
@@ -1,17 +0,0 @@
-# Generated by Django 5.0.4 on 2024-09-08 19:23
-
-from django.db import migrations
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ("payments", "0004_rename_stripe_product_id_price_stripe_price_id"),
- ]
-
- operations = [
- migrations.RemoveField(
- model_name="subscriptionplan",
- name="stripe_plan_id",
- ),
- ]
diff --git a/alphabetlearning/payments/migrations/0006_subscription_plan.py b/alphabetlearning/payments/migrations/0006_subscription_plan.py
deleted file mode 100644
index f54b5f9..0000000
--- a/alphabetlearning/payments/migrations/0006_subscription_plan.py
+++ /dev/null
@@ -1,24 +0,0 @@
-# Generated by Django 5.0.4 on 2024-09-08 20:21
-
-import django.db.models.deletion
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ("payments", "0005_remove_subscriptionplan_stripe_plan_id"),
- ]
-
- operations = [
- migrations.AddField(
- model_name="subscription",
- name="plan",
- field=models.ForeignKey(
- default=1,
- on_delete=django.db.models.deletion.CASCADE,
- to="payments.subscriptionplan",
- ),
- preserve_default=False,
- ),
- ]
diff --git a/alphabetlearning/payments/migrations/0007_remove_cartitem_quantity_and_more.py b/alphabetlearning/payments/migrations/0007_remove_cartitem_quantity_and_more.py
deleted file mode 100644
index e976059..0000000
--- a/alphabetlearning/payments/migrations/0007_remove_cartitem_quantity_and_more.py
+++ /dev/null
@@ -1,23 +0,0 @@
-# Generated by Django 5.0.4 on 2024-09-14 14:40
-
-from django.conf import settings
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('payments', '0006_subscription_plan'),
- migrations.swappable_dependency(settings.AUTH_USER_MODEL),
- ]
-
- operations = [
- migrations.RemoveField(
- model_name='cartitem',
- name='quantity',
- ),
- migrations.AddConstraint(
- model_name='subscription',
- constraint=models.UniqueConstraint(fields=('user', 'plan'), name='unique_user_plan'),
- ),
- ]
diff --git a/alphabetlearning/payments/migrations/0008_remove_price_product_price_resource.py b/alphabetlearning/payments/migrations/0008_remove_price_product_price_resource.py
deleted file mode 100644
index 9d52284..0000000
--- a/alphabetlearning/payments/migrations/0008_remove_price_product_price_resource.py
+++ /dev/null
@@ -1,25 +0,0 @@
-# Generated by Django 5.0.4 on 2024-10-19 15:11
-
-import django.db.models.deletion
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('payments', '0007_remove_cartitem_quantity_and_more'),
- ('resources', '0020_remove_resource_price_resource_stripe_product_id'),
- ]
-
- operations = [
- migrations.RemoveField(
- model_name='price',
- name='product',
- ),
- migrations.AddField(
- model_name='price',
- name='resource',
- field=models.ForeignKey(default=66, on_delete=django.db.models.deletion.CASCADE, related_name='price', to='resources.resource'),
- preserve_default=False,
- ),
- ]
diff --git a/alphabetlearning/payments/migrations/0009_emailsignup_alter_price_resource.py b/alphabetlearning/payments/migrations/0009_emailsignup_alter_price_resource.py
deleted file mode 100644
index 4be05a2..0000000
--- a/alphabetlearning/payments/migrations/0009_emailsignup_alter_price_resource.py
+++ /dev/null
@@ -1,28 +0,0 @@
-# Generated by Django 5.0.4 on 2024-11-25 11:32
-
-import django.db.models.deletion
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('payments', '0008_remove_price_product_price_resource'),
- ('resources', '0020_remove_resource_price_resource_stripe_product_id'),
- ]
-
- operations = [
- migrations.CreateModel(
- name='EmailSignup',
- fields=[
- ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ('email', models.EmailField(max_length=254, unique=True)),
- ('date_added', models.DateTimeField(auto_now_add=True)),
- ],
- ),
- migrations.AlterField(
- model_name='price',
- name='resource',
- field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='price_obj', to='resources.resource'),
- ),
- ]
diff --git a/alphabetlearning/payments/migrations/0010_pendingemailverification.py b/alphabetlearning/payments/migrations/0010_pendingemailverification.py
deleted file mode 100644
index c49a2cb..0000000
--- a/alphabetlearning/payments/migrations/0010_pendingemailverification.py
+++ /dev/null
@@ -1,26 +0,0 @@
-# Generated by Django 5.0.4 on 2024-12-03 16:18
-
-import uuid
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('payments', '0009_emailsignup_alter_price_resource'),
- ]
-
- operations = [
- migrations.CreateModel(
- name='PendingEmailVerification',
- fields=[
- ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ('email', models.EmailField(max_length=254)),
- ('first_name', models.CharField(max_length=100)),
- ('last_name', models.CharField(max_length=100)),
- ('verification_token', models.UUIDField(default=uuid.uuid4, editable=False)),
- ('created_at', models.DateTimeField(auto_now_add=True)),
- ('is_verified', models.BooleanField(default=False)),
- ],
- ),
- ]
diff --git a/alphabetlearning/payments/migrations/0011_remove_pendingemailverification_first_name_and_more.py b/alphabetlearning/payments/migrations/0011_remove_pendingemailverification_first_name_and_more.py
deleted file mode 100644
index e13f6d3..0000000
--- a/alphabetlearning/payments/migrations/0011_remove_pendingemailverification_first_name_and_more.py
+++ /dev/null
@@ -1,21 +0,0 @@
-# Generated by Django 5.0.4 on 2024-12-03 16:38
-
-from django.db import migrations
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('payments', '0010_pendingemailverification'),
- ]
-
- operations = [
- migrations.RemoveField(
- model_name='pendingemailverification',
- name='first_name',
- ),
- migrations.RemoveField(
- model_name='pendingemailverification',
- name='last_name',
- ),
- ]
diff --git a/alphabetlearning/payments/migrations/0012_rename_pendingemailverification_emailverification.py b/alphabetlearning/payments/migrations/0012_rename_pendingemailverification_emailverification.py
deleted file mode 100644
index 770aee5..0000000
--- a/alphabetlearning/payments/migrations/0012_rename_pendingemailverification_emailverification.py
+++ /dev/null
@@ -1,17 +0,0 @@
-# Generated by Django 5.0.4 on 2024-12-03 17:23
-
-from django.db import migrations
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('payments', '0011_remove_pendingemailverification_first_name_and_more'),
- ]
-
- operations = [
- migrations.RenameModel(
- old_name='PendingEmailVerification',
- new_name='EmailVerification',
- ),
- ]
diff --git a/alphabetlearning/payments/migrations/0013_alter_emailverification_email.py b/alphabetlearning/payments/migrations/0013_alter_emailverification_email.py
deleted file mode 100644
index 5d537bb..0000000
--- a/alphabetlearning/payments/migrations/0013_alter_emailverification_email.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# Generated by Django 5.0.4 on 2024-12-05 16:16
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('payments', '0012_rename_pendingemailverification_emailverification'),
- ]
-
- operations = [
- migrations.AlterField(
- model_name='emailverification',
- name='email',
- field=models.EmailField(max_length=254, unique=True),
- ),
- ]
diff --git a/alphabetlearning/payments/views.py b/alphabetlearning/payments/views.py
index 5a5d0df..6eaae1a 100644
--- a/alphabetlearning/payments/views.py
+++ b/alphabetlearning/payments/views.py
@@ -14,7 +14,6 @@ from django.views import View
from django.views.decorators.csrf import csrf_exempt
from django.views.generic import DeleteView
from django.views.generic import TemplateView
-from django_ratelimit.decorators import ratelimit
from alphabetlearning.payments.models import EmailSignup
from alphabetlearning.payments.models import EmailVerification
@@ -57,11 +56,8 @@ class SuccessEmailSignupView(TemplateView):
return context
-@ratelimit(key="ip", rate="2/m", block=True)
def email_signup_verification(request):
if request.method == "POST":
- if getattr(request, "limited", False):
- return render(request, "payments/rate_limited.html", status=429)
form = EmailVerificationForm(request.POST)
if form.is_valid():
# Create pending verification
diff --git a/alphabetlearning/resources/migrations/0001_initial.py b/alphabetlearning/resources/migrations/0001_initial.py
index 812c98f..9112f51 100644
--- a/alphabetlearning/resources/migrations/0001_initial.py
+++ b/alphabetlearning/resources/migrations/0001_initial.py
@@ -1,4 +1,4 @@
-# Generated by Django 5.0.4 on 2024-05-13 21:41
+# Generated by Django 5.1.4 on 2024-12-31 15:26
import django.db.models.deletion
from django.db import migrations, models
@@ -19,16 +19,22 @@ class Migration(migrations.Migration):
('file_name', models.CharField(max_length=255)),
('file_size', models.IntegerField()),
],
+ options={
+ 'verbose_name_plural': 'PDF Resources',
+ },
),
migrations.CreateModel(
name='Resource',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=255)),
+ ('stripe_product_id', models.CharField(max_length=100)),
('thumbnail_filenames', models.JSONField(default=list, verbose_name='Thumbnail filenames')),
- ('description', models.TextField(max_length=1000)),
- ('age_range', models.CharField(choices=[('3-5', '3-5'), ('5-7', '5-7'), ('7-11', '7-11'), ('11-14', '11-14'), ('14-16', '14-16'), ('16+', '16+'), ('Age not applicable', 'Age not applicable')], default='5-7', max_length=20)),
+ ('description', models.TextField(help_text='\n <strong>Markdown acceptable here!</strong>This is your opportunity to clearly explain what\n your resource is all about! It’s worth remembering that you are using the space to\n communicate to two different audiences. Firstly, think about what fellow teachers\n would like to know, such as exactly what the resource contains and how it could be used in\n the classroom. Secondly, the words you include on this page are also talking to internal and\n external search engines. External search engines, like Google, show the first 155 characters\n of the resource description, so make sure you take advantage\n of these characters by using lots of relevant keywords as part of an enticing pitch.\n', max_length=5000)),
+ ('card_description', models.TextField(blank=True, default='', help_text="If you enter text here, it will be used in the 'card' description box on the home page. Max 1000 characters.", max_length=1000)),
+ ('age_range', models.CharField(choices=[('Preschool (3-4yrs)', 'Preschool (3-4yrs)'), ('Nursery (2-5yrs)', 'Nursery (2-5yrs)'), ('Reception (4-5yrs)', 'Reception (4-5yrs)'), ('Year 1 (5-6yrs)', 'Year 1 (5-6yrs)'), ('Year 2 (6-7yrs)', 'Year 2 (6-7yrs)'), ('Early Years (0-5yrs)', 'Early Years (0-5yrs)'), ('Keystage 1 (5-7yrs)', 'Keystage 1 (5-7yrs)'), ('Keystage 2 (7-11yrs)', 'Keystage 2 (7-11yrs)'), ('Age not applicable', 'Age not applicable')], default='5-7', max_length=20)),
('curriculum', models.CharField(blank=True, choices=[('No curriculum', 'No curriculum'), ('English', 'English'), ('Scottish', 'Scottish')], default='English', max_length=20, null=True)),
+ ('feature_slot', models.IntegerField(blank=True, choices=[(1, 1), (2, 2), (3, 3)], default=0, null=True, unique=True)),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
],
@@ -40,12 +46,26 @@ class Migration(migrations.Migration):
('name', models.CharField(max_length=255)),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
+ ('colour_css_class', models.CharField(blank=True, max_length=56, null=True)),
+ ('badge_foreground_colour', models.CharField(blank=True, max_length=56, null=True)),
],
options={
'verbose_name_plural': 'Resource Categories',
},
),
migrations.CreateModel(
+ name='ResourceSubcategory',
+ fields=[
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('name', models.CharField(max_length=255)),
+ ('created_at', models.DateTimeField(auto_now_add=True)),
+ ('updated_at', models.DateTimeField(auto_now=True)),
+ ],
+ options={
+ 'verbose_name_plural': 'Resource Subcategories',
+ },
+ ),
+ migrations.CreateModel(
name='ResourceType',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
@@ -53,6 +73,9 @@ class Migration(migrations.Migration):
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
],
+ options={
+ 'verbose_name_plural': 'Resource Types',
+ },
),
migrations.CreateModel(
name='PDFPageSnapshot',
@@ -62,6 +85,9 @@ class Migration(migrations.Migration):
('file_name', models.CharField(max_length=255)),
('pdf_file', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='pdf_page_snapshots', to='resources.pdfresource')),
],
+ options={
+ 'verbose_name_plural': 'PDF Page Snapshots',
+ },
),
migrations.AddField(
model_name='pdfresource',
@@ -70,13 +96,13 @@ class Migration(migrations.Migration):
),
migrations.AddField(
model_name='resource',
- name='additional_resource_category',
- field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='additional_resource_category', to='resources.resourcecategory'),
+ name='main_resource_category',
+ field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='main_resource_category', to='resources.resourcecategory'),
),
migrations.AddField(
model_name='resource',
- name='main_resource_category',
- field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='main_resource_category', to='resources.resourcecategory'),
+ name='subcategories',
+ field=models.ManyToManyField(blank=True, related_name='additional_resource_category', to='resources.resourcesubcategory'),
),
migrations.AddField(
model_name='resource',
diff --git a/alphabetlearning/resources/migrations/0002_alter_resource_additional_resource_category_and_more.py b/alphabetlearning/resources/migrations/0002_alter_resource_additional_resource_category_and_more.py
deleted file mode 100644
index 3b70450..0000000
--- a/alphabetlearning/resources/migrations/0002_alter_resource_additional_resource_category_and_more.py
+++ /dev/null
@@ -1,24 +0,0 @@
-# Generated by Django 5.0.4 on 2024-05-15 20:00
-
-import django.db.models.deletion
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('resources', '0001_initial'),
- ]
-
- operations = [
- migrations.AlterField(
- model_name='resource',
- name='additional_resource_category',
- field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='additional_resource_category', to='resources.resourcecategory'),
- ),
- migrations.AlterField(
- model_name='resource',
- name='curriculum',
- field=models.CharField(blank=True, choices=[('No curriculum', 'No curriculum'), ('English', 'English'), ('Scottish', 'Scottish')], default='English', max_length=20),
- ),
- ]
diff --git a/alphabetlearning/resources/migrations/0003_alter_resource_description.py b/alphabetlearning/resources/migrations/0003_alter_resource_description.py
deleted file mode 100644
index f0ed99c..0000000
--- a/alphabetlearning/resources/migrations/0003_alter_resource_description.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# Generated by Django 5.0.4 on 2024-05-16 11:33
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('resources', '0002_alter_resource_additional_resource_category_and_more'),
- ]
-
- operations = [
- migrations.AlterField(
- model_name='resource',
- name='description',
- field=models.TextField(help_text='\n <strong>Markdown acceptable here!</strong>This is your opportunity to clearly explain what\n your resource is all about! It’s worth remembering that you are using the space to\n communicate to two different audiences. Firstly, think about what fellow teachers\n would like to know, such as exactly what the resource contains and how it could be used in\n the classroom. Secondly, the words you include on this page are also talking to internal and\n external search engines. External search engines, like Google, show the first 155 characters\n of the resource description, so make sure you take advantage\n of these characters by using lots of relevant keywords as part of an enticing pitch.\n', max_length=5000),
- ),
- ]
diff --git a/alphabetlearning/resources/migrations/0004_resource_feature_slot_1_resource_feature_slot_2_and_more.py b/alphabetlearning/resources/migrations/0004_resource_feature_slot_1_resource_feature_slot_2_and_more.py
deleted file mode 100644
index d595b62..0000000
--- a/alphabetlearning/resources/migrations/0004_resource_feature_slot_1_resource_feature_slot_2_and_more.py
+++ /dev/null
@@ -1,28 +0,0 @@
-# Generated by Django 5.0.4 on 2024-05-22 19:24
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('resources', '0003_alter_resource_description'),
- ]
-
- operations = [
- migrations.AddField(
- model_name='resource',
- name='feature_slot_1',
- field=models.IntegerField(choices=[(1, 1), (2, 2), (3, 3)], null=True, unique=True),
- ),
- migrations.AddField(
- model_name='resource',
- name='feature_slot_2',
- field=models.IntegerField(choices=[(1, 1), (2, 2), (3, 3)], null=True, unique=True),
- ),
- migrations.AddField(
- model_name='resource',
- name='feature_slot_3',
- field=models.IntegerField(choices=[(1, 1), (2, 2), (3, 3)], null=True, unique=True),
- ),
- ]
diff --git a/alphabetlearning/resources/migrations/0005_rename_feature_slot_1_resource_feature_slot_and_more.py b/alphabetlearning/resources/migrations/0005_rename_feature_slot_1_resource_feature_slot_and_more.py
deleted file mode 100644
index 2937c50..0000000
--- a/alphabetlearning/resources/migrations/0005_rename_feature_slot_1_resource_feature_slot_and_more.py
+++ /dev/null
@@ -1,26 +0,0 @@
-# Generated by Django 5.0.4 on 2024-05-22 19:26
-
-from django.db import migrations
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('resources', '0004_resource_feature_slot_1_resource_feature_slot_2_and_more'),
- ]
-
- operations = [
- migrations.RenameField(
- model_name='resource',
- old_name='feature_slot_1',
- new_name='feature_slot',
- ),
- migrations.RemoveField(
- model_name='resource',
- name='feature_slot_2',
- ),
- migrations.RemoveField(
- model_name='resource',
- name='feature_slot_3',
- ),
- ]
diff --git a/alphabetlearning/resources/migrations/0006_resource_card_description_and_more.py b/alphabetlearning/resources/migrations/0006_resource_card_description_and_more.py
deleted file mode 100644
index d343e76..0000000
--- a/alphabetlearning/resources/migrations/0006_resource_card_description_and_more.py
+++ /dev/null
@@ -1,23 +0,0 @@
-# Generated by Django 5.0.4 on 2024-05-26 15:09
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('resources', '0005_rename_feature_slot_1_resource_feature_slot_and_more'),
- ]
-
- operations = [
- migrations.AddField(
- model_name='resource',
- name='card_description',
- field=models.TextField(blank=True, help_text="If you enter text here, it will be used in the 'card' description box on the home page. Max 1000 characters.", max_length=1000, null=True),
- ),
- migrations.AlterField(
- model_name='resource',
- name='feature_slot',
- field=models.IntegerField(blank=True, choices=[(1, 1), (2, 2), (3, 3)], null=True, unique=True),
- ),
- ]
diff --git a/alphabetlearning/resources/migrations/0007_alter_resource_feature_slot.py b/alphabetlearning/resources/migrations/0007_alter_resource_feature_slot.py
deleted file mode 100644
index c5c17c7..0000000
--- a/alphabetlearning/resources/migrations/0007_alter_resource_feature_slot.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# Generated by Django 5.0.4 on 2024-05-26 15:23
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('resources', '0006_resource_card_description_and_more'),
- ]
-
- operations = [
- migrations.AlterField(
- model_name='resource',
- name='feature_slot',
- field=models.IntegerField(blank=True, choices=[(0, 0), (1, 1), (2, 2), (3, 3)], default=0, null=True, unique=True),
- ),
- ]
diff --git a/alphabetlearning/resources/migrations/0008_alter_resource_card_description.py b/alphabetlearning/resources/migrations/0008_alter_resource_card_description.py
deleted file mode 100644
index 18e5739..0000000
--- a/alphabetlearning/resources/migrations/0008_alter_resource_card_description.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# Generated by Django 5.0.4 on 2024-05-26 15:42
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('resources', '0007_alter_resource_feature_slot'),
- ]
-
- operations = [
- migrations.AlterField(
- model_name='resource',
- name='card_description',
- field=models.TextField(blank=True, default='', help_text="If you enter text here, it will be used in the 'card' description box on the home page. Max 1000 characters.", max_length=1000),
- ),
- ]
diff --git a/alphabetlearning/resources/migrations/0009_alter_resource_feature_slot.py b/alphabetlearning/resources/migrations/0009_alter_resource_feature_slot.py
deleted file mode 100644
index ca082d4..0000000
--- a/alphabetlearning/resources/migrations/0009_alter_resource_feature_slot.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# Generated by Django 5.0.4 on 2024-05-26 18:17
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('resources', '0008_alter_resource_card_description'),
- ]
-
- operations = [
- migrations.AlterField(
- model_name='resource',
- name='feature_slot',
- field=models.IntegerField(blank=True, choices=[(1, 1), (2, 2), (3, 3)], default=0, null=True, unique=True),
- ),
- ]
diff --git a/alphabetlearning/resources/migrations/0010_alter_resource_age_range.py b/alphabetlearning/resources/migrations/0010_alter_resource_age_range.py
deleted file mode 100644
index 49c7a2c..0000000
--- a/alphabetlearning/resources/migrations/0010_alter_resource_age_range.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# Generated by Django 5.0.4 on 2024-07-11 14:47
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('resources', '0009_alter_resource_feature_slot'),
- ]
-
- operations = [
- migrations.AlterField(
- model_name='resource',
- name='age_range',
- field=models.CharField(choices=[('Preschool (3-4yrs)', 'Preschool (3-4yrs)'), ('Nursery (2-5yrs)', 'Nursery (2-5yrs)'), ('Reception (4-5yrs)', 'Reception (4-5yrs)'), ('Year 1 (5-6yrs)', 'Year 1 (5-6yrs)'), ('Year 2 (6-7yrs)', 'Year 2 (6-7yrs)'), ('Early Years (0-5yrs)', 'Early Years (0-5yrs)'), ('Keystage 1 (5-7yrs)', 'Keystage 1 (5-7yrs)'), ('Keystage 2 (7-11yrs)', 'Keystage 2 (7-11yrs)'), ('Age not applicable', 'Age not applicable')], default='5-7', max_length=20),
- ),
- ]
diff --git a/alphabetlearning/resources/migrations/0011_alter_resource_curriculum.py b/alphabetlearning/resources/migrations/0011_alter_resource_curriculum.py
deleted file mode 100644
index 5e1b193..0000000
--- a/alphabetlearning/resources/migrations/0011_alter_resource_curriculum.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# Generated by Django 5.0.4 on 2024-07-11 14:50
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('resources', '0010_alter_resource_age_range'),
- ]
-
- operations = [
- migrations.AlterField(
- model_name='resource',
- name='curriculum',
- field=models.CharField(blank=True, choices=[('No curriculum', 'No curriculum'), ('English', 'English'), ('Scottish', 'Scottish')], default='English', max_length=20, null=True),
- ),
- ]
diff --git a/alphabetlearning/resources/migrations/0012_resourcecategory_colour_css_class.py b/alphabetlearning/resources/migrations/0012_resourcecategory_colour_css_class.py
deleted file mode 100644
index b48ba61..0000000
--- a/alphabetlearning/resources/migrations/0012_resourcecategory_colour_css_class.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# Generated by Django 5.0.4 on 2024-07-11 15:47
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('resources', '0011_alter_resource_curriculum'),
- ]
-
- operations = [
- migrations.AddField(
- model_name='resourcecategory',
- name='colour_css_class',
- field=models.CharField(blank=True, max_length=56, null=True),
- ),
- ]
diff --git a/alphabetlearning/resources/migrations/0013_resourcecategory_badge_foreground_colour.py b/alphabetlearning/resources/migrations/0013_resourcecategory_badge_foreground_colour.py
deleted file mode 100644
index 7644a5c..0000000
--- a/alphabetlearning/resources/migrations/0013_resourcecategory_badge_foreground_colour.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# Generated by Django 5.0.4 on 2024-07-28 15:12
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ("resources", "0012_resourcecategory_colour_css_class"),
- ]
-
- operations = [
- migrations.AddField(
- model_name="resourcecategory",
- name="badge_foreground_colour",
- field=models.CharField(blank=True, max_length=56, null=True),
- ),
- ]
diff --git a/alphabetlearning/resources/migrations/0014_remove_resource_additional_resource_category_and_more.py b/alphabetlearning/resources/migrations/0014_remove_resource_additional_resource_category_and_more.py
deleted file mode 100644
index 7573bf2..0000000
--- a/alphabetlearning/resources/migrations/0014_remove_resource_additional_resource_category_and_more.py
+++ /dev/null
@@ -1,27 +0,0 @@
-# Generated by Django 5.0.4 on 2024-07-29 14:52
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ("resources", "0013_resourcecategory_badge_foreground_colour"),
- ]
-
- operations = [
- migrations.RemoveField(
- model_name="resource",
- name="additional_resource_category",
- ),
- migrations.AddField(
- model_name="resource",
- name="additional_resource_category",
- field=models.ManyToManyField(
- blank=True,
- null=True,
- related_name="additional_resource_category",
- to="resources.resourcecategory",
- ),
- ),
- ]
diff --git a/alphabetlearning/resources/migrations/0015_resourcesubcategory.py b/alphabetlearning/resources/migrations/0015_resourcesubcategory.py
deleted file mode 100644
index 3eaada4..0000000
--- a/alphabetlearning/resources/migrations/0015_resourcesubcategory.py
+++ /dev/null
@@ -1,30 +0,0 @@
-# Generated by Django 5.0.4 on 2024-07-29 14:55
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ("resources", "0014_remove_resource_additional_resource_category_and_more"),
- ]
-
- operations = [
- migrations.CreateModel(
- name="ResourceSubcategory",
- fields=[
- (
- "id",
- models.BigAutoField(
- auto_created=True,
- primary_key=True,
- serialize=False,
- verbose_name="ID",
- ),
- ),
- ("name", models.CharField(max_length=255)),
- ("created_at", models.DateTimeField(auto_now_add=True)),
- ("updated_at", models.DateTimeField(auto_now=True)),
- ],
- ),
- ]
diff --git a/alphabetlearning/resources/migrations/0016_alter_resourcesubcategory_options_and_more.py b/alphabetlearning/resources/migrations/0016_alter_resourcesubcategory_options_and_more.py
deleted file mode 100644
index 77a3f27..0000000
--- a/alphabetlearning/resources/migrations/0016_alter_resourcesubcategory_options_and_more.py
+++ /dev/null
@@ -1,31 +0,0 @@
-# Generated by Django 5.0.4 on 2024-07-29 15:36
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ("resources", "0015_resourcesubcategory"),
- ]
-
- operations = [
- migrations.AlterModelOptions(
- name="resourcesubcategory",
- options={"verbose_name_plural": "Resource Subcategories"},
- ),
- migrations.AlterModelOptions(
- name="resourcetype",
- options={"verbose_name_plural": "Resource Types"},
- ),
- migrations.AlterField(
- model_name="resource",
- name="additional_resource_category",
- field=models.ManyToManyField(
- blank=True,
- null=True,
- related_name="additional_resource_category",
- to="resources.resourcesubcategory",
- ),
- ),
- ]
diff --git a/alphabetlearning/resources/migrations/0017_rename_additional_resource_category_resource_subcategories.py b/alphabetlearning/resources/migrations/0017_rename_additional_resource_category_resource_subcategories.py
deleted file mode 100644
index bc4d1e4..0000000
--- a/alphabetlearning/resources/migrations/0017_rename_additional_resource_category_resource_subcategories.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# Generated by Django 5.0.4 on 2024-08-01 14:29
-
-from django.db import migrations
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ("resources", "0016_alter_resourcesubcategory_options_and_more"),
- ]
-
- operations = [
- migrations.RenameField(
- model_name="resource",
- old_name="additional_resource_category",
- new_name="subcategories",
- ),
- ]
diff --git a/alphabetlearning/resources/migrations/0018_alter_resource_subcategories.py b/alphabetlearning/resources/migrations/0018_alter_resource_subcategories.py
deleted file mode 100644
index c981512..0000000
--- a/alphabetlearning/resources/migrations/0018_alter_resource_subcategories.py
+++ /dev/null
@@ -1,25 +0,0 @@
-# Generated by Django 5.0.4 on 2024-08-01 15:08
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- (
- "resources",
- "0017_rename_additional_resource_category_resource_subcategories",
- ),
- ]
-
- operations = [
- migrations.AlterField(
- model_name="resource",
- name="subcategories",
- field=models.ManyToManyField(
- blank=True,
- related_name="additional_resource_category",
- to="resources.resourcesubcategory",
- ),
- ),
- ]
diff --git a/alphabetlearning/resources/migrations/0019_alter_pdfpagesnapshot_options_and_more.py b/alphabetlearning/resources/migrations/0019_alter_pdfpagesnapshot_options_and_more.py
deleted file mode 100644
index 511d747..0000000
--- a/alphabetlearning/resources/migrations/0019_alter_pdfpagesnapshot_options_and_more.py
+++ /dev/null
@@ -1,26 +0,0 @@
-# Generated by Django 5.0.4 on 2024-09-03 19:21
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ("resources", "0018_alter_resource_subcategories"),
- ]
-
- operations = [
- migrations.AlterModelOptions(
- name="pdfpagesnapshot",
- options={"verbose_name_plural": "PDF Page Snapshots"},
- ),
- migrations.AlterModelOptions(
- name="pdfresource",
- options={"verbose_name_plural": "PDF Resources"},
- ),
- migrations.AddField(
- model_name="resource",
- name="price",
- field=models.DecimalField(decimal_places=2, default=0.0, max_digits=6),
- ),
- ]
diff --git a/alphabetlearning/resources/migrations/0020_remove_resource_price_resource_stripe_product_id.py b/alphabetlearning/resources/migrations/0020_remove_resource_price_resource_stripe_product_id.py
deleted file mode 100644
index 088556b..0000000
--- a/alphabetlearning/resources/migrations/0020_remove_resource_price_resource_stripe_product_id.py
+++ /dev/null
@@ -1,23 +0,0 @@
-# Generated by Django 5.0.4 on 2024-10-19 15:11
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('resources', '0019_alter_pdfpagesnapshot_options_and_more'),
- ]
-
- operations = [
- migrations.RemoveField(
- model_name='resource',
- name='price',
- ),
- migrations.AddField(
- model_name='resource',
- name='stripe_product_id',
- field=models.CharField(default='baws', max_length=100),
- preserve_default=False,
- ),
- ]
diff --git a/alphabetlearning/users/migrations/0001_initial.py b/alphabetlearning/users/migrations/0001_initial.py
index 20b1233..e5caa0b 100644
--- a/alphabetlearning/users/migrations/0001_initial.py
+++ b/alphabetlearning/users/migrations/0001_initial.py
@@ -1,118 +1,41 @@
-import django.contrib.auth.models
-import django.contrib.auth.validators
-import django.utils.timezone
-from django.db import migrations
-from django.db import models
+# Generated by Django 5.1.4 on 2024-12-31 15:26
-import alphabetlearning.users.models
+import alphabetlearning.users.managers
+import django.utils.timezone
+from django.db import migrations, models
class Migration(migrations.Migration):
+
initial = True
dependencies = [
- ("auth", "0012_alter_user_first_name_max_length"),
+ ('auth', '0012_alter_user_first_name_max_length'),
]
operations = [
migrations.CreateModel(
- name="User",
+ name='User',
fields=[
- (
- "id",
- models.BigAutoField(
- auto_created=True,
- primary_key=True,
- serialize=False,
- verbose_name="ID",
- ),
- ),
- ("password", models.CharField(max_length=128, verbose_name="password")),
- (
- "last_login",
- models.DateTimeField(
- blank=True,
- null=True,
- verbose_name="last login",
- ),
- ),
- (
- "is_superuser",
- models.BooleanField(
- default=False,
- help_text="Designates that this user has all permissions without explicitly assigning them.",
- verbose_name="superuser status",
- ),
- ),
- (
- "email",
- models.EmailField(
- unique=True,
- max_length=254,
- verbose_name="email address",
- ),
- ),
- (
- "is_staff",
- models.BooleanField(
- default=False,
- help_text="Designates whether the user can log into this admin site.",
- verbose_name="staff status",
- ),
- ),
- (
- "is_active",
- models.BooleanField(
- default=True,
- help_text="Designates whether this user should be treated as active. Unselect this instead of deleting accounts.",
- verbose_name="active",
- ),
- ),
- (
- "date_joined",
- models.DateTimeField(
- default=django.utils.timezone.now,
- verbose_name="date joined",
- ),
- ),
- (
- "name",
- models.CharField(
- blank=True,
- max_length=255,
- verbose_name="Name of User",
- ),
- ),
- (
- "groups",
- models.ManyToManyField(
- blank=True,
- help_text="The groups this user belongs to. A user will get all permissions granted to each of their groups.",
- related_name="user_set",
- related_query_name="user",
- to="auth.Group",
- verbose_name="groups",
- ),
- ),
- (
- "user_permissions",
- models.ManyToManyField(
- blank=True,
- help_text="Specific permissions for this user.",
- related_name="user_set",
- related_query_name="user",
- to="auth.Permission",
- verbose_name="user permissions",
- ),
- ),
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('password', models.CharField(max_length=128, verbose_name='password')),
+ ('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')),
+ ('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')),
+ ('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')),
+ ('is_active', models.BooleanField(default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')),
+ ('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')),
+ ('name', models.CharField(blank=True, max_length=255, verbose_name='Name of User')),
+ ('email', models.EmailField(max_length=254, unique=True, verbose_name='email address')),
+ ('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.group', verbose_name='groups')),
+ ('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.permission', verbose_name='user permissions')),
],
options={
- "verbose_name": "user",
- "verbose_name_plural": "users",
- "abstract": False,
+ 'verbose_name': 'user',
+ 'verbose_name_plural': 'users',
+ 'abstract': False,
},
managers=[
- ("objects", alphabetlearning.users.models.UserManager()),
+ ('objects', alphabetlearning.users.managers.UserManager()),
],
),
]
diff --git a/alphabetlearning/users/tasks.py b/alphabetlearning/users/tasks.py
deleted file mode 100644
index ca51cd7..0000000
--- a/alphabetlearning/users/tasks.py
+++ /dev/null
@@ -1,9 +0,0 @@
-from celery import shared_task
-
-from .models import User
-
-
-@shared_task()
-def get_users_count():
- """A pointless Celery task to demonstrate usage."""
- return User.objects.count()
diff --git a/alphabetlearning/users/tests/test_tasks.py b/alphabetlearning/users/tests/test_tasks.py
deleted file mode 100644
index 92f0e8a..0000000
--- a/alphabetlearning/users/tests/test_tasks.py
+++ /dev/null
@@ -1,17 +0,0 @@
-import pytest
-from celery.result import EagerResult
-
-from alphabetlearning.users.tasks import get_users_count
-from alphabetlearning.users.tests.factories import UserFactory
-
-pytestmark = pytest.mark.django_db
-
-
-def test_user_count(settings):
- """A basic test to execute the get_users_count Celery task."""
- batch_size = 3
- UserFactory.create_batch(batch_size)
- settings.CELERY_TASK_ALWAYS_EAGER = True
- task_result = get_users_count.delay()
- assert isinstance(task_result, EagerResult)
- assert task_result.result == batch_size
diff --git a/compose/local/django/Dockerfile b/compose/local/django/Dockerfile
deleted file mode 100644
index 553971b..0000000
--- a/compose/local/django/Dockerfile
+++ /dev/null
@@ -1,91 +0,0 @@
-# define an alias for the specific python version used in this file.
-FROM docker.io/python:3.12.3-slim-bookworm as python
-
-# Python build stage
-FROM python as python-build-stage
-
-ARG BUILD_ENVIRONMENT=local
-
-# Install apt packages
-RUN apt-get update && apt-get install --no-install-recommends -y \
- # dependencies for building Python packages
- build-essential \
- # psycopg dependencies
- libpq-dev
-
-# Requirements are installed here to ensure they will be cached.
-COPY ./requirements .
-
-# Create Python Dependency and Sub-Dependency Wheels.
-RUN pip wheel --wheel-dir /usr/src/app/wheels \
- -r ${BUILD_ENVIRONMENT}.txt
-
-
-# Python 'run' stage
-FROM python as python-run-stage
-
-ARG BUILD_ENVIRONMENT=local
-ARG APP_HOME=/app
-
-ENV PYTHONUNBUFFERED 1
-ENV PYTHONDONTWRITEBYTECODE 1
-ENV BUILD_ENV ${BUILD_ENVIRONMENT}
-
-WORKDIR ${APP_HOME}
-
-
-# devcontainer dependencies and utils
-RUN apt-get update && apt-get install --no-install-recommends -y \
- sudo git bash-completion nano ssh
-
-# Create devcontainer user and add it to sudoers
-RUN groupadd --gid 1000 dev-user \
- && useradd --uid 1000 --gid dev-user --shell /bin/bash --create-home dev-user \
- && echo dev-user ALL=\(root\) NOPASSWD:ALL > /etc/sudoers.d/dev-user \
- && chmod 0440 /etc/sudoers.d/dev-user
-
-
-# Install required system dependencies
-RUN apt-get update && apt-get install --no-install-recommends -y \
- libmagic1 \
- # psycopg dependencies
- libpq-dev \
- # Translations dependencies
- gettext \
- # cleaning up unused files
- && apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false \
- && rm -rf /var/lib/apt/lists/*
-
-# All absolute dir copies ignore workdir instruction. All relative dir copies are wrt to the workdir instruction
-# copy python dependency wheels from python-build-stage
-COPY --from=python-build-stage /usr/src/app/wheels /wheels/
-
-# use wheels to install python dependencies
-RUN pip install --no-cache-dir --no-index --find-links=/wheels/ /wheels/* \
- && rm -rf /wheels/
-
-COPY ./compose/production/django/entrypoint /entrypoint
-RUN sed -i 's/\r$//g' /entrypoint
-RUN chmod +x /entrypoint
-
-COPY ./compose/local/django/start /start
-RUN sed -i 's/\r$//g' /start
-RUN chmod +x /start
-
-COPY ./compose/local/django/celery/worker/start /start-celeryworker
-RUN sed -i 's/\r$//g' /start-celeryworker
-RUN chmod +x /start-celeryworker
-
-COPY ./compose/local/django/celery/beat/start /start-celerybeat
-RUN sed -i 's/\r$//g' /start-celerybeat
-RUN chmod +x /start-celerybeat
-
-COPY ./compose/local/django/celery/flower/start /start-flower
-RUN sed -i 's/\r$//g' /start-flower
-RUN chmod +x /start-flower
-
-
-# copy application code to WORKDIR
-COPY . ${APP_HOME}
-
-ENTRYPOINT ["/entrypoint"]
diff --git a/compose/local/django/celery/beat/start b/compose/local/django/celery/beat/start
deleted file mode 100644
index 8adc489..0000000
--- a/compose/local/django/celery/beat/start
+++ /dev/null
@@ -1,8 +0,0 @@
-#!/bin/bash
-
-set -o errexit
-set -o nounset
-
-
-rm -f './celerybeat.pid'
-exec watchfiles --filter python celery.__main__.main --args '-A config.celery_app beat -l INFO'
diff --git a/compose/local/django/celery/flower/start b/compose/local/django/celery/flower/start
deleted file mode 100644
index b4783d2..0000000
--- a/compose/local/django/celery/flower/start
+++ /dev/null
@@ -1,8 +0,0 @@
-#!/bin/bash
-
-set -o errexit
-set -o nounset
-
-exec watchfiles --filter python celery.__main__.main \
- --args \
- "-A config.celery_app -b \"${CELERY_BROKER_URL}\" flower --basic_auth=\"${CELERY_FLOWER_USER}:${CELERY_FLOWER_PASSWORD}\""
diff --git a/compose/local/django/celery/worker/start b/compose/local/django/celery/worker/start
deleted file mode 100644
index 183a801..0000000
--- a/compose/local/django/celery/worker/start
+++ /dev/null
@@ -1,7 +0,0 @@
-#!/bin/bash
-
-set -o errexit
-set -o nounset
-
-
-exec watchfiles --filter python celery.__main__.main --args '-A config.celery_app worker -l INFO'
diff --git a/compose/local/django/start b/compose/local/django/start
deleted file mode 100644
index ba96db4..0000000
--- a/compose/local/django/start
+++ /dev/null
@@ -1,9 +0,0 @@
-#!/bin/bash
-
-set -o errexit
-set -o pipefail
-set -o nounset
-
-
-python manage.py migrate
-exec python manage.py runserver_plus 0.0.0.0:8000
diff --git a/compose/local/docs/Dockerfile b/compose/local/docs/Dockerfile
deleted file mode 100644
index 3556504..0000000
--- a/compose/local/docs/Dockerfile
+++ /dev/null
@@ -1,62 +0,0 @@
-# define an alias for the specific python version used in this file.
-FROM docker.io/python:3.12.3-slim-bookworm as python
-
-
-# Python build stage
-FROM python as python-build-stage
-
-ENV PYTHONDONTWRITEBYTECODE 1
-
-RUN apt-get update && apt-get install --no-install-recommends -y \
- # dependencies for building Python packages
- build-essential \
- # psycopg dependencies
- libpq-dev \
- # cleaning up unused files
- && apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false \
- && rm -rf /var/lib/apt/lists/*
-
-# Requirements are installed here to ensure they will be cached.
-COPY ./requirements /requirements
-
-# create python dependency wheels
-RUN pip wheel --no-cache-dir --wheel-dir /usr/src/app/wheels \
- -r /requirements/local.txt -r /requirements/production.txt \
- && rm -rf /requirements
-
-
-# Python 'run' stage
-FROM python as python-run-stage
-
-ARG BUILD_ENVIRONMENT
-ENV PYTHONUNBUFFERED 1
-ENV PYTHONDONTWRITEBYTECODE 1
-
-RUN apt-get update && apt-get install --no-install-recommends -y \
- # To run the Makefile
- make \
- # psycopg dependencies
- libpq-dev \
- # Translations dependencies
- gettext \
- # Uncomment below lines to enable Sphinx output to latex and pdf
- # texlive-latex-recommended \
- # texlive-fonts-recommended \
- # texlive-latex-extra \
- # latexmk \
- # cleaning up unused files
- && apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false \
- && rm -rf /var/lib/apt/lists/*
-
-# copy python dependency wheels from python-build-stage
-COPY --from=python-build-stage /usr/src/app/wheels /wheels
-
-# use wheels to install python dependencies
-RUN pip install --no-cache /wheels/* \
- && rm -rf /wheels
-
-COPY ./compose/local/docs/start /start-docs
-RUN sed -i 's/\r$//g' /start-docs
-RUN chmod +x /start-docs
-
-WORKDIR /docs
diff --git a/compose/local/docs/start b/compose/local/docs/start
deleted file mode 100644
index 96a94f5..0000000
--- a/compose/local/docs/start
+++ /dev/null
@@ -1,7 +0,0 @@
-#!/bin/bash
-
-set -o errexit
-set -o pipefail
-set -o nounset
-
-exec make livehtml
diff --git a/compose/production/aws/Dockerfile b/compose/production/aws/Dockerfile
deleted file mode 100644
index 36eea7f..0000000
--- a/compose/production/aws/Dockerfile
+++ /dev/null
@@ -1,9 +0,0 @@
-FROM docker.io/garland/aws-cli-docker:1.16.140
-
-COPY ./compose/production/aws/maintenance /usr/local/bin/maintenance
-COPY ./compose/production/postgres/maintenance/_sourced /usr/local/bin/maintenance/_sourced
-
-RUN chmod +x /usr/local/bin/maintenance/*
-
-RUN mv /usr/local/bin/maintenance/* /usr/local/bin \
- && rmdir /usr/local/bin/maintenance
diff --git a/compose/production/aws/maintenance/download b/compose/production/aws/maintenance/download
deleted file mode 100644
index 9561d91..0000000
--- a/compose/production/aws/maintenance/download
+++ /dev/null
@@ -1,23 +0,0 @@
-#!/bin/sh
-
-### Download a file from your Amazon S3 bucket to the postgres /backups folder
-###
-### Usage:
-### $ docker compose -f production.yml run --rm awscli <1>
-
-set -o errexit
-set -o pipefail
-set -o nounset
-
-working_dir="$(dirname ${0})"
-source "${working_dir}/_sourced/constants.sh"
-source "${working_dir}/_sourced/messages.sh"
-
-export AWS_ACCESS_KEY_ID="${DJANGO_AWS_ACCESS_KEY_ID}"
-export AWS_SECRET_ACCESS_KEY="${DJANGO_AWS_SECRET_ACCESS_KEY}"
-export AWS_STORAGE_BUCKET_NAME="${DJANGO_AWS_STORAGE_BUCKET_NAME}"
-
-
-aws s3 cp s3://${AWS_STORAGE_BUCKET_NAME}${BACKUP_DIR_PATH}/${1} ${BACKUP_DIR_PATH}/${1}
-
-message_success "Finished downloading ${1}."
diff --git a/compose/production/aws/maintenance/upload b/compose/production/aws/maintenance/upload
deleted file mode 100644
index 73c1b9b..0000000
--- a/compose/production/aws/maintenance/upload
+++ /dev/null
@@ -1,29 +0,0 @@
-#!/bin/sh
-
-### Upload the /backups folder to Amazon S3
-###
-### Usage:
-### $ docker compose -f production.yml run --rm awscli upload
-
-set -o errexit
-set -o pipefail
-set -o nounset
-
-working_dir="$(dirname ${0})"
-source "${working_dir}/_sourced/constants.sh"
-source "${working_dir}/_sourced/messages.sh"
-
-export AWS_ACCESS_KEY_ID="${DJANGO_AWS_ACCESS_KEY_ID}"
-export AWS_SECRET_ACCESS_KEY="${DJANGO_AWS_SECRET_ACCESS_KEY}"
-export AWS_STORAGE_BUCKET_NAME="${DJANGO_AWS_STORAGE_BUCKET_NAME}"
-
-
-message_info "Upload the backups directory to S3 bucket {$AWS_STORAGE_BUCKET_NAME}"
-
-aws s3 cp ${BACKUP_DIR_PATH} s3://${AWS_STORAGE_BUCKET_NAME}${BACKUP_DIR_PATH} --recursive
-
-message_info "Cleaning the directory ${BACKUP_DIR_PATH}"
-
-rm -rf ${BACKUP_DIR_PATH}/*
-
-message_success "Finished uploading and cleaning."
diff --git a/compose/production/django/Dockerfile b/compose/production/django/Dockerfile
deleted file mode 100644
index e81a03c..0000000
--- a/compose/production/django/Dockerfile
+++ /dev/null
@@ -1,91 +0,0 @@
-
-# define an alias for the specific python version used in this file.
-FROM docker.io/python:3.12.3-slim-bookworm as python
-
-# Python build stage
-FROM python as python-build-stage
-
-ARG BUILD_ENVIRONMENT=production
-
-# Install apt packages
-RUN apt-get update && apt-get install --no-install-recommends -y \
- # dependencies for building Python packages
- build-essential \
- # psycopg dependencies
- libpq-dev
-
-# Requirements are installed here to ensure they will be cached.
-COPY ./requirements .
-
-# Create Python Dependency and Sub-Dependency Wheels.
-RUN pip wheel --wheel-dir /usr/src/app/wheels \
- -r ${BUILD_ENVIRONMENT}.txt
-
-
-# Python 'run' stage
-FROM python as python-run-stage
-
-ARG BUILD_ENVIRONMENT=production
-ARG APP_HOME=/app
-
-ENV PYTHONUNBUFFERED 1
-ENV PYTHONDONTWRITEBYTECODE 1
-ENV BUILD_ENV ${BUILD_ENVIRONMENT}
-
-WORKDIR ${APP_HOME}
-
-RUN addgroup --system django \
- && adduser --system --ingroup django django
-
-
-# Install required system dependencies
-RUN apt-get update && apt-get install --no-install-recommends -y \
- libmagic1 \
- # psycopg dependencies
- libpq-dev \
- # Translations dependencies
- gettext \
- # cleaning up unused files
- && apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false \
- && rm -rf /var/lib/apt/lists/*
-
-# All absolute dir copies ignore workdir instruction. All relative dir copies are wrt to the workdir instruction
-# copy python dependency wheels from python-build-stage
-COPY --from=python-build-stage /usr/src/app/wheels /wheels/
-
-# use wheels to install python dependencies
-RUN pip install --no-cache-dir --no-index --find-links=/wheels/ /wheels/* \
- && rm -rf /wheels/
-
-# COPY --chown=django:django ./compose/production/django/entrypoint /entrypoint
-# RUN sed -i 's/\r$//g' /entrypoint
-# RUN chmod +x /entrypoint
-
-# COPY --chown=django:django ./compose/production/django/start /start
-# RUN sed -i 's/\r$//g' /start
-# RUN chmod +x /start
-# COPY --chown=django:django ./compose/production/django/celery/worker/start /start-celeryworker
-# RUN sed -i 's/\r$//g' /start-celeryworker
-# RUN chmod +x /start-celeryworker
-
-
-# COPY --chown=django:django ./compose/production/django/celery/beat/start /start-celerybeat
-# RUN sed -i 's/\r$//g' /start-celerybeat
-# RUN chmod +x /start-celerybeat
-
-
-# COPY --chown=django:django ./compose/production/django/celery/flower/start /start-flower
-# RUN sed -i 's/\r$//g' /start-flower
-# RUN chmod +x /start-flower
-
-
-# copy application code to WORKDIR
-COPY --chown=django:django . ${APP_HOME}
-
-# make django owner of the WORKDIR directory as well.
-RUN chown -R django:django ${APP_HOME}
-
-USER django
-
-RUN chmod +x docker-entrypoint.sh
-ENTRYPOINT ["/app/docker-entrypoint.sh"]
diff --git a/compose/production/django/celery/beat/start b/compose/production/django/celery/beat/start
deleted file mode 100644
index 42ddca9..0000000
--- a/compose/production/django/celery/beat/start
+++ /dev/null
@@ -1,8 +0,0 @@
-#!/bin/bash
-
-set -o errexit
-set -o pipefail
-set -o nounset
-
-
-exec celery -A config.celery_app beat -l INFO
diff --git a/compose/production/django/celery/flower/start b/compose/production/django/celery/flower/start
deleted file mode 100644
index 4180d67..0000000
--- a/compose/production/django/celery/flower/start
+++ /dev/null
@@ -1,11 +0,0 @@
-#!/bin/bash
-
-set -o errexit
-set -o nounset
-
-
-exec celery \
- -A config.celery_app \
- -b "${CELERY_BROKER_URL}" \
- flower \
- --basic_auth="${CELERY_FLOWER_USER}:${CELERY_FLOWER_PASSWORD}"
diff --git a/compose/production/django/celery/worker/start b/compose/production/django/celery/worker/start
deleted file mode 100644
index af0c8f7..0000000
--- a/compose/production/django/celery/worker/start
+++ /dev/null
@@ -1,8 +0,0 @@
-#!/bin/bash
-
-set -o errexit
-set -o pipefail
-set -o nounset
-
-
-exec celery -A config.celery_app worker -l INFO
diff --git a/compose/production/django/entrypoint b/compose/production/django/entrypoint
deleted file mode 100644
index 249d8d9..0000000
--- a/compose/production/django/entrypoint
+++ /dev/null
@@ -1,49 +0,0 @@
-#!/bin/bash
-
-set -o errexit
-set -o pipefail
-set -o nounset
-
-
-
-# N.B. If only .env files supported variable expansion...
-export CELERY_BROKER_URL="${REDIS_URL}"
-
-
-if [ -z "${POSTGRES_USER}" ]; then
- base_postgres_image_default_user='postgres'
- export POSTGRES_USER="${base_postgres_image_default_user}"
-fi
-export DATABASE_URL="postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:${POSTGRES_PORT}/${POSTGRES_DB}"
-
-python << END
-import sys
-import time
-
-import psycopg
-
-suggest_unrecoverable_after = 30
-start = time.time()
-
-while True:
- try:
- psycopg.connect(
- dbname="${POSTGRES_DB}",
- user="${POSTGRES_USER}",
- password="${POSTGRES_PASSWORD}",
- host="${POSTGRES_HOST}",
- port="${POSTGRES_PORT}",
- )
- break
- except psycopg.OperationalError as error:
- sys.stderr.write("Waiting for PostgreSQL to become available...\n")
-
- if time.time() - start > suggest_unrecoverable_after:
- sys.stderr.write(" This is taking longer than expected. The following exception may be indicative of an unrecoverable error: '{}'\n".format(error))
-
- time.sleep(1)
-END
-
->&2 echo 'PostgreSQL is available'
-
-exec "$@"
diff --git a/compose/production/django/start b/compose/production/django/start
deleted file mode 100644
index 97216fa..0000000
--- a/compose/production/django/start
+++ /dev/null
@@ -1,10 +0,0 @@
-#!/bin/bash
-
-set -o errexit
-set -o pipefail
-set -o nounset
-
-
-python /app/manage.py collectstatic --noinput
-
-exec /usr/local/bin/gunicorn config.wsgi --bind 0.0.0.0:5000 --chdir=/app
diff --git a/compose/production/nginx/Dockerfile b/compose/production/nginx/Dockerfile
deleted file mode 100644
index ec2ad35..0000000
--- a/compose/production/nginx/Dockerfile
+++ /dev/null
@@ -1,2 +0,0 @@
-FROM docker.io/nginx:1.17.8-alpine
-COPY ./compose/production/nginx/default.conf /etc/nginx/conf.d/default.conf
diff --git a/compose/production/nginx/default.conf b/compose/production/nginx/default.conf
deleted file mode 100644
index 562dba8..0000000
--- a/compose/production/nginx/default.conf
+++ /dev/null
@@ -1,7 +0,0 @@
-server {
- listen 80;
- server_name localhost;
- location /media/ {
- alias /usr/share/nginx/media/;
- }
-}
diff --git a/compose/production/postgres/Dockerfile b/compose/production/postgres/Dockerfile
deleted file mode 100644
index 176a5f1..0000000
--- a/compose/production/postgres/Dockerfile
+++ /dev/null
@@ -1,6 +0,0 @@
-FROM docker.io/postgres:16
-
-COPY ./compose/production/postgres/maintenance /usr/local/bin/maintenance
-RUN chmod +x /usr/local/bin/maintenance/*
-RUN mv /usr/local/bin/maintenance/* /usr/local/bin \
- && rmdir /usr/local/bin/maintenance
diff --git a/compose/production/postgres/maintenance/_sourced/constants.sh b/compose/production/postgres/maintenance/_sourced/constants.sh
deleted file mode 100644
index 6ca4f0c..0000000
--- a/compose/production/postgres/maintenance/_sourced/constants.sh
+++ /dev/null
@@ -1,5 +0,0 @@
-#!/usr/bin/env bash
-
-
-BACKUP_DIR_PATH='/backups'
-BACKUP_FILE_PREFIX='backup'
diff --git a/compose/production/postgres/maintenance/_sourced/countdown.sh b/compose/production/postgres/maintenance/_sourced/countdown.sh
deleted file mode 100644
index e6cbfb6..0000000
--- a/compose/production/postgres/maintenance/_sourced/countdown.sh
+++ /dev/null
@@ -1,12 +0,0 @@
-#!/usr/bin/env bash
-
-
-countdown() {
- declare desc="A simple countdown. Source: https://superuser.com/a/611582"
- local seconds="${1}"
- local d=$(($(date +%s) + "${seconds}"))
- while [ "$d" -ge `date +%s` ]; do
- echo -ne "$(date -u --date @$(($d - `date +%s`)) +%H:%M:%S)\r";
- sleep 0.1
- done
-}
diff --git a/compose/production/postgres/maintenance/_sourced/messages.sh b/compose/production/postgres/maintenance/_sourced/messages.sh
deleted file mode 100644
index f6be756..0000000
--- a/compose/production/postgres/maintenance/_sourced/messages.sh
+++ /dev/null
@@ -1,41 +0,0 @@
-#!/usr/bin/env bash
-
-
-message_newline() {
- echo
-}
-
-message_debug()
-{
- echo -e "DEBUG: ${@}"
-}
-
-message_welcome()
-{
- echo -e "\e[1m${@}\e[0m"
-}
-
-message_warning()
-{
- echo -e "\e[33mWARNING\e[0m: ${@}"
-}
-
-message_error()
-{
- echo -e "\e[31mERROR\e[0m: ${@}"
-}
-
-message_info()
-{
- echo -e "\e[37mINFO\e[0m: ${@}"
-}
-
-message_suggestion()
-{
- echo -e "\e[33mSUGGESTION\e[0m: ${@}"
-}
-
-message_success()
-{
- echo -e "\e[32mSUCCESS\e[0m: ${@}"
-}
diff --git a/compose/production/postgres/maintenance/_sourced/yes_no.sh b/compose/production/postgres/maintenance/_sourced/yes_no.sh
deleted file mode 100644
index fd9cae1..0000000
--- a/compose/production/postgres/maintenance/_sourced/yes_no.sh
+++ /dev/null
@@ -1,16 +0,0 @@
-#!/usr/bin/env bash
-
-
-yes_no() {
- declare desc="Prompt for confirmation. \$\"\{1\}\": confirmation message."
- local arg1="${1}"
-
- local response=
- read -r -p "${arg1} (y/[n])? " response
- if [[ "${response}" =~ ^[Yy]$ ]]
- then
- exit 0
- else
- exit 1
- fi
-}
diff --git a/compose/production/postgres/maintenance/backup b/compose/production/postgres/maintenance/backup
deleted file mode 100644
index f72304c..0000000
--- a/compose/production/postgres/maintenance/backup
+++ /dev/null
@@ -1,38 +0,0 @@
-#!/usr/bin/env bash
-
-
-### Create a database backup.
-###
-### Usage:
-### $ docker compose -f <environment>.yml (exec |run --rm) postgres backup
-
-
-set -o errexit
-set -o pipefail
-set -o nounset
-
-
-working_dir="$(dirname ${0})"
-source "${working_dir}/_sourced/constants.sh"
-source "${working_dir}/_sourced/messages.sh"
-
-
-message_welcome "Backing up the '${POSTGRES_DB}' database..."
-
-
-if [[ "${POSTGRES_USER}" == "postgres" ]]; then
- message_error "Backing up as 'postgres' user is not supported. Assign 'POSTGRES_USER' env with another one and try again."
- exit 1
-fi
-
-export PGHOST="${POSTGRES_HOST}"
-export PGPORT="${POSTGRES_PORT}"
-export PGUSER="${POSTGRES_USER}"
-export PGPASSWORD="${POSTGRES_PASSWORD}"
-export PGDATABASE="${POSTGRES_DB}"
-
-backup_filename="${BACKUP_FILE_PREFIX}_$(date +'%Y_%m_%dT%H_%M_%S').sql.gz"
-pg_dump | gzip > "${BACKUP_DIR_PATH}/${backup_filename}"
-
-
-message_success "'${POSTGRES_DB}' database backup '${backup_filename}' has been created and placed in '${BACKUP_DIR_PATH}'."
diff --git a/compose/production/postgres/maintenance/backups b/compose/production/postgres/maintenance/backups
deleted file mode 100644
index a18937d..0000000
--- a/compose/production/postgres/maintenance/backups
+++ /dev/null
@@ -1,22 +0,0 @@
-#!/usr/bin/env bash
-
-
-### View backups.
-###
-### Usage:
-### $ docker compose -f <environment>.yml (exec |run --rm) postgres backups
-
-
-set -o errexit
-set -o pipefail
-set -o nounset
-
-
-working_dir="$(dirname ${0})"
-source "${working_dir}/_sourced/constants.sh"
-source "${working_dir}/_sourced/messages.sh"
-
-
-message_welcome "These are the backups you have got:"
-
-ls -lht "${BACKUP_DIR_PATH}"
diff --git a/compose/production/postgres/maintenance/restore b/compose/production/postgres/maintenance/restore
deleted file mode 100644
index c68f17d..0000000
--- a/compose/production/postgres/maintenance/restore
+++ /dev/null
@@ -1,55 +0,0 @@
-#!/usr/bin/env bash
-
-
-### Restore database from a backup.
-###
-### Parameters:
-### <1> filename of an existing backup.
-###
-### Usage:
-### $ docker compose -f <environment>.yml (exec |run --rm) postgres restore <1>
-
-
-set -o errexit
-set -o pipefail
-set -o nounset
-
-
-working_dir="$(dirname ${0})"
-source "${working_dir}/_sourced/constants.sh"
-source "${working_dir}/_sourced/messages.sh"
-
-
-if [[ -z ${1+x} ]]; then
- message_error "Backup filename is not specified yet it is a required parameter. Make sure you provide one and try again."
- exit 1
-fi
-backup_filename="${BACKUP_DIR_PATH}/${1}"
-if [[ ! -f "${backup_filename}" ]]; then
- message_error "No backup with the specified filename found. Check out the 'backups' maintenance script output to see if there is one and try again."
- exit 1
-fi
-
-message_welcome "Restoring the '${POSTGRES_DB}' database from the '${backup_filename}' backup..."
-
-if [[ "${POSTGRES_USER}" == "postgres" ]]; then
- message_error "Restoring as 'postgres' user is not supported. Assign 'POSTGRES_USER' env with another one and try again."
- exit 1
-fi
-
-export PGHOST="${POSTGRES_HOST}"
-export PGPORT="${POSTGRES_PORT}"
-export PGUSER="${POSTGRES_USER}"
-export PGPASSWORD="${POSTGRES_PASSWORD}"
-export PGDATABASE="${POSTGRES_DB}"
-
-message_info "Dropping the database..."
-dropdb "${PGDATABASE}"
-
-message_info "Creating a new database..."
-createdb --owner="${POSTGRES_USER}"
-
-message_info "Applying the backup to the new database..."
-gunzip -c "${backup_filename}" | psql "${POSTGRES_DB}"
-
-message_success "The '${POSTGRES_DB}' database has been restored from the '${backup_filename}' backup."
diff --git a/compose/production/postgres/maintenance/rmbackup b/compose/production/postgres/maintenance/rmbackup
deleted file mode 100644
index fdfd20e..0000000
--- a/compose/production/postgres/maintenance/rmbackup
+++ /dev/null
@@ -1,36 +0,0 @@
-#!/usr/bin/env bash
-
-### Remove a database backup.
-###
-### Parameters:
-### <1> filename of a backup to remove.
-###
-### Usage:
-### $ docker-compose -f <environment>.yml (exec |run --rm) postgres rmbackup <1>
-
-
-set -o errexit
-set -o pipefail
-set -o nounset
-
-
-working_dir="$(dirname ${0})"
-source "${working_dir}/_sourced/constants.sh"
-source "${working_dir}/_sourced/messages.sh"
-
-
-if [[ -z ${1+x} ]]; then
- message_error "Backup filename is not specified yet it is a required parameter. Make sure you provide one and try again."
- exit 1
-fi
-backup_filename="${BACKUP_DIR_PATH}/${1}"
-if [[ ! -f "${backup_filename}" ]]; then
- message_error "No backup with the specified filename found. Check out the 'backups' maintenance script output to see if there is one and try again."
- exit 1
-fi
-
-message_welcome "Removing the '${backup_filename}' backup file..."
-
-rm -r "${backup_filename}"
-
-message_success "The '${backup_filename}' database backup has been removed."
diff --git a/compose/production/traefik/Dockerfile b/compose/production/traefik/Dockerfile
deleted file mode 100644
index d54bf27..0000000
--- a/compose/production/traefik/Dockerfile
+++ /dev/null
@@ -1,5 +0,0 @@
-FROM docker.io/traefik:2.11.2
-RUN mkdir -p /etc/traefik/acme \
- && touch /etc/traefik/acme/acme.json \
- && chmod 600 /etc/traefik/acme/acme.json
-COPY ./compose/production/traefik/traefik.yml /etc/traefik
diff --git a/compose/production/traefik/traefik.yml b/compose/production/traefik/traefik.yml
deleted file mode 100644
index 4c274d1..0000000
--- a/compose/production/traefik/traefik.yml
+++ /dev/null
@@ -1,75 +0,0 @@
-log:
- level: INFO
-
-entryPoints:
- web:
- # http
- address: ':80'
- http:
- # https://doc.traefik.io/traefik/routing/entrypoints/#entrypoint
- redirections:
- entryPoint:
- to: web-secure
-
- web-secure:
- # https
- address: ':443'
-
- flower:
- address: ':5555'
-
-certificatesResolvers:
- letsencrypt:
- # https://doc.traefik.io/traefik/https/acme/#lets-encrypt
- acme:
- email: 'y@yulqen.org'
- storage: /etc/traefik/acme/acme.json
- # https://doc.traefik.io/traefik/https/acme/#httpchallenge
- httpChallenge:
- entryPoint: web
-
-http:
- routers:
- web-secure-router:
- rule: 'Host(`resources.joannalemon.com`)'
- entryPoints:
- - web-secure
- middlewares:
- - csrf
- service: django
- tls:
- # https://doc.traefik.io/traefik/routing/routers/#certresolver
- certResolver: letsencrypt
-
- flower-secure-router:
- rule: 'Host(`resources.joannalemon.com`)'
- entryPoints:
- - flower
- service: flower
- tls:
- # https://doc.traefik.io/traefik/master/routing/routers/#certresolver
- certResolver: letsencrypt
-
- middlewares:
- csrf:
- # https://doc.traefik.io/traefik/master/middlewares/http/headers/#hostsproxyheaders
- # https://docs.djangoproject.com/en/dev/ref/csrf/#ajax
- headers:
- hostsProxyHeaders: ['X-CSRFToken']
-
- services:
- django:
- loadBalancer:
- servers:
- - url: http://django:5000
-
- flower:
- loadBalancer:
- servers:
- - url: http://flower:5555
-
-providers:
- # https://doc.traefik.io/traefik/master/providers/file/
- file:
- filename: /etc/traefik/traefik.yml
- watch: true
diff --git a/config/__init__.py b/config/__init__.py
index 10f5014..e69de29 100644
--- a/config/__init__.py
+++ b/config/__init__.py
@@ -1,5 +0,0 @@
-# This will make sure the app is always imported when
-# Django starts so that shared_task will use this app.
-from .celery_app import app as celery_app
-
-__all__ = ("celery_app",)
diff --git a/config/celery_app.py b/config/celery_app.py
deleted file mode 100644
index 7af2082..0000000
--- a/config/celery_app.py
+++ /dev/null
@@ -1,17 +0,0 @@
-import os
-
-from celery import Celery
-
-# set the default Django settings module for the 'celery' program.
-os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings.local")
-
-app = Celery("alphabetlearning")
-
-# Using a string here means the worker doesn't have to serialize
-# the configuration object to child processes.
-# - namespace='CELERY' means all celery-related configuration keys
-# should have a `CELERY_` prefix.
-app.config_from_object("django.conf:settings", namespace="CELERY")
-
-# Load task modules from all registered Django app configs.
-app.autodiscover_tasks()
diff --git a/config/settings/base.py b/config/settings/base.py
index aaf88fc..a218856 100644
--- a/config/settings/base.py
+++ b/config/settings/base.py
@@ -44,8 +44,30 @@ LOCALE_PATHS = [str(BASE_DIR / "locale")]
# DATABASES
# ------------------------------------------------------------------------------
# https://docs.djangoproject.com/en/dev/ref/settings/#databases
-DATABASES = {"default": env.db("DATABASE_URL")}
-DATABASES["default"]["ATOMIC_REQUESTS"] = True
+# DATABASES = {"default": env.db("DATABASE_URL")}
+# DATABASES["default"]["ATOMIC_REQUESTS"] = True
+
+DATABASES = {
+ "default": {
+ "ENGINE": "django.db.backends.sqlite3",
+ "NAME": BASE_DIR / "db.sqlite3",
+ "OPTIONS": {
+ "init_command": (
+ "PRAGMA foreign_keys=ON;"
+ "PRAGMA journal_mode = WAL;"
+ "PRAGMA synchronous = NORMAL;"
+ "PRAGMA busy_timeout = 5000;"
+ "PRAGMA temp_store = MEMORY;"
+ "PRAGMA mmap_size = 134217729;"
+ "PRAGMA journal_size_limit = 67108864;"
+ "PRAGMA cache_size = 2000;"
+ ),
+ "transaction_mode": "IMMEDIATE",
+ },
+ },
+}
+
+
# https://docs.djangoproject.com/en/stable/ref/settings/#std:setting-DEFAULT_AUTO_FIELD
DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
@@ -75,8 +97,7 @@ THIRD_PARTY_APPS = [
"allauth",
"allauth.account",
"allauth.mfa",
- "allauth.socialaccount",
- "django_celery_beat",
+ # "allauth.socialaccount",
"django_recaptcha",
]
@@ -266,41 +287,6 @@ LOGGING = {
"root": {"level": "INFO", "handlers": ["console"]},
}
-# Celery
-# ------------------------------------------------------------------------------
-# if USE_TZ:
-# # https://docs.celeryq.dev/en/stable/userguide/configuration.html#std:setting-timezone
-# CELERY_TIMEZONE = TIME_ZONE
-# # https://docs.celeryq.dev/en/stable/userguide/configuration.html#std:setting-broker_url
-# CELERY_BROKER_URL = env("CELERY_BROKER_URL")
-# # https://docs.celeryq.dev/en/stable/userguide/configuration.html#std:setting-result_backend
-# CELERY_RESULT_BACKEND = CELERY_BROKER_URL
-# # https://docs.celeryq.dev/en/stable/userguide/configuration.html#result-extended
-# CELERY_RESULT_EXTENDED = True
-# # https://docs.celeryq.dev/en/stable/userguide/configuration.html#result-backend-always-retry
-# # https://github.com/celery/celery/pull/6122
-# CELERY_RESULT_BACKEND_ALWAYS_RETRY = True
-# # https://docs.celeryq.dev/en/stable/userguide/configuration.html#result-backend-max-retries
-# CELERY_RESULT_BACKEND_MAX_RETRIES = 10
-# # https://docs.celeryq.dev/en/stable/userguide/configuration.html#std:setting-accept_content
-# CELERY_ACCEPT_CONTENT = ["json"]
-# # https://docs.celeryq.dev/en/stable/userguide/configuration.html#std:setting-task_serializer
-# CELERY_TASK_SERIALIZER = "json"
-# # https://docs.celeryq.dev/en/stable/userguide/configuration.html#std:setting-result_serializer
-# CELERY_RESULT_SERIALIZER = "json"
-# # https://docs.celeryq.dev/en/stable/userguide/configuration.html#task-time-limit
-# # TODO: set to whatever value is adequate in your circumstances
-# CELERY_TASK_TIME_LIMIT = 5 * 60
-# # https://docs.celeryq.dev/en/stable/userguide/configuration.html#task-soft-time-limit
-# # TODO: set to whatever value is adequate in your circumstances
-# CELERY_TASK_SOFT_TIME_LIMIT = 60
-# # https://docs.celeryq.dev/en/stable/userguide/configuration.html#beat-scheduler
-# CELERY_BEAT_SCHEDULER = "django_celery_beat.schedulers:DatabaseScheduler"
-# # https://docs.celeryq.dev/en/stable/userguide/configuration.html#worker-send-task-events
-# CELERY_WORKER_SEND_TASK_EVENTS = True
-# # https://docs.celeryq.dev/en/stable/userguide/configuration.html#std-setting-task_send_sent_event
-# CELERY_TASK_SEND_SENT_EVENT = True
-
# django-allauth
# ------------------------------------------------------------------------------
ACCOUNT_ALLOW_REGISTRATION = env.bool("DJANGO_ACCOUNT_ALLOW_REGISTRATION", True)
@@ -358,12 +344,12 @@ AWS_S3_ENDPOINT_URL = env("SPACES_ENDPOINT_URL")
# Your stuff...
# ------------------------------------------------------------------------------
-CACHES = {
- "default": {
- "BACKEND": "django.core.cache.backends.db.DatabaseCache",
- "LOCATION": "rate_limit_cache",
- },
-}
+# CACHES = {
+# "default": {
+# "BACKEND": "django.core.cache.backends.db.DatabaseCache",
+# "LOCATION": "rate_limit_cache",
+# },
+# }
RECAPTCHA_PUBLIC_KEY = env("RECAPTCHA_PUBLIC_KEY")
RECAPTCHA_PRIVATE_KEY = env("RECAPTCHA_PRIVATE_KEY")
diff --git a/config/settings/local.py b/config/settings/local.py
index e95cbb9..07f38f9 100644
--- a/config/settings/local.py
+++ b/config/settings/local.py
@@ -73,10 +73,6 @@ if env("USE_DOCKER") == "yes":
# ------------------------------------------------------------------------------
# https://django-extensions.readthedocs.io/en/latest/installation_instructions.html#configuration
# INSTALLED_APPS += ["django_extensions"]
-# Celery
-# ------------------------------------------------------------------------------
-# https://docs.celeryq.dev/en/stable/userguide/configuration.html#task-eager-propagates
-CELERY_TASK_EAGER_PROPAGATES = True
# Your stuff...
# ------------------------------------------------------------------------------
diff --git a/config/settings/production.py b/config/settings/production.py
index 9a267e6..fcc772e 100644
--- a/config/settings/production.py
+++ b/config/settings/production.py
@@ -2,7 +2,6 @@
from .base import * # noqa: F403
-from .base import DATABASES
from .base import env
# GENERAL
@@ -14,7 +13,7 @@ ALLOWED_HOSTS = env.list("DJANGO_ALLOWED_HOSTS", default=["alphabetlearning.onli
# DATABASES
# ------------------------------------------------------------------------------
-DATABASES["default"]["CONN_MAX_AGE"] = env.int("CONN_MAX_AGE", default=60)
+# DATABASES["default"]["CONN_MAX_AGE"] = env.int("CONN_MAX_AGE", default=60)
# CACHES
# ------------------------------------------------------------------------------
diff --git a/local.yml b/local.yml
deleted file mode 100644
index 1565704..0000000
--- a/local.yml
+++ /dev/null
@@ -1,75 +0,0 @@
-volumes:
- alphabetlearning_local_postgres_data: {}
- alphabetlearning_local_postgres_data_backups: {}
-
-services:
- django: &django
- build:
- context: .
- dockerfile: ./compose/local/django/Dockerfile
- image: alphabetlearning_local_django
- container_name: alphabetlearning_local_django
- depends_on:
- - postgres
- - redis
- - mailpit
- volumes:
- - .:/app:z
- env_file:
- - ./.envs/.local/.django
- - ./.envs/.local/.postgres
- ports:
- - '8000:8000'
- command: /start
-
- postgres:
- build:
- context: .
- dockerfile: ./compose/production/postgres/Dockerfile
- image: alphabetlearning_production_postgres
- container_name: alphabetlearning_local_postgres
- volumes:
- - alphabetlearning_local_postgres_data:/var/lib/postgresql/data
- - alphabetlearning_local_postgres_data_backups:/backups
- env_file:
- - ./.envs/.local/.postgres
-
- mailpit:
- image: docker.io/axllent/mailpit:latest
- container_name: alphabetlearning_local_mailpit
- ports:
- - "8025:8025"
-
- redis:
- image: docker.io/redis:6
- container_name: alphabetlearning_local_redis
-
- celeryworker:
- <<: *django
- image: alphabetlearning_local_celeryworker
- container_name: alphabetlearning_local_celeryworker
- depends_on:
- - redis
- - postgres
- - mailpit
- ports: []
- command: /start-celeryworker
-
- celerybeat:
- <<: *django
- image: alphabetlearning_local_celerybeat
- container_name: alphabetlearning_local_celerybeat
- depends_on:
- - redis
- - postgres
- - mailpit
- ports: []
- command: /start-celerybeat
-
- flower:
- <<: *django
- image: alphabetlearning_local_flower
- container_name: alphabetlearning_local_flower
- ports:
- - '5555:5555'
- command: /start-flower
diff --git a/pyproject.toml b/pyproject.toml
index d09d98b..c5f7812 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -10,22 +10,12 @@ dependencies = [
# https://github.com/hynek/argon2_cffi
"whitenoise==6.6.0",
# https://github.com/evansd/whitenoise
- "redis==5.0.4",
- # https://github.com/redis/redis-py
- "hiredis==2.3.2",
- # https://github.com/redis/hiredis-py
- "celery==5.4.0",
- # pyup: < 6.0 # https://github.com/celery/celery
- "django-celery-beat==2.6.0",
- # https://github.com/celery/django-celery-beat
- "flower==2.0.1",
- # https://github.com/mher/flower
"botocore~=1.34.93",
# Django
# ------------------------------------------------------------------------------
"django-storages==1.14.3",
"gunicorn==22.0.0",
- "Django==5.0.4",
+ "Django==5.1.4",
# pyup: < 5.0 # https://www.djangoproject.com/
"django-environ==0.11.2",
# https://github.com/joke2k/django-environ
@@ -55,7 +45,7 @@ dependencies = [
# https://github.com/samuelcolvin/watchfiles
"stripe==11.1.0",
"django-stubs[compatible-mypy]>=5.0.4",
- "django-ratelimit==4.1.0",
+ # "django-ratelimit==4.1.0",
"django-recaptcha==4.0.0"
]
[tool.uv] # https://docs.astral.sh/uv/concepts/dependencies/#development-dependencies
diff --git a/requirements.txt b/requirements.txt
index e69f1e6..63d441d 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,99 +1,65 @@
# This file was autogenerated by uv via the following command:
# uv pip compile pyproject.toml -o requirements.txt
-amqp==5.2.0
- # via kombu
-anyio==4.4.0
+anyio==4.7.0
# via watchfiles
argon2-cffi==23.1.0
- # via pyblackbird-cc (pyproject.toml)
+ # via alphabetlearning (pyproject.toml)
argon2-cffi-bindings==21.2.0
# via argon2-cffi
asgiref==3.8.1
# via
# django
# django-stubs
-async-timeout==4.0.3
- # via redis
-billiard==4.2.0
- # via celery
boto3==1.34.89
- # via pyblackbird-cc (pyproject.toml)
+ # via alphabetlearning (pyproject.toml)
botocore==1.34.162
# via
- # pyblackbird-cc (pyproject.toml)
+ # alphabetlearning (pyproject.toml)
# boto3
# s3transfer
-celery==5.4.0
- # via
- # pyblackbird-cc (pyproject.toml)
- # django-celery-beat
- # flower
-certifi==2024.8.30
+certifi==2024.12.14
# via requests
cffi==1.17.1
# via argon2-cffi-bindings
-charset-normalizer==3.3.2
+charset-normalizer==3.4.1
# via requests
-click==8.1.7
- # via
- # celery
- # click-didyoumean
- # click-plugins
- # click-repl
-click-didyoumean==0.3.1
- # via celery
-click-plugins==1.1.1
- # via celery
-click-repl==0.3.0
- # via celery
crispy-bootstrap5==2024.2
- # via pyblackbird-cc (pyproject.toml)
-cron-descriptor==1.4.5
- # via django-celery-beat
-django==5.0.4
+ # via alphabetlearning (pyproject.toml)
+django==5.1.4
# via
- # pyblackbird-cc (pyproject.toml)
+ # alphabetlearning (pyproject.toml)
# crispy-bootstrap5
# django-allauth
- # django-celery-beat
# django-crispy-forms
# django-model-utils
+ # django-recaptcha
# django-redis
# django-storages
# django-stubs
# django-stubs-ext
- # django-timezone-field
django-allauth==0.62.1
- # via pyblackbird-cc (pyproject.toml)
-django-celery-beat==2.6.0
- # via pyblackbird-cc (pyproject.toml)
+ # via alphabetlearning (pyproject.toml)
django-crispy-forms==2.1
# via
- # pyblackbird-cc (pyproject.toml)
+ # alphabetlearning (pyproject.toml)
# crispy-bootstrap5
django-environ==0.11.2
- # via pyblackbird-cc (pyproject.toml)
+ # via alphabetlearning (pyproject.toml)
django-model-utils==4.5.1
- # via pyblackbird-cc (pyproject.toml)
+ # via alphabetlearning (pyproject.toml)
+django-recaptcha==4.0.0
+ # via alphabetlearning (pyproject.toml)
django-redis==5.4.0
- # via pyblackbird-cc (pyproject.toml)
+ # via alphabetlearning (pyproject.toml)
django-storages==1.14.3
- # via pyblackbird-cc (pyproject.toml)
-django-stubs==5.0.4
- # via pyblackbird-cc (pyproject.toml)
-django-stubs-ext==5.0.4
+ # via alphabetlearning (pyproject.toml)
+django-stubs==5.1.1
+ # via alphabetlearning (pyproject.toml)
+django-stubs-ext==5.1.1
# via django-stubs
-django-timezone-field==7.0
- # via django-celery-beat
-flower==2.0.1
- # via pyblackbird-cc (pyproject.toml)
gunicorn==22.0.0
- # via pyblackbird-cc (pyproject.toml)
-hiredis==2.3.2
- # via pyblackbird-cc (pyproject.toml)
-humanize==4.10.0
- # via flower
-idna==3.8
+ # via alphabetlearning (pyproject.toml)
+idna==3.10
# via
# anyio
# requests
@@ -101,107 +67,77 @@ jmespath==1.0.1
# via
# boto3
# botocore
-kombu==5.4.1
- # via celery
markdown==3.6
- # via pyblackbird-cc (pyproject.toml)
-markupsafe==2.1.5
+ # via alphabetlearning (pyproject.toml)
+markupsafe==3.0.2
# via werkzeug
-mypy==1.11.2
+mypy==1.13.0
# via django-stubs
mypy-extensions==1.0.0
# via mypy
-packaging==24.1
+packaging==24.2
# via gunicorn
pdf2image==1.17.0
- # via pyblackbird-cc (pyproject.toml)
+ # via alphabetlearning (pyproject.toml)
pillow==10.3.0
# via
- # pyblackbird-cc (pyproject.toml)
+ # alphabetlearning (pyproject.toml)
# pdf2image
-prometheus-client==0.20.0
- # via flower
-prompt-toolkit==3.0.47
- # via click-repl
psycopg==3.1.19
- # via pyblackbird-cc (pyproject.toml)
+ # via alphabetlearning (pyproject.toml)
psycopg-c==3.1.19
# via psycopg
pycparser==2.22
# via cffi
pypdf2==3.0.1
- # via pyblackbird-cc (pyproject.toml)
-pypng==0.20220715.0
- # via qrcode
-python-crontab==3.2.0
- # via django-celery-beat
+ # via alphabetlearning (pyproject.toml)
python-dateutil==2.9.0.post0
- # via
- # botocore
- # celery
- # python-crontab
+ # via botocore
python-dotenv==1.0.1
- # via pyblackbird-cc (pyproject.toml)
+ # via alphabetlearning (pyproject.toml)
python-magic==0.4.27
- # via pyblackbird-cc (pyproject.toml)
+ # via alphabetlearning (pyproject.toml)
python-slugify==8.0.4
- # via pyblackbird-cc (pyproject.toml)
-pytz==2024.2
- # via flower
-qrcode==7.4.2
+ # via alphabetlearning (pyproject.toml)
+qrcode==8.0
# via django-allauth
-redis==5.0.4
- # via
- # pyblackbird-cc (pyproject.toml)
- # django-redis
+redis==5.2.1
+ # via django-redis
requests==2.32.3
# via stripe
-s3transfer==0.10.2
+s3transfer==0.10.4
# via boto3
-six==1.16.0
+six==1.17.0
# via python-dateutil
sniffio==1.3.1
# via anyio
sqlparse==0.5.0
# via
- # pyblackbird-cc (pyproject.toml)
+ # alphabetlearning (pyproject.toml)
# django
-stripe==10.10.0
- # via pyblackbird-cc (pyproject.toml)
+stripe==11.1.0
+ # via alphabetlearning (pyproject.toml)
text-unidecode==1.3
# via python-slugify
-tornado==6.4.1
- # via flower
-types-pyyaml==6.0.12.20240808
+types-pyyaml==6.0.12.20241230
# via django-stubs
typing-extensions==4.12.2
# via
+ # anyio
# django-stubs
# django-stubs-ext
# mypy
# psycopg
- # qrcode
# stripe
-tzdata==2024.1
- # via
- # celery
- # django-celery-beat
-urllib3==2.2.3
+urllib3==2.3.0
# via
# botocore
# requests
-vine==5.1.0
- # via
- # amqp
- # celery
- # kombu
-watchdog==5.0.2
+watchdog==6.0.0
# via werkzeug
watchfiles==0.21.0
- # via pyblackbird-cc (pyproject.toml)
-wcwidth==0.2.13
- # via prompt-toolkit
+ # via alphabetlearning (pyproject.toml)
werkzeug==3.0.2
- # via pyblackbird-cc (pyproject.toml)
+ # via alphabetlearning (pyproject.toml)
whitenoise==6.6.0
- # via pyblackbird-cc (pyproject.toml)
+ # via alphabetlearning (pyproject.toml)
diff --git a/requirements/base.txt b/requirements/base.txt
deleted file mode 100644
index 51b046e..0000000
--- a/requirements/base.txt
+++ /dev/null
@@ -1,30 +0,0 @@
-python-slugify==8.0.4 # https://github.com/un33k/python-slugify
-pillow==10.3.0 # https://github.com/python-pillow/Pillow
-argon2-cffi==23.1.0 # https://github.com/hynek/argon2_cffi
-whitenoise==6.6.0 # https://github.com/evansd/whitenoise
-redis==5.0.4 # https://github.com/redis/redis-py
-hiredis==2.3.2 # https://github.com/redis/hiredis-py
-celery==5.4.0 # pyup: < 6.0 # https://github.com/celery/celery
-django-celery-beat==2.6.0 # https://github.com/celery/django-celery-beat
-flower==2.0.1 # https://github.com/mher/flower
-
-# migrated stuff from pyblackbird
-pdf2image==1.17.0
-pypdf2==3.0.1
-python-magic==0.4.27
-python-dotenv==1.0.1
-boto3==1.34.89
-sqlparse==0.5.0
-
-markdown==3.6.0
-
-# Django
-# ------------------------------------------------------------------------------
-django-storages==1.14.3
-Django==5.0.4 # pyup: < 5.0 # https://www.djangoproject.com/
-django-environ==0.11.2 # https://github.com/joke2k/django-environ
-django-model-utils==4.5.1 # https://github.com/jazzband/django-model-utils
-django-allauth[mfa]==0.62.1 # https://github.com/pennersr/django-allauth
-django-crispy-forms==2.1 # https://github.com/django-crispy-forms/django-crispy-forms
-crispy-bootstrap5==2024.2 # https://github.com/django-crispy-forms/crispy-bootstrap5
-django-redis==5.4.0 # https://github.com/jazzband/django-redis
diff --git a/requirements/local.txt b/requirements/local.txt
deleted file mode 100644
index d6a27d9..0000000
--- a/requirements/local.txt
+++ /dev/null
@@ -1,207 +0,0 @@
-# This file was autogenerated by uv via the following command:
-# uv pip compile pyproject.toml -o requirements/local.txt
-amqp==5.2.0
- # via kombu
-anyio==4.4.0
- # via watchfiles
-argon2-cffi==23.1.0
- # via pyblackbird-cc (pyproject.toml)
-argon2-cffi-bindings==21.2.0
- # via argon2-cffi
-asgiref==3.8.1
- # via
- # django
- # django-stubs
-async-timeout==4.0.3
- # via redis
-billiard==4.2.0
- # via celery
-boto3==1.34.89
- # via pyblackbird-cc (pyproject.toml)
-botocore==1.34.162
- # via
- # pyblackbird-cc (pyproject.toml)
- # boto3
- # s3transfer
-celery==5.4.0
- # via
- # pyblackbird-cc (pyproject.toml)
- # django-celery-beat
- # flower
-certifi==2024.8.30
- # via requests
-cffi==1.17.1
- # via argon2-cffi-bindings
-charset-normalizer==3.3.2
- # via requests
-click==8.1.7
- # via
- # celery
- # click-didyoumean
- # click-plugins
- # click-repl
-click-didyoumean==0.3.1
- # via celery
-click-plugins==1.1.1
- # via celery
-click-repl==0.3.0
- # via celery
-crispy-bootstrap5==2024.2
- # via pyblackbird-cc (pyproject.toml)
-cron-descriptor==1.4.5
- # via django-celery-beat
-django==5.0.4
- # via
- # pyblackbird-cc (pyproject.toml)
- # crispy-bootstrap5
- # django-allauth
- # django-celery-beat
- # django-crispy-forms
- # django-model-utils
- # django-redis
- # django-storages
- # django-stubs
- # django-stubs-ext
- # django-timezone-field
-django-allauth==0.62.1
- # via pyblackbird-cc (pyproject.toml)
-django-celery-beat==2.6.0
- # via pyblackbird-cc (pyproject.toml)
-django-crispy-forms==2.1
- # via
- # pyblackbird-cc (pyproject.toml)
- # crispy-bootstrap5
-django-environ==0.11.2
- # via pyblackbird-cc (pyproject.toml)
-django-model-utils==4.5.1
- # via pyblackbird-cc (pyproject.toml)
-django-redis==5.4.0
- # via pyblackbird-cc (pyproject.toml)
-django-storages==1.14.3
- # via pyblackbird-cc (pyproject.toml)
-django-stubs==5.0.4
- # via pyblackbird-cc (pyproject.toml)
-django-stubs-ext==5.0.4
- # via django-stubs
-django-timezone-field==7.0
- # via django-celery-beat
-flower==2.0.1
- # via pyblackbird-cc (pyproject.toml)
-gunicorn==22.0.0
- # via pyblackbird-cc (pyproject.toml)
-hiredis==2.3.2
- # via pyblackbird-cc (pyproject.toml)
-humanize==4.10.0
- # via flower
-idna==3.8
- # via
- # anyio
- # requests
-jmespath==1.0.1
- # via
- # boto3
- # botocore
-kombu==5.4.1
- # via celery
-markdown==3.6
- # via pyblackbird-cc (pyproject.toml)
-markupsafe==2.1.5
- # via werkzeug
-mypy==1.11.2
- # via django-stubs
-mypy-extensions==1.0.0
- # via mypy
-packaging==24.1
- # via gunicorn
-pdf2image==1.17.0
- # via pyblackbird-cc (pyproject.toml)
-pillow==10.3.0
- # via
- # pyblackbird-cc (pyproject.toml)
- # pdf2image
-prometheus-client==0.20.0
- # via flower
-prompt-toolkit==3.0.47
- # via click-repl
-psycopg==3.1.19
- # via pyblackbird-cc (pyproject.toml)
-psycopg-c==3.1.19
- # via psycopg
-pycparser==2.22
- # via cffi
-pypdf2==3.0.1
- # via pyblackbird-cc (pyproject.toml)
-pypng==0.20220715.0
- # via qrcode
-python-crontab==3.2.0
- # via django-celery-beat
-python-dateutil==2.9.0.post0
- # via
- # botocore
- # celery
- # python-crontab
-python-dotenv==1.0.1
- # via pyblackbird-cc (pyproject.toml)
-python-magic==0.4.27
- # via pyblackbird-cc (pyproject.toml)
-python-slugify==8.0.4
- # via pyblackbird-cc (pyproject.toml)
-pytz==2024.2
- # via flower
-qrcode==7.4.2
- # via django-allauth
-redis==5.0.4
- # via
- # pyblackbird-cc (pyproject.toml)
- # django-redis
-requests==2.32.3
- # via stripe
-s3transfer==0.10.2
- # via boto3
-six==1.16.0
- # via python-dateutil
-sniffio==1.3.1
- # via anyio
-sqlparse==0.5.0
- # via
- # pyblackbird-cc (pyproject.toml)
- # django
-stripe==10.10.0
- # via pyblackbird-cc (pyproject.toml)
-text-unidecode==1.3
- # via python-slugify
-tornado==6.4.1
- # via flower
-types-pyyaml==6.0.12.20240808
- # via django-stubs
-typing-extensions==4.12.2
- # via
- # django-stubs
- # django-stubs-ext
- # mypy
- # psycopg
- # qrcode
- # stripe
-tzdata==2024.1
- # via
- # celery
- # django-celery-beat
-urllib3==2.2.2
- # via
- # botocore
- # requests
-vine==5.1.0
- # via
- # amqp
- # celery
- # kombu
-watchdog==5.0.2
- # via werkzeug
-watchfiles==0.21.0
- # via pyblackbird-cc (pyproject.toml)
-wcwidth==0.2.13
- # via prompt-toolkit
-werkzeug==3.0.2
- # via pyblackbird-cc (pyproject.toml)
-whitenoise==6.6.0
- # via pyblackbird-cc (pyproject.toml)
diff --git a/requirements/production.txt b/requirements/production.txt
deleted file mode 100644
index 9024883..0000000
--- a/requirements/production.txt
+++ /dev/null
@@ -1,12 +0,0 @@
-# PRECAUTION: avoid production dependencies that aren't in development
-
--r base.txt
-
-gunicorn==22.0.0 # https://github.com/benoitc/gunicorn
-psycopg[c]==3.1.19 # https://github.com/psycopg/psycopg
-sentry-sdk==2.1.1 # https://github.com/getsentry/sentry-python
-
-# Django
-# ------------------------------------------------------------------------------
-django-storages[s3]==1.14.3 # https://github.com/jschneier/django-storages
-django-anymail[mailgun]==10.3 # https://github.com/anymail/django-anymail
diff --git a/uv.lock b/uv.lock
index 43f4a55..805e035 100644
--- a/uv.lock
+++ b/uv.lock
@@ -9,22 +9,17 @@ dependencies = [
{ name = "argon2-cffi" },
{ name = "boto3" },
{ name = "botocore" },
- { name = "celery" },
{ name = "crispy-bootstrap5" },
{ name = "django" },
{ name = "django-allauth", extra = ["mfa"] },
- { name = "django-celery-beat" },
{ name = "django-crispy-forms" },
{ name = "django-environ" },
{ name = "django-model-utils" },
- { name = "django-ratelimit" },
{ name = "django-recaptcha" },
{ name = "django-redis" },
{ name = "django-storages" },
{ name = "django-stubs", extra = ["compatible-mypy"] },
- { name = "flower" },
{ name = "gunicorn" },
- { name = "hiredis" },
{ name = "markdown" },
{ name = "pdf2image" },
{ name = "pillow" },
@@ -33,7 +28,6 @@ dependencies = [
{ name = "python-dotenv" },
{ name = "python-magic" },
{ name = "python-slugify" },
- { name = "redis" },
{ name = "sqlparse" },
{ name = "stripe" },
{ name = "watchfiles" },
@@ -58,22 +52,17 @@ requires-dist = [
{ name = "argon2-cffi", specifier = "==23.1.0" },
{ name = "boto3", specifier = "==1.34.89" },
{ name = "botocore", specifier = "~=1.34.93" },
- { name = "celery", specifier = "==5.4.0" },
{ name = "crispy-bootstrap5", specifier = "==2024.2" },
- { name = "django", specifier = "==5.0.4" },
+ { name = "django", specifier = "==5.1.4" },
{ name = "django-allauth", extras = ["mfa"], specifier = "==0.62.1" },
- { name = "django-celery-beat", specifier = "==2.6.0" },
{ name = "django-crispy-forms", specifier = "==2.1" },
{ name = "django-environ", specifier = "==0.11.2" },
{ name = "django-model-utils", specifier = "==4.5.1" },
- { name = "django-ratelimit", specifier = "==4.1.0" },
{ name = "django-recaptcha", specifier = "==4.0.0" },
{ name = "django-redis", specifier = "==5.4.0" },
{ name = "django-storages", specifier = "==1.14.3" },
{ name = "django-stubs", extras = ["compatible-mypy"], specifier = ">=5.0.4" },
- { name = "flower", specifier = "==2.0.1" },
{ name = "gunicorn", specifier = "==22.0.0" },
- { name = "hiredis", specifier = "==2.3.2" },
{ name = "markdown", specifier = "==3.6.0" },
{ name = "pdf2image", specifier = "==1.17.0" },
{ name = "pillow", specifier = "==10.3.0" },
@@ -82,7 +71,6 @@ requires-dist = [
{ name = "python-dotenv", specifier = "==1.0.1" },
{ name = "python-magic", specifier = "==0.4.27" },
{ name = "python-slugify", specifier = "==8.0.4" },
- { name = "redis", specifier = "==5.0.4" },
{ name = "sqlparse", specifier = "==0.5.0" },
{ name = "stripe", specifier = "==11.1.0" },
{ name = "watchfiles", specifier = "==0.21.0" },
@@ -103,18 +91,6 @@ dev = [
]
[[package]]
-name = "amqp"
-version = "5.2.0"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "vine" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/32/2c/6eb09fbdeb3c060b37bd33f8873832897a83e7a428afe01aad333fc405ec/amqp-5.2.0.tar.gz", hash = "sha256:a1ecff425ad063ad42a486c902807d1482311481c8ad95a72694b2975e75f7fd", size = 128754 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/b3/f0/8e5be5d5e0653d9e1d02b1144efa33ff7d2963dfad07049e02c0fa9b2e8d/amqp-5.2.0-py3-none-any.whl", hash = "sha256:827cb12fb0baa892aad844fd95258143bce4027fdac4fccddbc43330fd281637", size = 50917 },
-]
-
-[[package]]
name = "anyio"
version = "4.4.0"
source = { registry = "https://pypi.org/simple" }
@@ -188,15 +164,6 @@ wheels = [
]
[[package]]
-name = "billiard"
-version = "4.2.0"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/09/52/f10d74fd56e73b430c37417658158ad8386202b069b70ff97d945c3ab67a/billiard-4.2.0.tar.gz", hash = "sha256:9a3c3184cb275aa17a732f93f65b20c525d3d9f253722d26a82194803ade5a2c", size = 154665 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/50/8d/6e9fdeeab04d803abc5a715175f87e88893934d5590595eacff23ca12b07/billiard-4.2.0-py3-none-any.whl", hash = "sha256:07aa978b308f334ff8282bd4a746e681b3513db5c9a514cbdd810cbbdc19714d", size = 86720 },
-]
-
-[[package]]
name = "boto3"
version = "1.34.89"
source = { registry = "https://pypi.org/simple" }
@@ -225,26 +192,6 @@ wheels = [
]
[[package]]
-name = "celery"
-version = "5.4.0"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "billiard" },
- { name = "click" },
- { name = "click-didyoumean" },
- { name = "click-plugins" },
- { name = "click-repl" },
- { name = "kombu" },
- { name = "python-dateutil" },
- { name = "tzdata" },
- { name = "vine" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/8a/9c/cf0bce2cc1c8971bf56629d8f180e4ca35612c7e79e6e432e785261a8be4/celery-5.4.0.tar.gz", hash = "sha256:504a19140e8d3029d5acad88330c541d4c3f64c789d85f94756762d8bca7e706", size = 1575692 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/90/c4/6a4d3772e5407622feb93dd25c86ce3c0fee746fa822a777a627d56b4f2a/celery-5.4.0-py3-none-any.whl", hash = "sha256:369631eb580cf8c51a82721ec538684994f8277637edde2dfc0dacd73ed97f64", size = 425983 },
-]
-
-[[package]]
name = "certifi"
version = "2024.8.30"
source = { registry = "https://pypi.org/simple" }
@@ -338,55 +285,6 @@ wheels = [
]
[[package]]
-name = "click"
-version = "8.1.7"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "colorama", marker = "platform_system == 'Windows'" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/96/d3/f04c7bfcf5c1862a2a5b845c6b2b360488cf47af55dfa79c98f6a6bf98b5/click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de", size = 336121 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/00/2e/d53fa4befbf2cfa713304affc7ca780ce4fc1fd8710527771b58311a3229/click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28", size = 97941 },
-]
-
-[[package]]
-name = "click-didyoumean"
-version = "0.3.1"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "click" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/30/ce/217289b77c590ea1e7c24242d9ddd6e249e52c795ff10fac2c50062c48cb/click_didyoumean-0.3.1.tar.gz", hash = "sha256:4f82fdff0dbe64ef8ab2279bd6aa3f6a99c3b28c05aa09cbfc07c9d7fbb5a463", size = 3089 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/1b/5b/974430b5ffdb7a4f1941d13d83c64a0395114503cc357c6b9ae4ce5047ed/click_didyoumean-0.3.1-py3-none-any.whl", hash = "sha256:5c4bb6007cfea5f2fd6583a2fb6701a22a41eb98957e63d0fac41c10e7c3117c", size = 3631 },
-]
-
-[[package]]
-name = "click-plugins"
-version = "1.1.1"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "click" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/5f/1d/45434f64ed749540af821fd7e42b8e4d23ac04b1eda7c26613288d6cd8a8/click-plugins-1.1.1.tar.gz", hash = "sha256:46ab999744a9d831159c3411bb0c79346d94a444df9a3a3742e9ed63645f264b", size = 8164 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/e9/da/824b92d9942f4e472702488857914bdd50f73021efea15b4cad9aca8ecef/click_plugins-1.1.1-py2.py3-none-any.whl", hash = "sha256:5d262006d3222f5057fd81e1623d4443e41dcda5dc815c06b442aa3c02889fc8", size = 7497 },
-]
-
-[[package]]
-name = "click-repl"
-version = "0.3.0"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "click" },
- { name = "prompt-toolkit" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/cb/a2/57f4ac79838cfae6912f997b4d1a64a858fb0c86d7fcaae6f7b58d267fca/click-repl-0.3.0.tar.gz", hash = "sha256:17849c23dba3d667247dc4defe1757fff98694e90fe37474f3feebb69ced26a9", size = 10449 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/52/40/9d857001228658f0d59e97ebd4c346fe73e138c6de1bce61dc568a57c7f8/click_repl-0.3.0-py3-none-any.whl", hash = "sha256:fb7e06deb8da8de86180a33a9da97ac316751c094c6899382da7feeeeb51b812", size = 10289 },
-]
-
-[[package]]
name = "colorama"
version = "0.4.6"
source = { registry = "https://pypi.org/simple" }
@@ -437,26 +335,17 @@ wheels = [
]
[[package]]
-name = "cron-descriptor"
-version = "1.4.5"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/02/83/70bd410dc6965e33a5460b7da84cf0c5a7330a68d6d5d4c3dfdb72ca117e/cron_descriptor-1.4.5.tar.gz", hash = "sha256:f51ce4ffc1d1f2816939add8524f206c376a42c87a5fca3091ce26725b3b1bca", size = 30666 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/88/20/2cfe598ead23a715a00beb716477cfddd3e5948cf203c372d02221e5b0c6/cron_descriptor-1.4.5-py3-none-any.whl", hash = "sha256:736b3ae9d1a99bc3dbfc5b55b5e6e7c12031e7ba5de716625772f8b02dcd6013", size = 50370 },
-]
-
-[[package]]
name = "django"
-version = "5.0.4"
+version = "5.1.4"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "asgiref" },
{ name = "sqlparse" },
{ name = "tzdata", marker = "sys_platform == 'win32'" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/5a/51/30097ef45b0f9bd32908bb3309e9d8c04e2635846e9c73f61b427f6a6c78/Django-5.0.4.tar.gz", hash = "sha256:4bd01a8c830bb77a8a3b0e7d8b25b887e536ad17a81ba2dce5476135c73312bd", size = 10638006 }
+sdist = { url = "https://files.pythonhosted.org/packages/d3/e8/536555596dbb79f6e77418aeb40bdc1758c26725aba31919ba449e6d5e6a/Django-5.1.4.tar.gz", hash = "sha256:de450c09e91879fa5a307f696e57c851955c910a438a35e6b4c895e86bedc82a", size = 10716397 }
wheels = [
- { url = "https://files.pythonhosted.org/packages/d3/31/32ce7eb77accc1678054fe951228766b47f9ec7d68d96d1caaa2611cbafe/Django-5.0.4-py3-none-any.whl", hash = "sha256:916423499d75d62da7aa038d19aef23d23498d8df229775eb0a6309ee1013775", size = 8183007 },
+ { url = "https://files.pythonhosted.org/packages/58/0b/8a4ab2c02982df4ed41e29f28f189459a7eba37899438e6bea7f39db793b/Django-5.1.4-py3-none-any.whl", hash = "sha256:236e023f021f5ce7dee5779de7b286565fdea5f4ab86bae5338e3f7b69896cf0", size = 8276471 },
]
[[package]]
@@ -474,20 +363,6 @@ mfa = [
]
[[package]]
-name = "django-celery-beat"
-version = "2.6.0"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "celery" },
- { name = "cron-descriptor" },
- { name = "django" },
- { name = "django-timezone-field" },
- { name = "python-crontab" },
- { name = "tzdata" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/1b/ce/308fdad8c073051c0a1e494939d5c304b4efbbeb4bee1115495a60c139e8/django-celery-beat-2.6.0.tar.gz", hash = "sha256:f75b2d129731f1214be8383e18fae6bfeacdb55dffb2116ce849222c0106f9ad", size = 160452 }
-
-[[package]]
name = "django-coverage-plugin"
version = "3.1.0"
source = { registry = "https://pypi.org/simple" }
@@ -533,15 +408,6 @@ wheels = [
]
[[package]]
-name = "django-ratelimit"
-version = "4.1.0"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/6f/8f/94038fe739b095aca3e4708ecc8a4e77f1fcfd87bed5d6baff43d4c80bc4/django-ratelimit-4.1.0.tar.gz", hash = "sha256:555943b283045b917ad59f196829530d63be2a39adb72788d985b90c81ba808b", size = 11551 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/fb/78/2c59b30cd8bc8068d02349acb6aeed5c4e05eb01cdf2107ccd76f2e81487/django_ratelimit-4.1.0-py2.py3-none-any.whl", hash = "sha256:d047a31cf94d83ef1465d7543ca66c6fc16695559b5f8d814d1b51df15110b92", size = 11608 },
-]
-
-[[package]]
name = "django-recaptcha"
version = "4.0.0"
source = { registry = "https://pypi.org/simple" }
@@ -613,18 +479,6 @@ wheels = [
]
[[package]]
-name = "django-timezone-field"
-version = "7.0"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "django" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/94/b3/992aa517b95f2e6934aa05b8160cf55f91c49c7b91e33076ea9af2f29920/django_timezone_field-7.0.tar.gz", hash = "sha256:aa6f4965838484317b7f08d22c0d91a53d64e7bbbd34264468ae83d4023898a7", size = 13683 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/2a/f9/11769c4414026f1a9ce3e581731d07b084683fc7b4c580703dc71ef81347/django_timezone_field-7.0-py3-none-any.whl", hash = "sha256:3232e7ecde66ba4464abb6f9e6b8cc739b914efb9b29dc2cf2eee451f7cc2acb", size = 13161 },
-]
-
-[[package]]
name = "factory-boy"
version = "3.3.0"
source = { registry = "https://pypi.org/simple" }
@@ -662,22 +516,6 @@ wheels = [
]
[[package]]
-name = "flower"
-version = "2.0.1"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "celery" },
- { name = "humanize" },
- { name = "prometheus-client" },
- { name = "pytz" },
- { name = "tornado" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/09/a1/357f1b5d8946deafdcfdd604f51baae9de10aafa2908d0b7322597155f92/flower-2.0.1.tar.gz", hash = "sha256:5ab717b979530770c16afb48b50d2a98d23c3e9fe39851dcf6bc4d01845a02a0", size = 3220408 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/a6/ff/ee2f67c0ff146ec98b5df1df637b2bc2d17beeb05df9f427a67bd7a7d79c/flower-2.0.1-py2.py3-none-any.whl", hash = "sha256:9db2c621eeefbc844c8dd88be64aef61e84e2deb29b271e02ab2b5b9f01068e2", size = 383553 },
-]
-
-[[package]]
name = "gunicorn"
version = "22.0.0"
source = { registry = "https://pypi.org/simple" }
@@ -690,53 +528,6 @@ wheels = [
]
[[package]]
-name = "hiredis"
-version = "2.3.2"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/fe/2d/a5ae61da1157644f7e52e088fa158ac6f5d09775112d14b1c9b9a5156bf1/hiredis-2.3.2.tar.gz", hash = "sha256:733e2456b68f3f126ddaf2cd500a33b25146c3676b97ea843665717bda0c5d43", size = 87598 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/83/94/6e729760c66b3eeeec1ea02e06596d7a36b97fa414dd7e25130a34be5238/hiredis-2.3.2-cp311-cp311-macosx_10_15_universal2.whl", hash = "sha256:adfbf2e9c38b77d0db2fb32c3bdaea638fa76b4e75847283cd707521ad2475ef", size = 82807 },
- { url = "https://files.pythonhosted.org/packages/d9/08/e1de65a655ad7ab3b76d01e718a5f4e3503d8c25bf107731608e49a859df/hiredis-2.3.2-cp311-cp311-macosx_10_15_x86_64.whl", hash = "sha256:80b02d27864ebaf9b153d4b99015342382eeaed651f5591ce6f07e840307c56d", size = 45498 },
- { url = "https://files.pythonhosted.org/packages/6f/89/2bbc2d9400dbb18161e9e6ce5821ff06725a78df2e471875a667f6b61d08/hiredis-2.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:bd40d2e2f82a483de0d0a6dfd8c3895a02e55e5c9949610ecbded18188fd0a56", size = 42829 },
- { url = "https://files.pythonhosted.org/packages/4b/d3/b99f6bc21971a7ee889711e62fa9444012999821b06d4e0db300195ef17e/hiredis-2.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dfa904045d7cebfb0f01dad51352551cce1d873d7c3f80c7ded7d42f8cac8f89", size = 167089 },
- { url = "https://files.pythonhosted.org/packages/2e/a1/e149bbe353c202eb578f8f13426fa9e8a9bedb72e6afd1947d830890db3f/hiredis-2.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:28bd184b33e0dd6d65816c16521a4ba1ffbe9ff07d66873c42ea4049a62fed83", size = 177968 },
- { url = "https://files.pythonhosted.org/packages/a2/3d/1b6a97fd60998206a09b7c9426d83f1401cd63e358ba385800d2ffad7174/hiredis-2.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f70481213373d44614148f0f2e38e7905be3f021902ae5167289413196de4ba4", size = 167638 },
- { url = "https://files.pythonhosted.org/packages/cd/24/86f9359ec4de465bafc90890b57439eca907882d03602a08eb3abec05a68/hiredis-2.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eb8797b528c1ff81eef06713623562b36db3dafa106b59f83a6468df788ff0d1", size = 167286 },
- { url = "https://files.pythonhosted.org/packages/b3/01/f71c0ca9acfb2ce6eacb8bfa9963a19220fdfce4b7bc0c6af247ffdec6ec/hiredis-2.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:02fc71c8333586871602db4774d3a3e403b4ccf6446dc4603ec12df563127cee", size = 163141 },
- { url = "https://files.pythonhosted.org/packages/0f/81/1a87b64a4c33e18c30b4e41d8a4cf222c2fff74933d27ed293c2465a6439/hiredis-2.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0da56915bda1e0a49157191b54d3e27689b70960f0685fdd5c415dacdee2fbed", size = 175058 },
- { url = "https://files.pythonhosted.org/packages/49/c0/35cf55026829e06b9c3e9927b7f66f1310afc1f08087796462b02c27eaef/hiredis-2.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:e2674a5a3168349435b08fa0b82998ed2536eb9acccf7087efe26e4cd088a525", size = 170405 },
- { url = "https://files.pythonhosted.org/packages/dd/15/c582c84db3854275d80786cf4e1a01ef11fd70e016a083707a9e24bdd36a/hiredis-2.3.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:dc1c3fd49930494a67dcec37d0558d99d84eca8eb3f03b17198424538f2608d7", size = 183469 },
- { url = "https://files.pythonhosted.org/packages/6c/6a/d8e9c48487e6f0e39fa921d88d04fef7e96500e17c5b7eb0c5bcf9406c1f/hiredis-2.3.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:14c7b43205e515f538a9defb4e411e0f0576caaeeda76bb9993ed505486f7562", size = 173803 },
- { url = "https://files.pythonhosted.org/packages/77/8d/7a0b1e463073f987a2d269af687877a295dde4daad3842f2731d92564888/hiredis-2.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:7bac7e02915b970c3723a7a7c5df4ba7a11a3426d2a3f181e041aa506a1ff028", size = 173922 },
- { url = "https://files.pythonhosted.org/packages/2f/f9/9a770058242120a66a3a256ce4d0fec10312566272b7096d5f3666f77f17/hiredis-2.3.2-cp311-cp311-win32.whl", hash = "sha256:63a090761ddc3c1f7db5e67aa4e247b4b3bb9890080bdcdadd1b5200b8b89ac4", size = 19598 },
- { url = "https://files.pythonhosted.org/packages/a7/f5/7ed05fba6ab598ef760b5b2ee62762d0cfdef6d0bdffbebb6f9550f67a53/hiredis-2.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:70d226ab0306a5b8d408235cabe51d4bf3554c9e8a72d53ce0b3c5c84cf78881", size = 21199 },
- { url = "https://files.pythonhosted.org/packages/50/69/51db3aaac7c862b3023675cf71c0f41d9abb82edceb7fe31b3872238f861/hiredis-2.3.2-cp312-cp312-macosx_10_15_universal2.whl", hash = "sha256:5c614552c6bd1d0d907f448f75550f6b24fb56cbfce80c094908b7990cad9702", size = 82914 },
- { url = "https://files.pythonhosted.org/packages/0f/47/c0ea174d1b9416f5847f9010d2879ab4493ad104c8dbdec868779cee210a/hiredis-2.3.2-cp312-cp312-macosx_10_15_x86_64.whl", hash = "sha256:9c431431abf55b64347ddc8df68b3ef840269cb0aa5bc2d26ad9506eb4b1b866", size = 45588 },
- { url = "https://files.pythonhosted.org/packages/92/63/5bf5c439c1af0b7480b7f75f6af02eb7cdcd70b882b50597b68178c3f7ef/hiredis-2.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a45857e87e9d2b005e81ddac9d815a33efd26ec67032c366629f023fe64fb415", size = 42847 },
- { url = "https://files.pythonhosted.org/packages/95/a0/e59d94e353bcb745149d3c0265ce972b059a03ceb583d575b49ed2124a32/hiredis-2.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e138d141ec5a6ec800b6d01ddc3e5561ce1c940215e0eb9960876bfde7186aae", size = 169068 },
- { url = "https://files.pythonhosted.org/packages/63/2f/92ce21c16d31ba48a0ab29a7ca8d418adf755fa3396abafe1dc5150526d8/hiredis-2.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:387f655444d912a963ab68abf64bf6e178a13c8e4aa945cb27388fd01a02e6f1", size = 179907 },
- { url = "https://files.pythonhosted.org/packages/8d/68/97535298184446c7dfa6bf8c5a7bce0ca5a105a85ffd0b2e4ecaed8cd721/hiredis-2.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4852f4bf88f0e2d9bdf91279892f5740ed22ae368335a37a52b92a5c88691140", size = 169407 },
- { url = "https://files.pythonhosted.org/packages/54/8e/379a340be1bc2e39d8060c97ac050bb0f5b0c78d69bcf7384d153dc1030e/hiredis-2.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d711c107e83117129b7f8bd08e9820c43ceec6204fff072a001fd82f6d13db9f", size = 169874 },
- { url = "https://files.pythonhosted.org/packages/17/33/25716dbb79df5f9221e6d1fe9db47178a449c2046acd317ba6683e00570e/hiredis-2.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:92830c16885f29163e1c2da1f3c1edb226df1210ec7e8711aaabba3dd0d5470a", size = 165363 },
- { url = "https://files.pythonhosted.org/packages/d2/50/e4e5ebbfbd844e087d34ddb7404e9bece9ac46f9e96fd6c8534498871033/hiredis-2.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:16b01d9ceae265d4ab9547be0cd628ecaff14b3360357a9d30c029e5ae8b7e7f", size = 176388 },
- { url = "https://files.pythonhosted.org/packages/5c/35/559d858578b39836d4d5a824bacc8fbd3fecdf76000454f352cf38343931/hiredis-2.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:5986fb5f380169270a0293bebebd95466a1c85010b4f1afc2727e4d17c452512", size = 171773 },
- { url = "https://files.pythonhosted.org/packages/4a/6c/8ab99e4fead92498dc9cfe5eb0a6b712b97ab0d93d95809d36b75a38cd37/hiredis-2.3.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:49532d7939cc51f8e99efc326090c54acf5437ed88b9c904cc8015b3c4eda9c9", size = 184502 },
- { url = "https://files.pythonhosted.org/packages/1d/bd/ca13dc4341e57a7405246e7857054bb3a4cc9546f9030fa6001c05e62459/hiredis-2.3.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:8f34801b251ca43ad70691fb08b606a2e55f06b9c9fb1fc18fd9402b19d70f7b", size = 175147 },
- { url = "https://files.pythonhosted.org/packages/e3/8e/31c66f2e1f5b28ef7872b49e991f7442415495727f10055e547c6ea566ed/hiredis-2.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:7298562a49d95570ab1c7fc4051e72824c6a80e907993a21a41ba204223e7334", size = 175650 },
- { url = "https://files.pythonhosted.org/packages/39/f1/ab594bb6a08dfc9465a2d06315d9ce24c6d189b997d85c373fad60cd3efb/hiredis-2.3.2-cp312-cp312-win32.whl", hash = "sha256:e1d86b75de787481b04d112067a4033e1ecfda2a060e50318a74e4e1c9b2948c", size = 19739 },
- { url = "https://files.pythonhosted.org/packages/d1/37/9f95e75bd0c8da937f57899d2de73e57fd9f87b81375abd4ab58f6d7c23a/hiredis-2.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:6dbfe1887ffa5cf3030451a56a8f965a9da2fa82b7149357752b67a335a05fc6", size = 21269 },
-]
-
-[[package]]
-name = "humanize"
-version = "4.10.0"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/5d/b1/c8f05d5dc8f64030d8cc71e91307c1daadf6ec0d70bcd6eabdfd9b6f153f/humanize-4.10.0.tar.gz", hash = "sha256:06b6eb0293e4b85e8d385397c5868926820db32b9b654b932f57fa41c23c9978", size = 79192 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/8f/49/a29c79bea335e52fb512a43faf84998c184c87fef82c65f568f8c56f2642/humanize-4.10.0-py3-none-any.whl", hash = "sha256:39e7ccb96923e732b5c2e27aeaa3b10a8dfeeba3eb965ba7b74a3eb0e30040a6", size = 126957 },
-]
-
-[[package]]
name = "idna"
version = "3.8"
source = { registry = "https://pypi.org/simple" }
@@ -764,19 +555,6 @@ wheels = [
]
[[package]]
-name = "kombu"
-version = "5.4.1"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "amqp" },
- { name = "vine" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/16/2c/079a5d59a167543205fbf54d663bbdc171e2a29f4d45fa6a8bc315e57192/kombu-5.4.1.tar.gz", hash = "sha256:1c05178826dab811f8cab5b0a154d42a7a33d8bcdde9fa3d7b4582e43c3c03db", size = 442786 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/c1/78/c556bf2954bd36f166959ac33d23cde4a8967e61cdb74678585a66edbae0/kombu-5.4.1-py3-none-any.whl", hash = "sha256:621d365f234e4c089596f3a2510f1ade07026efc28caca426161d8f458786cab", size = 201338 },
-]
-
-[[package]]
name = "markdown"
version = "3.6"
source = { registry = "https://pypi.org/simple" }
@@ -920,27 +698,6 @@ wheels = [
]
[[package]]
-name = "prometheus-client"
-version = "0.20.0"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/3d/39/3be07741a33356127c4fe633768ee450422c1231c6d34b951fee1458308d/prometheus_client-0.20.0.tar.gz", hash = "sha256:287629d00b147a32dcb2be0b9df905da599b2d82f80377083ec8463309a4bb89", size = 78278 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/c7/98/745b810d822103adca2df8decd4c0bbe839ba7ad3511af3f0d09692fc0f0/prometheus_client-0.20.0-py3-none-any.whl", hash = "sha256:cde524a85bce83ca359cc837f28b8c0db5cac7aa653a588fd7e84ba061c329e7", size = 54474 },
-]
-
-[[package]]
-name = "prompt-toolkit"
-version = "3.0.47"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "wcwidth" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/47/6d/0279b119dafc74c1220420028d490c4399b790fc1256998666e3a341879f/prompt_toolkit-3.0.47.tar.gz", hash = "sha256:1e1b29cb58080b1e69f207c893a1a7bf16d127a5c30c9d17a25a5d77792e5360", size = 425859 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/e8/23/22750c4b768f09386d1c3cc4337953e8936f48a888fa6dddfb669b2c9088/prompt_toolkit-3.0.47-py3-none-any.whl", hash = "sha256:0d7bfa67001d5e39d02c224b663abc33687405033a8c422d0d675a5a13361d10", size = 386411 },
-]
-
-[[package]]
name = "psycopg"
version = "3.1.19"
source = { registry = "https://pypi.org/simple" }
@@ -1054,18 +811,6 @@ wheels = [
]
[[package]]
-name = "python-crontab"
-version = "3.2.0"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "python-dateutil" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/e2/f0/25775565c133d4e29eeb607bf9ddba0075f3af36041a1844dd207881047f/python_crontab-3.2.0.tar.gz", hash = "sha256:40067d1dd39ade3460b2ad8557c7651514cd3851deffff61c5c60e1227c5c36b", size = 57001 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/3b/91/832fb3b3a1f62bd2ab4924f6be0c7736c9bc4f84d3b153b74efcf6d4e4a1/python_crontab-3.2.0-py3-none-any.whl", hash = "sha256:82cb9b6a312d41ff66fd3caf3eed7115c28c195bfb50711bc2b4b9592feb9fe5", size = 27351 },
-]
-
-[[package]]
name = "python-dateutil"
version = "2.9.0.post0"
source = { registry = "https://pypi.org/simple" }
@@ -1108,15 +853,6 @@ wheels = [
]
[[package]]
-name = "pytz"
-version = "2024.2"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/3a/31/3c70bf7603cc2dca0f19bdc53b4537a797747a58875b552c8c413d963a3f/pytz-2024.2.tar.gz", hash = "sha256:2aa355083c50a0f93fa581709deac0c9ad65cca8a9e9beac660adcbd493c798a", size = 319692 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/11/c3/005fcca25ce078d2cc29fd559379817424e94885510568bc1bc53d7d5846/pytz-2024.2-py2.py3-none-any.whl", hash = "sha256:31c7c1817eb7fae7ca4b8c7ee50c72f93aa2dd863de768e1ef4245d426aa0725", size = 508002 },
-]
-
-[[package]]
name = "qrcode"
version = "7.4.2"
source = { registry = "https://pypi.org/simple" }
@@ -1253,24 +989,6 @@ wheels = [
]
[[package]]
-name = "tornado"
-version = "6.4.1"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/ee/66/398ac7167f1c7835406888a386f6d0d26ee5dbf197d8a571300be57662d3/tornado-6.4.1.tar.gz", hash = "sha256:92d3ab53183d8c50f8204a51e6f91d18a15d5ef261e84d452800d4ff6fc504e9", size = 500623 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/00/d9/c33be3c1a7564f7d42d87a8d186371a75fd142097076767a5c27da941fef/tornado-6.4.1-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:163b0aafc8e23d8cdc3c9dfb24c5368af84a81e3364745ccb4427669bf84aec8", size = 435924 },
- { url = "https://files.pythonhosted.org/packages/2e/0f/721e113a2fac2f1d7d124b3279a1da4c77622e104084f56119875019ffab/tornado-6.4.1-cp38-abi3-macosx_10_9_x86_64.whl", hash = "sha256:6d5ce3437e18a2b66fbadb183c1d3364fb03f2be71299e7d10dbeeb69f4b2a14", size = 433883 },
- { url = "https://files.pythonhosted.org/packages/13/cf/786b8f1e6fe1c7c675e79657448178ad65e41c1c9765ef82e7f6f765c4c5/tornado-6.4.1-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e2e20b9113cd7293f164dc46fffb13535266e713cdb87bd2d15ddb336e96cfc4", size = 437224 },
- { url = "https://files.pythonhosted.org/packages/e4/8e/a6ce4b8d5935558828b0f30f3afcb2d980566718837b3365d98e34f6067e/tornado-6.4.1-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8ae50a504a740365267b2a8d1a90c9fbc86b780a39170feca9bcc1787ff80842", size = 436597 },
- { url = "https://files.pythonhosted.org/packages/22/d4/54f9d12668b58336bd30defe0307e6c61589a3e687b05c366f804b7faaf0/tornado-6.4.1-cp38-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:613bf4ddf5c7a95509218b149b555621497a6cc0d46ac341b30bd9ec19eac7f3", size = 436797 },
- { url = "https://files.pythonhosted.org/packages/cf/3f/2c792e7afa7dd8b24fad7a2ed3c2f24a5ec5110c7b43a64cb6095cc106b8/tornado-6.4.1-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:25486eb223babe3eed4b8aecbac33b37e3dd6d776bc730ca14e1bf93888b979f", size = 437516 },
- { url = "https://files.pythonhosted.org/packages/71/63/c8fc62745e669ac9009044b889fc531b6f88ac0f5f183cac79eaa950bb23/tornado-6.4.1-cp38-abi3-musllinux_1_2_i686.whl", hash = "sha256:454db8a7ecfcf2ff6042dde58404164d969b6f5d58b926da15e6b23817950fc4", size = 436958 },
- { url = "https://files.pythonhosted.org/packages/94/d4/f8ac1f5bd22c15fad3b527e025ce219bd526acdbd903f52053df2baecc8b/tornado-6.4.1-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a02a08cc7a9314b006f653ce40483b9b3c12cda222d6a46d4ac63bb6c9057698", size = 436882 },
- { url = "https://files.pythonhosted.org/packages/4b/3e/a8124c21cc0bbf144d7903d2a0cadab15cadaf683fa39a0f92bc567f0d4d/tornado-6.4.1-cp38-abi3-win32.whl", hash = "sha256:d9a566c40b89757c9aa8e6f032bcdb8ca8795d7c1a9762910c722b1635c9de4d", size = 438092 },
- { url = "https://files.pythonhosted.org/packages/d9/2f/3f2f05e84a7aff787a96d5fb06821323feb370fe0baed4db6ea7b1088f32/tornado-6.4.1-cp38-abi3-win_amd64.whl", hash = "sha256:b24b8982ed444378d7f21d563f4180a2de31ced9d8d84443907a0a64da2072e7", size = 438532 },
-]
-
-[[package]]
name = "types-pyyaml"
version = "6.0.12.20240808"
source = { registry = "https://pypi.org/simple" }
@@ -1307,15 +1025,6 @@ wheels = [
]
[[package]]
-name = "vine"
-version = "5.1.0"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/bd/e4/d07b5f29d283596b9727dd5275ccbceb63c44a1a82aa9e4bfd20426762ac/vine-5.1.0.tar.gz", hash = "sha256:8b62e981d35c41049211cf62a0a1242d8c1ee9bd15bb196ce38aefd6799e61e0", size = 48980 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/03/ff/7c0c86c43b3cbb927e0ccc0255cb4057ceba4799cd44ae95174ce8e8b5b2/vine-5.1.0-py3-none-any.whl", hash = "sha256:40fdf3c48b2cfe1c38a49e9ae2da6fda88e4794c810050a728bd7413811fb1dc", size = 9636 },
-]
-
-[[package]]
name = "watchdog"
version = "5.0.2"
source = { registry = "https://pypi.org/simple" }
@@ -1380,15 +1089,6 @@ wheels = [
]
[[package]]
-name = "wcwidth"
-version = "0.2.13"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/6c/63/53559446a878410fc5a5974feb13d31d78d752eb18aeba59c7fef1af7598/wcwidth-0.2.13.tar.gz", hash = "sha256:72ea0c06399eb286d978fdedb6923a9eb47e1c486ce63e9b4e64fc18303972b5", size = 101301 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/fd/84/fd2ba7aafacbad3c4201d395674fc6348826569da3c0937e75505ead3528/wcwidth-0.2.13-py2.py3-none-any.whl", hash = "sha256:3da69048e4540d84af32131829ff948f1e022c1c6bdb8d6102117aac784f6859", size = 34166 },
-]
-
-[[package]]
name = "werkzeug"
version = "3.0.2"
source = { registry = "https://pypi.org/simple" }