@@ -363,6 +290,63 @@
+
+
{% endblock content %}
\ No newline at end of file
diff --git a/osinaweb/osinacore/views.py b/osinaweb/osinacore/views.py
index 8afab2ad..745966f5 100644
--- a/osinaweb/osinacore/views.py
+++ b/osinaweb/osinacore/views.py
@@ -511,21 +511,22 @@ def staffdetails( request, staff_id):
positions = StaffPosition.objects.filter(staff=staff).order_by('-id')
if request.method == 'POST':
position_ids = request.POST.getlist('position[]')
+ print(position_ids)
start_dates = request.POST.getlist('start_date[]')
end_dates = request.POST.getlist('end_date[]')
for position_id, start_date, end_date in zip(position_ids, start_dates, end_dates):
- position = get_object_or_404(JobPosition, id=position_id)
- if end_date:
- end_date = end_date
- else:
- end_date = None
- StaffPosition.objects.create(
- staff = staff,
- position = position,
- start_date = start_date,
- end_date = end_date
- )
- return HttpResponse('')
+ position = get_object_or_404(JobPosition, id=position_id)
+ if end_date:
+ end_date = end_date
+ else:
+ end_date = None
+ StaffPosition.objects.create(
+ staff = staff,
+ position = position,
+ start_date = start_date,
+ end_date = end_date
+ )
+ return redirect('userdetails', staff_id=staff.staff_id)
context = {
diff --git a/osinaweb/osinaweb/__pycache__/celery.cpython-310.pyc b/osinaweb/osinaweb/__pycache__/celery.cpython-310.pyc
index f2a473e5..004ac710 100644
Binary files a/osinaweb/osinaweb/__pycache__/celery.cpython-310.pyc and b/osinaweb/osinaweb/__pycache__/celery.cpython-310.pyc differ
diff --git a/osinaweb/osinaweb/__pycache__/settings.cpython-310.pyc b/osinaweb/osinaweb/__pycache__/settings.cpython-310.pyc
index a086f78f..9f7aba03 100644
Binary files a/osinaweb/osinaweb/__pycache__/settings.cpython-310.pyc and b/osinaweb/osinaweb/__pycache__/settings.cpython-310.pyc differ
diff --git a/osinaweb/static/js/add-staffs-position-in-staffs-details.js b/osinaweb/static/js/add-staffs-position-in-staffs-details.js
index ad09a96e..91d89fcf 100644
--- a/osinaweb/static/js/add-staffs-position-in-staffs-details.js
+++ b/osinaweb/static/js/add-staffs-position-in-staffs-details.js
@@ -30,7 +30,13 @@ document.addEventListener('DOMContentLoaded', function () {
selectElement.name = 'position[]';
const dateInputElements = newPositionContainer.querySelectorAll('input[type="date"]');
- dateInputElements.forEach(input => input.name = input.name.replace('date[]', '') + '[]');
+ dateInputElements.forEach((input, index) => {
+ if (index === 0) {
+ input.name = 'start_date[]';
+ } else {
+ input.name = 'end_date[]';
+ }
+ });
const removeButton = newPositionContainer.querySelector('.removePositionOption');
removeButton.addEventListener('click', removePositionContainer);
@@ -44,4 +50,4 @@ document.addEventListener('DOMContentLoaded', function () {
});
updateSubmitButtonVisibility();
-});
\ No newline at end of file
+});
diff --git a/osinaweb/static/js/tickets/tickets-room.js b/osinaweb/static/js/tickets/tickets-room.js
index 8736bd93..0a794b5e 100644
--- a/osinaweb/static/js/tickets/tickets-room.js
+++ b/osinaweb/static/js/tickets/tickets-room.js
@@ -63,7 +63,7 @@ function app(socket) {
function initializeWebSocket() {
const ticketId = document.getElementById('ticketId').textContent.trim();
- const wsUrl = `wss://${window.location.host}/ws/ticketroom/${ticketId}/`;
+ const wsUrl = `ws://${window.location.host}/ws/ticketroom/${ticketId}/`;
const socket = new WebSocket(wsUrl);
socket.onopen = () => {
diff --git a/osinaweb/support/__pycache__/consumers.cpython-310.pyc b/osinaweb/support/__pycache__/consumers.cpython-310.pyc
index 33702a34..162211a1 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 201c3570..59a96abb 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__/routing.cpython-310.pyc b/osinaweb/support/__pycache__/routing.cpython-310.pyc
index 30f4ad73..d4f3d3c8 100644
Binary files a/osinaweb/support/__pycache__/routing.cpython-310.pyc and b/osinaweb/support/__pycache__/routing.cpython-310.pyc differ
diff --git a/osinaweb/support/__pycache__/views.cpython-310.pyc b/osinaweb/support/__pycache__/views.cpython-310.pyc
index 4325d55a..742b1c4c 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 7a987ab9..d549ca91 100644
--- a/osinaweb/support/consumers.py
+++ b/osinaweb/support/consumers.py
@@ -12,7 +12,6 @@ from django.template.loader import render_to_string
from asgiref.sync import async_to_sync
-
class TicketRoomConsumer(WebsocketConsumer):
def connect(self):
self.user = self.scope['user']
@@ -20,7 +19,7 @@ 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()
+ existing_connection = TicketConnection.objects.filter(ticket=self.ticket, user=self.user, terminated_at__isnull=True).delete()
TicketConnection.objects.create(
ticket=self.ticket,
user=self.user,
@@ -46,7 +45,7 @@ class TicketRoomConsumer(WebsocketConsumer):
TicketConnection.objects.filter(
ticket=self.ticket,
user=self.user,
- ).update(terminated=True)
+ ).update(terminated_at=datetime.now())
async_to_sync(self.channel_layer.group_discard)(
self.ticket_number, self.channel_name
)
@@ -171,7 +170,7 @@ class TicketRoomConsumer(WebsocketConsumer):
}))
def modify_online_user(self):
- connections = TicketConnection.objects.filter(ticket=self.ticket, terminated=False)
+ connections = TicketConnection.objects.filter(ticket=self.ticket, terminated_at__isnull=True)
event = {
'type': 'user_connection_handler',
'user': self.user,
@@ -190,4 +189,81 @@ class TicketRoomConsumer(WebsocketConsumer):
self.send(text_data=json.dumps({
'event_type': 'user_status',
'html': html
- }))
\ No newline at end of file
+ }))
+
+
+
+class NewTicketConsumer(WebsocketConsumer):
+ def connect(self):
+ self.user = self.scope['user']
+ async_to_sync(self.channel_layer.group_add)(
+ "new_ticket_group",
+ self.channel_name
+ )
+ self.accept()
+
+ def disconnect(self, close_code):
+ async_to_sync(self.channel_layer.group_discard)(
+ "new_ticket_group",
+ self.channel_name
+ )
+
+ def new_ticket_event(self, event):
+ ticket_id = event['ticket_id']
+ ticket = Ticket.objects.get(id=ticket_id)
+ is_staff_or_superuser = (
+ hasattr(self.user, 'staffprofile') and
+ (self.user.staffprofile in ticket.get_all_ticket_staff() or self.user.is_superuser)
+ )
+ if is_staff_or_superuser:
+ context = {'ticket': ticket, 'new': True}
+ html = render_to_string("details_templates/partials/ticket-display.html", context)
+
+ self.send(text_data=json.dumps({
+ 'event_type': 'new_ticket',
+ 'html': html
+ }))
+
+
+
+class NewTicketUpdateConsumer(WebsocketConsumer):
+ def connect(self):
+ self.user = self.scope['user']
+ async_to_sync(self.channel_layer.group_add)(
+ "new_ticket_update_group",
+ self.channel_name
+ )
+ self.accept()
+
+ def disconnect(self, close_code):
+ async_to_sync(self.channel_layer.group_discard)(
+ "new_ticket_update_group",
+ self.channel_name
+ )
+
+ def new_ticket_update_event(self, event):
+ ticket_update_id = event['ticket_update_id']
+ ticket_update = TicketUpdate.objects.get(id=ticket_update_id)
+ ticket = ticket_update.ticket
+ ticket_id = ticket.id
+ unread_updates_count = 0
+ for ticket_update in ticket.ticketupdate_set.exclude(added_by=self.user):
+ if not TicketRead.objects.filter(ticket_update=ticket_update, user=self.user, read=True).exists():
+ unread_updates_count += 1
+ ticket.unread_updates_count = unread_updates_count
+ is_staff_or_superuser = (
+ hasattr(self.user, 'staffprofile') and
+ (self.user.staffprofile in ticket.get_all_ticket_staff() or self.user.is_superuser)
+ )
+ if is_staff_or_superuser:
+ context = {'ticket': ticket, 'new': True}
+ html = render_to_string("details_templates/partials/ticket-display.html", context)
+
+ self.send(text_data=json.dumps({
+ 'event_type': 'new_ticket_update_group',
+ 'html': html,
+ 'ticket_id': ticket_id
+ }))
+
+
+
diff --git a/osinaweb/support/migrations/0009_ticketconnection_terminated_at.py b/osinaweb/support/migrations/0009_ticketconnection_terminated_at.py
new file mode 100644
index 00000000..a6f1a4be
--- /dev/null
+++ b/osinaweb/support/migrations/0009_ticketconnection_terminated_at.py
@@ -0,0 +1,18 @@
+# Generated by Django 4.2.5 on 2024-07-04 06:17
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('support', '0008_alter_tickettask_ticket'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='ticketconnection',
+ name='terminated_at',
+ field=models.DateTimeField(blank=True, null=True),
+ ),
+ ]
diff --git a/osinaweb/support/migrations/0010_remove_ticketconnection_terminated.py b/osinaweb/support/migrations/0010_remove_ticketconnection_terminated.py
new file mode 100644
index 00000000..94b7609b
--- /dev/null
+++ b/osinaweb/support/migrations/0010_remove_ticketconnection_terminated.py
@@ -0,0 +1,17 @@
+# Generated by Django 4.2.5 on 2024-07-04 06:17
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('support', '0009_ticketconnection_terminated_at'),
+ ]
+
+ operations = [
+ migrations.RemoveField(
+ model_name='ticketconnection',
+ name='terminated',
+ ),
+ ]
diff --git a/osinaweb/support/migrations/__pycache__/0009_ticketconnection_terminated_at.cpython-310.pyc b/osinaweb/support/migrations/__pycache__/0009_ticketconnection_terminated_at.cpython-310.pyc
new file mode 100644
index 00000000..459a3cae
Binary files /dev/null and b/osinaweb/support/migrations/__pycache__/0009_ticketconnection_terminated_at.cpython-310.pyc differ
diff --git a/osinaweb/support/migrations/__pycache__/0010_remove_ticketconnection_terminated.cpython-310.pyc b/osinaweb/support/migrations/__pycache__/0010_remove_ticketconnection_terminated.cpython-310.pyc
new file mode 100644
index 00000000..c98c7f69
Binary files /dev/null and b/osinaweb/support/migrations/__pycache__/0010_remove_ticketconnection_terminated.cpython-310.pyc differ
diff --git a/osinaweb/support/models.py b/osinaweb/support/models.py
index 239f5b77..d18a7aac 100644
--- a/osinaweb/support/models.py
+++ b/osinaweb/support/models.py
@@ -9,13 +9,13 @@ 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
+from asgiref.sync import async_to_sync
+from channels.layers import get_channel_layer
def send_ticket(ticket_id):
ticket = get_object_or_404(Ticket, id=ticket_id)
- department = TicketDepartment.objects.filter(ticket=ticket).last().department
- staff_profiles = department.get_staff()
- for staff in staff_profiles:
+ ticket_staffs = ticket.get_all_ticket_staff()
+ for staff in ticket_staffs:
subject = f"New Ticket Opened: {ticket.title}"
html_message = render_to_string('email_templates/new-ticket.html', {
'ticket': ticket,
@@ -42,14 +42,9 @@ def send_ticket(ticket_id):
def send_update(update_id):
update = get_object_or_404(TicketUpdate, id=update_id)
- if TicketDepartment.objects.filter(ticket=update.ticket):
- department = TicketDepartment.objects.filter(ticket=update.ticket).last().department
- else:
- department = Department.objects.get(name='Support')
- if department:
- staff_profiles = department.get_staff()
- print(staff_profiles)
- for staff in staff_profiles:
+ staff_profiles = update.ticket.get_all_ticket_staff()
+ print(staff_profiles)
+ for staff in staff_profiles:
subject = f"New Ticket Update: {update.ticket.title}"
html_message = render_to_string('email_templates/new-ticket-update.html', {
'update': update,
@@ -113,10 +108,10 @@ class Ticket(models.Model):
last_connection = connections.first()
- if not last_connection.terminated:
+ if not last_connection.terminated_at:
return "Online"
- last_seen_time = last_connection.date
+ last_seen_time = last_connection.terminated_at
now = timezone.now()
time_diff = now - last_seen_time
@@ -155,6 +150,18 @@ def send_signal_on_save(sender, instance, created, **kwargs):
+@receiver(post_save, sender=Ticket)
+def new_ticket_handler(sender, instance, created, **kwargs):
+ if created:
+ channel_layer = get_channel_layer()
+ event = {
+ 'type': 'new_ticket_event',
+ 'ticket_id': instance.id,
+ }
+ async_to_sync(channel_layer.group_send)("new_ticket_group", event)
+
+
+
class TicketDepartment(models.Model):
department = models.ForeignKey(Department, on_delete=models.CASCADE)
ticket = models.ForeignKey(Ticket, on_delete=models.CASCADE)
@@ -172,10 +179,10 @@ class TicketStaff(models.Model):
last_connection = connections.first()
- if not last_connection.terminated:
+ if not last_connection.terminated_at:
return "Online"
- last_seen_time = last_connection.date
+ last_seen_time = last_connection.terminated_at
now = timezone.now()
time_diff = now - last_seen_time
@@ -211,7 +218,7 @@ 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)
+ terminated_at = models.DateTimeField(null=True, blank=True)
@@ -230,6 +237,18 @@ def send_signal_on_save(sender, instance, created, **kwargs):
timer.start()
+@receiver(post_save, sender=TicketUpdate)
+def new_ticket_update_handler(sender, instance, created, **kwargs):
+ if created:
+ channel_layer = get_channel_layer()
+ event = {
+ 'type': 'new_ticket_update_event',
+ 'ticket_update_id': instance.id,
+ }
+ async_to_sync(channel_layer.group_send)("new_ticket_update_group", event)
+
+
+
class TicketRead(models.Model):
ticket_update = models.ForeignKey(TicketUpdate, on_delete=models.CASCADE)
diff --git a/osinaweb/support/routing.py b/osinaweb/support/routing.py
index 727f7416..75d355b4 100644
--- a/osinaweb/support/routing.py
+++ b/osinaweb/support/routing.py
@@ -3,6 +3,8 @@ from .consumers import *
websocket_urlpatterns = [
path("ws/ticketroom/
/", TicketRoomConsumer.as_asgi()),
+ path('ws/new-tickets/', NewTicketConsumer.as_asgi()),
+ path('ws/new-ticket-updates/', NewTicketUpdateConsumer.as_asgi()),
]
\ No newline at end of file
diff --git a/osinaweb/support/templates/add_templates/add-ticket-member-modal.html b/osinaweb/support/templates/add_templates/add-ticket-member-modal.html
index f550a6dd..e9d92a68 100644
--- a/osinaweb/support/templates/add_templates/add-ticket-member-modal.html
+++ b/osinaweb/support/templates/add_templates/add-ticket-member-modal.html
@@ -19,7 +19,7 @@
Add New Member
-
diff --git a/osinaweb/support/views.py b/osinaweb/support/views.py
index 97409a5d..9594df13 100644
--- a/osinaweb/support/views.py
+++ b/osinaweb/support/views.py
@@ -24,7 +24,7 @@ def ticket_room(request, ticket_number):
last_ticket_status = TicketStatus.objects.filter(ticket=ticket).last()
- connections = TicketConnection.objects.filter(terminated=False).order_by('-id')
+ connections = TicketConnection.objects.filter(terminated_at__isnull=True).order_by('-id')
context = {
'base_template': base_template,