Initial import

This commit is contained in:
Flatlogic Bot 2026-05-20 02:35:48 +00:00
commit f0c99fd4aa
172 changed files with 5415 additions and 0 deletions

View File

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,20 @@
from django.contrib import admin
from django.utils.html import format_html
from accounts.models import Profile
@admin.register(Profile)
class ProfileAdmin(admin.ModelAdmin):
list_display = ('user', 'is_seller', 'image_preview')
def image_preview(self, obj):
if obj.image:
return format_html(
'<img src="{}" width="40" height="40" '
'style="border-radius: 50%; object-fit: cover;" />',
obj.image.url
)
return "No Image"
image_preview.short_description = 'Image'

View File

@ -0,0 +1,5 @@
from django.apps import AppConfig
class AccountsConfig(AppConfig):
name = 'accounts'

View File

@ -0,0 +1,27 @@
from django import forms
from django.contrib.auth.models import User
from .models import Profile
class ProfileForm(forms.ModelForm):
first_name = forms.CharField(required=False, max_length=30)
last_name = forms.CharField(required=False, max_length=150)
email = forms.EmailField(required=False)
class Meta:
model = Profile
fields = ['image', 'bio']
def save(self, commit=True):
profile = super().save(commit=False)
# update related user fields
user = profile.user
user.first_name = self.cleaned_data.get('first_name', user.first_name)
user.last_name = self.cleaned_data.get('last_name', user.last_name)
email = self.cleaned_data.get('email')
if email:
user.email = email
if commit:
user.save()
profile.save()
return profile

View File

@ -0,0 +1,27 @@
# Generated by Django 6.0.5 on 2026-05-18 11:13
import accounts.models
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='Profile',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('bio', models.TextField(blank=True, null=True)),
('image', models.ImageField(blank=True, null=True, upload_to=accounts.models.user_profile_upload_path)),
('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='profile', to=settings.AUTH_USER_MODEL)),
],
),
]

View File

@ -0,0 +1,18 @@
# Generated by Django 6.0.5 on 2026-05-19
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('accounts', '0001_initial'),
]
operations = [
migrations.AddField(
model_name='profile',
name='is_seller',
field=models.BooleanField(default=False),
),
]

View File

@ -0,0 +1,31 @@
from django.db import models
from django.contrib.auth.models import User
from django.db.models.signals import post_save
from django.dispatch import receiver
def user_profile_upload_path(instance, filename):
# Files will be uploaded to MEDIA_ROOT/profile_pics/user_<id>/<filename>
return f'profile_pics/user_{instance.user.id}/{filename}'
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='profile')
bio = models.TextField(blank=True, null=True)
image = models.ImageField(upload_to=user_profile_upload_path, blank=True, null=True)
is_seller = models.BooleanField(default=False)
def __str__(self):
return f'Profile for {self.user.username}'
@receiver(post_save, sender=User)
def ensure_profile_exists(sender, instance, created, **kwargs):
if created:
Profile.objects.create(user=instance)
else:
# save existing profile to ensure any related signals run
try:
instance.profile.save()
except Exception:
pass

View File

@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

View File

@ -0,0 +1,10 @@
from django.urls import path
from . import views
urlpatterns = [
path('login/', views.login_view, name='login'),
path('register/', views.register_view, name='register'),
path('logout/', views.logout_view, name='logout'),
path('profile/', views.profile_view, name='profile'),
path('profile/edit/', views.edit_profile, name='edit_profile'),
]

132
myproject/accounts/views.py Normal file
View File

