From de58cf39cffa0537206f2a0b63be46a62bf10206 Mon Sep 17 00:00:00 2001 From: delano Date: Thu, 21 Sep 2023 20:00:04 -0700 Subject: [PATCH 01/13] Implemented User and Profile models Signed-off-by: delano --- afb/afbcore/migrations/0001_initial.py | 206 ++++++++++++++++++-- afb/afbcore/models/__init__.py | 4 +- afb/afbcore/models/profile.py | 0 afb/afbcore/models/users/__init__.py | 7 + afb/afbcore/models/users/base_profile.py | 59 ++++++ afb/afbcore/models/{ => users}/client.py | 30 +-- afb/afbcore/models/{ => users}/manager.py | 18 +- afb/afbcore/models/users/user.py | 9 + afb/afbcore/models/{ => users}/volunteer.py | 8 +- 9 files changed, 277 insertions(+), 64 deletions(-) delete mode 100644 afb/afbcore/models/profile.py create mode 100644 afb/afbcore/models/users/__init__.py create mode 100644 afb/afbcore/models/users/base_profile.py rename afb/afbcore/models/{ => users}/client.py (69%) rename afb/afbcore/models/{ => users}/manager.py (79%) create mode 100644 afb/afbcore/models/users/user.py rename afb/afbcore/models/{ => users}/volunteer.py (80%) diff --git a/afb/afbcore/migrations/0001_initial.py b/afb/afbcore/migrations/0001_initial.py index c1eddec0..2634d148 100644 --- a/afb/afbcore/migrations/0001_initial.py +++ b/afb/afbcore/migrations/0001_initial.py @@ -1,16 +1,137 @@ -# Generated by Django 4.2.5 on 2023-09-19 20:35 +# Generated by Django 4.2.5 on 2023-09-22 02:58 +from django.conf import settings +import django.contrib.auth.models +import django.contrib.auth.validators from django.db import migrations, models import django.db.models.deletion +import django.utils.timezone +import phonenumber_field.modelfields import uuid class Migration(migrations.Migration): initial = True - dependencies = [] + dependencies = [ + ("auth", "0012_alter_user_first_name_max_length"), + ] operations = [ + migrations.CreateModel( + name="User", + fields=[ + ("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", + ), + ), + ( + "username", + models.CharField( + error_messages={ + "unique": "A user with that username already exists." + }, + help_text="Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.", + max_length=150, + unique=True, + validators=[ + django.contrib.auth.validators.UnicodeUsernameValidator() + ], + verbose_name="username", + ), + ), + ( + "first_name", + models.CharField( + blank=True, max_length=150, verbose_name="first name" + ), + ), + ( + "last_name", + models.CharField( + blank=True, max_length=150, verbose_name="last name" + ), + ), + ( + "email", + models.EmailField( + blank=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" + ), + ), + ( + "id", + models.UUIDField( + default=uuid.uuid4, + editable=False, + primary_key=True, + serialize=False, + ), + ), + ( + "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, + }, + managers=[ + ("objects", django.contrib.auth.models.UserManager()), + ], + ), migrations.CreateModel( name="Branch", fields=[ @@ -65,15 +186,19 @@ class Migration(migrations.Migration): serialize=False, ), ), - ("first_name", models.CharField(max_length=255)), - ("last_name", models.CharField(max_length=255)), + ("first_name", models.CharField(max_length=64)), + ("last_name", models.CharField(max_length=64)), ("email", models.EmailField(max_length=254, unique=True)), - ("address", models.CharField(max_length=255)), + ( + "phone_number", + phonenumber_field.modelfields.PhoneNumberField( + blank=True, max_length=20, null=True, region="US" + ), + ), + ("address_verbatim", models.CharField(blank=True, max_length=255)), + ("address", models.CharField(max_length=255, null=True)), ("validated_postal_code", models.CharField(max_length=20, null=True)), ("country", models.CharField(blank=True, max_length=255)), - ("phone_number", models.CharField(max_length=20)), - ("agreed_to_terms", models.BooleanField()), - ("agreed_on_date", models.DateField(blank=True, null=True)), ( "status", models.CharField( @@ -87,13 +212,22 @@ class Migration(migrations.Migration): ), ), ( - "branch", - models.ForeignKey( + "branches", + models.ManyToManyField( + blank=True, related_name="+", to="afbcore.branch" + ), + ), + ( + "user", + models.OneToOneField( on_delete=django.db.models.deletion.DO_NOTHING, - to="afbcore.branch", + to=settings.AUTH_USER_MODEL, ), ), ], + options={ + "abstract": False, + }, ), migrations.CreateModel( name="DeliveryRegion", @@ -208,16 +342,34 @@ class Migration(migrations.Migration): serialize=False, ), ), - ("first_name", models.CharField(max_length=255)), - ("last_name", models.CharField(max_length=255)), - ("email", models.EmailField(max_length=254)), + ("first_name", models.CharField(max_length=64)), + ("last_name", models.CharField(max_length=64)), + ("email", models.EmailField(max_length=254, unique=True)), + ("address_verbatim", models.CharField(blank=True, max_length=255)), + ("address", models.CharField(max_length=255, null=True)), ("phone_number", models.CharField(max_length=20)), ("points_earned", models.IntegerField(default=0)), + ( + "branches", + models.ManyToManyField( + blank=True, related_name="+", to="afbcore.branch" + ), + ), ( "delivery_regions", models.ManyToManyField(to="afbcore.deliveryregion"), ), + ( + "user", + models.OneToOneField( + on_delete=django.db.models.deletion.DO_NOTHING, + to=settings.AUTH_USER_MODEL, + ), + ), ], + options={ + "abstract": False, + }, ), migrations.CreateModel( name="PetRequest", @@ -233,6 +385,7 @@ class Migration(migrations.Migration): ), ("confirm_address", models.BooleanField()), ("confirm_phone_number", models.BooleanField()), + ("agreed_to_terms", models.BooleanField()), ("method_of_contact", models.CharField(max_length=100)), ("date_requested", models.DateField(auto_now_add=True)), ("safe_drop", models.BooleanField()), @@ -302,10 +455,17 @@ class Migration(migrations.Migration): serialize=False, ), ), - ("first_name", models.CharField(max_length=255)), - ("last_name", models.CharField(max_length=255)), - ("email_address", models.EmailField(max_length=254)), - ("phone_number", models.CharField(max_length=20)), + ("first_name", models.CharField(max_length=64)), + ("last_name", models.CharField(max_length=64)), + ("email", models.EmailField(max_length=254, unique=True)), + ( + "phone_number", + phonenumber_field.modelfields.PhoneNumberField( + blank=True, max_length=20, null=True, region="US" + ), + ), + ("address_verbatim", models.CharField(blank=True, max_length=255)), + ("address", models.CharField(max_length=255, null=True)), ("role_level", models.IntegerField()), ( "branches", @@ -313,7 +473,17 @@ class Migration(migrations.Migration): blank=True, related_name="+", to="afbcore.branch" ), ), + ( + "user", + models.OneToOneField( + on_delete=django.db.models.deletion.DO_NOTHING, + to=settings.AUTH_USER_MODEL, + ), + ), ], + options={ + "abstract": False, + }, ), migrations.CreateModel( name="Delivery", diff --git a/afb/afbcore/models/__init__.py b/afb/afbcore/models/__init__.py index 3ceb18d3..51f49d66 100644 --- a/afb/afbcore/models/__init__.py +++ b/afb/afbcore/models/__init__.py @@ -3,9 +3,7 @@ from django.db import models from .branch import Branch # noqa -from .manager import Manager -from .client import Client -from .volunteer import Volunteer +from .users import Client, Manager, Volunteer, User from .pet_request import PetRequest from .pet import Pet from .food_available import FoodAvailable diff --git a/afb/afbcore/models/profile.py b/afb/afbcore/models/profile.py deleted file mode 100644 index e69de29b..00000000 diff --git a/afb/afbcore/models/users/__init__.py b/afb/afbcore/models/users/__init__.py new file mode 100644 index 00000000..60808e88 --- /dev/null +++ b/afb/afbcore/models/users/__init__.py @@ -0,0 +1,7 @@ + + +from .client import Client +from .manager import Manager +from .volunteer import Volunteer +from .base_profile import BaseProfile +from .user import User diff --git a/afb/afbcore/models/users/base_profile.py b/afb/afbcore/models/users/base_profile.py new file mode 100644 index 00000000..1d9bf16a --- /dev/null +++ b/afb/afbcore/models/users/base_profile.py @@ -0,0 +1,59 @@ + +import uuid + +from django.db import models + +# https://django-phonenumber-field.readthedocs.io/en/latest/reference.html#model-field +# https://github.com/google/libphonenumber/blob/master/FALSEHOODS.md +from phonenumber_field.modelfields import PhoneNumberField + +from .user import User + +# Define default arguments for ManyToManyField +many_to_many_defaults = { + 'related_name': '+', + 'blank': True, + 'symmetrical': False, +} + + +class BaseProfile(models.Model): + + class Meta: + abstract = True + + id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) + + user = models.OneToOneField(User, on_delete=models.DO_NOTHING) + + # Usually just one, but can be multiple + branches = models.ManyToManyField("Branch", **many_to_many_defaults) + + # First Name + first_name = models.CharField(max_length=64) + + # Last Name + last_name = models.CharField(max_length=64) + + # Email - Unique - don't allow duplicates. + email = models.EmailField(unique=True) + + # Phone - Validate format - numbers only + phone_number = PhoneNumberField( + blank=True, + null=True, + region='US', + max_length=20, + ) + + # We allow free form text entry but store the validated + # address in the `address` field. These fields should + # generally be updated together. + # + # e.g. A client may enter "123 Main St" and another + # client may enter "123 Main Street" + address_verbatim = models.CharField(max_length=255, blank=True) + + # Validated Address + # i.e. An address from Canada Post or Google Maps + address = models.CharField(max_length=255, null=True) diff --git a/afb/afbcore/models/client.py b/afb/afbcore/models/users/client.py similarity index 69% rename from afb/afbcore/models/client.py rename to afb/afbcore/models/users/client.py index fe92d33f..2fa5e0b6 100644 --- a/afb/afbcore/models/client.py +++ b/afb/afbcore/models/users/client.py @@ -1,8 +1,11 @@ import uuid + from django.db import models +from django.contrib.auth.models import AbstractUser from django.urls import reverse +from .base_profile import BaseProfile # Status - Active, On Hold, Banned STATUS_CHOICES = [ @@ -11,29 +14,19 @@ ('banned', 'Banned'), ] -class Client(models.Model): + + +class Client(BaseProfile): """ Clients could try to scam and create duplicate accounts to circumvent frequency, change pet info, etc. We need to do our best to ensure each account is unique. """ - id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) - - branch = models.ForeignKey('Branch', on_delete=models.DO_NOTHING) - - # First Name - first_name = models.CharField(max_length=255) - - # Last Name - last_name = models.CharField(max_length=255) - - # Email - Unique - don't allow duplicates. - email = models.EmailField(unique=True) # Address - If address is duplicate to another clients, both accounts need to be placed on hold and manually reviewed/approved bc people are scammers. # Has to be a validated address (google?) and not permitted to be overwritten. The last amount of free form text entry as possible. # You'd be amazed how many clients don't know their postal code and we route by postal code sooooo - address = models.CharField(max_length=255) + # Postal/Zip Code - Has to be a validated address (google?) and not permitted to be overwritten. The last amount of free form text entry as possible. # You'd be amazed how many clients don't know their postal code and we route by postal code sooooo @@ -42,15 +35,6 @@ class Client(models.Model): # Country - I don't know if we need this but google addresses populate country too. It may be useful for analytics country = models.CharField(max_length=255, blank=True) - # Phone - Validate format - numbers only - phone_number = models.CharField(max_length=20) - - # Agree to Terms and Conditions - agreed_to_terms = models.BooleanField() - - # Agreed on date - Yes/No - agreed_on_date = models.DateField(null=True, blank=True) - status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='active') diff --git a/afb/afbcore/models/manager.py b/afb/afbcore/models/users/manager.py similarity index 79% rename from afb/afbcore/models/manager.py rename to afb/afbcore/models/users/manager.py index 6e3be547..c9f4b5c5 100644 --- a/afb/afbcore/models/manager.py +++ b/afb/afbcore/models/users/manager.py @@ -3,24 +3,12 @@ from django.db import models +from .base_profile import BaseProfile -class Manager(models.Model): + +class Manager(BaseProfile): """ """ - # Define default arguments for ManyToManyField - many_to_many_defaults = { - 'related_name': '+', - 'blank': True, - 'symmetrical': False, - } - - id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) - first_name = models.CharField(max_length=255) - last_name = models.CharField(max_length=255) - email_address = models.EmailField() - phone_number = models.CharField(max_length=20) - - branches = models.ManyToManyField("Branch", **many_to_many_defaults)# Usually just one, but can be multiple role_level = models.IntegerField() diff --git a/afb/afbcore/models/users/user.py b/afb/afbcore/models/users/user.py new file mode 100644 index 00000000..909f6d35 --- /dev/null +++ b/afb/afbcore/models/users/user.py @@ -0,0 +1,9 @@ + + +import uuid + +from django.db import models +from django.contrib.auth.models import AbstractUser + +class User(AbstractUser): + id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) diff --git a/afb/afbcore/models/volunteer.py b/afb/afbcore/models/users/volunteer.py similarity index 80% rename from afb/afbcore/models/volunteer.py rename to afb/afbcore/models/users/volunteer.py index 1c1b4dd7..34ac5598 100644 --- a/afb/afbcore/models/volunteer.py +++ b/afb/afbcore/models/users/volunteer.py @@ -2,12 +2,10 @@ import uuid from django.db import models -class Volunteer(models.Model): - id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) +from .base_profile import BaseProfile - first_name = models.CharField(max_length=255) - last_name = models.CharField(max_length=255) - email = models.EmailField() + +class Volunteer(BaseProfile): # Would love for the client and volunteer to be able to communicate through the software - and not have the volunteer have to use their personal phone to arrange delivery. However, we, as admin, need their phone number phone_number = models.CharField(max_length=20) From 72008035a898504582e5035925081106b126ce38 Mon Sep 17 00:00:00 2001 From: delano Date: Thu, 21 Sep 2023 20:01:32 -0700 Subject: [PATCH 02/13] [#20230921] Misc related to users and profiles Signed-off-by: delano --- .ruff.toml | 2 +- afb/afb/settings.py | 9 +++++++++ afb/afb/urls.py | 6 +++--- afb/afb/wsgi.py | 2 +- afb/afbcore/admin.py | 9 +++++++++ afb/afbcore/forms/__init__.py | 7 +++++++ afb/afbcore/models/pet_request.py | 4 ++++ afb/afbcore/templates/afbcore/login.html | 10 ++++++++++ afb/{theme => afbcore}/templates/appshell.html | 5 +++-- afb/afbcore/views/__init__.py | 7 +++++++ afb/requirements.txt | 12 +++++++----- 11 files changed, 61 insertions(+), 12 deletions(-) create mode 100644 afb/afbcore/templates/afbcore/login.html rename afb/{theme => afbcore}/templates/appshell.html (97%) diff --git a/.ruff.toml b/.ruff.toml index a5b363bd..337ceed3 100644 --- a/.ruff.toml +++ b/.ruff.toml @@ -1,6 +1,6 @@ project_type = "python" -#python_version = "3.8" +python_version = "3.10" linter = "flake8" formatter = "black" test_framework = "pytest" diff --git a/afb/afb/settings.py b/afb/afb/settings.py index 0da60b1f..37c86d45 100644 --- a/afb/afb/settings.py +++ b/afb/afb/settings.py @@ -40,6 +40,7 @@ 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', + 'phonenumber_field', 'crispy_forms', 'tailwind', "crispy_tailwind", @@ -54,6 +55,12 @@ CRISPY_TEMPLATE_PACK = "tailwind" +PHONENUMBER_DB_FORMAT = "INTERNATIONAL" +PHONENUMBER_DEFAULT_FORMAT = "E164" +PHONENUMBER_DEFAULT_REGION = 'CA' + + + MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', @@ -72,6 +79,8 @@ TAILWIND_APP_NAME = 'theme' +AUTH_USER_MODEL = 'afbcore.User' + INTERNAL_IPS = [ # Add local IP addresses here for tailwind to work, then run: # * (Dev) `python manage.py tailwind install` diff --git a/afb/afb/urls.py b/afb/afb/urls.py index eb4e42eb..e38aabc9 100644 --- a/afb/afb/urls.py +++ b/afb/afb/urls.py @@ -17,13 +17,12 @@ from django.contrib import admin from django.urls import include, path -from afbcore.views import AboutView, ClientCreateView, CreateClientFormView, DashboardView - +from afbcore.views import AboutView, ClientCreateView, CreateClientFormView, DashboardView, MyLoginView urlpatterns = [ path('admin/', admin.site.urls), - path('dashboard', DashboardView.as_view()), + path('dashboard', DashboardView.as_view(), name='dashboard'), path('', CreateClientFormView.as_view()), @@ -33,5 +32,6 @@ # See https://django-tailwind.readthedocs.io/en/latest/installation.html#configuration path('__reload__/', include('django_browser_reload.urls')), + path('login/', MyLoginView.as_view(), name='login'), path('about/', AboutView.as_view()), ] diff --git a/afb/afb/wsgi.py b/afb/afb/wsgi.py index be55040b..cf57b0a4 100644 --- a/afb/afb/wsgi.py +++ b/afb/afb/wsgi.py @@ -11,6 +11,6 @@ from django.core.wsgi import get_wsgi_application -os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'afb.settings') +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "afb.settings") application = get_wsgi_application() diff --git a/afb/afbcore/admin.py b/afb/afbcore/admin.py index 8c38f3f3..47589575 100644 --- a/afb/afbcore/admin.py +++ b/afb/afbcore/admin.py @@ -1,3 +1,12 @@ + from django.contrib import admin +from django.contrib.auth.admin import UserAdmin +from .models import Client, Manager, Volunteer, User + +admin.site.register(User, UserAdmin) +admin.site.register(Client) +admin.site.register(Manager) +admin.site.register(Volunteer) + # Register your models here. diff --git a/afb/afbcore/forms/__init__.py b/afb/afbcore/forms/__init__.py index 4793f91b..b33bd833 100644 --- a/afb/afbcore/forms/__init__.py +++ b/afb/afbcore/forms/__init__.py @@ -1,3 +1,10 @@ # +from django import forms + from .client import ClientForm + + +class LoginForm(forms.Form): + username = forms.CharField(max_length=100) + password = forms.CharField(widget=forms.PasswordInput) diff --git a/afb/afbcore/models/pet_request.py b/afb/afbcore/models/pet_request.py index 3abef4c2..8acda1e6 100644 --- a/afb/afbcore/models/pet_request.py +++ b/afb/afbcore/models/pet_request.py @@ -35,6 +35,9 @@ class PetRequest(models.Model): # Yes/No - No requires them to update phone number (validate format) confirm_phone_number = models.BooleanField() + # Agree to Terms and Conditions + agreed_to_terms = models.BooleanField() + # Text or Phone method_of_contact = models.CharField(max_length=100) @@ -62,6 +65,7 @@ class PetRequest(models.Model): # Not sure what to call this one, but if the volunteer has an issue with the client - they are rude or aggressive for example, can we allow the driver to mark the client as suspended and admin to review? needs_review = models.BooleanField() + def __str__(self): return f"Pet Request {self.id}" diff --git a/afb/afbcore/templates/afbcore/login.html b/afb/afbcore/templates/afbcore/login.html new file mode 100644 index 00000000..93febf51 --- /dev/null +++ b/afb/afbcore/templates/afbcore/login.html @@ -0,0 +1,10 @@ +{% extends 'appshell.html' %} + +{% block content %} +

