Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ jobs:
#pre-commit autoupdate
pre-commit install
pre-commit run --all-files

tests:
runs-on: ubuntu-latest
steps:
Expand Down
69 changes: 0 additions & 69 deletions web/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
from django import forms
from django.contrib.auth.models import User
from django.db import IntegrityError
from django.utils import timezone
from django.utils.crypto import get_random_string
from django.utils.text import slugify
from markdownx.fields import MarkdownxFormField
Expand All @@ -30,8 +29,6 @@
Storefront,
Subject,
SuccessStory,
TeamGoal,
TeamInvite,
)
from .referrals import handle_referral
from .widgets import (
Expand Down Expand Up @@ -71,8 +68,6 @@
"EducationalVideoForm",
"ProgressTrackerForm",
"SuccessStoryForm",
"TeamGoalForm",
"TeamInviteForm",
"MemeForm",
"QuizForm",
"QuizQuestionForm",
Expand Down Expand Up @@ -1104,70 +1099,6 @@ class Meta:
]


class TeamGoalForm(forms.ModelForm):
"""Form for creating and editing team goals."""

class Meta:
model = TeamGoal
fields = ["title", "description", "deadline"]
widgets = {
"deadline": forms.DateTimeInput(attrs={"type": "datetime-local"}),
"description": forms.Textarea(attrs={"rows": 4}),
}

def clean_deadline(self):
"""Validate that the deadline is in the future."""
deadline = self.cleaned_data.get("deadline")
if deadline and deadline < timezone.now():
raise forms.ValidationError("Deadline cannot be in the past.")
return deadline


class TeamInviteForm(forms.ModelForm):
"""Form for inviting users to a team goal."""

recipient_search = forms.CharField(
label="Invite User",
widget=forms.TextInput(
attrs={
"class": "form-control",
"placeholder": "Search by username or email",
"list": "user-list",
"autocomplete": "off",
}
),
required=False,
)

class Meta:
model = TeamInvite
fields = ["recipient"]
widgets = {
"recipient": forms.HiddenInput(),
}

def __init__(self, *args, **kwargs):
current_user = kwargs.pop("current_user", None)
self.team_goal = kwargs.pop("team_goal", None)
super().__init__(*args, **kwargs)
# Get all users except the current user (will be filtered in the view)
if current_user:
self.fields["recipient"].queryset = User.objects.exclude(id=current_user.id)
else:
self.fields["recipient"].queryset = User.objects.all()

def clean_recipient(self):
recipient = self.cleaned_data.get("recipient")
if self.team_goal and recipient:
# Check if the user is already a member of the team
if self.team_goal.members.filter(user=recipient).exists():
raise forms.ValidationError("This user is already a member of the team.")
# Check if there's already a pending invitation
if TeamInvite.objects.filter(goal=self.team_goal, recipient=recipient, status="pending").exists():
raise forms.ValidationError("This user already has a pending invitation.")
return recipient


class ProgressTrackerForm(forms.ModelForm):
class Meta:
model = ProgressTracker
Expand Down

This file was deleted.

98 changes: 0 additions & 98 deletions web/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -1223,104 +1223,6 @@ def __str__(self):
return f"{self.quantity}x {self.goods.name}"


class TeamGoal(models.Model):
"""A goal that team members work together to achieve."""

title = models.CharField(max_length=200)
description = models.TextField()
creator = models.ForeignKey(User, on_delete=models.CASCADE, related_name="created_goals")
deadline = models.DateTimeField(null=True, blank=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)

STATUS_CHOICES = [("active", "Active"), ("completed", "Completed"), ("cancelled", "Cancelled")]
status = models.CharField(max_length=20, choices=STATUS_CHOICES, default="active")

def __str__(self):
return self.title

@property
def completion_percentage(self):
"""Calculate the percentage of members who have completed the goal."""
total_members = self.members.count()
if total_members == 0:
return 0
completed_members = self.members.filter(completed=True).count()
return int((completed_members / total_members) * 100)


class TeamGoalMember(models.Model):
"""Represents a member of a team goal."""

team_goal = models.ForeignKey(TeamGoal, on_delete=models.CASCADE, related_name="members")
user = models.ForeignKey(User, on_delete=models.CASCADE)
joined_at = models.DateTimeField(auto_now_add=True)
completed = models.BooleanField(default=False)
completed_at = models.DateTimeField(null=True, blank=True)

ROLE_CHOICES = [("leader", "Team Leader"), ("member", "Team Member")]
role = models.CharField(max_length=20, choices=ROLE_CHOICES, default="member")

class Meta:
unique_together = ["team_goal", "user"]

def __str__(self):
return f"{self.user.username} - {self.team_goal.title}"

def mark_completed(self):
"""Mark this member's participation as completed."""
self.completed = True
self.completed_at = timezone.now()
self.save()

Notification.objects.create(
user=self.team_goal.creator,
title="Goal Progress Update",
message=f"{self.user.get_full_name() or self.user.username} completed'{self.team_goal.title}'",
notification_type="success",
)


class TeamInvite(models.Model):
"""Invitation to join a team goal."""

goal = models.ForeignKey(TeamGoal, on_delete=models.CASCADE, related_name="invites")
sender = models.ForeignKey(User, on_delete=models.CASCADE, related_name="sent_invites")
recipient = models.ForeignKey(User, on_delete=models.CASCADE, related_name="received_invites")
created_at = models.DateTimeField(auto_now_add=True)
responded_at = models.DateTimeField(null=True, blank=True)

STATUS_CHOICES = [("pending", "Pending"), ("accepted", "Accepted"), ("declined", "Declined")]
status = models.CharField(max_length=20, choices=STATUS_CHOICES, default="pending")

class Meta:
unique_together = ["goal", "recipient"]

def __str__(self):
return f"Invite to {self.goal.title} for {self.recipient.username}"

def save(self, *args, **kwargs):
created = not self.pk
super().save(*args, **kwargs)

if created and self.status == "pending":
Notification.objects.create(
user=self.recipient,
title="New Team Invitation",
message=f"Invited to '{self.goal.title}' by {self.sender.get_full_name() or self.sender.username}",
notification_type="info",
)

# Create notification when invite is accepted
if not created and self.status == "accepted":
Notification.objects.create(
user=self.sender,
title="Team Invitation Accepted",
message=f"{self.recipient.get_full_name() or self.recipient.username} has accepted your invitation",
notification_type="success",
)


def validate_image_size(image):
"""Validate that the image file is not too large."""
file_size = image.size
Expand Down
Loading