Skip to content

Commit

Permalink
Merge branch 'Priyansh61-ticket' into develop
Browse files Browse the repository at this point in the history
  • Loading branch information
arminpatel committed Nov 18, 2023
2 parents 9990497 + d9913ab commit 8db6e74
Show file tree
Hide file tree
Showing 37 changed files with 710 additions and 32 deletions.
Empty file added backend/accounts/__init__.py
Empty file.
48 changes: 48 additions & 0 deletions backend/accounts/admin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
from django.contrib import admin
from django import forms
from django.contrib.auth.admin import UserAdmin
from .models import Account


class AccountAddForm(forms.ModelForm):
class Meta:
model = Account
fields = ('email',
'first_name',
'last_name',
'password',
'is_active',
'is_staff')


class AccountAdmin(UserAdmin):
add_form = AccountAddForm
list_display = ('email',
'first_name',
'last_name',
'last_login',
'date_joined',
'is_active',
'is_staff')
add_fieldsets = (
(None, {
'classes': ('wide',),
'fields': ('email',
'first_name',
'last_name',
'password',
'is_active',
'is_staff')
}),
)

list_display_links = ('email', 'first_name', 'last_name')
readonly_fields = ('last_login', 'date_joined')
ordering = ('-date_joined',)

filter_horizontal = ()
list_filter = ()
fieldsets = ()


admin.site.register(Account, AccountAdmin)
6 changes: 6 additions & 0 deletions backend/accounts/apps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from django.apps import AppConfig


class AccountsConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'accounts'
69 changes: 69 additions & 0 deletions backend/accounts/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
from django.db import models
from django.core.validators import RegexValidator
from django.contrib.auth.models import AbstractBaseUser, BaseUserManager


class MyAccountManager(BaseUserManager):
def create_user(self, first_name, last_name, email, password=None):
if not email:
raise ValueError("Users must have an email address")

user = self.model(
email=self.normalize_email(email),
first_name=first_name,
last_name=last_name
)

user.set_password(password)
user.save(using=self._db)

return user

def create_superuser(self, first_name, last_name, email, password):
user = self.create_user(
email=self.normalize_email(email),
first_name=first_name,
last_name=last_name,
password=password
)

user.is_admin = True
user.is_active = True
user.is_staff = True
user.is_superadmin = True

user.save(using=self._db)

return user


class Account(AbstractBaseUser):
phone_regex = RegexValidator(regex=r'^(\+91)?\d{10}$', message="Invalid phone number")

first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
email = models.EmailField(unique=True)
phone_number = models.CharField(max_length=13, validators=[phone_regex], blank=True)
address = models.CharField(max_length=128, blank=True)

# required
date_joined = models.DateTimeField(auto_now_add=True)
last_login = models.DateTimeField(auto_now=True)
is_admin = models.BooleanField(default=False)
is_staff = models.BooleanField(default=False)
is_superadmin = models.BooleanField(default=False)
is_active = models.BooleanField(default=True)

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

objects = MyAccountManager()

def __str__(self):
return str(self.email)

def has_perm(self, perm, obj=None):
return self.is_admin

def has_module_perms(self, add_label):
return True
33 changes: 33 additions & 0 deletions backend/accounts/serializers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
from rest_framework import serializers
from .models import Account


class AccountSerializer(serializers.ModelSerializer):
class Meta:
model = Account
fields = ['id',
'first_name',
'last_name',
'email',
'password',
'address',
'phone_number',
'date_joined',
'last_login']
extra_kwargs = {
'password': {'write_only': True},
'date_joined': {'read_only': True},
'last_login': {'read_only': True},
}

def create(self, validated_data):
password = validated_data.pop('password', None)

if not password:
raise serializers.ValidationError('Password is required')

account = self.Meta.model(**validated_data)
account.set_password(password)
account.save()

return account
122 changes: 122 additions & 0 deletions backend/accounts/tests.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
import pytest
from rest_framework.test import APIClient
from rest_framework import status
from .models import Account

account_data = {
"email": "test@email.com",
"password": "testpassword",
"first_name": "test",
"last_name": "account",
"phone_number": "1234567890",
"address": "test address",
}


@pytest.fixture
def api_client():
account = Account.objects.create(**account_data)
client = APIClient()
client.force_authenticate(user=account)
return client


@pytest.mark.django_db
def testCreateAccountView_validAccountDetails_accountCreationSuccesful():
# Arrange
client = APIClient()

# Act
response = client.post('/api/accounts/', account_data, format='json')

print(response.data)

# Assert
assert response.status_code == status.HTTP_201_CREATED
assert Account.objects.count() == 1
assert response.data['email'] == account_data['email']

# check that password is not returned and the stored password is hashed
assert 'password' not in response.data
assert Account.objects.get().password != account_data['password']


@pytest.mark.django_db
@pytest.mark.parametrize('field', ['email', 'password', 'first_name', 'last_name'])
def testCreateAccountView_missingRequiredDetails_returnsBadRequest(field):
# Arrange
client = APIClient()
data = account_data.copy()
data.pop(field)

# Act
response = client.post('/api/accounts/', data, format='json')

# Assert
assert response.status_code == status.HTTP_400_BAD_REQUEST


