Skip to content

Commit

Permalink
Implement user authentication, dashboard, and profile views. Add test…
Browse files Browse the repository at this point in the history
…s and improve project structure.
  • Loading branch information
Robocoders committed Nov 24, 2024
1 parent 8cfada9 commit 7ca841e
Show file tree
Hide file tree
Showing 17 changed files with 687 additions and 657 deletions.
103 changes: 20 additions & 83 deletions dropship_project/admin.py
Original file line number Diff line number Diff line change
@@ -1,94 +1,31 @@
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from django.db.models import Count, F
from django.utils import timezone
from django.urls import path
from django.shortcuts import render
from .models import CustomUser, Product, Order, CartItem
from .admin_filters import DateRangeFilter
from .admin_widgets import QuickLinksWidget, RecentOrdersWidget, SalesStatisticsWidget
import json
from .models import CustomUser, Order, CartItem, Product

class CustomAdminSite(admin.AdminSite):
site_header = 'Dropship Admin'
site_title = 'Dropship Admin Portal'
index_title = 'Welcome to Dropship Admin Portal'
login_template = 'admin/login.html'
index_template = 'admin/dashboard.html'

def get_urls(self):
urls = super().get_urls()
custom_urls = [
path('order-stats/', self.admin_view(self.order_stats_view), name='order_stats'),
]
return custom_urls + urls

def index(self, request, extra_context=None):
extra_context = extra_context or {}
extra_context['quick_links'] = QuickLinksWidget()
extra_context['recent_orders'] = RecentOrdersWidget()
extra_context['sales_statistics'] = SalesStatisticsWidget()
return super().index(request, extra_context)

def order_stats_view(self, request):
end_date = timezone.now().date()
start_date = end_date - timezone.timedelta(days=6)
order_data = Order.objects.filter(created_at__date__range=[start_date, end_date]) .values('created_at__date') .annotate(count=Count('id')) .order_by('created_at__date')

labels = [(start_date + timezone.timedelta(days=i)).strftime('%Y-%m-%d') for i in range(7)]
data = [0] * 7

for item in order_data:
index = (item['created_at__date'] - start_date).days
data[index] = item['count']

context = {
'labels': json.dumps(labels),
'data': json.dumps(data),
}
return render(request, 'admin/order_stats.html', context)

admin_site = CustomAdminSite(name='customadmin')

@admin.register(CustomUser, site=admin_site)
class CustomUserAdmin(UserAdmin):
list_display = ('username', 'email', 'is_staff', 'is_active')
list_filter = ('is_staff', 'is_active')
model = CustomUser
list_display = ['email', 'username', 'is_staff', 'is_active']
list_filter = ['is_staff', 'is_active']
fieldsets = UserAdmin.fieldsets + (
('Additional Info', {'fields': ('phone_number', 'address')}),
('Additional Info', {'fields': ('phone_number', 'address', 'date_of_birth')}),
)
add_fieldsets = UserAdmin.add_fieldsets + (
('Additional Info', {'fields': ('phone_number', 'address', 'date_of_birth')}),
)

@admin.register(Product, site=admin_site)
class ProductAdmin(admin.ModelAdmin):
list_display = ('name', 'price', 'stock', 'category')
list_filter = ('category',)
search_fields = ('name', 'description')
actions = ['increase_stock', 'decrease_stock']

def increase_stock(self, request, queryset):
updated = queryset.update(stock=F('stock') + 10)
self.message_user(request, f'Successfully increased stock for {updated} products.')
increase_stock.short_description = "Increase stock by 10"

def decrease_stock(self, request, queryset):
updated = queryset.update(stock=F('stock') - 10)
self.message_user(request, f'Successfully decreased stock for {updated} products.')
decrease_stock.short_description = "Decrease stock by 10"

@admin.register(Order, site=admin_site)
class OrderAdmin(admin.ModelAdmin):
list_display = ('id', 'user', 'total_price', 'status', 'created_at')
list_filter = ('status', DateRangeFilter)
search_fields = ('user__username', 'user__email')
actions = ['mark_as_shipped']
list_display = ['id', 'user', 'created_at', 'total_price']
list_filter = ['created_at']

def mark_as_shipped(self, request, queryset):
updated = queryset.update(status='shipped', shipped_at=timezone.now())
self.message_user(request, f'{updated} orders were successfully marked as shipped.')
mark_as_shipped.short_description = "Mark selected orders as shipped"

@admin.register(CartItem, site=admin_site)
class CartItemAdmin(admin.ModelAdmin):
list_display = ('user', 'product', 'quantity')
list_filter = ('user', 'product')
list_display = ['user', 'product', 'quantity']

class ProductAdmin(admin.ModelAdmin):
list_display = ['name', 'price', 'stock']
list_filter = ['price', 'stock']

admin.site.register(CustomUser, CustomUserAdmin)
admin.site.register(Order, OrderAdmin)
admin.site.register(CartItem, CartItemAdmin)
admin.site.register(Product, ProductAdmin)

13 changes: 13 additions & 0 deletions dropship_project/management/commands/create_admin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
from django.core.management.base import BaseCommand
from django.contrib.auth import get_user_model

class Command(BaseCommand):
help = 'Creates an admin user non-interactively if it doesn\'t exist'