@ -0,0 +1,132 @@
from django.contrib import messages
from django.contrib.auth import authenticate, login, logout
from django.contrib.auth.decorators import login_required
from django.contrib.auth.models import User
from django.db.models import Sum
from django.shortcuts import redirect, render
from orders.models import Order
from products.models import WishlistItem
def login_view(request):
if request.user.is_authenticated:
return redirect('profile')
if request.method == 'POST':
username = request.POST.get('username', '').strip()
password = request.POST.get('password', '').strip()
if not username or not password:
return render(request, 'accounts/login.html', {'error': 'Username and password are required'})
user = authenticate(request, username=username, password=password)
if user:
login(request, user)
messages.success(request, f'Welcome back, {username}!')
return redirect('profile')
return render(request, 'accounts/login.html', {'error': 'Invalid username or password. Please check and try again.'})
return render(request, 'accounts/login.html')
def register_view(request):
if request.user.is_authenticated:
return redirect('profile')
if request.method == 'POST':
username = request.POST.get('username', '').strip()
password = request.POST.get('password', '').strip()
confirm_password = request.POST.get('confirm_password', '').strip()
email = request.POST.get('email', '').strip()
register_as_seller = request.POST.get('register_as_seller') == 'on'
if not username or not password or not confirm_password:
return render(request, 'accounts/register.html', {'error': 'All fields are required', 'username': username, 'email': email, 'register_as_seller': register_as_seller})
if len(username) < 3:
return render(request, 'accounts/register.html', {'error': 'Username must be at least 3 characters long', 'username': username, 'email': email, 'register_as_seller': register_as_seller})
if len(password) < 6:
return render(request, 'accounts/register.html', {'error': 'Password must be at least 6 characters long', 'username': username, 'email': email, 'register_as_seller': register_as_seller})
if password != confirm_password:
return render(request, 'accounts/register.html', {'error': 'Passwords do not match', 'username': username, 'email': email, 'register_as_seller': register_as_seller})
if User.objects.filter(username=username).exists():
return render(request, 'accounts/register.html', {'error': 'Username already exists', 'email': email, 'register_as_seller': register_as_seller})
if email and User.objects.filter(email=email).exists():
return render(request, 'accounts/register.html', {'error': 'Email already registered', 'username': username, 'register_as_seller': register_as_seller})
user = User.objects.create_user(username=username, password=password, email=email)
if register_as_seller:
user.profile.is_seller = True
user.profile.save(update_fields=['is_seller'])
messages.success(request, 'Account created successfully! Please log in.')
return redirect('login')
return render(
request,
'accounts/register.html',
{'register_as_seller': request.GET.get('seller') == '1'},
)
def logout_view(request):
logout(request)
return redirect('/')
@login_required
def profile_view(request):
user_orders = Order.objects.filter(user=request.user)
delivered_orders = user_orders.filter(status='Delivered')
recent_orders = user_orders.order_by('-created_at')[:5]
total_spent = delivered_orders.aggregate(total=Sum('total_price')).get('total') or 0
wishlist_count = WishlistItem.objects.filter(user=request.user).count()
return render(
request,
'accounts/profile.html',
{
'user': request.user,
'orders_count': user_orders.count(),
'delivered_count': delivered_orders.count(),
'pending_count': user_orders.exclude(status='Delivered').count(),
'wishlist_count': wishlist_count,
'total_spent': total_spent,
'recent_orders': recent_orders,
},
)
@login_required
def edit_profile(request):
from .forms import ProfileForm
profile = getattr(request.user, 'profile', None)
if profile is None:
# ensure profile exists
from .models import Profile
profile = Profile.objects.create(user=request.user)
if request.method == 'POST':
form = ProfileForm(request.POST, request.FILES, instance=profile)
# populate user fields into form for display/save
form.fields['first_name'].initial = request.user.first_name
form.fields['last_name'].initial = request.user.last_name
form.fields['email'].initial = request.user.email
if form.is_valid():
form.save()
messages.success(request, 'Profile updated successfully.')
return redirect('profile')
else:
messages.error(request, 'Please correct the errors below.')
else:
form = ProfileForm(instance=profile)
form.fields['first_name'].initial = request.user.first_name
form.fields['last_name'].initial = request.user.last_name
form.fields['email'].initial = request.user.email
return render(request, 'accounts/edit_profile.html', {'form': form, 'profile': profile})

View File

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

38
myproject/cart/admin.py Normal file
View File

