38531-vm/core/views.py
2026-02-17 19:09:56 +00:00

581 lines
21 KiB
Python

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