You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

510 lines
19 KiB
Python

from django.db import models
from django.contrib.auth.models import User
from colorfield.fields import ColorField
from datetime import datetime
from django.db.models import Max
from django.utils import timezone
from django.db.models import Sum, F
from datetime import timedelta
from django.db.models.signals import post_save
from django.dispatch import receiver
from asgiref.sync import async_to_sync
from channels.layers import get_channel_layer
from datetime import timedelta
# Create your models here.
class Reference(models.Model):
name = models.CharField(max_length=50)
date = models.DateField()
def __str__(self):
return self.name
class Tag(models.Model):
name = models.CharField(max_length=50)
def __str__(self):
return self.name
class BusinessType(models.Model):
name = models.CharField(max_length=50)
def __str__(self):
return self.name
class CustomerProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, blank=True)
mobile_number = models.CharField(max_length=50, blank=True)
personal_website = models.URLField(null=True, blank=True)
STATUS_CHOICES = (
('Pending', 'Pending'),
('Active', 'Active'),
('Suspended', 'Suspended'),
('Terminated', 'Terminated'),
)
status = models.CharField(max_length=200, choices=STATUS_CHOICES, default='Active', blank=True)
reference = models.ForeignKey(Reference, on_delete=models.CASCADE, null=True, blank=True)
customer_id = models.CharField(max_length=20, null=True, blank=True)
def __str__(self):
return self.user.username
def save(self, *args, **kwargs):
if not self.customer_id:
# Get the last two digits of the current year
current_year = str(datetime.now().year)[-2:]
# Find the maximum project ID in the database and increment it
max_id = CustomerProfile.objects.aggregate(models.Max('customer_id'))['customer_id__max']
new_id = str(int(max_id[-4:]) + 1).zfill(4) if max_id else '0001' # If no existing records, start with '0001'
self.customer_id = current_year + new_id # Add 'p' prefix
super(CustomerProfile, self).save(*args, **kwargs)
@property
def get_last_seen(self):
connection = Connection.objects.filter(user=self.user).last()
if not connection:
return "Not seen yet"
if connection.online:
return "Online"
last_seen_time = connection.last_seen
now = timezone.now()
time_diff = now - last_seen_time
if time_diff < timedelta(days=1):
if last_seen_time.date() == now.date():
return f"last seen today at {last_seen_time.strftime('%I:%M %p')}"
else:
return f"last seen yesterday at {last_seen_time.strftime('%I:%M %p')}"
else:
return f"last seen on {last_seen_time.strftime('%b %d at %I:%M %p')}"
class Business(models.Model):
name = models.CharField(max_length=50)
email = models.EmailField(unique=True)
financial_number = models.CharField(max_length=50)
vat = models.BooleanField(default=False)
commercial_registration = models.CharField(max_length=50)
phone_number = models.CharField(max_length=50)
website = models.URLField(null=True)
customer = models.ForeignKey(CustomerProfile, on_delete=models.CASCADE, null=True, blank=True)
type = models.ForeignKey(BusinessType, on_delete=models.CASCADE, null=True, blank=True)
logo = models.ImageField(upload_to='uploaded_images')
business_id = models.CharField(max_length=20, null=True, blank=True)
def __str__(self):
return self.name
class Meta:
verbose_name_plural = u'Businesses'
def __str__(self):
return self.name
def save(self, *args, **kwargs):
if not self.business_id:
# Get the last two digits of the current year
current_year = str(datetime.now().year)[-2:]
# Find the maximum project ID in the database and increment it
max_id = Business.objects.aggregate(models.Max('business_id'))['business_id__max']
new_id = str(int(max_id[-4:]) + 1).zfill(4) if max_id else '0001' # If no existing records, start with '0001'
self.business_id = 'B' + current_year + new_id # Add 'p' prefix
super(Business, self).save(*args, **kwargs)
class Department(models.Model):
name = models.CharField(max_length=100)
def __str__(self):
return self.name
def get_staff(self):
# Retrieve all staff profiles associated with positions in this department
staff_profiles = StaffProfile.objects.filter(staffposition__position__department=self)
return staff_profiles
class JobPosition(models.Model):
name = models.CharField(max_length=100)
department = models.ForeignKey(Department, on_delete=models.SET_NULL, null=True)
def __str__(self):
return self.name
class StaffProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
image = models.ImageField(upload_to='uploaded_images', null=True, blank=True)
mobile_number = models.CharField(max_length=50)
intern = models.BooleanField(default=False)
staff_id = models.CharField(max_length=20, null=True, blank=True) # Allow null and blank for initial creation
def __str__(self):
return self.user.username
def save(self, *args, **kwargs):
if not self.staff_id:
# Get the last two digits of the current year
current_year = str(datetime.now().year)[-2:]
# Find the maximum project ID in the database and increment it
max_id = StaffProfile.objects.aggregate(models.Max('staff_id'))['staff_id__max']
new_id = str(int(max_id[-4:]) + 1).zfill(4) if max_id else '0001' # If no existing records, start with '0001'
self.staff_id = 'O' + current_year + new_id # Add 'p' prefix
super(StaffProfile, self).save(*args, **kwargs)
@property
def get_last_seen(self):
connection = Connection.objects.filter(user=self.user).last()
if not connection:
return "Not seen yet"
if connection.online:
return "Online"
last_seen_time = connection.last_seen
now = timezone.now()
time_diff = now - last_seen_time
if time_diff < timedelta(days=1):
if last_seen_time.date() == now.date():
return f"last seen today at {last_seen_time.strftime('%I:%M %p')}"
else:
return f"last seen yesterday at {last_seen_time.strftime('%I:%M %p')}"
else:
return f"last seen on {last_seen_time.strftime('%b %d at %I:%M %p')}"
@property
def active(self):
return self.staffposition_set.filter(end_date__isnull=True).exists()
class StaffPosition(models.Model):
staff = models.ForeignKey(StaffProfile, on_delete=models.CASCADE)
position = models.ForeignKey(JobPosition, null=True, on_delete=models.SET_NULL)
start_date = models.DateField()
end_date = models.DateField(null=True, blank=True)
class ProjectType(models.Model):
name = models.CharField(max_length=50)
department = models.ForeignKey(Department, on_delete=models.SET_NULL, null=True)
def __str__(self):
return self.name
class Project(models.Model):
name = models.CharField(max_length=50)
logo = models.ImageField(null=True, blank=True)
customer = models.ForeignKey(CustomerProfile, on_delete=models.CASCADE, null=True)
manager = models.ForeignKey(StaffProfile, on_delete=models.CASCADE, null=True)
project_type = models.ManyToManyField('ProjectType', default='', related_name='manager_project')
details = models.TextField()
members = models.ManyToManyField('StaffProfile', default='', related_name='members_project')
start_date = models.DateField()
end_date = models.DateField()
active = models.BooleanField(default=True, null=True)
project_id = models.CharField(max_length=20, null=True, blank=True)
def __str__(self):
return self.name
def save(self, *args, **kwargs):
if not self.project_id:
# Get the last two digits of the current year
current_year = str(datetime.now().year)[-2:]
# Find the maximum project ID in the database and increment it
max_id = Project.objects.aggregate(models.Max('project_id'))['project_id__max']
new_id = str(int(max_id[-4:]) + 1).zfill(4) if max_id else '0001' # If no existing records, start with '0001'
self.project_id = 'P' + current_year + new_id # Add 'p' prefix
super(Project, self).save(*args, **kwargs)
def is_pinned(self, user):
return PinnedProject.objects.filter(user=user, project=self).exists()
def total_time_worked(self, user):
total_time_seconds = 0
tasks = self.task_set.all() if user.is_superuser else self.task_set.filter(assigned_to=user.staffprofile)
for task in tasks:
total_time_hours, total_time_minutes, total_time_seconds_task = task.total_task_time()
total_time_seconds += (total_time_hours * 3600) + (total_time_minutes * 60) + total_time_seconds_task
total_time_hours = total_time_seconds // 3600
total_time_minutes = (total_time_seconds % 3600) // 60
total_time_seconds = total_time_seconds % 60
return {
'hours': total_time_hours,
'minutes': total_time_minutes,
'seconds': total_time_seconds
}
def open_tasks_count(self, user):
if user.is_superuser:
return Task.objects.filter(project=self).exclude(status='Closed').count()
else:
return Task.objects.filter(project=self, assigned_to=user.staffprofile).exclude(status='Closed').count()
class ProjectStatus(models.Model):
STATUS_CHOICES = (
('In Progress', 'In Progress'),
('Completed', 'Completed'),
('Pending', 'Pending'),
('Cancelled', 'Cancelled'),
)
project = models.ForeignKey(Project, on_delete=models.CASCADE)
status = models.CharField(max_length=200, choices=STATUS_CHOICES)
date = models.DateTimeField()
class PinnedProject(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
project = models.ForeignKey(Project, on_delete=models.CASCADE)
class Milestone(models.Model):
title = models.CharField(max_length=150)
description = models.TextField()
project = models.ForeignKey(Project, on_delete=models.CASCADE, null=True)
start_date = models.DateField()
end_date = models.DateField()
def __str__(self):
return self.title
class Epic(models.Model):
title = models.CharField(max_length=150)
STATUS_CHOICES = (
('Open', 'Open'),
('Closed', 'Closed')
)
status = models.CharField(max_length=200, choices=STATUS_CHOICES, null=True)
description = models.TextField()
project = models.ForeignKey(Project, on_delete=models.CASCADE, null=True)
start_date = models.CharField(max_length=200)
end_date = models.CharField(max_length=200)
def __str__(self):
return self.title
class ProjectRequirement(models.Model):
content = models.CharField(max_length=350)
project = models.ForeignKey(Project, on_delete=models.CASCADE, null=True)
date = models.DateField(null=True, auto_now=True)
added_by = models.ForeignKey(User, on_delete=models.SET_NULL, null=True)
def __str__(self):
return self.content
class ProjectFileAlbum(models.Model):
name = models.CharField(max_length=350, null=True)
project = models.ForeignKey(Project, on_delete=models.CASCADE, null=True)
class ProjectFile(models.Model):
album = models.ForeignKey(ProjectFileAlbum, on_delete=models.CASCADE, null=True)
file = models.FileField(null=True, upload_to='project_files')
date_added = models.DateTimeField(null=True)
class ProjectCredential(models.Model):
identifier = models.CharField(max_length=350)
password = models.CharField(max_length=350)
description = models.TextField(null=True, blank=True)
project = models.ForeignKey(Project, on_delete=models.CASCADE, null=True)
date_added = models.DateTimeField(null=True)
class Note(models.Model):
text = models.TextField(blank=True)
date = models.DateTimeField(null=True,blank=True)
user = models.ForeignKey(User, on_delete=models.CASCADE, null=True,blank=True)
color = ColorField(default='#FF0000',blank=True)
project = models.ForeignKey(Project, on_delete=models.CASCADE, null=True, blank=True)
class Task(models.Model):
name = models.CharField(max_length=250)
project = models.ForeignKey(Project, on_delete=models.CASCADE, null=True)
epic = models.ForeignKey(Epic, on_delete=models.CASCADE, null=True)
STATUS_CHOICES = (
('Open', 'Open'),
('Working On', 'Working On'),
('Closed', 'Closed')
)
status = models.CharField(max_length=200, choices=STATUS_CHOICES, null=True)
status_date = models.DateTimeField(default=timezone.now)
extra = models.BooleanField(default=False)
description = models.TextField()
start_date = models.DateField(null=True)
end_date = models.DateField(null=True)
assigned_to = models.ForeignKey(StaffProfile, on_delete=models.CASCADE, null=True)
requirement = models.ForeignKey(ProjectRequirement, on_delete=models.SET_NULL, null=True, blank=True)
task_id = models.CharField(max_length=20, null=True, blank=True)
def save(self, *args, **kwargs):
if not self.task_id:
# Get the last two digits of the current year
current_year = str(datetime.now().year)[-2:]
# Find the maximum project ID in the database and increment it
max_id = Task.objects.aggregate(models.Max('task_id'))['task_id__max']
new_id = str(int(max_id[-4:]) + 1).zfill(4) if max_id else '0001' # If no existing records, start with '0001'
self.task_id = 'T' + current_year + new_id # Add 'p' prefix
if self.pk is not None:
original_task = Task.objects.get(pk=self.pk)
if original_task.status != self.status:
self.status_date = timezone.now()
super(Task, self).save(*args, **kwargs)
def total_task_time(self):
total_time_seconds = 0
for point in self.point_set.all():
if point:
point_hours, point_minutes, point_seconds = point.total_activity_time()
total_time_seconds += (point_hours * 3600) + (point_minutes * 60) + point_seconds
total_time_hours = total_time_seconds // 3600
total_time_minutes = (total_time_seconds % 3600) // 60
total_time_seconds = total_time_seconds % 60
return total_time_hours, total_time_minutes, total_time_seconds
class Point(models.Model):
text = models.TextField(blank=True)
STATUS_CHOICES = (
('Not Completed', 'Not Completed'),
('Working On', 'Working On'),
('Paused', 'Paused'),
('Completed', 'Completed')
)
status = models.CharField(max_length=200, choices=STATUS_CHOICES, null=True)
task = models.ForeignKey(Task, on_delete=models.CASCADE, null=True)
def total_activity_time(self):
total_time_seconds = 0
for activity in self.pointactivity_set.all():
if activity.total_time:
total_time_seconds += activity.total_time.total_seconds()
total_time_hours = int(total_time_seconds // 3600)
total_time_minutes = int((total_time_seconds % 3600) // 60)
total_time_seconds = int(total_time_seconds % 60)
return total_time_hours, total_time_minutes, total_time_seconds
class PointActivity(models.Model):
point = models.ForeignKey(Point, on_delete=models.CASCADE, null=True)
start_time = models.DateTimeField()
end_time = models.DateTimeField(null=True, blank=True)
total_time = models.DurationField(null=True, blank=True)
def save(self, *args, **kwargs):
if self.start_time and not self.end_time:
self.total_time = timezone.now() - self.start_time
elif self.start_time and self.end_time:
self.total_time = self.end_time - self.start_time
super(PointActivity, self).save(*args, **kwargs)
def total_time_in_hours_minutes_seconds(self):
if self.total_time:
total_seconds = self.total_time.total_seconds()
hours = int(total_seconds // 3600)
minutes = int((total_seconds % 3600) // 60)
seconds = int(total_seconds % 60)
return hours, minutes, seconds
return 0, 0, 0
class Status(models.Model):
TYPE_CHOICES = (
('Task', 'Task'),
('Daily Report', 'Daily Report'),
)
text = models.TextField(blank=True)
staff = models.ForeignKey(StaffProfile, on_delete=models.CASCADE, null=True,blank=True, related_name='staff')
type = models.CharField(max_length=200, choices=TYPE_CHOICES, null=True, blank=True)
type_id = models.IntegerField(null=True, blank=True)
date_time = models.DateTimeField(null=True, blank=True)
def __str__(self):
return self.text
@property
def time_ago(self):
if not self.date_time:
return "No date provided"
now = timezone.now()
diff = now - self.date_time
if diff < timedelta(hours=1):
minutes = int(diff.total_seconds() / 60)
return f"{minutes}min ago"
elif diff < timedelta(days=1):
hours = int(diff.total_seconds() / 3600)
minutes = int((diff.total_seconds() % 3600) / 60)
return f"{hours}hr {minutes}min ago"
else:
return self.date_time.strftime('%d %m %Y, %I:%M %p')
@property
def project(self):
if self.type == 'Task' and self.type_id:
try:
task = Task.objects.get(id=self.type_id)
return task.project.project_id
except Task.DoesNotExist:
return None
return None
@property
def project_name(self):
if self.type == 'Task' and self.type_id:
try:
task = Task.objects.get(id=self.type_id)
return task.project.name
except Task.DoesNotExist:
return None
return None
@receiver(post_save, sender=Status)
def new_ticket_update_handler(sender, instance, created, **kwargs):
if created:
channel_layer = get_channel_layer()
event = {
'type': 'new_status_event',
'status_id': instance.id,
}
async_to_sync(channel_layer.group_send)("new_status_group", event)
class Reaction(models.Model):
status = models.ForeignKey(Status, on_delete=models.CASCADE)
emoji = models.CharField(max_length=15)
user = models.ForeignKey(User, on_delete=models.CASCADE)
class DailyReport(models.Model):
text = models.TextField(blank=True)
date = models.CharField(max_length=40)
time = models.CharField(max_length=40)
staff = models.ForeignKey(StaffProfile, on_delete=models.CASCADE, null=True,blank=True, related_name='dailyreport_staff')
class Connection(models.Model):
date = models.DateTimeField(null=True)
user = models.OneToOneField(User, on_delete=models.CASCADE)
online = models.BooleanField(default=True)
last_seen = models.DateTimeField(null=True, blank=True)
disconnected = models.BooleanField(default=False)