diff --git a/osinaweb/db.sqlite3 b/osinaweb/db.sqlite3 index 2f06f225..4c7905b4 100644 Binary files a/osinaweb/db.sqlite3 and b/osinaweb/db.sqlite3 differ diff --git a/osinaweb/support/__pycache__/consumers.cpython-310.pyc b/osinaweb/support/__pycache__/consumers.cpython-310.pyc index 03b3d2a0..33702a34 100644 Binary files a/osinaweb/support/__pycache__/consumers.cpython-310.pyc and b/osinaweb/support/__pycache__/consumers.cpython-310.pyc differ diff --git a/osinaweb/support/__pycache__/models.cpython-310.pyc b/osinaweb/support/__pycache__/models.cpython-310.pyc index 0767b636..5f8acdc0 100644 Binary files a/osinaweb/support/__pycache__/models.cpython-310.pyc and b/osinaweb/support/__pycache__/models.cpython-310.pyc differ diff --git a/osinaweb/support/__pycache__/urls.cpython-310.pyc b/osinaweb/support/__pycache__/urls.cpython-310.pyc index 279564d5..de29f7e6 100644 Binary files a/osinaweb/support/__pycache__/urls.cpython-310.pyc and b/osinaweb/support/__pycache__/urls.cpython-310.pyc differ diff --git a/osinaweb/support/__pycache__/views.cpython-310.pyc b/osinaweb/support/__pycache__/views.cpython-310.pyc index 5ba7c531..0304ad4b 100644 Binary files a/osinaweb/support/__pycache__/views.cpython-310.pyc and b/osinaweb/support/__pycache__/views.cpython-310.pyc differ diff --git a/osinaweb/support/consumers.py b/osinaweb/support/consumers.py index 89135a40..7a987ab9 100644 --- a/osinaweb/support/consumers.py +++ b/osinaweb/support/consumers.py @@ -20,12 +20,20 @@ class TicketRoomConsumer(WebsocketConsumer): self.ticket = get_object_or_404(Ticket, id=self.ticket_id) self.ticket_number = self.ticket.ticket_number + existing_connection = TicketConnection.objects.filter(ticket=self.ticket, user=self.user, terminated=False).delete() TicketConnection.objects.create( ticket=self.ticket, user=self.user, - type='Online', date=datetime.now() ) + staff_profile = StaffProfile.objects.filter(user=self.user).first() + if staff_profile: + if not TicketStaff.objects.filter(staff=staff_profile, ticket=self.ticket).exists(): + TicketStaff.objects.create( + staff=staff_profile, + ticket=self.ticket, + date_added=datetime.now() + ) async_to_sync(self.channel_layer.group_add)( self.ticket_number, self.channel_name @@ -38,8 +46,7 @@ class TicketRoomConsumer(WebsocketConsumer): TicketConnection.objects.filter( ticket=self.ticket, user=self.user, - type='Online' - ).delete() + ).update(terminated=True) async_to_sync(self.channel_layer.group_discard)( self.ticket_number, self.channel_name ) @@ -164,7 +171,7 @@ class TicketRoomConsumer(WebsocketConsumer): })) def modify_online_user(self): - connections = TicketConnection.objects.filter(ticket=self.ticket, type='Online') + connections = TicketConnection.objects.filter(ticket=self.ticket, terminated=False) event = { 'type': 'user_connection_handler', 'user': self.user, diff --git a/osinaweb/support/migrations/0006_remove_ticketconnection_type_and_more.py b/osinaweb/support/migrations/0006_remove_ticketconnection_type_and_more.py new file mode 100644 index 00000000..8320f116 --- /dev/null +++ b/osinaweb/support/migrations/0006_remove_ticketconnection_type_and_more.py @@ -0,0 +1,22 @@ +# Generated by Django 4.2.5 on 2024-07-03 06:21 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('support', '0005_alter_ticketconnection_ticket'), + ] + + operations = [ + migrations.RemoveField( + model_name='ticketconnection', + name='type', + ), + migrations.AddField( + model_name='ticketconnection', + name='terminated', + field=models.BooleanField(default=False), + ), + ] diff --git a/osinaweb/support/migrations/0007_alter_ticketstaff_staff.py b/osinaweb/support/migrations/0007_alter_ticketstaff_staff.py new file mode 100644 index 00000000..692eb6fe --- /dev/null +++ b/osinaweb/support/migrations/0007_alter_ticketstaff_staff.py @@ -0,0 +1,20 @@ +# Generated by Django 4.2.5 on 2024-07-03 07:15 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('osinacore', '0085_rename_date_staffposition_start_date_and_more'), + ('support', '0006_remove_ticketconnection_type_and_more'), + ] + + operations = [ + migrations.AlterField( + model_name='ticketstaff', + name='staff', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='osinacore.staffprofile'), + ), + ] diff --git a/osinaweb/support/migrations/0008_alter_tickettask_ticket.py b/osinaweb/support/migrations/0008_alter_tickettask_ticket.py new file mode 100644 index 00000000..7cb8b985 --- /dev/null +++ b/osinaweb/support/migrations/0008_alter_tickettask_ticket.py @@ -0,0 +1,19 @@ +# Generated by Django 4.2.5 on 2024-07-03 07:50 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('support', '0007_alter_ticketstaff_staff'), + ] + + operations = [ + migrations.AlterField( + model_name='tickettask', + name='ticket', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='support.ticket'), + ), + ] diff --git a/osinaweb/support/migrations/__pycache__/0006_remove_ticketconnection_type_and_more.cpython-310.pyc b/osinaweb/support/migrations/__pycache__/0006_remove_ticketconnection_type_and_more.cpython-310.pyc new file mode 100644 index 00000000..3aeb3eb4 Binary files /dev/null and b/osinaweb/support/migrations/__pycache__/0006_remove_ticketconnection_type_and_more.cpython-310.pyc differ diff --git a/osinaweb/support/migrations/__pycache__/0007_alter_ticketstaff_staff.cpython-310.pyc b/osinaweb/support/migrations/__pycache__/0007_alter_ticketstaff_staff.cpython-310.pyc new file mode 100644 index 00000000..cde3953f Binary files /dev/null and b/osinaweb/support/migrations/__pycache__/0007_alter_ticketstaff_staff.cpython-310.pyc differ diff --git a/osinaweb/support/migrations/__pycache__/0008_alter_tickettask_ticket.cpython-310.pyc b/osinaweb/support/migrations/__pycache__/0008_alter_tickettask_ticket.cpython-310.pyc new file mode 100644 index 00000000..459208e0 Binary files /dev/null and b/osinaweb/support/migrations/__pycache__/0008_alter_tickettask_ticket.cpython-310.pyc differ diff --git a/osinaweb/support/models.py b/osinaweb/support/models.py index ca042a9b..6ccdb3bb 100644 --- a/osinaweb/support/models.py +++ b/osinaweb/support/models.py @@ -9,6 +9,7 @@ from django.conf import settings from django.db.models.signals import post_save from django.dispatch import receiver from threading import Timer +from datetime import time def send_ticket(ticket_id): ticket = get_object_or_404(Ticket, id=ticket_id) @@ -74,10 +75,6 @@ def send_update(update_id): - - - - # Create your models here. class Ticket(models.Model): @@ -109,6 +106,30 @@ class Ticket(models.Model): super().save(*args, **kwargs) + def get_customer_last_seen(self): + connections = TicketConnection.objects.filter(user=self.customer.user).order_by('-date') + if not connections.exists(): + return "Not seen yet" + + last_connection = connections.first() + + if not last_connection.terminated: + return "Online" + + last_seen_time = last_connection.date + 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')}" + + + @receiver(post_save, sender=Ticket) def send_signal_on_save(sender, instance, created, **kwargs): @@ -127,9 +148,32 @@ class TicketDepartment(models.Model): class TicketStaff(models.Model): - staff = models.ForeignKey(StaffPosition, on_delete=models.CASCADE) + staff = models.ForeignKey(StaffProfile, on_delete=models.CASCADE) ticket = models.ForeignKey(Ticket, on_delete=models.CASCADE) date_added = models.DateTimeField() + def get_last_seen(self): + connections = TicketConnection.objects.filter(user=self.staff.user).order_by('-date') + if not connections.exists(): + return "Not seen yet" + + last_connection = connections.first() + + if not last_connection.terminated: + return "Online" + + last_seen_time = last_connection.date + 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 TicketStatus(models.Model): STATUS_CHOICES = ( @@ -143,6 +187,20 @@ class TicketStatus(models.Model): date_added = models.DateTimeField() +class TicketTask(models.Model): + ticket = models.ForeignKey(Ticket, on_delete=models.CASCADE) + task = models.ForeignKey(Task, on_delete=models.CASCADE) + + + +class TicketConnection(models.Model): + ticket = models.ForeignKey(Ticket, on_delete=models.CASCADE) + user = models.ForeignKey(User, on_delete=models.CASCADE) + date = models.DateTimeField() + terminated = models.BooleanField(default=False) + + + class TicketUpdate(models.Model): ticket = models.ForeignKey(Ticket, on_delete=models.CASCADE) description = models.TextField(null=True, blank=True) @@ -180,19 +238,3 @@ class TicketUpdateReaction(models.Model): reaction = models.CharField(max_length=50, choices=REACTION_CHOICES, null=True) ticket_update = models.ForeignKey(TicketUpdate, on_delete=models.CASCADE) - -class TicketTask(models.Model): - ticket = models.ForeignKey(TicketUpdate, on_delete=models.CASCADE) - task = models.ForeignKey(Task, on_delete=models.CASCADE) - - - -class TicketConnection(models.Model): - CONNECTION_CHOICES = ( - ('Online', 'Online'), - ('Offline', 'Offline'), - ) - ticket = models.ForeignKey(Ticket, on_delete=models.CASCADE) - user = models.ForeignKey(User, on_delete=models.CASCADE) - type = models.CharField(max_length=50, choices=CONNECTION_CHOICES, null=True) - date = models.DateTimeField() \ No newline at end of file diff --git a/osinaweb/support/templates/details_templates/ticket-room.html b/osinaweb/support/templates/details_templates/ticket-room.html index 20e4e734..3d5bdd88 100644 --- a/osinaweb/support/templates/details_templates/ticket-room.html +++ b/osinaweb/support/templates/details_templates/ticket-room.html @@ -26,20 +26,23 @@
- - - + + {% if request.user.staffprofile %} + + + + {% endif %}
@@ -65,39 +53,66 @@
- - +
+ class="w-full h-full flex flex-col gap-1 px-3 py-3 bg-secondosiblue rounded-md shadow-md cursor-pointer hover:scale-105 duration-500">
- {% if project.manager.image %} - - {% else %} - - {% endif %} +
+ {{ ticket.customer.user.first_name.0 }}{{ ticket.customer.user.last_name.0 }} +
-

Emile Elliye

-
-

Project Manager

+

{{ticket.customer.user.first_name}} {{ticket.customer.user.last_name}}

+
+

{{customer_last_seen}}

-
- - - -
+ + + {% for member in ticket_members %} + +
+
+
+
+ {% if member.staff.image %} + + {% else %} + + {% endif %} +
+ + +
+

{{member.staff.user.first_name}} {{member.staff.user.last_name}}

+
+

{{member.get_last_seen}}

+
+
+
+ +
+ + + +
+
+
+
+ {% endfor %}
diff --git a/osinaweb/support/urls.py b/osinaweb/support/urls.py index c54e2b47..d6d9e493 100644 --- a/osinaweb/support/urls.py +++ b/osinaweb/support/urls.py @@ -21,7 +21,7 @@ from django.conf import settings urlpatterns = [ path('tickets//', views.ticket_room, name='ticketroom'), - path('ticket-settings', views.ticket_settings, name='ticketsettings'), + path('tickets//settings/', views.ticket_settings, name='ticketsettings'), path('add-ticket-department', views.add_ticket_department_modal, name='addticketdepartmentmodal'), path('add-ticket-member', views.add_ticket_member_modal, name='addticketmembermodal'), diff --git a/osinaweb/support/views.py b/osinaweb/support/views.py index f8f7c86b..19b0911c 100644 --- a/osinaweb/support/views.py +++ b/osinaweb/support/views.py @@ -1,10 +1,7 @@ from django.shortcuts import render, get_object_or_404 from .models import * from .decorators import * -from django.core.mail import send_mail -from django.template.loader import render_to_string -from django.utils.html import strip_tags -from django.conf import settings +from osinacore.decorators import * # Create your views here. @ticket_member_required @@ -37,10 +34,44 @@ def ticket_room(request, ticket_number): -@ticket_member_required -def ticket_settings(request): + +def get_last_seen_order_key(member): + last_seen_text = member.get_last_seen() + + if last_seen_text == "Online": + return (0, datetime.max) + elif "last seen today" in last_seen_text: + time_part = last_seen_text.split(" at ")[-1] + last_seen_time = datetime.strptime(time_part, '%I:%M %p') + return (1, last_seen_time) + elif "last seen yesterday" in last_seen_text: + time_part = last_seen_text.split(" at ")[-1] + last_seen_time = datetime.now() - timedelta(days=1) + last_seen_time = last_seen_time.replace(hour=datetime.strptime(time_part, '%I:%M %p').hour, + minute=datetime.strptime(time_part, '%I:%M %p').minute) + return (2, last_seen_time) + elif "last seen on" in last_seen_text: + date_time_part = last_seen_text.split(" on ")[-1] + last_seen_time = datetime.strptime(date_time_part, '%b %d at %I:%M %p') + return (3, last_seen_time) + else: + return (4, datetime.min) + + + +@staff_login_required +def ticket_settings(request, ticket_number): + ticket = get_object_or_404(Ticket, ticket_number=ticket_number) + ticket_departments = TicketDepartment.objects.filter(ticket=ticket).order_by('-id') + ticket_members = TicketStaff.objects.filter(ticket=ticket) + ticket_members = sorted(ticket_members, key=get_last_seen_order_key) + customer_last_seen = ticket.get_customer_last_seen() context = { + 'ticket': ticket, + 'ticket_departments': ticket_departments, + 'ticket_members': ticket_members, + 'customer_last_seen': customer_last_seen, }