@pytest.mark.django_db
@pytest.mark.parametrize('new_email', ['test@email.com', 'testemail'])
def testCreateAccountView_invalidOrDuplicateEmail_returnsBadRequest(new_email):
# Arrange
client = APIClient()
client.post('/api/accounts/', account_data, format='json')
data = account_data.copy()
data['email'] = new_email

# Act
response = client.post('/api/accounts/', data, format='json')

# Assert
assert response.status_code == status.HTTP_400_BAD_REQUEST


@pytest.mark.django_db
def testRetrieveLoggedInAccountView_userLoggedIn_returnsAccountDetails(api_client):
# Act
response = api_client.get('/api/accounts/me/', format='json')

# Assert
assert response.status_code == status.HTTP_200_OK
assert response.data['email'] == account_data['email']


@pytest.mark.django_db
def testRetrieveLoggedInAccountView_userNotLoggedIn_returnsForbidden():
# Arrange
client = APIClient()

# Act
response = client.get('/api/accounts/me/', format='json')

# Assert
assert response.status_code == status.HTTP_403_FORBIDDEN


@pytest.mark.django_db
@pytest.mark.parametrize('field', ['email', 'first_name', 'last_name', 'address'])
def testUpdateLogInAccountView_userLoggedIn_updateSuccesful(api_client, field):
# Arrange
data = {
field: f'new{account_data[field]}'
}

# Act
response = api_client.put('/api/accounts/me/', data, format='json')

# Assert
assert response.status_code == status.HTTP_200_OK
assert response.data[field] == data[field]


@pytest.mark.django_db
def testUpdateLogInAccountView_userNotLoggedIn_returnsForbidden():
# Arrange
client = APIClient()

# Act
response = client.put('/api/accounts/me/', {}, format='json')

# Assert
assert response.status_code == status.HTTP_403_FORBIDDEN
7 changes: 7 additions & 0 deletions backend/accounts/urls.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
from django.urls import path
from .views import CreateAccountView, RetrieveUpdateLoggedInAccountView

urlpatterns = [
path('', CreateAccountView.as_view()),
path('me/', RetrieveUpdateLoggedInAccountView.as_view()),
]
29 changes: 29 additions & 0 deletions backend/accounts/views.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
from rest_framework.generics import CreateAPIView
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.permissions import IsAuthenticated
from .serializers import AccountSerializer
from .models import Account


class CreateAccountView(CreateAPIView):
""" View to create accounts """
queryset = Account.objects.all()
serializer_class = AccountSerializer


class RetrieveUpdateLoggedInAccountView(APIView):
""" View to retrieve and change logged in account """
permission_classes = (IsAuthenticated,)

def get(self, request):
serializer = AccountSerializer(request.user)
return Response(serializer.data)

def put(self, request):
# let update be partial
serializer = AccountSerializer(request.user, data=request.data, partial=True)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
return Response(serializer.errors)
8 changes: 8 additions & 0 deletions backend/auth/urls.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from django.urls import path
from rest_framework_simplejwt.views import TokenObtainPairView, TokenRefreshView


urlpatterns = [
path('', TokenObtainPairView.as_view(), name='token_obtain_pair'),
path('refresh/', TokenRefreshView.as_view(), name='token_refresh'),
]
30 changes: 16 additions & 14 deletions backend/build.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,22 @@
import os
import sys
import subprocess
import random


def in_venv():
return sys.prefix != sys.base_prefix


# Copy content from .env.template to .env
try :
try:

# Generate a random secret key
django_secret = ''.join (random.SystemRandom ().choice ('abcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*(-_=+)') for i in range (50))
django_secret = ''.join(random.SystemRandom().choice(
'abcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*(-_=+)') for i in range(50))

with open('.env', 'w') as env :
env.write ('\n')
env.write ('DJANGO_SECRET=' + "'" + django_secret + "'")
with open('.env', 'w') as env:
env.write('\n')
env.write('DJANGO_SECRET=' + "'" + django_secret + "'")

# Check for venv
if in_venv():
Expand All @@ -24,30 +26,30 @@ def in_venv():
exit()

# Print the python version
subprocess.run (['python3', '--version'])
subprocess.run(['python3', '--version'])

# Install dependencies
print('Installing dependencies...')
subprocess.run (['pip3', 'install', '-r', 'requirements.txt'])
subprocess.run(['pip3', 'install', '-r', 'requirements.txt'])
print("Dependencies installed!")

# Make Migrations
print('Making migrations...')
subprocess.run (['python3', 'manage.py', 'makemigrations'])
subprocess.run(['python3', 'manage.py', 'makemigrations', 'accounts'])
subprocess.run(['python3', 'manage.py', 'makemigrations'])
print("Migrations made!")

# Migrate the database
print('Migrating the database...')
subprocess.run (['python3', 'manage.py', 'migrate'])
subprocess.run(['python3', 'manage.py', 'migrate'])
print("Database migrated!")

# Run the server

print('Running the server...')
subprocess.run (['python3', 'manage.py', 'runserver'])
subprocess.run(['python3', 'manage.py', 'runserver'])

except KeyboardInterrupt :
except KeyboardInterrupt:
print("Process interrupted!")

print("Build Stopped!")

print("Build Stopped!")
Loading

0 comments on commit 8db6e74

Please sign in to comment.