@ -0,0 +1,38 @@
from django.contrib import admin
from .models import Cart, CartItem, Coupon
@admin.register(Coupon)
class CouponAdmin(admin.ModelAdmin):
list_display = ('code', 'discount_percent', 'min_purchase', 'active', 'valid_from', 'valid_to')
list_filter = ('active',)
search_fields = ('code',)
class CartItemInline(admin.TabularInline):
model = CartItem
extra = 0
autocomplete_fields = ('product',)
@admin.register(Cart)
class CartAdmin(admin.ModelAdmin):
list_display = ('user', 'created_at', 'updated_at', 'item_count', 'total_quantity')
search_fields = ('user__username', 'user__email')
inlines = (CartItemInline,)
def item_count(self, obj):
return obj.items.count()
item_count.short_description = 'Items'
def total_quantity(self, obj):
return sum(i.quantity for i in obj.items.all())
total_quantity.short_description = 'Total Qty'
@admin.register(CartItem)
class CartItemAdmin(admin.ModelAdmin):
list_display = ('cart', 'product', 'quantity')
search_fields = ('product__name', 'cart__user__username')
autocomplete_fields = ('product',)

5
myproject/cart/apps.py Normal file
View File

@ -0,0 +1,5 @@
from django.apps import AppConfig
class CartConfig(AppConfig):
name = 'cart'

View File

@ -0,0 +1,39 @@
# Generated by Django 6.0.5 on 2026-05-18 10:08
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
('products', '0004_product_updated_at'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='Cart',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='cart', 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)),
('cart', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='items', to='cart.cart')),
('product', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='products.product')),
],
options={
'unique_together': {('cart', 'product')},
},
),
]

View File

@ -0,0 +1,28 @@
# Generated by Django 6.0.5 on 2026-05-18 10:24
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('cart', '0001_initial'),
]
operations = [
migrations.CreateModel(
name='Coupon',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('code', models.CharField(max_length=32, unique=True)),
('discount_percent', models.DecimalField(decimal_places=2, max_digits=5)),
('min_purchase', models.DecimalField(decimal_places=2, default=0, max_digits=10)),
('active', models.BooleanField(default=True)),
('valid_from', models.DateTimeField(blank=True, null=True)),
('valid_to', models.DateTimeField(blank=True, null=True)),
],
options={
'ordering': ['code'],
},
),
]

View File

40
myproject/cart/models.py Normal file
View File

@ -0,0 +1,40 @@
from django.contrib.auth.models import User
from django.db import models
from products.models import Product
class Cart(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='cart')
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def __str__(self):
return f"{self.user.username}'s cart"
class CartItem(models.Model):
cart = models.ForeignKey(Cart, on_delete=models.CASCADE, related_name='items')
product = models.ForeignKey(Product, on_delete=models.CASCADE)
quantity = models.PositiveIntegerField(default=1)
class Meta:
unique_together = ('cart', 'product')
def __str__(self):
return f"{self.product.name} x {self.quantity}"
class Coupon(models.Model):
code = models.CharField(max_length=32, unique=True)
discount_percent = models.DecimalField(max_digits=5, decimal_places=2)
min_purchase = models.DecimalField(max_digits=10, decimal_places=2, default=0)
active = models.BooleanField(default=True)
valid_from = models.DateTimeField(null=True, blank=True)
valid_to = models.DateTimeField(null=True, blank=True)
class Meta:
ordering = ['code']
def __str__(self):
return self.code

3
myproject/cart/tests.py Normal file
View File

@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

13
myproject/cart/urls.py Normal file
View File

@ -0,0 +1,13 @@
from django.urls import path
from . import views
urlpatterns = [
path('', views.cart_view, name='cart'),
path('add/<int:id>/', views.add_to_cart, name='add_to_cart'),
path('buy-now/<int:id>/', views.buy_now, name='buy_now'),
path('update/<int:id>/', views.update_cart, name='update_cart'),
path('remove/<int:id>/', views.remove_from_cart, name='remove_from_cart'),
path('coupon/apply/', views.apply_coupon, name='apply_coupon'),
path('coupon/remove/', views.remove_coupon, name='remove_coupon'),
]

159
myproject/cart/views.py Normal file
View File

