Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(deps): remove drfaddons dependency #208

Draft
wants to merge 5 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 3 additions & 7 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,13 @@ repos:
rev: 22.10.0
hooks:
- id: black

args:
- --line-length=100
- repo: https://github.com/PyCQA/flake8
rev: 5.0.4
hooks:
- id: flake8
args: [--max-line-length=88]

- repo: https://github.com/asottile/reorder_python_imports
rev: v3.8.3
hooks:
- id: reorder-python-imports
args: [--max-line-length=100]

- repo: https://github.com/econchick/interrogate
rev: 1.5.0
Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ docs:
python setup.py build_sphinx

lint:
flake8 --exclude=*/migrations/* --max-line-length 88 drf_user
flake8 --exclude=*/migrations/* --max-line-length 100 drf_user

format:
black --exclude .+/migrations/.+\.py drf_user
Expand Down
5 changes: 2 additions & 3 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,13 @@
# documentation root, use os.path.abspath to make it absolute, like shown here.
#
import os

# sys.path.insert(0, os.path.abspath('.'))
from datetime import datetime


# -- Project information -----------------------------------------------------

project = "drf-user"
copyright = "2020, 101 Loop"
copyright = f"{datetime.now().year}, 101 Loop"

Check warning on line 20 in docs/conf.py

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

docs/conf.py#L20

Redefining built-in 'copyright'
author = "101 Loop"

parent_dir = os.path.dirname(os.path.dirname(__file__))
Expand Down
17 changes: 6 additions & 11 deletions drf_user/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,7 @@ def update_user_settings() -> dict:

Author: Himanshu Shankar (https://himanshus.com)
"""
custom_settings = getattr(settings, "USER_SETTINGS", None)

if custom_settings:
if custom_settings := getattr(settings, "USER_SETTINGS", None):
if not isinstance(custom_settings, dict):
raise TypeError("USER_SETTING must be a dict.")

Expand All @@ -55,17 +53,14 @@ def update_user_settings() -> dict:
user_settings[key] = value
elif key == "OTP":
if not isinstance(value, dict):
raise TypeError("USER_SETTING attribute OTP must be a" " dict.")
raise TypeError("USER_SETTING attribute OTP must be a dict.")
for otp_key, otp_value in value.items():
user_settings["OTP"][otp_key] = otp_value
elif key == "REGISTRATION":
if isinstance(value, dict):
for reg_key, reg_value in value.items():
user_settings["REGISTRATION"][reg_key] = reg_value
else:
raise TypeError(
"USER_SETTING attribute REGISTRATION" " must be a dict."
)
if not isinstance(value, dict):
raise TypeError("USER_SETTING attribute REGISTRATION must be a dict.")
for reg_key, reg_value in value.items():
user_settings["REGISTRATION"][reg_key] = reg_value
if user_settings["REGISTRATION"]["SEND_MAIL"]:
if not getattr(settings, "EMAIL_HOST", None):
raise ValueError(
Expand Down
20 changes: 5 additions & 15 deletions drf_user/admin.py
Original file line number Diff line number Diff line change
@@ -1,26 +1,18 @@
"""
All Admin configuration related to drf_user

Author: Himanshu Shankar (https://himanshus.com)
"""
from django.contrib import admin
from django.contrib.auth.admin import Group
from django.contrib.auth.admin import GroupAdmin
from django.contrib.auth.admin import UserAdmin
from django.contrib.auth.admin import Group, GroupAdmin, UserAdmin
from django.utils.text import gettext_lazy as _

from .models import AuthTransaction
from .models import OTPValidation
from .models import Role
from .models import User
from drf_user.models import AuthTransaction, OTPValidation, Role, User


@admin.register(User)
class DRFUserAdmin(UserAdmin):
"""
Overrides UserAdmin to show fields name & mobile and remove fields:
first_name, last_name

Author: Himanshu Shankar (https://himanshus.com)
"""

fieldsets = (
Expand Down Expand Up @@ -57,12 +49,14 @@ class DRFUserAdmin(UserAdmin):
readonly_fields = ("date_joined", "last_login", "update_date")


@admin.register(OTPValidation)
class OTPValidationAdmin(admin.ModelAdmin):
"""OTP Validation Admin"""

list_display = ("destination", "otp", "prop")


@admin.register(AuthTransaction)
class AuthTransactionAdmin(admin.ModelAdmin):
"""AuthTransaction Admin"""

Expand All @@ -89,7 +83,3 @@ def has_delete_permission(self, request, obj=None):
# Source: https://stackoverflow.com/a/32445368
admin.site.unregister(Group)
admin.site.register(Role, GroupAdmin)

admin.site.register(User, DRFUserAdmin)
admin.site.register(OTPValidation, OTPValidationAdmin)
admin.site.register(AuthTransaction, AuthTransactionAdmin)
16 changes: 16 additions & 0 deletions drf_user/constants.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
"""
All constants used in the system.
"""

EMAIL: str = "E"
MOBILE: str = "M"
DESTINATION_CHOICES: list = [(EMAIL, "EMail Address"), (MOBILE, "Mobile Number")]


class CoreConstants:
"""Core Constants"""

EMAIL_PROP: str = "E"
MOBILE_PROP: str = "M"
EMAIL_STR: str = "email"
MOBILE_STR: str = "mobile"
4 changes: 1 addition & 3 deletions drf_user/managers.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,7 @@ def _create_user(
Creates and saves a User with given details
"""
email = self.normalize_email(email)
user = self.model(
username=username, email=email, name=fullname, mobile=mobile, **kwargs
)
user = self.model(username=username, email=email, name=fullname, mobile=mobile, **kwargs)
user.set_password(password)
user.save(using=self._db)
return user
Expand Down
31 changes: 9 additions & 22 deletions drf_user/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@
from django.utils.text import gettext_lazy as _

from drf_user.managers import UserManager
from drf_user.variables import DESTINATION_CHOICES
from drf_user.variables import EMAIL
from drf_user.constants import DESTINATION_CHOICES, EMAIL


class Role(Group):
Expand All @@ -34,9 +33,7 @@ class User(AbstractBaseUser, PermissionsMixin):
Author: Himanshu Shankar (https://himanshus.com)
"""

username = models.CharField(
verbose_name=_("Unique UserName"), max_length=254, unique=True
)
username = models.CharField(verbose_name=_("Unique UserName"), max_length=254, unique=True)
email = models.EmailField(verbose_name=_("Email Address"), unique=True)
mobile = models.CharField(
verbose_name=_("Mobile Number"),
Expand Down Expand Up @@ -86,7 +83,7 @@ def get_full_name(self) -> str:
def __str__(self):
"""String representation of model"""

return str(self.name) + " | " + str(self.username)
return f"{str(self.name)} | {str(self.username)}"


class AuthTransaction(models.Model):
Expand All @@ -104,21 +101,15 @@ class AuthTransaction(models.Model):
blank=True,
verbose_name=_("JWT Refresh Token"),
)
expires_at = models.DateTimeField(
blank=True, null=True, verbose_name=_("Expires At")
)
create_date = models.DateTimeField(
verbose_name=_("Create Date/Time"), auto_now_add=True
)
update_date = models.DateTimeField(
verbose_name=_("Date/Time Modified"), auto_now=True
)
expires_at = models.DateTimeField(blank=True, null=True, verbose_name=_("Expires At"))
create_date = models.DateTimeField(verbose_name=_("Create Date/Time"), auto_now_add=True)
update_date = models.DateTimeField(verbose_name=_("Date/Time Modified"), auto_now=True)
created_by = models.ForeignKey(to=User, on_delete=models.PROTECT)

def __str__(self):
"""String representation of model"""

return str(self.created_by.name) + " | " + str(self.created_by.username)
return f"{str(self.created_by.name)} | {str(self.created_by.username)}"

class Meta:
"""Passing model metadata"""
Expand All @@ -143,19 +134,15 @@ class OTPValidation(models.Model):
create_date = models.DateTimeField(verbose_name=_("Create Date"), auto_now_add=True)
update_date = models.DateTimeField(verbose_name=_("Date Modified"), auto_now=True)
is_validated = models.BooleanField(verbose_name=_("Is Validated"), default=False)
validate_attempt = models.IntegerField(
verbose_name=_("Attempted Validation"), default=3
)
validate_attempt = models.IntegerField(verbose_name=_("Attempted Validation"), default=3)
prop = models.CharField(
verbose_name=_("Destination Property"),
default=EMAIL,
max_length=3,
choices=DESTINATION_CHOICES,
)
send_counter = models.IntegerField(verbose_name=_("OTP Sent Counter"), default=0)
sms_id = models.CharField(
verbose_name=_("SMS ID"), max_length=254, null=True, blank=True
)
sms_id = models.CharField(verbose_name=_("SMS ID"), max_length=254, null=True, blank=True)
reactive_at = models.DateTimeField(verbose_name=_("ReActivate Sending OTP"))

def __str__(self):
Expand Down
62 changes: 26 additions & 36 deletions drf_user/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,8 @@

from drf_user import user_settings
from drf_user.models import User
from drf_user.utils import check_validation
from drf_user.variables import EMAIL
from drf_user.variables import MOBILE
from drf_user.utils import check_validation, is_mobile_valid
from drf_user.constants import EMAIL, MOBILE


class UserSerializer(serializers.ModelSerializer):
Expand Down Expand Up @@ -39,9 +38,7 @@
if check_validation(value=value):
return value
else:
raise serializers.ValidationError(
"The email must be " "pre-validated via OTP."
)
raise serializers.ValidationError("The email must be " "pre-validated via OTP.")

def validate_mobile(self, value: str) -> str:
"""
Expand All @@ -62,9 +59,7 @@
if check_validation(value=value):
return value
else:
raise serializers.ValidationError(
"The mobile must be " "pre-validated via OTP."
)
raise serializers.ValidationError("The mobile must be " "pre-validated via OTP.")

def validate_password(self, value: str) -> str:
"""Validate whether the password meets all django validator requirements."""
Expand Down Expand Up @@ -195,22 +190,17 @@
else:
attrs["prop"] = EMAIL

user = self.get_user(attrs.get("prop"), attrs.get("destination"))
if user := self.get_user(attrs.get("prop"), attrs.get("destination")):
attrs["email"] = user.email
attrs["user"] = user

if not user:
else:
if attrs["is_login"]:
raise NotFound(_("No user exists with provided details"))
if "email" not in attrs.keys() and "verify_otp" not in attrs.keys():
raise serializers.ValidationError(
_(
"email field is compulsory while verifying a"
" non-existing user's OTP."
)
_("Email field is compulsory while verifying a non-existing user's OTP.")
)
else:
attrs["email"] = user.email
attrs["user"] = user

return attrs


Expand Down Expand Up @@ -242,6 +232,11 @@
verify_otp = serializers.CharField(default=None, required=False)
mobile = serializers.CharField(required=True)

def validate_mobile(self, value: str) -> str:
"""Validate whether the mobile is unique."""
is_mobile_valid(value)
return value

@staticmethod
def get_user(email: str, mobile: str):
"""Fetches user object"""
Expand All @@ -251,33 +246,32 @@
try:
user = User.objects.get(mobile=mobile)
except User.DoesNotExist:
# new user is trying to register
user = None

if user:
if user.email != email:
raise serializers.ValidationError(
_(
"Your account is registered with {mobile} does not has "
"{email} as registered email. Please login directly via "
"OTP with your mobile.".format(mobile=mobile, email=email)
f"Your account is registered with {mobile} does not has "
f"{email} as registered email. Please login directly via "
"OTP with your mobile."
)
)
if user.mobile != mobile:
raise serializers.ValidationError(
_(
"Your account is registered with {email} does not has "
"{mobile} as registered mobile. Please login directly via "
"OTP with your email.".format(mobile=mobile, email=email)
f"Your account is registered with {email} does not has "
f"{mobile} as registered mobile. Please login directly via "
"OTP with your email."
)
)
return user

def validate(self, attrs: dict) -> dict:
"""Validates the response"""

attrs["user"] = self.get_user(
email=attrs.get("email"), mobile=attrs.get("mobile")
)
attrs["user"] = self.get_user(email=attrs.get("email"), mobile=attrs.get("mobile"))
return attrs


Expand Down Expand Up @@ -332,13 +326,11 @@
"""
validator = EmailValidator()
validator(attrs.get("email"))
user = self.get_user(attrs.get("email"))

if not user:
if user := self.get_user(attrs.get("email")):

Check warning on line 329 in drf_user/serializers.py

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

drf_user/serializers.py#L329

Unused variable 'user'

Check notice on line 329 in drf_user/serializers.py

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

drf_user/serializers.py#L329

local variable 'user' is assigned to but never used (F841)
return attrs
else:
raise NotFound(_("User with the provided email does not exist."))

return attrs


class ImageSerializer(serializers.ModelSerializer):
"""This serializer is for Image Upload API.
Expand All @@ -363,9 +355,7 @@
certain extra data in payload such as: email, mobile, name
"""

default_error_messages = {
"no_active_account": _("username or password is invalid.")
}
default_error_messages = {"no_active_account": _("username or password is invalid.")}

@classmethod
def get_token(cls, user):
Expand Down
Loading