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')