Skip to content

Commit

Permalink
Merge branch 'dev'
Browse files Browse the repository at this point in the history
  • Loading branch information
50-Course committed May 25, 2024
2 parents 68be4df + f41f7ae commit 75ac3db
Show file tree
Hide file tree
Showing 35 changed files with 163 additions and 30 deletions.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -14,30 +14,26 @@ black==22.3.0
# via -r backend/paycheck/requirements/dev.in
click==8.1.7
# via black
coverage[toml]==7.5.1
coverage[toml]==7.4.4
# via pytest-cov
decorator==5.1.1
# via
# ipdb
# ipython
django==5.0.6
django==5.0.3
# via
# -c backend/paycheck/requirements/base.in
# django-silk
django-silk==5.1.0
# via -r backend/paycheck/requirements/dev.in
exceptiongroup==1.2.1
exceptiongroup==1.2.0
# via
# ipython
# pytest
executing==2.0.1
# via stack-data
factory-boy==3.3.0
faker==24.3.0
# via -r backend/paycheck/requirements/dev.in
faker==25.1.0
# via
# -r backend/paycheck/requirements/dev.in
# factory-boy
flake8==4.0.1
# via -r backend/paycheck/requirements/dev.in
gprof2dot==2022.7.29
Expand All @@ -46,29 +42,30 @@ iniconfig==2.0.0
# via pytest
ipdb==0.13.13
# via -r backend/paycheck/requirements/dev.in
ipython==8.24.0
ipython==8.22.2
# via ipdb
isort==5.10.1
# via -r backend/paycheck/requirements/dev.in
jedi==0.19.1
# via ipython
matplotlib-inline==0.1.7
matplotlib-inline==0.1.6
# via ipython

mccabe==0.6.1
# via flake8
mypy-extensions==1.0.0
# via black
packaging==24.0
# via pytest
parso==0.8.4
parso==0.8.3
# via jedi
pathspec==0.12.1
# via black
pexpect==4.9.0
# via ipython
platformdirs==4.2.1
platformdirs==4.2.0
# via black
pluggy==1.5.0
pluggy==1.4.0
# via pytest
prompt-toolkit==3.0.43
# via ipython
Expand All @@ -82,11 +79,10 @@ pycodestyle==2.8.0
# flake8
pyflakes==2.4.0
# via flake8
pygments==2.18.0
pygments==2.17.2
# via ipython
pytest==8.2.0
pytest==8.1.1
# via
# -r backend/paycheck/requirements/dev.in
# pytest-cov
# pytest-django
pytest-cov==3.0.0
Expand All @@ -99,7 +95,7 @@ six==1.16.0
# via
# asttokens
# python-dateutil
sqlparse==0.5.0
sqlparse==0.4.4
# via
# django
# django-silk
Expand All @@ -113,13 +109,11 @@ tomli==2.0.1
# coverage
# ipdb
# pytest
traitlets==5.14.3
traitlets==5.14.2
# via
# ipython
# matplotlib-inline
typing-extensions==4.11.0
# via
# asgiref
# ipython
typing-extensions==4.10.0
# via asgiref
wcwidth==0.2.13
# via prompt-toolkit
File renamed without changes.
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
DEBUG = os.environ.get("PAYCHECK_BACKEND_DEBUG", "off") == "on"

ALLOWED_HOSTS = ["127.0.0.1", "localhost", "0.0.0.0"]
ALLOWED_HOSTS += os.getenv("PAYCHECK_ALLOWED_HOSTS", "").split(",")
ALLOWED_HOSTS += os.getenv("PAYCHECK_EXTRA_ALLOWED_HOSTS", "").split(",")

# Application definition

Expand All @@ -44,7 +44,7 @@
"rest_framework",
]

LOCAL_APPS = []
LOCAL_APPS = ["core"]

INSTALLED_APPS = DJANGO_APPS + THIRD_PARTY_APPS + LOCAL_APPS

Expand Down Expand Up @@ -132,3 +132,5 @@
DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"

APPEND_SLASH = False

AUTH_USER_MODEL = "core.BaseUser"
File renamed without changes.
File renamed without changes.
15 changes: 15 additions & 0 deletions backend/paycheck_core/src/paycheck/core/auth/backends.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
from django.contrib.auth.backends import BaseBackend
from django.http import HttpRequest


class PhoneEmailOrAccountNumberAuthBackend(BaseBackend):
"""
Authenticates a user into the platform using the user's assigned
unique account number, email address or phone number
"""