@ -0,0 +1,159 @@
from decimal import Decimal
from django.contrib import messages
from django.shortcuts import get_object_or_404, redirect, render
from django.utils import timezone
from products.models import Product
from .models import Coupon
def get_cart(request):
return request.session.get('cart', {})
def save_cart(request, cart):
request.session['cart'] = cart
request.session.modified = True
def _redirect_after_add(request):
next_url = request.GET.get('next', '').strip()
if next_url.startswith('/'):
return redirect(next_url)
return redirect('cart')
def add_to_cart(request, id):
product = get_object_or_404(Product, id=id)
cart = get_cart(request)
current_qty = cart.get(str(id), 0)
if current_qty + 1 > product.stock:
messages.warning(request, f'Only {product.stock} units of {product.name} are available.')
else:
cart[str(id)] = current_qty + 1
messages.success(request, f'Added {product.name} to your cart.')
save_cart(request, cart)
return _redirect_after_add(request)
def buy_now(request, id):
product = get_object_or_404(Product, id=id)
if product.stock <= 0:
messages.error(request, f'{product.name} is out of stock.')
return redirect('product_detail', product_id=product.id)
request.session['buy_now'] = {str(product.id): 1}
request.session.modified = True
messages.info(request, f'Proceeding to checkout for {product.name}.')
return redirect('checkout')
def update_cart(request, id):
if request.method == 'POST':
qty = int(request.POST.get('quantity', 1))
product = get_object_or_404(Product, id=id)
cart = get_cart(request)
if qty <= 0:
cart.pop(str(id), None)
messages.info(request, f'{product.name} removed from cart.')
elif qty > product.stock:
cart[str(id)] = product.stock
messages.warning(request, f'Quantity adjusted to {product.stock} for {product.name}.')
else:
cart[str(id)] = qty
messages.success(request, f'Cart updated for {product.name}.')
save_cart(request, cart)
return redirect('cart')
def remove_from_cart(request, id):
cart = get_cart(request)
if str(id) in cart:
del cart[str(id)]
messages.info(request, 'Item removed from your cart.')
save_cart(request, cart)
return redirect('cart')
def _get_coupon_for_cart(code, subtotal):
if not code:
return None, Decimal('0')
now = timezone.now()
coupon = Coupon.objects.filter(code=code, active=True).first()
if not coupon:
return None, Decimal('0')
if coupon.valid_from and now < coupon.valid_from:
return None, Decimal('0')
if coupon.valid_to and now > coupon.valid_to:
return None, Decimal('0')
if subtotal < coupon.min_purchase:
return coupon, Decimal('0')
discount = (subtotal * (coupon.discount_percent / Decimal('100'))).quantize(Decimal('0.01'))
return coupon, discount
def apply_coupon(request):
if request.method == 'POST':
code = request.POST.get('coupon_code', '').strip().upper()
request.session['coupon_code'] = code
messages.success(request, f'Coupon {code} applied.')
return redirect('cart')
def remove_coupon(request):
request.session.pop('coupon_code', None)
messages.info(request, 'Coupon removed.')
return redirect('cart')
def cart_view(request):
cart = get_cart(request)
products = []
subtotal = Decimal('0')
for id, qty in cart.items():
product = get_object_or_404(Product, id=int(id))
product.qty = qty
product.subtotal = product.display_price * qty
subtotal += product.subtotal
products.append(product)
shipping = Decimal('60') if products else Decimal('0')
coupon_code = request.session.get('coupon_code', '')
coupon, discount = _get_coupon_for_cart(coupon_code, subtotal)
if coupon_code and not coupon:
request.session.pop('coupon_code', None)
messages.warning(request, 'Coupon is invalid or expired.')
coupon_code = ''
if coupon and subtotal < coupon.min_purchase:
messages.warning(request, f'Coupon requires minimum purchase of Rs. {coupon.min_purchase}.')
discount = Decimal('0')
grand_total = subtotal - discount + shipping
return render(
request,
'cart/cart.html',
{
'products': products,
'subtotal': subtotal,
'total': subtotal,
'shipping': shipping,
'discount': discount,
'grand_total': grand_total,
'coupon_code': coupon_code,
'coupon': coupon,
},
)

View File

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

3
myproject/core/admin.py Normal file
View File