Login

+
+ {% csrf_token %} + {{ form.as_p }} + +
+{% endblock %} diff --git a/afb/theme/templates/appshell.html b/afb/afbcore/templates/appshell.html similarity index 97% rename from afb/theme/templates/appshell.html rename to afb/afbcore/templates/appshell.html index ed1e002b..8d380ba1 100644 --- a/afb/theme/templates/appshell.html +++ b/afb/afbcore/templates/appshell.html @@ -105,7 +105,7 @@ Your Profile Settings - Sign out + Sign In @@ -155,7 +155,8 @@ diff --git a/afb/afbcore/views/__init__.py b/afb/afbcore/views/__init__.py index 003fee7b..cf0b9e34 100644 --- a/afb/afbcore/views/__init__.py +++ b/afb/afbcore/views/__init__.py @@ -3,3 +3,10 @@ from .client import * from .dashboard import (ClientDashboardView, DashboardRouterView, ManagerDashboardView, VolunteerDashboardView) + +from django.contrib.auth.views import LoginView +from django.urls import reverse_lazy + +class MyLoginView(LoginView): + template_name = 'afbcore/login.html' + success_url = reverse_lazy('afbcore:dashboard') diff --git a/afb/requirements.txt b/afb/requirements.txt index bf629613..f6db5901 100644 --- a/afb/requirements.txt +++ b/afb/requirements.txt @@ -7,10 +7,12 @@ django-crispy-forms crispy-tailwind pillow -# Copilot suggestions -django-extensions -django-allauth -django-countries +phonenumbers django-phonenumber-field -django-address + +# Copilot suggestions +# django-extensions +# django-allauth +# django-address # django-money +# django-countries From df3c824c4bf82259ce4589a56cfc936708966f68 Mon Sep 17 00:00:00 2001 From: delano Date: Thu, 21 Sep 2023 20:09:51 -0700 Subject: [PATCH 03/13] [#20230921] Try to fix cwd for django test action Otherwise we'll 86 since we have our own anyway. Signed-off-by: delano --- .github/workflows/workflow.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/workflow.yml b/.github/workflows/workflow.yml index 2311a2cd..c2e12c9a 100644 --- a/.github/workflows/workflow.yml +++ b/.github/workflows/workflow.yml @@ -48,6 +48,7 @@ jobs: - name: Django Test CI uses: UKnowWhoIm/django-test-action@v0.6.1 + working-directory: ./afb with: requirements-file: afb/requirements.txt settings-dir-path: afb/afb From d20bff428679745c0d1a65567c40fbfa67441ca7 Mon Sep 17 00:00:00 2001 From: delano Date: Thu, 21 Sep 2023 20:13:11 -0700 Subject: [PATCH 04/13] [#20230921] Removed dat action Signed-off-by: delano --- .github/workflows/{workflow.yml => audit.yml} | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) rename .github/workflows/{workflow.yml => audit.yml} (83%) diff --git a/.github/workflows/workflow.yml b/.github/workflows/audit.yml similarity index 83% rename from .github/workflows/workflow.yml rename to .github/workflows/audit.yml index c2e12c9a..84a8858c 100644 --- a/.github/workflows/workflow.yml +++ b/.github/workflows/audit.yml @@ -10,7 +10,7 @@ # requirements file and a settings directory path. -name: Django CI (actions) +name: Audit on: push: branches: [main] @@ -46,13 +46,5 @@ jobs: # run: | # pip install --upgrade pip - - name: Django Test CI - uses: UKnowWhoIm/django-test-action@v0.6.1 - working-directory: ./afb - with: - requirements-file: afb/requirements.txt - settings-dir-path: afb/afb - parallel-tests: false - - name: gh-action-pip-audit uses: pypa/gh-action-pip-audit@v1.0.8 From bede5319c684059956225ed913d2da501b6f5a40 Mon Sep 17 00:00:00 2001 From: delano Date: Fri, 29 Sep 2023 12:08:27 -0700 Subject: [PATCH 05/13] [#20230921] Added client request Signed-off-by: delano --- afb/afbcore/templates/afbcore/client_request.html | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 afb/afbcore/templates/afbcore/client_request.html diff --git a/afb/afbcore/templates/afbcore/client_request.html b/afb/afbcore/templates/afbcore/client_request.html new file mode 100644 index 00000000..6931a3e7 --- /dev/null +++ b/afb/afbcore/templates/afbcore/client_request.html @@ -0,0 +1,13 @@ + +{% extends 'appshell.html' %} + +{% load tailwind_filters %} + + +{% block content %} + +Make a request + +{{form | crispy}} + +{% endblock %} From 9bd5a6099861b600e26c05f0a5d236a25cad5be9 Mon Sep 17 00:00:00 2001 From: delano Date: Fri, 29 Sep 2023 12:29:57 -0700 Subject: [PATCH 06/13] [#20230921] . Signed-off-by: delano --- afb/afb/urls.py | 3 +++ afb/afbcore/templates/appshell.html | 2 ++ afb/afbcore/views/client.py | 4 ++++ 3 files changed, 9 insertions(+) diff --git a/afb/afb/urls.py b/afb/afb/urls.py index e38aabc9..52f1c053 100644 --- a/afb/afb/urls.py +++ b/afb/afb/urls.py @@ -18,6 +18,7 @@ from django.urls import include, path from afbcore.views import AboutView, ClientCreateView, CreateClientFormView, DashboardView, MyLoginView +from afbcore.views import ClientRequestView urlpatterns = [ path('admin/', admin.site.urls), @@ -32,6 +33,8 @@ # See https://django-tailwind.readthedocs.io/en/latest/installation.html#configuration path('__reload__/', include('django_browser_reload.urls')), + path('request/', ClientRequestView.as_view(), name='create_request'), path('login/', MyLoginView.as_view(), name='login'), + path('about/', AboutView.as_view()), ] diff --git a/afb/afbcore/templates/appshell.html b/afb/afbcore/templates/appshell.html index 8d380ba1..fde60108 100644 --- a/afb/afbcore/templates/appshell.html +++ b/afb/afbcore/templates/appshell.html @@ -56,6 +56,7 @@ @@ -133,6 +134,7 @@
diff --git a/afb/afbcore/views/client.py b/afb/afbcore/views/client.py index 23104e03..2f29cfad 100644 --- a/afb/afbcore/views/client.py +++ b/afb/afbcore/views/client.py @@ -26,3 +26,7 @@ class CreateClientFormView(FormView): class ClientCreateView(CreateView): model = Client fields = ["first_name", "last_name", "email", "address", "phone_number", "status"] + + +class ClientRequestView(TemplateView): + template_name = "afbcore/client_request.html" From d46f4b7a6f484bd1af328130116b621c64a29147 Mon Sep 17 00:00:00 2001 From: delano Date: Fri, 29 Sep 2023 12:30:48 -0700 Subject: [PATCH 07/13] [#20230921] Adding pip-tools for dep management 1/2 Signed-off-by: delano --- afb/{requirements.txt => requirements.in} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename afb/{requirements.txt => requirements.in} (100%) diff --git a/afb/requirements.txt b/afb/requirements.in similarity index 100% rename from afb/requirements.txt rename to afb/requirements.in From f5dde256e6c1db353828d2f535e001de2591ed38 Mon Sep 17 00:00:00 2001 From: delano Date: Fri, 29 Sep 2023 12:38:45 -0700 Subject: [PATCH 08/13] [#20230921] Re-generated requirements.txt Signed-off-by: delano --- .gitignore | 1 - README.md | 15 +++++++++++- afb/requirements.in | 20 +++++++++++++++- afb/requirements.txt | 56 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 89 insertions(+), 3 deletions(-) create mode 100644 afb/requirements.txt diff --git a/.gitignore b/.gitignore index b3dd0852..fc7ffc2b 100644 --- a/.gitignore +++ b/.gitignore @@ -84,5 +84,4 @@ local_settings.py .env* !.env.empty db.sqlite3 -*.txt .vscode/ diff --git a/README.md b/README.md index 7cf5cd27..36fc6c22 100644 --- a/README.md +++ b/README.md @@ -1 +1,14 @@ -# afb-platform \ No newline at end of file +# afb-requests + + +## Commands + + +```bash + $ pip-compile --output-file=- requirements.in > requirements.txt + + $ pip-compile --upgrade --output-file=- requirements.in | tee requirements.txt + + $ pip-compile --output-file=- > requirements.txt + +``` diff --git a/afb/requirements.in b/afb/requirements.in index f6db5901..9b5dee3b 100644 --- a/afb/requirements.in +++ b/afb/requirements.in @@ -1,4 +1,20 @@ - +# +# Requirements file for afb-requests. Modify this file to +# include the packages you want to install. Then, run: +# +# pip-compile --output-file requirements.txt requirements.in +# +# This will generate a new requirements.txt file with the +# latest versions of the packages listed here. +# +# You can also run: +# +# pip-compile --upgrade --output-file requirements.txt requirements.in +# +# to upgrade all packages to their latest versions. +# +# See: https://github.com/jazzband/pip-tools +# Django==4.2.5 django-tailwind[reload] @@ -10,6 +26,8 @@ pillow phonenumbers django-phonenumber-field +pip-tools + # Copilot suggestions # django-extensions # django-allauth diff --git a/afb/requirements.txt b/afb/requirements.txt new file mode 100644 index 00000000..5fe1ad16 --- /dev/null +++ b/afb/requirements.txt @@ -0,0 +1,56 @@ +# +# This file is autogenerated by pip-compile with Python 3.11 +# by the following command: +# +# pip-compile --output-file=- +# +asgiref==3.7.2 + # via django +build==1.0.3 + # via pip-tools +click==8.1.7 + # via pip-tools +crispy-tailwind==0.5.0 + # via -r requirements.in +django==4.2.5 + # via + # -r requirements.in + # django-browser-reload + # django-crispy-forms + # django-phonenumber-field + # django-tailwind + # django-unfold +django-browser-reload==1.11.0 + # via django-tailwind +django-crispy-forms==2.0 + # via + # -r requirements.in + # crispy-tailwind +django-phonenumber-field==7.1.0 + # via -r requirements.in +django-tailwind[reload]==3.6.0 + # via -r requirements.in +django-unfold==0.10.0 + # via -r requirements.in +importlib-metadata==6.7.0 + # via django-unfold +packaging==23.1 + # via build +phonenumbers==8.13.22 + # via -r requirements.in +pillow==10.0.1 + # via -r requirements.in +pip-tools==7.3.0 + # via -r requirements.in +pyproject-hooks==1.0.0 + # via build +sqlparse==0.4.4 + # via django +wheel==0.41.2 + # via pip-tools +zipp==3.17.0 + # via importlib-metadata + +# The following packages are considered to be unsafe in a requirements file: +# pip +# setuptools From 143483f420cbc9664c755a30db549a38a3a27a78 Mon Sep 17 00:00:00 2001 From: delano Date: Fri, 29 Sep 2023 12:51:11 -0700 Subject: [PATCH 09/13] [#20230921] Add path arg for pip audit Signed-off-by: delano --- .github/workflows/audit.yml | 11 +++++++++++ .gitignore | 1 + 2 files changed, 12 insertions(+) diff --git a/.github/workflows/audit.yml b/.github/workflows/audit.yml index 84a8858c..fd7e52c8 100644 --- a/.github/workflows/audit.yml +++ b/.github/workflows/audit.yml @@ -8,6 +8,14 @@ # # The `django-test-action` action is used to run the tests and requires a # requirements file and a settings directory path. +# +# The `pypa/gh-action-pip-audit` action is used to audit the requirements file +# for security vulnerabilities. It requires a requirements file and can be +# configured to require hashes for all packages. +# +# See: +# - https://github.com/pypa/gh-action-pip-audit +# - https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions name: Audit @@ -48,3 +56,6 @@ jobs: - name: gh-action-pip-audit uses: pypa/gh-action-pip-audit@v1.0.8 + with: + inputs: afb/requirements.txt + require-hashes: true diff --git a/.gitignore b/.gitignore index fc7ffc2b..f62c1749 100644 --- a/.gitignore +++ b/.gitignore @@ -85,3 +85,4 @@ local_settings.py !.env.empty db.sqlite3 .vscode/ +tmp/* From 346ab62f0d39711b3b55254036a7c0a19177f26b Mon Sep 17 00:00:00 2001 From: delano Date: Fri, 29 Sep 2023 12:54:50 -0700 Subject: [PATCH 10/13] [#20230921] Added dep pre-commit Signed-off-by: delano --- afb/requirements.in | 1 + afb/requirements.txt | 20 +++++++++++++++++++- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/afb/requirements.in b/afb/requirements.in index 9b5dee3b..dea8c3c4 100644 --- a/afb/requirements.in +++ b/afb/requirements.in @@ -27,6 +27,7 @@ phonenumbers django-phonenumber-field pip-tools +pre-commit # Copilot suggestions # django-extensions diff --git a/afb/requirements.txt b/afb/requirements.txt index 5fe1ad16..8c366a6d 100644 --- a/afb/requirements.txt +++ b/afb/requirements.txt @@ -2,16 +2,20 @@ # This file is autogenerated by pip-compile with Python 3.11 # by the following command: # -# pip-compile --output-file=- +# pip-compile --output-file=requirements.txt requirements.in # asgiref==3.7.2 # via django build==1.0.3 # via pip-tools +cfgv==3.4.0 + # via pre-commit click==8.1.7 # via pip-tools crispy-tailwind==0.5.0 # via -r requirements.in +distlib==0.3.7 + # via virtualenv django==4.2.5 # via # -r requirements.in @@ -32,8 +36,14 @@ django-tailwind[reload]==3.6.0 # via -r requirements.in django-unfold==0.10.0 # via -r requirements.in +filelock==3.12.4 + # via virtualenv +identify==2.5.29 + # via pre-commit importlib-metadata==6.7.0 # via django-unfold +nodeenv==1.8.0 + # via pre-commit packaging==23.1 # via build phonenumbers==8.13.22 @@ -42,10 +52,18 @@ pillow==10.0.1 # via -r requirements.in pip-tools==7.3.0 # via -r requirements.in +platformdirs==3.10.0 + # via virtualenv +pre-commit==3.4.0 + # via -r requirements.in pyproject-hooks==1.0.0 # via build +pyyaml==6.0.1 + # via pre-commit sqlparse==0.4.4 # via django +virtualenv==20.24.5 + # via pre-commit wheel==0.41.2 # via pip-tools zipp==3.17.0 From 7d89a7fc90b89ae2939cfabb5fce142795149ac1 Mon Sep 17 00:00:00 2001 From: delano Date: Fri, 29 Sep 2023 13:02:28 -0700 Subject: [PATCH 11/13] Added pre-commit config Signed-off-by: delano --- .pre-commit-config.yaml | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 .pre-commit-config.yaml diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 00000000..ff4a7366 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,26 @@ +# +# See https://pre-commit.com for more information +# See https://pre-commit.com/hooks.html for more hooks +# +# To use pre-commit, install it using pip: +# +# pip install pre-commit +# +# Then, run it on all the files in this repo: +# +# pre-commit run --all-files +# +repos: +- repo: https://github.com/pre-commit/pre-commit-hooks + rev: v3.2.0 + hooks: + - id: trailing-whitespace + - id: end-of-file-fixer + - id: check-yaml + - id: check-added-large-files + +# TODO: Enable black after formatting is standardized. +# - repo: https://github.com/psf/black +# rev: 22.10.0 +# hooks: +# - id: black From fa7e7a7c8890994f62c3bb2171fe169b10f5fae3 Mon Sep 17 00:00:00 2001 From: delano Date: Fri, 29 Sep 2023 13:04:24 -0700 Subject: [PATCH 12/13] Adds audit summary Signed-off-by: delano --- .github/workflows/audit.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/audit.yml b/.github/workflows/audit.yml index fd7e52c8..6da24d12 100644 --- a/.github/workflows/audit.yml +++ b/.github/workflows/audit.yml @@ -58,4 +58,5 @@ jobs: uses: pypa/gh-action-pip-audit@v1.0.8 with: inputs: afb/requirements.txt + summary: true require-hashes: true From 1d053ed276349324f6d5e0db5c8df17ef87900f8 Mon Sep 17 00:00:00 2001 From: delano Date: Fri, 29 Sep 2023 13:05:17 -0700 Subject: [PATCH 13/13] Disable hash req Signed-off-by: delano --- .github/workflows/audit.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/audit.yml b/.github/workflows/audit.yml index 6da24d12..d36cc5ca 100644 --- a/.github/workflows/audit.yml +++ b/.github/workflows/audit.yml @@ -59,4 +59,4 @@ jobs: with: inputs: afb/requirements.txt summary: true - require-hashes: true + require-hashes: false