def authenticate(self, request, username, password, *args, **kwargs):
"""
Authenticate a user based on the username and password
"""
pass
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
from django.utils.translation import gettext_lazy as _
from phone_field import PhoneField
from uuid import uuid4
from django.contrib.auth import get_user_model
from django.db.models import Q
from django.conf import settings


class BaseUser(AbstractUser):
Expand Down Expand Up @@ -86,11 +89,14 @@ class BaseUser(AbstractUser):
help_text="Designates the phone number of the user",
)

is_customer = models.BooleanField(default=False)

REQUIRED_FIELDS = ["email", "first_name", "last_name"]
USERNAME_FIELD = "email"

class Meta:
abstract = True
verbose_name = _("user")
verbose_name_plural = _("users")

def __str__(self):
return self.username
Expand All @@ -100,6 +106,17 @@ def full_name(self) -> str:
full_name = f"{self.first_name} {self.last_name}"
return full_name.strip()

def save(self, *args, **kwargs):
if not self.username:
self.username = self.email

if kwargs.get("is_customer") is None:
self.is_customer = True

profile = UserProfile(user=self).save()
kwargs["profile"] = profile
super().save(*args, **kwargs)


class Address(models.Model):
"""
Expand Down Expand Up @@ -164,7 +181,7 @@ class VerificationStatus(models.TextChoices):
("INTERNATIONAL_PASSPORT", "INTERNATIONAL_PASSPORT"),
]

user = models.OneToOneField(BaseUser, on_delete=models.CASCADE)
user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
occupation = models.CharField(
_("occupation"),
max_length=100,
Expand Down Expand Up @@ -220,8 +237,7 @@ class VerificationStatus(models.TextChoices):
)

class Meta:
verbose_name = _("user")
verbose_name_plural = _("users")
verbose_name = _("user_profile")
indexes = [
models.Index(fields=["user"]),
models.Index(fields=["verification_status"]),
Expand All @@ -238,3 +254,49 @@ def is_verified(self):
@is_verified.setter
def is_verified(self, value: str) -> None:
self.verification_status = value


class Customer(BaseUser):
"""
A proxy model that represents a customer.
Helps abstracting away the `BaseUser` model and provide, initutive way to access
and work with the model.
Usage:
customer = Customer.objects.get(email="email@email.com")
print(customer.full_name)
customer = Customer(**fields, **kwargs)
customer.save() # overrides the user.save() method
"""

user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
profile = models.OneToOneField(UserProfile, on_delete=models.CASCADE)

class Meta:
proxy = True


class Support(BaseUser):
"""
A support user is essentially, the a member of the customer support team,
they can adminster and manage support tickets
"""

class Meta:
proxy = True

def __str__(self):
return self.user.full_name()


class Admin(BaseUser):

class Meta:
proxy = True

def __str__(self):
return self.user.full_name()
49 changes: 49 additions & 0 deletions backend/paycheck_core/src/paycheck/core/serializers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
from rest_framework import serializers
from django.conf import settings

User = settings.AUTH_USER_MODEL

from src.paycheck.core.models import UserProfile


class UserSerializer(serializers.ModelSerializer):

class Meta:
model = User


class UserProfileSerializer(serializers.ModelSerializer):
user_data = UserSerializer()

class Meta:
model = UserProfile
exclude = ("id",)


class WritableUserProfileSerializer(serializers.Serializer):
"""
This serializer is used to create or update a user profile.
Do not use this serializer in READ operation contexts for retrieving
user information as it may display sensitive information.
"""

user_data = UserSerializer()

def create(self, validated_data):
user_data = validated_data.pop("user_data")
user = User.objects.create(**user_data)
user_profile = UserProfile.objects.create(user=user, **validated_data)
return user_profile

def update(self, instance, validated_data):
user_data = validated_data.pop("user_data")
user = instance.user
user.username = user_data.get("username", user.username)
user.email = user_data.get("email", user.email)
user.save()
instance.__dict__.update(**validated_data)
instance.save()
return instance
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,3 +1 @@
from django.shortcuts import render

# Create your views here.
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,24 @@
"""Django's command-line utility for administrative tasks."""
import os
import sys
from pprint import pprint


def main():
"""Run administrative tasks."""
import pdb

project_root = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
src_root = os.path.join(project_root, "src")

if src_root not in sys.path:
sys.path.append(src_root)

print(f"DJANGO_SETTINGS_MODULE: {os.environ.get('DJANGO_SETTINGS_MODULE')}")
print(f"sys.path: {sys.path}")

os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings.base")

try:
from django.core.management import execute_from_command_line
except ImportError as exc:
Expand Down
Empty file.

0 comments on commit 75ac3db

Please sign in to comment.