@ -0,0 +1,3 @@
from django.contrib import admin
# Register your models here.

5
myproject/core/apps.py Normal file
View File

@ -0,0 +1,5 @@
from django.apps import AppConfig
class CoreConfig(AppConfig):
name = 'core'

View File

@ -0,0 +1,159 @@
from orders.models import Order
from products.models import Product
CATEGORY_ICONS = {
'fashion': '👕',
'mobile': '📱',
'mobiles': '📱',
'electronics': '💻',
'home': '🛋️',
'appliances': '📺',
'toys': '🧸',
'beauty': '💄',
'books': '📚',
'sports': '🏀',
'groceries': '🛒',
'health': '💊',
'jewelry': '💍',
'home decor': '🕯️',
'stationery': '📝',
'pets': '🐾',
'accessories': '🧢',
'shoes': '👟',
'kids': '🧒',
'tools': '🛠️',
'office': '📎',
'kitchen': '🍳',
'travel': '✈️',
'automotive': '🚗',
'garden': '🌿',
'party': '🎉',
}
DEFAULT_CATEGORIES = [
{'value': 'fashion', 'name': 'Fashion', 'icon': '👕'},
{'value': 'mobiles', 'name': 'Mobiles', 'icon': '📱'},
{'value': 'electronics', 'name': 'Electronics', 'icon': '💻'},
{'value': 'home', 'name': 'Home', 'icon': '🛋️'},
{'value': 'appliances', 'name': 'Appliances', 'icon': '📺'},
{'value': 'toys', 'name': 'Toys', 'icon': '🧸'},
{'value': 'beauty', 'name': 'Beauty', 'icon': '💄'},
{'value': 'books', 'name': 'Books', 'icon': '📚'},
{'value': 'sports', 'name': 'Sports', 'icon': '🏀'},
{'value': 'groceries', 'name': 'Groceries', 'icon': '🛒'},
{'value': 'health', 'name': 'Health', 'icon': '💊'},
{'value': 'jewelry', 'name': 'Jewelry', 'icon': '💍'},
{'value': 'pets', 'name': 'Pets', 'icon': '🐾'},
{'value': 'shoes', 'name': 'Shoes', 'icon': '👟'},
]
EN_LABELS = {
'home': 'Home',
'products': 'Products',
'all_products': 'All Products',
'featured': 'Featured',
'categories': 'Categories',
'cart': 'Cart',
'wishlist': 'Wishlist',
'about': 'About',
'support': 'Help & Support',
'orders': 'Orders',
'my_profile': 'My Profile',
'my_orders': 'My Orders',
'login': 'Login',
'register': 'Register',
'logout': 'Logout',
'shop_now': 'Shop Now',
'learn_more': 'Learn More',
'choose_language': 'Language',
'english': 'English',
'nepali': 'Nepali',
'theme_toggle': 'Light Mode',
'for_you': 'For You',
}
NE_LABELS = {
'home': 'Griha',
'products': 'Utpadan',
'all_products': 'Sabai Utpadan',
'featured': 'Bisesh',
'categories': 'Shreni',
'cart': 'Kart',
'wishlist': 'Icchha Suchi',
'about': 'Hamro Barema',
'support': 'Sahayog',
'orders': 'Orderharu',
'my_profile': 'Mero Profile',
'my_orders': 'Mero Order',
'login': 'Login',
'register': 'Darta',
'logout': 'Logout',
'shop_now': 'Ahile Kinmel',
'learn_more': 'Thap Jankari',
'choose_language': 'Bhasha',
'for_you': 'Tapaiko Lagi',
'english': 'English',
'nepali': 'Nepali',
'theme_toggle': 'Light Mode',
}
def cart_summary(request):
cart = request.session.get('cart', {})
total_items = sum(cart.values()) if isinstance(cart, dict) else 0
order_count = 0
if request.user.is_authenticated:
order_count = Order.objects.filter(user=request.user).count()
return {
'cart_count': total_items,
'order_count': order_count,
}
def language_context(request):
site_language = request.session.get('site_language', 'en')
if site_language not in {'en', 'ne'}:
site_language = 'en'
ui = NE_LABELS if site_language == 'ne' else EN_LABELS
raw_categories = Product.objects.values_list('category', flat=True).exclude(category='').distinct()
category_items = []
seen = set()
for raw_value in raw_categories:
cleaned = (raw_value or '').strip()
if not cleaned:
continue
key = cleaned.lower()
if key in seen:
continue
seen.add(key)
category_items.append({
'value': cleaned,
'name': cleaned.title(),
'icon': CATEGORY_ICONS.get(key, '🛍️'),
})
for default_category in DEFAULT_CATEGORIES:
if default_category['value'].lower() not in seen:
category_items.append(default_category)
seen.add(default_category['value'].lower())
delivery_location = request.session.get('delivery_location', '').strip()
if not delivery_location and request.user.is_authenticated:
last_address = Order.objects.filter(user=request.user).exclude(address='').order_by('-created_at').values_list('address', flat=True).first()
if last_address:
delivery_location = last_address.split('\n', 1)[0].strip()
return {
'site_language': site_language,
'ui': ui,
'language_options': [
{'code': 'en', 'label': ui['english']},
{'code': 'ne', 'label': ui['nepali']},
],
'categories': category_items,
'delivery_location': delivery_location,
}

