370 lines
13 KiB
Python
370 lines
13 KiB
Python
import datetime
|
|
import json
|
|
from django.shortcuts import render, redirect, get_object_or_404
|
|
from django.db.models import Count, Q
|
|
from django.views.decorators.http import require_POST
|
|
from django.http import JsonResponse
|
|
from django.contrib.auth.models import User
|
|
from .models import Category, WardrobeItem, Accessory, Outfit, OutfitFolder, CalendarAssignment, UserProfile
|
|
from .forms import WardrobeItemForm, AccessoryForm, OutfitForm, UserProfileForm
|
|
|
|
def get_current_user_profile(request):
|
|
if request.user.is_authenticated:
|
|
profile, _ = UserProfile.objects.get_or_create(user=request.user)
|
|
return profile
|
|
else:
|
|
# Fallback to first user if not authenticated (common for simple dev setups)
|
|
user = User.objects.first()
|
|
if user:
|
|
profile, _ = UserProfile.objects.get_or_create(user=user)
|
|
return profile
|
|
return None
|
|
|
|
def home(request):
|
|
"""Dashboard view with current week calendar and total items count."""
|
|
season_filter = request.GET.get('season', 'summer')
|
|
|
|
total_wardrobe = WardrobeItem.objects.count()
|
|
total_accessories = Accessory.objects.count()
|
|
total_items = total_wardrobe + total_accessories
|
|
|
|
today = datetime.date.today()
|
|
start_of_week = today - datetime.timedelta(days=today.weekday())
|
|
week_dates = [start_of_week + datetime.timedelta(days=i) for i in range(7)]
|
|
|
|
day_names = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]
|
|
|
|
calendar_data = []
|
|
assignments = {a.date: a.outfit for a in CalendarAssignment.objects.filter(date__in=week_dates)}
|
|
|
|
for i, date in enumerate(week_dates):
|
|
calendar_data.append({
|
|
'day': day_names[i],
|
|
'date': date,
|
|
'outfit': assignments.get(date)
|
|
})
|
|
|
|
# All outfits for the "quick add" modal on home screen
|
|
all_outfits = Outfit.objects.all().order_by('-date_created')
|
|
|
|
profile = get_current_user_profile(request)
|
|
avatar_form = UserProfileForm(instance=profile)
|
|
|
|
context = {
|
|
'total_items': total_items,
|
|
'calendar_data': calendar_data,
|
|
'today': today,
|
|
'season_filter': season_filter,
|
|
'all_outfits': all_outfits,
|
|
'week_dates_json': json.dumps([d.strftime('%Y-%m-%d') for d in week_dates]),
|
|
'day_names': day_names,
|
|
'profile': profile,
|
|
'avatar_form': avatar_form,
|
|
}
|
|
return render(request, 'core/index.html', context)
|
|
|
|
@require_POST
|
|
def update_avatar(request):
|
|
profile = get_current_user_profile(request)
|
|
if profile:
|
|
form = UserProfileForm(request.POST, request.FILES, instance=profile)
|
|
if form.is_valid():
|
|
form.save()
|
|
return redirect('home')
|
|
|
|
@require_POST
|
|
def remove_assignment(request, date):
|
|
CalendarAssignment.objects.filter(date=date).delete()
|
|
return redirect('home')
|
|
|
|
@require_POST
|
|
def assign_outfit(request):
|
|
outfit_id = request.POST.get('outfit_id')
|
|
date_str = request.POST.get('date')
|
|
if outfit_id and date_str:
|
|
outfit = get_object_or_404(Outfit, id=outfit_id)
|
|
try:
|
|
date_obj = datetime.datetime.strptime(date_str, '%Y-%m-%d').date()
|
|
CalendarAssignment.objects.update_or_create(
|
|
date=date_obj,
|
|
defaults={'outfit': outfit}
|
|
)
|
|
except ValueError:
|
|
pass
|
|
return redirect(request.META.get('HTTP_REFERER', 'home'))
|
|
|
|
def wardrobe_list(request):
|
|
query = request.GET.get('q')
|
|
main_category_id = request.GET.get('main_category')
|
|
subcategory_id = request.GET.get('subcategory')
|
|
sort = request.GET.get('sort', 'recent')
|
|
|
|
main_categories = Category.objects.filter(item_type='wardrobe', parent=None).annotate(
|
|
num_items=Count('wardrobe_items', distinct=True),
|
|
num_child_items=Count('children__wardrobe_items', distinct=True)
|
|
).filter(Q(is_preset=True) | Q(num_items__gt=0) | Q(num_child_items__gt=0))
|
|
|
|
subcategories = None
|
|
current_main = None
|
|
current_sub = None
|
|
|
|
if sort == 'alpha':
|
|
items = WardrobeItem.objects.all().order_by('name')
|
|
else:
|
|
items = WardrobeItem.objects.all().order_by('-date_added')
|
|
|
|
if main_category_id:
|
|
current_main = get_object_or_404(Category, id=main_category_id, parent=None)
|
|
subcategories = Category.objects.filter(parent=current_main).annotate(
|
|
num_items=Count('wardrobe_items', distinct=True)
|
|
).filter(Q(is_preset=True) | Q(num_items__gt=0))
|
|
|
|
items = items.filter(Q(category=current_main) | Q(category__parent=current_main))
|
|
|
|
if subcategory_id:
|
|
current_sub = get_object_or_404(Category, id=subcategory_id, parent=current_main)
|
|
items = items.filter(category=current_sub)
|
|
|
|
if query:
|
|
items = items.filter(Q(name__icontains=query) | Q(category__name__icontains=query))
|
|
|
|
context = {
|
|
'main_categories': main_categories,
|
|
'subcategories': subcategories,
|
|
'current_main': current_main,
|
|
'current_sub': current_sub,
|
|
'items': items,
|
|
'title': 'Wardrobe',
|
|
'sort': sort
|
|
}
|
|
return render(request, 'core/wardrobe_list.html', context)
|
|
|
|
def add_wardrobe_item(request):
|
|
if request.method == 'POST':
|
|
form = WardrobeItemForm(request.POST, request.FILES)
|
|
if form.is_valid():
|
|
form.save()
|
|
return redirect('wardrobe_list')
|
|
else:
|
|
form = WardrobeItemForm()
|
|
|
|
return render(request, 'core/item_form.html', {'form': form, 'title': 'Add Wardrobe Item', 'type': 'wardrobe'})
|
|
|
|
def accessory_list(request):
|
|
query = request.GET.get('q')
|
|
main_category_id = request.GET.get('main_category')
|
|
subcategory_id = request.GET.get('subcategory')
|
|
sort = request.GET.get('sort', 'recent')
|
|
|
|
main_categories = Category.objects.filter(item_type='accessory', parent=None).annotate(
|
|
num_items=Count('accessories', distinct=True),
|
|
num_child_items=Count('children__accessories', distinct=True)
|
|
).filter(Q(is_preset=True) | Q(num_items__gt=0) | Q(num_child_items__gt=0))
|
|
|
|
subcategories = None
|
|
current_main = None
|
|
current_sub = None
|
|
|
|
if sort == 'alpha':
|
|
items = Accessory.objects.all().order_by('name')
|
|
else:
|
|
items = Accessory.objects.all().order_by('-date_added')
|
|
|
|
if main_category_id:
|
|
current_main = get_object_or_404(Category, id=main_category_id, parent=None)
|
|
subcategories = Category.objects.filter(parent=current_main).annotate(
|
|
num_items=Count('accessories', distinct=True)
|
|
).filter(Q(is_preset=True) | Q(num_items__gt=0))
|
|
|
|
items = items.filter(Q(category=current_main) | Q(category__parent=current_main))
|
|
|
|
if subcategory_id:
|
|
current_sub = get_object_or_404(Category, id=subcategory_id, parent=current_main)
|
|
items = items.filter(category=current_sub)
|
|
|
|
if query:
|
|
items = items.filter(Q(name__icontains=query) | Q(category__name__icontains=query))
|
|
|
|
context = {
|
|
'main_categories': main_categories,
|
|
'subcategories': subcategories,
|
|
'current_main': current_main,
|
|
'current_sub': current_sub,
|
|
'items': items,
|
|
'title': 'Accessories',
|
|
'sort': sort
|
|
}
|
|
return render(request, 'core/accessory_list.html', context)
|
|
|
|
def add_accessory_item(request):
|
|
if request.method == 'POST':
|
|
form = AccessoryForm(request.POST, request.FILES)
|
|
if form.is_valid():
|
|
form.save()
|
|
return redirect('accessory_list')
|
|
else:
|
|
form = AccessoryForm()
|
|
|
|
return render(request, 'core/item_form.html', {'form': form, 'title': 'Add Accessory', 'type': 'accessory'})
|
|
|
|
def outfit_list(request):
|
|
query = request.GET.get('q')
|
|
folder_id = request.GET.get('folder')
|
|
sort = request.GET.get('sort', 'recent')
|
|
|
|
def get_valid_folders(parent=None):
|
|
return OutfitFolder.objects.filter(parent=parent).annotate(
|
|
num_outfits=Count('outfits', distinct=True),
|
|
num_child_outfits=Count('children__outfits', distinct=True)
|
|
).filter(Q(is_preset=True) | Q(num_outfits__gt=0) | Q(num_child_outfits__gt=0))
|
|
|
|
folders = get_valid_folders(None)
|
|
|
|
if sort == 'alpha':
|
|
outfits = Outfit.objects.all().order_by('name')
|
|
else:
|
|
outfits = Outfit.objects.all().order_by('-date_created')
|
|
|
|
if folder_id:
|
|
current_folder = get_object_or_404(OutfitFolder, id=folder_id)
|
|
folders = get_valid_folders(current_folder)
|
|
if not folders.exists():
|
|
folders = get_valid_folders(current_folder.parent)
|
|
outfits = outfits.filter(Q(folder=current_folder) | Q(folder__parent=current_folder))
|
|
else:
|
|
current_folder = None
|
|
|
|
if query:
|
|
outfits = outfits.filter(Q(name__icontains=query))
|
|
|
|
# For weekday assignment in the list view
|
|
today = datetime.date.today()
|
|
start_of_week = today - datetime.timedelta(days=today.weekday())
|
|
week_dates = [start_of_week + datetime.timedelta(days=i) for i in range(7)]
|
|
day_names = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]
|
|
week_data = [{'day': day_names[i], 'date': week_dates[i].strftime('%Y-%m-%d')} for i in range(7)]
|
|
|
|
# All folders for categorization
|
|
all_folders = OutfitFolder.objects.all().order_by('name')
|
|
|
|
context = {
|
|
'outfits': outfits,
|
|
'folders': folders,
|
|
'all_folders': all_folders,
|
|
'current_folder': current_folder,
|
|
'title': 'Outfits',
|
|
'week_data': week_data,
|
|
'sort': sort
|
|
}
|
|
return render(request, 'core/outfit_list.html', context)
|
|
|
|
def new_fit(request):
|
|
date_str = request.GET.get('date')
|
|
if request.method == 'POST':
|
|
form = OutfitForm(request.POST)
|
|
if form.is_valid():
|
|
outfit = form.save()
|
|
if date_str:
|
|
try:
|
|
date_obj = datetime.datetime.strptime(date_str, '%Y-%m-%d').date()
|
|
CalendarAssignment.objects.update_or_create(
|
|
date=date_obj,
|
|
defaults={'outfit': outfit}
|
|
)
|
|
return redirect('home')
|
|
except ValueError:
|
|
pass
|
|
return redirect('outfit_list')
|
|
else:
|
|
form = OutfitForm()
|
|
|
|
wardrobe_items = WardrobeItem.objects.all().order_by('-date_added')
|
|
accessories = Accessory.objects.all().order_by('-date_added')
|
|
|
|
main_categories = Category.objects.filter(parent=None)
|
|
|
|
return render(request, 'core/new_fit.html', {
|
|
'form': form,
|
|
'title': f'New Fit for {date_str}' if date_str else 'New Fit',
|
|
'wardrobe_items': wardrobe_items,
|
|
'accessories': accessories,
|
|
'main_categories': main_categories,
|
|
'date_str': date_str
|
|
})
|
|
|
|
def get_subcategories(request):
|
|
parent_id = request.GET.get('parent_id')
|
|
if parent_id:
|
|
subcategories = Category.objects.filter(parent_id=parent_id).values('id', 'name')
|
|
return JsonResponse(list(subcategories), safe=False)
|
|
return JsonResponse([], safe=False)
|
|
|
|
def wardrobe_item_detail(request, pk):
|
|
item = get_object_or_404(WardrobeItem, pk=pk)
|
|
return render(request, 'core/item_detail.html', {'item': item, 'type': 'wardrobe'})
|
|
|
|
def accessory_item_detail(request, pk):
|
|
item = get_object_or_404(Accessory, pk=pk)
|
|
return render(request, 'core/item_detail.html', {'item': item, 'type': 'accessory'})
|
|
|
|
def cleanup_category(category):
|
|
if category and not category.is_preset:
|
|
if not category.wardrobe_items.exists() and not category.accessories.exists() and not category.children.exists():
|
|
parent = category.parent
|
|
category.delete()
|
|
if parent:
|
|
cleanup_category(parent)
|
|
|
|
def cleanup_folder(folder):
|
|
if folder and not folder.is_preset:
|
|
if not folder.outfits.exists() and not folder.children.exists():
|
|
parent = folder.parent
|
|
folder.delete()
|
|
if parent:
|
|
cleanup_folder(parent)
|
|
|
|
@require_POST
|
|
def delete_wardrobe_item(request, pk):
|
|
item = get_object_or_404(WardrobeItem, pk=pk)
|
|
category = item.category
|
|
item.delete()
|
|
cleanup_category(category)
|
|
return redirect('wardrobe_list')
|
|
|
|
@require_POST
|
|
def delete_accessory_item(request, pk):
|
|
item = get_object_or_404(Accessory, pk=pk)
|
|
category = item.category
|
|
item.delete()
|
|
cleanup_category(category)
|
|
return redirect('accessory_list')
|
|
|
|
@require_POST
|
|
def delete_outfit(request, pk):
|
|
outfit = get_object_or_404(Outfit, pk=pk)
|
|
folder = outfit.folder
|
|
outfit.delete()
|
|
cleanup_folder(folder)
|
|
return redirect('outfit_list')
|
|
|
|
@require_POST
|
|
def move_outfit(request, pk):
|
|
outfit = get_object_or_404(Outfit, pk=pk)
|
|
folder_id = request.POST.get('folder_id')
|
|
new_folder_name = request.POST.get('new_folder_name')
|
|
|
|
old_folder = outfit.folder
|
|
|
|
if new_folder_name:
|
|
folder = OutfitFolder.objects.create(name=new_folder_name)
|
|
outfit.folder = folder
|
|
elif folder_id:
|
|
folder = get_object_or_404(OutfitFolder, id=folder_id)
|
|
outfit.folder = folder
|
|
else:
|
|
outfit.folder = None
|
|
|
|
outfit.save()
|
|
cleanup_folder(old_folder)
|
|
return redirect('outfit_list')
|