import os import platform from django.shortcuts import render, redirect, get_object_or_404 from django.utils import timezone from .models import ( Profile, Intent, ValueTag, Message, Post, Comment, Reaction, HiddenPost, Follow, ConnectionRequest, Like, Match, Thread, Block, Event, RSVP, EventTag, EventInvite ) from .forms import EventForm from django.contrib.auth.models import User from django.contrib.auth.forms import UserCreationForm from django.contrib.auth import login from django.contrib.auth.decorators import login_required from django.db.models import Q def get_dashboard_context(request): """Helper to get consistent context for dashboard-like views.""" stats = {} suggested_members = [] suggested_events = [] following_ids = [] if request.user.is_authenticated: # Quick Stats stats = { 'unread_messages': Message.objects.filter(recipient=request.user, is_read=False).count(), 'pending_connections': ConnectionRequest.objects.filter(to_user=request.user, status='pending').count(), 'upcoming_events_count': request.user.rsvps.filter(status='going', event__start_datetime__gt=timezone.now()).count(), 'completion_percentage': request.user.profile.profile_completion_percentage, 'streak': request.user.profile.accountability_streak, 'followers_count': request.user.profile.followers_count, 'following_count': request.user.profile.following_count, } following_ids = list(Follow.objects.filter(follower=request.user).values_list('followed_id', flat=True)) # Suggestions user_intents = request.user.profile.intents.all() user_values = request.user.profile.value_tags.all() suggested_members = Profile.objects.filter( Q(intents__in=user_intents) | Q(value_tags__in=user_values) ).exclude(user=request.user).distinct()[:10] # Suggested Events suggested_events = Event.objects.filter(start_datetime__gt=timezone.now()).order_by('start_datetime')[:5] return { "stats": stats, "suggested_members": suggested_members, "suggested_events": suggested_events, "following_ids": following_ids, "post_types": Post.POST_TYPE_CHOICES, "current_time": timezone.now(), "project_name": "Roster", } def home(request): """Render the landing screen or member dashboard.""" # Simple logic to seed data for the first run if not Intent.objects.exists(): intents_data = [ ('Friendship', 'bi-people'), ('Networking', 'bi-briefcase'), ('Activity Partner', 'bi-bicycle'), ('Accountability', 'bi-check-circle') ] for name, icon in intents_data: Intent.objects.create(name=name, icon=icon) if not Profile.objects.exists(): # Create a demo user/profile demo_user, _ = User.objects.get_or_create(username='marcus_v', first_name='Marcus', last_name='V.') p = Profile.objects.create( user=demo_user, professional_headline='Architect & Urban Planner', transition_status='new-in-town', bio='Passionate about sustainable cities. Recently moved here from Chicago and looking for local communities.', location_city='Austin, TX', ) p.intents.add(Intent.objects.get(name='Networking')) demo_user2, _ = User.objects.get_or_create(username='sarah_l', first_name='Sarah', last_name='L.') p2 = Profile.objects.create( user=demo_user2, professional_headline='UX Researcher | Growth Mindset', transition_status='post-divorce', bio='Rediscovering my love for hiking and photography. Seeking authentic connections and shared growth.', location_city='Austin, TX', ) p2.intents.add(Intent.objects.get(name='Friendship')) # Social Feed Logic hidden_post_ids = [] if request.user.is_authenticated: hidden_post_ids = HiddenPost.objects.filter(user=request.user).values_list('post_id', flat=True) posts = Post.objects.exclude(id__in=hidden_post_ids).select_related('author', 'author__profile').prefetch_related('comments', 'comments__author', 'reactions') # Filtering by intent (for discovery) intent_filter = request.GET.get('intent') if intent_filter: profiles = Profile.objects.filter(intents__name__iexact=intent_filter) else: profiles = Profile.objects.all() intents = Intent.objects.all() context = get_dashboard_context(request) context.update({ "profiles": profiles, "intents": intents, "current_intent": intent_filter, "posts": posts, }) return render(request, "core/index.html", context) @login_required def create_post(request): if request.method == 'POST': content = request.POST.get('content') image = request.FILES.get('image') post_type = request.POST.get('post_type', 'reflection') if content or image: Post.objects.create(author=request.user, content=content, image=image, post_type=post_type) return redirect('home') @login_required def delete_post(request, post_id): post = get_object_or_404(Post, id=post_id, author=request.user) post.delete() return redirect(request.META.get('HTTP_REFERER', 'home')) @login_required def add_comment(request, post_id): if request.method == 'POST': post = get_object_or_404(Post, id=post_id) content = request.POST.get('content') if content: Comment.objects.create(post=post, author=request.user, content=content) return redirect(request.META.get('HTTP_REFERER', 'home')) @login_required def toggle_reaction(request, post_id): post = get_object_or_404(Post, id=post_id) reaction_type = request.GET.get('type', 'heart') reaction, created = Reaction.objects.get_or_create( post=post, user=request.user, reaction_type=reaction_type ) if not created: reaction.delete() return redirect(request.META.get('HTTP_REFERER', 'home')) @login_required def toggle_follow(request, username): target_user = get_object_or_404(User, username=username) if target_user == request.user: return redirect(request.META.get('HTTP_REFERER', 'home')) follow, created = Follow.objects.get_or_create(follower=request.user, followed=target_user) if not created: follow.delete() return redirect(request.META.get('HTTP_REFERER', 'home')) @login_required def hide_post(request, post_id): post = get_object_or_404(Post, id=post_id) HiddenPost.objects.get_or_create(user=request.user, post=post) return redirect('home') def about(request): return render(request, "core/about.html") def signup(request): if request.method == 'POST': form = UserCreationForm(request.POST) if form.is_valid(): user = form.save() # Create profile Profile.objects.get_or_create(user=user) login(request, user) return redirect('onboarding') else: form = UserCreationForm() return render(request, 'registration/signup.html', {'form': form}) @login_required def onboarding(request): profile = request.user.profile if request.method == 'POST': profile.gamer_tag = request.POST.get('gamer_tag', '') profile.platform = request.POST.get('platform', 'pc') profile.primary_game_id = request.POST.get('primary_game') profile.bio = request.POST.get('bio', '') profile.onboarding_completed = True profile.save() return redirect('home') from .models import Game games = Game.objects.all().order_by('name') return render(request, 'core/onboarding.html', { 'profile': profile, 'games': games, 'platforms': profile.PLATFORM_CHOICES }) @login_required def settings_view(request): profile = request.user.profile if request.method == 'POST': profile.two_factor_enabled = 'two_factor' in request.POST profile.save() return redirect('settings') return render(request, 'core/settings.html', {'profile': profile}) def get_started(request): if not request.user.is_authenticated: return redirect('signup') if not request.user.profile.onboarding_completed: return redirect('onboarding') return redirect('home') @login_required def inbox(request): # Get all users the current user has messaged or received messages from sent_to = Message.objects.filter(sender=request.user).values_list('recipient', flat=True) received_from = Message.objects.filter(recipient=request.user).values_list('sender', flat=True) partner_ids = set(list(sent_to) + list(received_from)) partners = User.objects.filter(id__in=partner_ids).select_related('profile') for partner in partners: last_message = Message.objects.filter( Q(sender=request.user, recipient=partner) | Q(sender=partner, recipient=request.user) ).order_by('-timestamp').first() partner.last_message = last_message return render(request, 'core/inbox.html', {'partners': partners}) @login_required def chat_detail(request, username): partner = get_object_or_404(User, username=username) if partner == request.user: return redirect('inbox') thread = Thread.objects.filter(participants=request.user).filter(participants=partner).first() if not thread: thread = Thread.objects.create() thread.participants.add(request.user, partner) if request.method == 'POST': body = request.POST.get('body') if body: Message.objects.create(sender=request.user, recipient=partner, body=body, thread=thread) thread.updated_at = timezone.now() thread.save() return redirect('chat_detail', username=username) messages = Message.objects.filter(thread=thread).order_by('timestamp') messages.filter(recipient=request.user, is_read=False).update(is_read=True) return render(request, 'core/chat.html', { 'partner': partner, 'chat_messages': messages, 'thread': thread }) @login_required def profile_view(request): return redirect('profile_detail', username=request.user.username) def profile_detail(request, username): target_user = get_object_or_404(User, username=username) is_following = False if request.user.is_authenticated: is_following = Follow.objects.filter(follower=request.user, followed=target_user).exists() return render(request, 'core/profile_detail.html', { 'target_user': target_user, 'is_following': is_following }) @login_required def edit_profile(request): profile = request.user.profile if request.method == 'POST': profile.professional_headline = request.POST.get('headline', '') profile.bio = request.POST.get('bio', '') profile.location_city = request.POST.get('location', '') profile.platform = request.POST.get('platform', '') profile.gamer_tag = request.POST.get('gamer_tag', '') profile.rank = request.POST.get('rank', '') profile.preferred_role = request.POST.get('preferred_role', '') primary_game_id = request.POST.get('primary_game') if primary_game_id: profile.primary_game_id = primary_game_id if 'avatar' in request.FILES: profile.avatar = request.FILES['avatar'] profile.save() return redirect('my_profile') from .models import Game games = Game.objects.all().order_by('name') return render(request, 'core/edit_profile.html', { 'profile': profile, 'games': games, 'platforms': profile.PLATFORM_CHOICES }) @login_required def matches(request, tab=None): """Main hub for 'My Matches'.""" if not tab: tab = request.GET.get('tab', 'mutual') search_query = request.GET.get('q', '') intent_filter = request.GET.get('intent', '') sort_by = request.GET.get('sort', 'newest') user = request.user if tab == 'requests': queryset = User.objects.filter(requests_sent__to_user=user, requests_sent__status='pending') elif tab == 'sent': queryset = User.objects.filter(requests_received__from_user=user, requests_received__status='pending') elif tab == 'liked': queryset = User.objects.filter(likes_received__from_user=user).exclude( Q(matches_a__user_b=user) | Q(matches_b__user_a=user) ) elif tab == 'blocked': queryset = User.objects.filter(blocks_received__blocker=user) else: # mutual queryset = User.objects.filter( Q(matches_a__user_b=user, matches_a__status='active') | Q(matches_b__user_a=user, matches_b__status='active') ) if search_query: queryset = queryset.filter( Q(first_name__icontains=search_query) | Q(last_name__icontains=search_query) | Q(username__icontains=search_query) | Q(profile__professional_headline__icontains=search_query) | Q(profile__location_city__icontains=search_query) ) if intent_filter: queryset = queryset.filter(profile__intents__name=intent_filter) if sort_by == 'newest': queryset = queryset.order_by('-date_joined') elif sort_by == 'aligned': queryset = queryset.order_by('-profile__accountability_streak') queryset = queryset.select_related('profile').prefetch_related('profile__intents').distinct() context = get_dashboard_context(request) incoming_requests = ConnectionRequest.objects.filter(to_user=user, status='pending') request_map = {r.from_user_id: r.id for r in incoming_requests} context.update({ 'matches_list': queryset, 'current_tab': tab, 'search_query': search_query, 'current_intent': intent_filter, 'current_sort': sort_by, 'intents': Intent.objects.all(), 'title': 'My Matches', 'request_map': request_map }) return render(request, 'core/matches.html', context) @login_required def send_match_request(request, username): target_user = get_object_or_404(User, username=username) if target_user == request.user: return redirect('home') if Like.objects.filter(from_user=target_user, to_user=request.user).exists(): Match.objects.get_or_create( user_a=min(request.user, target_user, key=lambda u: u.id), user_b=max(request.user, target_user, key=lambda u: u.id) ) return redirect('matches') ConnectionRequest.objects.get_or_create(from_user=request.user, to_user=target_user, status='pending') return redirect(request.META.get('HTTP_REFERER', 'home')) @login_required def handle_match_request(request, request_id): conn_request = get_object_or_404(ConnectionRequest, id=request_id, to_user=request.user) action = request.GET.get('action') if action == 'accept': conn_request.status = 'accepted' conn_request.responded_at = timezone.now() conn_request.save() Match.objects.get_or_create( user_a=min(conn_request.from_user, conn_request.to_user, key=lambda u: u.id), user_b=max(conn_request.from_user, conn_request.to_user, key=lambda u: u.id) ) elif action == 'decline': conn_request.status = 'declined' conn_request.responded_at = timezone.now() conn_request.save() return redirect('matches') @login_required def cancel_match_request(request, username): target_user = get_object_or_404(User, username=username) ConnectionRequest.objects.filter(from_user=request.user, to_user=target_user, status='pending').delete() return redirect('matches') @login_required def block_user(request, username): target_user = get_object_or_404(User, username=username) Block.objects.get_or_create(blocker=request.user, blocked=target_user) Match.objects.filter( Q(user_a=request.user, user_b=target_user) | Q(user_a=target_user, user_b=request.user) ).delete() ConnectionRequest.objects.filter( Q(from_user=request.user, to_user=target_user) | Q(from_user=target_user, to_user=request.user) ).delete() return redirect('matches') @login_required def toggle_like(request, username): target_user = get_object_or_404(User, username=username) if target_user == request.user: return redirect('home') like, created = Like.objects.get_or_create(from_user=request.user, to_user=target_user) if not created: like.delete() else: if Like.objects.filter(from_user=target_user, to_user=request.user).exists(): Match.objects.get_or_create( user_a=min(request.user, target_user, key=lambda u: u.id), user_b=max(request.user, target_user, key=lambda u: u.id) ) return redirect(request.META.get('HTTP_REFERER', 'home')) @login_required def groups(request): return render(request, 'core/placeholder.html', {'title': 'Groups'}) @login_required def my_posts(request): posts = Post.objects.filter(author=request.user).select_related('author', 'author__profile').prefetch_related('comments', 'reactions') context = get_dashboard_context(request) context.update({ 'posts': posts, 'title': 'My Posts' }) return render(request, 'core/index.html', context) @login_required def events(request): tab = request.GET.get('tab', 'upcoming') query = request.GET.get('q', '') events = Event.objects.all().order_by('start_datetime') if query: events = events.filter( Q(title__icontains=query) | Q(description__icontains=query) | Q(location_name__icontains=query) ) if tab == 'mine': events = events.filter(creator=request.user) elif tab == 'attending': events = events.filter(rsvps__user=request.user, rsvps__status='going') elif tab == 'past': events = Event.objects.filter(start_datetime__lt=timezone.now()).order_by('-start_datetime') elif tab == 'calendar': # Simple grouping by date for calendar view events = events.filter(start_datetime__gt=timezone.now()) else: # upcoming events = events.filter(start_datetime__gt=timezone.now()) context = get_dashboard_context(request) # For calendar view, group events by date calendar_events = {} if tab == 'calendar': for event in events: date_key = event.start_datetime.date() if date_key not in calendar_events: calendar_events[date_key] = [] calendar_events[date_key].append(event) context.update({ 'events': events, 'calendar_events': calendar_events, 'tab': tab, 'query': query, }) return render(request, 'core/events_list.html', context) @login_required def event_detail(request, event_id): event = get_object_or_404(Event, id=event_id) user_rsvp = RSVP.objects.filter(event=event, user=request.user).first() rsvps = event.rsvps.select_related('user', 'user__profile') context = get_dashboard_context(request) context.update({ 'event': event, 'user_rsvp': user_rsvp, 'rsvps': rsvps, 'going_count': rsvps.filter(status='going').count(), 'maybe_count': rsvps.filter(status='maybe').count(), }) return render(request, 'core/event_detail.html', context) @login_required def event_create(request): if request.method == 'POST': form = EventForm(request.POST, request.FILES) if form.is_valid(): event = form.save(commit=False) event.creator = request.user event.save() form.save_m2m() return redirect('event_detail', event_id=event.id) else: form = EventForm() context = get_dashboard_context(request) context.update({'form': form, 'title': 'Create Session'}) return render(request, 'core/event_form.html', context) @login_required def event_edit(request, event_id): event = get_object_or_404(Event, id=event_id, creator=request.user) if request.method == 'POST': form = EventForm(request.POST, request.FILES, instance=event) if form.is_valid(): form.save() return redirect('event_detail', event_id=event.id) else: form = EventForm(instance=event) context = get_dashboard_context(request) context.update({'form': form, 'title': 'Edit Session', 'event': event}) return render(request, 'core/event_form.html', context) @login_required def event_delete(request, event_id): event = get_object_or_404(Event, id=event_id, creator=request.user) if request.method == 'POST': event.delete() return redirect('events') return render(request, 'core/event_confirm_delete.html', {'event': event}) @login_required def event_rsvp(request, event_id): if request.method == 'POST': event = get_object_or_404(Event, id=event_id) status = request.POST.get('status') if status in ['going', 'maybe', 'not_going']: rsvp, created = RSVP.objects.update_or_create( event=event, user=request.user, defaults={'status': status} ) elif status == 'cancel': RSVP.objects.filter(event=event, user=request.user).delete() return redirect(request.META.get('HTTP_REFERER', 'event_detail', event_id=event.id)) return redirect('events')