View File

3
myproject/core/models.py Normal file
View File

@ -0,0 +1,3 @@
from django.db import models
# Create your models here.

3
myproject/core/tests.py Normal file
View File

@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

12
myproject/core/urls.py Normal file
View File

@ -0,0 +1,12 @@
from django.urls import path
from . import views
urlpatterns = [
path('', views.home, name='home'),
path('home/', views.home, name='home'),
path('about/', views.about, name='about'),
path('support/', views.support, name='support'),
path('set-language/', views.set_language_preference, name='set_language_preference'),
path('landing/', views.landing, name='landing'),
path('settings/', views.settings_view, name='settings'),
]

139
myproject/core/views.py Normal file
View File

@ -0,0 +1,139 @@
from decimal import Decimal
from django.db.models import Avg
from django.shortcuts import redirect, render
from products.models import Product
def _format_discount_offer(product):
if not product or product.price == 0 or product.discount_price is None:
return None
savings = product.price - product.discount_price
percent = int((savings / product.price * Decimal('100')).quantize(Decimal('1')))
return {
'title': f'{percent}% off',
'description': f'{product.name} now available for Rs. {product.discount_price:.0f}',
'note': f'Save Rs. {savings:.0f} on {product.category}',
}
def home(request):
trending_products = Product.objects.filter(featured=True).order_by('-created_at')[:4]
if not trending_products.exists():
trending_products = Product.objects.order_by('-created_at')[:4]
aggregates = Product.objects.aggregate(avg_rating=Avg('rating'))
discounted_products = list(Product.objects.filter(discount_price__isnull=False).order_by('discount_price')[:6])
best_discount = None
if discounted_products:
best_discount = max(
discounted_products,
key=lambda p: ((p.price - p.discount_price) / p.price) if p.price else Decimal('0'),
)
featured_count = Product.objects.filter(featured=True).count()
categories_count = Product.objects.values('category').exclude(category='').distinct().count()
special_deals = []
offer_deal = _format_discount_offer(best_discount)
if offer_deal:
special_deals.append(offer_deal)
special_deals.append({
'title': f'{len(discounted_products)} offers live',
'description': 'Discounts available across top Nepali categories.',
'note': 'Browse products with extra savings today.',
})
if featured_count:
special_deals.append({
'title': 'Featured Seller Picks',
'description': f'{featured_count} curated products from trusted sellers.',
'note': 'Popular with Nepali shoppers.',
})
else:
special_deals.append({
'title': 'Daily Deals',
'description': 'Fresh discount offers updated every day.',
'note': 'Check back for more savings.',
})
advertised_products = sorted(
discounted_products,
key=lambda p: ((p.price - p.discount_price) / p.price if p.price else Decimal('0')),
reverse=True,
)[:4]
if not advertised_products:
advertised_products = list(trending_products[:4])
for index, product in enumerate(advertised_products):
savings = (product.price - product.discount_price) if product.discount_price else Decimal('0')
percent = int((savings / product.price * Decimal('100')).quantize(Decimal('1'))) if product.price else 0
if percent >= 50:
label = 'Mega Deal'
elif percent >= 30:
label = 'Hot Deal'
elif percent >= 15:
label = 'Limited Time'
else:
label = 'Special Offer'
if product.featured and percent >= 10:
label = 'Featured Deal'
product.deal_label = label
return render(
request,
'core/home.html',
{
'trending_products': trending_products,
'total_products': Product.objects.count(),
'featured_products': featured_count,
'categories_count': categories_count,
'avg_rating': aggregates.get('avg_rating') or 0,
'special_deals': special_deals,
'advertised_products': advertised_products,
},
)
def about(request):
return render(request, 'core/about.html')
def support(request):
return render(request, 'core/support.html')
def set_language_preference(request):
if request.method == 'POST':
language = request.POST.get('language', 'en').strip().lower()
next_url = request.POST.get('next', '/').strip()
else:
language = request.GET.get('language', 'en').strip().lower()
next_url = request.GET.get('next', '/').strip()
if language not in {'en', 'ne'}:
language = 'en'
request.session['site_language'] = language
if next_url.startswith('/'):
return redirect(next_url)
return redirect('home')
def landing(request):
return render(request, 'core/landing.html')
def settings_view(request):
if request.method == 'POST':
delivery_location = request.POST.get('delivery_location', '').strip()
if delivery_location:
request.session['delivery_location'] = delivery_location
else:
request.session.pop('delivery_location', None)
return redirect('settings')
return render(request, 'core/settings.html')

