-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmodels.py
More file actions
195 lines (161 loc) · 8.24 KB
/
models.py
File metadata and controls
195 lines (161 loc) · 8.24 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
import uuid
from django.conf import settings
from django.contrib.auth.models import AbstractUser
from django.db import models
from django.utils import timezone
# Common timezone choices for user preferences
TIMEZONE_CHOICES = [
('America/New_York', 'Eastern Time (US & Canada)'),
('America/Chicago', 'Central Time (US & Canada)'),
('America/Denver', 'Mountain Time (US & Canada)'),
('America/Los_Angeles', 'Pacific Time (US & Canada)'),
('America/Anchorage', 'Alaska'),
('Pacific/Honolulu', 'Hawaii'),
('America/Phoenix', 'Arizona'),
('America/Toronto', 'Toronto'),
('America/Vancouver', 'Vancouver'),
('Europe/London', 'London'),
('Europe/Paris', 'Paris'),
('Europe/Berlin', 'Berlin'),
('Europe/Amsterdam', 'Amsterdam'),
('Europe/Rome', 'Rome'),
('Europe/Madrid', 'Madrid'),
('Asia/Tokyo', 'Tokyo'),
('Asia/Shanghai', 'Shanghai'),
('Asia/Hong_Kong', 'Hong Kong'),
('Asia/Singapore', 'Singapore'),
('Asia/Seoul', 'Seoul'),
('Asia/Dubai', 'Dubai'),
('Asia/Kolkata', 'India Standard Time'),
('Australia/Sydney', 'Sydney'),
('Australia/Melbourne', 'Melbourne'),
('Pacific/Auckland', 'Auckland'),
('UTC', 'UTC'),
]
# Email frequency choices
EMAIL_FREQUENCY_CHOICES = [
('instant', 'Instant'),
('daily', 'Daily Digest'),
('none', 'None'),
]
# Create your models here.
# Create user
class User(AbstractUser):
pass
# Household Model: Represents the shared space.
class Household(models.Model):
name = models.CharField(max_length=100, default="Our Home")
# A unique code to share with the partner to join
invite_code = models.UUIDField(default=uuid.uuid4, editable=False, unique=True)
created_at = models.DateTimeField(auto_now_add=True)
def get_short_invite_code(self):
"""Return a shorter, more readable version of the invite code"""
code_str = str(self.invite_code).upper()
return f"{code_str[:3]}-{code_str[4:7]}"
def __str__(self):
member_names = ", ".join([profile.user.username for profile in self.members.all()])
if member_names:
return f"{self.name} ({member_names})"
return self.name
# UserProfile Model: Extends the standard User to link them to a Household.
class UserProfile(models.Model):
user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='profile')
# The link! If null, they haven't joined a family yet.
household = models.ForeignKey('Household', on_delete=models.SET_NULL, null=True, blank=True, related_name='members')
# Identity Card fields
avatar = models.ImageField(upload_to='avatars/', null=True, blank=True)
display_name = models.CharField(max_length=50, blank=True, help_text="The name your partner sees (e.g., 'Honey', 'Mom', 'The Boss')")
tagline = models.CharField(max_length=100, blank=True, help_text="A fun status (e.g., 'Currently overworked', 'Dishwasher Champion')")
# Notification Preferences
email_frequency = models.CharField(max_length=10, choices=EMAIL_FREQUENCY_CHOICES, default='instant')
crime_alerts_enabled = models.BooleanField(default=True, help_text="Receive dramatic HTML warnings for 'Crime' tasks")
# Time Zone
timezone = models.CharField(max_length=50, choices=TIMEZONE_CHOICES, default='America/New_York')
def __str__(self):
if self.household:
return f"{self.user.username} | {self.household.name}"
return f"{self.user.username} | No Household"
@property
def invite_code(self):
"""Return the household's invite code if user belongs to one"""
return self.household.invite_code if self.household else None
@property
def short_invite_code(self):
"""Return a shorter invite code for display"""
return self.household.get_short_invite_code() if self.household else None
def get_display_name(self):
"""Return display name or fall back to username"""
return self.display_name if self.display_name else self.user.username
def get_household_members(self):
"""Return other members in the same household"""
if self.household:
return self.household.members.exclude(user=self.user)
return UserProfile.objects.none()
# Stats methods
def tasks_completed_count(self):
"""Total tasks this user has completed"""
return Task.objects.filter(completed_by=self.user).count()
def crimes_committed_count(self):
"""Tasks marked as 'crime' that this user created"""
return Task.objects.filter(created_by=self.user, is_crime=True).count()
def crimes_fulfilled_count(self):
"""Crime tasks that this user completed for others"""
return Task.objects.filter(completed_by=self.user, is_crime=True).exclude(created_by=self.user).count()
def karma_score(self):
"""Karma = Tasks Completed - Crimes Committed"""
return self.tasks_completed_count() - self.crimes_committed_count()
# House-work category
class Category(models.Model):
name = models.CharField(max_length=64)
class Meta:
verbose_name_plural = "Categories"
def __str__(self):
return f"{self.name}"
# task details
class Task(models.Model):
# Tasks belong to the Household, not just the User!
household = models.ForeignKey(Household, on_delete=models.CASCADE, null=True, blank=True, related_name='tasks')
created_by = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.SET_NULL, null=True, related_name='created_tasks') # Who logged it?
# Beneficiary - who this task is for (must be in the same household, can be null)
beneficiary = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.SET_NULL, null=True, blank=True, related_name='benefiting_tasks')
title = models.CharField(max_length=200)
description = models.TextField(blank=True, null=True)
is_crime = models.BooleanField(default=False) # Your specific logic flag
created_at = models.DateTimeField(auto_now_add=True)
due_date = models.DateTimeField(null=True, blank=True, help_text="Expected completion date and time")
is_completed = models.BooleanField(default=False)
completed_at = models.DateTimeField(null=True, blank=True)
category = models.ForeignKey(Category, on_delete=models.CASCADE, blank=True, null=True, related_name="task_type")
# Link to the user who completed the task
completed_by = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.SET_NULL, null=True, blank=True, related_name='completed_tasks')
# Completion proof image
completion_image = models.ImageField(upload_to='completions/', null=True, blank=True, help_text="Photo proof of task completion")
# Crime acceptance fields
crime_accepted = models.BooleanField(default=False, help_text="Whether the crime request has been accepted by someone")
crime_accepted_by = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.SET_NULL, null=True, blank=True, related_name='accepted_crimes', help_text="Who accepted this crime request")
@property
def is_overdue(self):
"""Check if task is past due date"""
if self.due_date and not self.is_completed:
from django.utils import timezone
return timezone.now() > self.due_date
return False
def save(self, *args, **kwargs):
# Call the original save method to save the data to the database
super().save(*args, **kwargs)
def __str__(self):
if self.is_completed:
completed_by_user = self.completed_by.username if self.completed_by else "an unknown user"
return f"{self.title} (Completed by {completed_by_user})"
else:
return f"{self.title} (Incomplete)"
# Comment model for social interaction on completed tasks
class Comment(models.Model):
task = models.ForeignKey(Task, on_delete=models.CASCADE, related_name='comments')
author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='task_comments')
content = models.TextField(max_length=500)
created_at = models.DateTimeField(auto_now_add=True)
class Meta:
ordering = ['created_at'] # Oldest first
def __str__(self):
return f"{self.author.username} on {self.task.title}: {self.content[:30]}..."