def handle(self, *args, **options):
User = get_user_model()
if not User.objects.filter(username='admin').exists():
User.objects.create_superuser('admin', '[email protected]', 'adminpassword')
self.stdout.write(self.style.SUCCESS('Admin user has been created'))
else:
self.stdout.write(self.style.SUCCESS('Admin user already exists'))
76 changes: 76 additions & 0 deletions dropship_project/migrations/0001_initial.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
# Generated by Django 3.2.10 on 2024-11-24 14:00

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


class Migration(migrations.Migration):

initial = True

dependencies = [
('auth', '0012_alter_user_first_name_max_length'),
]

operations = [
migrations.CreateModel(
name='CustomUser',
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')),
('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')),
('phone_number', models.CharField(blank=True, max_length=15, null=True)),
('address', models.TextField(blank=True, null=True)),
('date_of_birth', models.DateField(blank=True, null=True)),
('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='customuser_set', related_query_name='customuser', to='auth.Group', verbose_name='groups')),
('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='customuser_set', related_query_name='customuser', 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='Product',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=200)),
('price', models.DecimalField(decimal_places=2, max_digits=10)),
('stock', models.PositiveIntegerField()),
],
),
migrations.CreateModel(
name='Order',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('created_at', models.DateTimeField(auto_now_add=True)),
('total_price', models.DecimalField(decimal_places=2, max_digits=10)),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
),
migrations.CreateModel(
name='CartItem',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('quantity', models.PositiveIntegerField(default=1)),
('product', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='dropship_project.product')),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
),
]
Empty file.
60 changes: 22 additions & 38 deletions dropship_project/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,54 +6,38 @@ class CustomUser(AbstractUser):
address = models.TextField(blank=True, null=True)
date_of_birth = models.DateField(blank=True, null=True)

def __str__(self):
return self.email

class Category(models.Model):
name = models.CharField(max_length=100)
slug = models.SlugField(unique=True)

def __str__(self):
return self.name

class Product(models.Model):
name = models.CharField(max_length=200)
description = models.TextField()
price = models.DecimalField(max_digits=10, decimal_places=2)
category = models.ForeignKey(Category, on_delete=models.CASCADE)
stock = models.PositiveIntegerField()
groups = models.ManyToManyField(
'auth.Group',
verbose_name='groups',
blank=True,
help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.',
related_name="customuser_set",
related_query_name="customuser",
)
user_permissions = models.ManyToManyField(
'auth.Permission',
verbose_name='user permissions',
blank=True,
help_text='Specific permissions for this user.',
related_name="customuser_set",
related_query_name="customuser",
)

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

class Order(models.Model):
user = models.ForeignKey(CustomUser, on_delete=models.CASCADE)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
total_price = models.DecimalField(max_digits=10, decimal_places=2)
status = models.CharField(max_length=20, default='pending')

def __str__(self):
return f"Order {self.id} by {self.user.username}"

class CartItem(models.Model):
user = models.ForeignKey(CustomUser, on_delete=models.CASCADE)
product = models.ForeignKey(Product, on_delete=models.CASCADE)
product = models.ForeignKey('Product', on_delete=models.CASCADE)
quantity = models.PositiveIntegerField(default=1)

def __str__(self):
return f"{self.quantity} x {self.product.name}"


from django.utils.crypto import get_random_string

class EmailVerificationToken(models.Model):
user = models.OneToOneField(CustomUser, on_delete=models.CASCADE)
token = models.CharField(max_length=64, unique=True)
created_at = models.DateTimeField(auto_now_add=True)

@classmethod
def create_token(cls, user):
token = get_random_string(64)
return cls.objects.create(user=user, token=token)
class Product(models.Model):
name = models.CharField(max_length=200)
price = models.DecimalField(max_digits=10, decimal_places=2)
stock = models.PositiveIntegerField()

107 changes: 10 additions & 97 deletions dropship_project/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,97 +1,10 @@
aiohappyeyeballs
aiohttp
aiosignal
annotated-types
anyio
argon2-cffi
argon2-cffi-bindings
arrow
asgiref
astroid
asttokens
async-lru
async-timeout
attrs
babel
bash_kernel
beautifulsoup4
bleach
blinker
certifi
cffi
charset-normalizer
click
comm
cryptography
debugpy
decorator
defusedxml
dill
diskcache
distro
Django
django-allauth
django-axes
django-formtools
django-oauth-toolkit
django-otp
django-password-validation
django-phonenumber-field
django-recaptcha
django-two-factor-auth
djangorestframework
exceptiongroup
executing
fastjsonschema
filelock
flake8
Flask
fqdn
frozenlist
fsspec
gitdb
GitPython
grep-ast
h11
httpcore
httpx
huggingface-hub
idna
importlib_metadata
ipykernel
ipython
isoduration
isort
itsdangerous
jedi
Jinja2
jiter
json5
jsonpointer
jsonschema
jsonschema-specifications
jupyter-events
jupyter-kernel-gateway
jupyter-lsp
jupyter_client
jupyter_core
jupyter_server
jupyter_server_terminals
jupyterlab
jupyterlab_pygments
jupyterlab_server
jwcrypto
litellm
lxml
MarkupSafe
matplotlib-inline
mccabe
meson
mistune
multidict
nbclient
nbconvert
nbformat
nest-asyncio
networkx
notebook
Django==3.2.10
djangorestframework==3.12.4
django-oauth-toolkit==1.5.0
django-allauth==0.45.0
django-axes==5.13.0
django-recaptcha==2.0.6
django-otp==1.0.2
django-two-factor-auth==1.13.0
django-sendgrid-v5==1.1.1
django-crispy-forms==1.14.0
Loading

0 comments on commit 7ca841e

Please sign in to comment.