BIN
myproject/db.sqlite3 Normal file

Binary file not shown.

22
myproject/manage.py Normal file
View File

@ -0,0 +1,22 @@
#!/usr/bin/env python
"""Django's command-line utility for administrative tasks."""
import os
import sys
def main():
"""Run administrative tasks."""
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'myproject.settings')
try:
from django.core.management import execute_from_command_line
except ImportError as exc:
raise ImportError(
"Couldn't import Django. Are you sure it's installed and "
"available on your PYTHONPATH environment variable? Did you "
"forget to activate a virtual environment?"
) from exc
execute_from_command_line(sys.argv)
if __name__ == '__main__':
main()

Binary file not shown.

After

Width:  |  Height:  |  Size: 224 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 112 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 112 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 118 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.0 KiB

View File

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,16 @@
"""
ASGI config for myproject project.
It exposes the ASGI callable as a module-level variable named ``application``.
For more information on this file, see
https://docs.djangoproject.com/en/6.0/howto/deployment/asgi/
"""
import os
from django.core.asgi import get_asgi_application
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'myproject.settings')
application = get_asgi_application()

View File

@ -0,0 +1,136 @@
"""
Django settings for myproject project.
Generated by 'django-admin startproject' using Django 6.0.5.
For more information on this file, see
https://docs.djangoproject.com/en/6.0/topics/settings/
For the full list of settings and their values, see
https://docs.djangoproject.com/en/6.0/ref/settings/
"""
from pathlib import Path
# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/6.0/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'django-insecure-%)ie75*7@1xl91^h6^$d!npm$=cf7@oqcc9b&jqxqc8t!@9uj1'
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
ALLOWED_HOSTS = ['localhost', '127.0.0.1', '[::1]', 'testserver']
# Application definition
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'core', # core app for home, about, contact
'accounts', # accounts app for user profiles
'cart', # cart app for shopping cart functionality
'orders', # orders app for order management
'products', # products app for product management
]
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
ROOT_URLCONF = 'myproject.urls'
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [BASE_DIR / 'tempelates'], # ✅ FIX HERE
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
'core.context_processors.cart_summary',
'core.context_processors.language_context',
],
},
},
]
WSGI_APPLICATION = 'myproject.wsgi.application'
# Database
# https://docs.djangoproject.com/en/6.0/ref/settings/#databases
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'db.sqlite3',
}
}
# Password validation
# https://docs.djangoproject.com/en/6.0/ref/settings/#auth-password-validators
AUTH_PASSWORD_VALIDATORS = [
{
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
},
]
MEDIA_URL = '/media/'
MEDIA_ROOT = BASE_DIR / 'media'
# Internationalization
# https://docs.djangoproject.com/en/6.0/topics/i18n/
LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'UTC'
USE_I18N = True
USE_TZ = True
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/6.0/howto/static-files/
STATIC_URL = 'static/'
STATICFILES_DIRS = [
BASE_DIR / "static",
]
# Authentication Settings
LOGIN_URL = 'login'
LOGIN_REDIRECT_URL = 'profile'
LOGOUT_REDIRECT_URL = 'home'

