from django.contrib.auth.decorators import login_required from django.db.models import Q from django.http import HttpResponse, JsonResponse from django.shortcuts import get_object_or_404, redirect, render from .forms import ThingFormSet from .models import Box, Thing, ThingType @login_required def index(request): """Home page with boxes and thing types.""" boxes = Box.objects.select_related('box_type').all().order_by('id') thing_types = ThingType.objects.all() type_counts = {} for thing_type in thing_types: descendants = thing_type.get_descendants(include_self=True) count = Thing.objects.filter(thing_type__in=descendants).count() type_counts[thing_type.pk] = count return render(request, 'boxes/index.html', { 'boxes': boxes, 'thing_types': thing_types, 'type_counts': type_counts, }) @login_required def box_detail(request, box_id): """Display contents of a box.""" box = get_object_or_404(Box, pk=box_id) things = box.things.select_related('thing_type').all() return render(request, 'boxes/box_detail.html', { 'box': box, 'things': things, }) @login_required def thing_detail(request, thing_id): """Display details of a thing.""" thing = get_object_or_404( Thing.objects.select_related('thing_type', 'box', 'box__box_type'), pk=thing_id ) boxes = Box.objects.select_related('box_type').all().order_by('id') if request.method == 'POST': new_box_id = request.POST.get('new_box') if new_box_id: new_box = get_object_or_404(Box, pk=new_box_id) thing.box = new_box thing.save() return redirect('thing_detail', thing_id=thing.id) return render(request, 'boxes/thing_detail.html', { 'thing': thing, 'boxes': boxes, }) @login_required def search(request): """Search page for things.""" return render(request, 'boxes/search.html') @login_required def search_api(request): """AJAX endpoint for searching things.""" query = request.GET.get('q', '').strip() if len(query) < 2: return JsonResponse({'results': []}) things = Thing.objects.filter( Q(name__icontains=query) | Q(description__icontains=query) | Q(thing_type__name__icontains=query) ).select_related('thing_type', 'box')[:50] results = [ { 'id': thing.id, 'name': thing.name, 'type': thing.thing_type.name, 'box': thing.box.id, 'description': thing.description[:100] if thing.description else '', } for thing in things ] return JsonResponse({'results': results}) @login_required def add_things(request, box_id): """Add multiple things to a box at once.""" box = get_object_or_404(Box, pk=box_id) success_message = None if request.method == 'POST': formset = ThingFormSet(request.POST, request.FILES, queryset=Thing.objects.filter(box=box)) if formset.is_valid(): things = formset.save(commit=False) created_count = 0 for thing in things: if thing.name or thing.thing_type or thing.description or thing.picture: thing.box = box thing.save() created_count += 1 if created_count > 0: success_message = f'Added {created_count} thing{"s" if created_count > 1 else ""} successfully.' formset = ThingFormSet(queryset=Thing.objects.filter(box=box)) else: formset = ThingFormSet(queryset=Thing.objects.filter(box=box)) return render(request, 'boxes/add_things.html', { 'box': box, 'formset': formset, 'success_message': success_message, }) @login_required def thing_type_detail(request, type_id): """Display details of a thing type with its hierarchy and things.""" thing_type = get_object_or_404(ThingType, pk=type_id) descendants = thing_type.get_descendants(include_self=True) things_by_type = {} for descendant in descendants: things = descendant.things.select_related('box', 'box__box_type').all() if things: things_by_type[descendant] = things return render(request, 'boxes/thing_type_detail.html', { 'thing_type': thing_type, 'things_by_type': things_by_type, })