View File

@ -0,0 +1,20 @@
from django.contrib import admin
from django.urls import path, include
from django.conf import settings
from django.conf.urls.static import static
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('core.urls')), # home, about, contact
# profile system
path('accounts/', include('accounts.urls')),
path('products/', include('products.urls')),
path('cart/', include('cart.urls')),
path('orders/', include('orders.urls')),
]
if settings.DEBUG:
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
urlpatterns += static(settings.STATIC_URL, document_root=settings.STATICFILES_DIRS[0])

View File

@ -0,0 +1,16 @@
"""
WSGI config for myproject project.
It exposes the WSGI callable as a module-level variable named ``application``.
For more information on this file, see
https://docs.djangoproject.com/en/6.0/howto/deployment/wsgi/
"""
import os
from django.core.wsgi import get_wsgi_application
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'myproject.settings')
application = get_wsgi_application()

View File

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

19
myproject/orders/admin.py Normal file
View File

@ -0,0 +1,19 @@
from django.contrib import admin
from .models import Order, OrderItem
class OrderItemInline(admin.TabularInline):
model = OrderItem
extra = 0
@admin.register(Order)
class OrderAdmin(admin.ModelAdmin):
list_display = ('id', 'user', 'status', 'payment_method', 'total_price', 'created_at')
list_filter = ('status', 'payment_method')
search_fields = ('user__username', 'id')
inlines = [OrderItemInline]
admin.site.register(OrderItem)

5
myproject/orders/apps.py Normal file
View File

@ -0,0 +1,5 @@
from django.apps import AppConfig
class OrdersConfig(AppConfig):
name = 'orders'

View File

@ -0,0 +1,37 @@
# Generated by Django 6.0.5 on 2026-05-15 02:32
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
('products', '0001_initial'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
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='OrderItem',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('quantity', models.IntegerField()),
('price', models.DecimalField(decimal_places=2, max_digits=10)),
('order', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='items', to='orders.order')),
('product', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='products.product')),
],
),
]

View File

@ -0,0 +1,28 @@
# Generated by Django 6.0.5 on 2026-05-15 10:27
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('orders', '0001_initial'),
]
operations = [
migrations.AddField(
model_name='order',
name='payment_method',
field=models.CharField(default='COD', max_length=100),
),
migrations.AddField(
model_name='order',
name='status',
field=models.CharField(choices=[('Pending', 'Pending'), ('Paid', 'Paid'), ('Shipped', 'Shipped'), ('Delivered', 'Delivered')], default='Pending', max_length=20),
),
migrations.AlterField(
model_name='orderitem',
name='quantity',
field=models.PositiveIntegerField(default=1),
),
]

View File

@ -0,0 +1,17 @@
# Generated by Django 6.0.5 on 2026-05-18 10:08
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('orders', '0002_order_payment_method_order_status_and_more'),
]
operations = [
migrations.AlterModelOptions(
name='order',
options={'ordering': ['-created_at']},
),
]

View File

@ -0,0 +1,28 @@
# Generated by Django 6.0.5 on 2026-05-19
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('orders', '0003_alter_order_options'),
]
operations = [
migrations.AddField(
model_name='order',
name='address',
field=models.TextField(blank=True, default=''),
),
migrations.AddField(
model_name='order',
name='full_name',
field=models.CharField(blank=True, default='', max_length=150),
),
migrations.AddField(
model_name='order',
name='phone',
field=models.CharField(blank=True, default='', max_length=30),
),
]

View File

Some files were not shown because too many files have changed in this diff Show More