diff --git a/osinaweb/db.sqlite3 b/osinaweb/db.sqlite3 index 663da097..f316631d 100644 Binary files a/osinaweb/db.sqlite3 and b/osinaweb/db.sqlite3 differ diff --git a/osinaweb/osinacore/__pycache__/consumers.cpython-310.pyc b/osinaweb/osinacore/__pycache__/consumers.cpython-310.pyc index bbb085f3..eae2be93 100644 Binary files a/osinaweb/osinacore/__pycache__/consumers.cpython-310.pyc and b/osinaweb/osinacore/__pycache__/consumers.cpython-310.pyc differ diff --git a/osinaweb/osinacore/__pycache__/custom_context.cpython-310.pyc b/osinaweb/osinacore/__pycache__/custom_context.cpython-310.pyc index cae0b7ee..fe084a9c 100644 Binary files a/osinaweb/osinacore/__pycache__/custom_context.cpython-310.pyc and b/osinaweb/osinacore/__pycache__/custom_context.cpython-310.pyc differ diff --git a/osinaweb/osinacore/__pycache__/models.cpython-310.pyc b/osinaweb/osinacore/__pycache__/models.cpython-310.pyc index 4ff22a53..0856a296 100644 Binary files a/osinaweb/osinacore/__pycache__/models.cpython-310.pyc and b/osinaweb/osinacore/__pycache__/models.cpython-310.pyc differ diff --git a/osinaweb/osinacore/__pycache__/routing.cpython-310.pyc b/osinaweb/osinacore/__pycache__/routing.cpython-310.pyc index fa5e200b..17c9ec1a 100644 Binary files a/osinaweb/osinacore/__pycache__/routing.cpython-310.pyc and b/osinaweb/osinacore/__pycache__/routing.cpython-310.pyc differ diff --git a/osinaweb/osinacore/__pycache__/views.cpython-310.pyc b/osinaweb/osinacore/__pycache__/views.cpython-310.pyc index a0b99cad..94457723 100644 Binary files a/osinaweb/osinacore/__pycache__/views.cpython-310.pyc and b/osinaweb/osinacore/__pycache__/views.cpython-310.pyc differ diff --git a/osinaweb/osinacore/consumers.py b/osinaweb/osinacore/consumers.py new file mode 100644 index 00000000..2afe06d8 --- /dev/null +++ b/osinaweb/osinacore/consumers.py @@ -0,0 +1,93 @@ +from channels.generic.websocket import WebsocketConsumer +from .models import * +import json +from django.template.loader import render_to_string +from asgiref.sync import async_to_sync +import threading + +def get_last_seen(user): + connection = Connection.objects.filter(user=user).last() + if not connection.exists(): + 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 OnlineUserConsumer(WebsocketConsumer): + def connect(self): + self.user = self.scope['user'] + existing_connection = Connection.objects.filter(user=self.user).last() + if existing_connection: + self.connection = existing_connection + self.connection.online = True + self.connection.disconnected = False + self.connection.save() + else: + self.connection = Connection.objects.create(user=self.user, online=True) + + async_to_sync(self.channel_layer.group_add)( + 'online_users', self.channel_name + ) + self.accept() + self.modify_online_user() + + def disconnect(self, close_code): + self.last_seen = datetime.now() + self.connection.disconnected = True + self.connection.save() + timer_thread = threading.Timer(10, self.check_disconnect_status) + timer_thread.start() + + def check_disconnect_status(self): + connection = Connection.objects.filter(user=self.user).last() + if connection.disconnected: + self.connection.last_seen = self.last_seen + self.connection.online = False + self.connection.save() + self.modify_online_user() + + def modify_online_user(self): + connections = Connection.objects.filter(online=True) + online_users_ids = [connection.user.id for connection in connections] + customer_connections = [] + staff_connections = [] + for connection in connections: + if hasattr(connection.user, 'customerprofile'): + customer_connections.append(connection) + elif hasattr(connection.user, 'staffprofile'): + staff_connections.append(connection) + + print(staff_connections) + event = { + 'type': 'online_user_connection_handler', + 'staff_connections': staff_connections, + 'customer_connections': customer_connections, + 'online_users_ids': online_users_ids + } + async_to_sync(self.channel_layer.group_send)( + 'online_users', event + ) + + def online_user_connection_handler(self, event): + context = { + 'staff_connections': event['staff_connections'], + 'customer_connections': event['customer_connections'], + } + html = render_to_string("details_templates/partials/recently-online.html", context=context) + self.send(text_data=json.dumps({ + 'event_type': 'online_user_status', + 'html': html, + 'online_users_ids': event.get('online_users_ids', []) + })) diff --git a/osinaweb/osinacore/custom_context.py b/osinaweb/osinacore/custom_context.py index 8da14f85..342d940e 100644 --- a/osinaweb/osinacore/custom_context.py +++ b/osinaweb/osinacore/custom_context.py @@ -23,15 +23,6 @@ def calculate_time_ago(status): def utilities(request): - latest_connections = Connection.objects.filter(user__staffprofile__isnull=False).values('user').annotate(latest_connection=Max('date')) - online_staff_profiles = [] - for connection in latest_connections: - user_id = connection['user'] - latest_connection = connection['latest_connection'] - last_connection = Connection.objects.filter(user_id=user_id, date=latest_connection).first() - if last_connection.status == 'Online': - online_staff_profiles.append(last_connection.user.staffprofile) - notes = None recent_note = None @@ -45,12 +36,6 @@ def utilities(request): if request.user.is_authenticated and StaffProfile.objects.filter(user=request.user): notes = Note.objects.filter(user=request.user).order_by('-date')[:6] recent_note = Note.objects.filter(user=request.user).last() - last_user_activity = Connection.objects.filter(user=request.user).last() - if last_user_activity and last_user_activity.status == 'Offline': - user_offline = True - else: - user_offline = False - if request.user.is_superuser: open_task_count = Task.objects.filter(status='Open').count() @@ -141,7 +126,6 @@ def utilities(request): 'latest_statuses_time_ago': latest_statuses_time_ago, 'notes' : notes, 'recent_note' : recent_note, - 'online_staff_profiles' : online_staff_profiles, 'user_offline' : user_offline, 'recent_logged_in_staffs' : recent_logged_in_staffs, 'recent_logged_in_customers' : recent_logged_in_customers, diff --git a/osinaweb/osinacore/delete/__pycache__/urls.cpython-310.pyc b/osinaweb/osinacore/delete/__pycache__/urls.cpython-310.pyc index 4550f0b0..4d6ecfc7 100644 Binary files a/osinaweb/osinacore/delete/__pycache__/urls.cpython-310.pyc and b/osinaweb/osinacore/delete/__pycache__/urls.cpython-310.pyc differ diff --git a/osinaweb/osinacore/delete/__pycache__/views.cpython-310.pyc b/osinaweb/osinacore/delete/__pycache__/views.cpython-310.pyc index 28de45ec..ec01aeaa 100644 Binary files a/osinaweb/osinacore/delete/__pycache__/views.cpython-310.pyc and b/osinaweb/osinacore/delete/__pycache__/views.cpython-310.pyc differ diff --git a/osinaweb/osinacore/migrations/0086_remove_connection_status_connection_terminated_at.py b/osinaweb/osinacore/migrations/0086_remove_connection_status_connection_terminated_at.py new file mode 100644 index 00000000..fc0363fe --- /dev/null +++ b/osinaweb/osinacore/migrations/0086_remove_connection_status_connection_terminated_at.py @@ -0,0 +1,22 @@ +# Generated by Django 4.2.5 on 2024-07-08 05:45 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('osinacore', '0085_rename_date_staffposition_start_date_and_more'), + ] + + operations = [ + migrations.RemoveField( + model_name='connection', + name='status', + ), + migrations.AddField( + model_name='connection', + name='terminated_at', + field=models.DateTimeField(blank=True, null=True), + ), + ] diff --git a/osinaweb/osinacore/migrations/0087_remove_connection_date.py b/osinaweb/osinacore/migrations/0087_remove_connection_date.py new file mode 100644 index 00000000..bcf920cf --- /dev/null +++ b/osinaweb/osinacore/migrations/0087_remove_connection_date.py @@ -0,0 +1,17 @@ +# Generated by Django 4.2.5 on 2024-07-08 06:05 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('osinacore', '0086_remove_connection_status_connection_terminated_at'), + ] + + operations = [ + migrations.RemoveField( + model_name='connection', + name='date', + ), + ] diff --git a/osinaweb/osinacore/migrations/0088_connection_date.py b/osinaweb/osinacore/migrations/0088_connection_date.py new file mode 100644 index 00000000..1c49d8e5 --- /dev/null +++ b/osinaweb/osinacore/migrations/0088_connection_date.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.5 on 2024-07-08 06:15 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('osinacore', '0087_remove_connection_date'), + ] + + operations = [ + migrations.AddField( + model_name='connection', + name='date', + field=models.DateTimeField(null=True), + ), + ] diff --git a/osinaweb/osinacore/migrations/0089_connection_connected.py b/osinaweb/osinacore/migrations/0089_connection_connected.py new file mode 100644 index 00000000..5998b4ac --- /dev/null +++ b/osinaweb/osinacore/migrations/0089_connection_connected.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.5 on 2024-07-08 08:52 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('osinacore', '0088_connection_date'), + ] + + operations = [ + migrations.AddField( + model_name='connection', + name='connected', + field=models.BooleanField(default=True), + ), + ] diff --git a/osinaweb/osinacore/migrations/0090_rename_terminated_at_connection_last_seen.py b/osinaweb/osinacore/migrations/0090_rename_terminated_at_connection_last_seen.py new file mode 100644 index 00000000..524e0168 --- /dev/null +++ b/osinaweb/osinacore/migrations/0090_rename_terminated_at_connection_last_seen.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.5 on 2024-07-08 08:55 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('osinacore', '0089_connection_connected'), + ] + + operations = [ + migrations.RenameField( + model_name='connection', + old_name='terminated_at', + new_name='last_seen', + ), + ] diff --git a/osinaweb/osinacore/migrations/0091_alter_connection_user.py b/osinaweb/osinacore/migrations/0091_alter_connection_user.py new file mode 100644 index 00000000..f7d3af73 --- /dev/null +++ b/osinaweb/osinacore/migrations/0091_alter_connection_user.py @@ -0,0 +1,21 @@ +# Generated by Django 4.2.5 on 2024-07-08 08:55 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('osinacore', '0090_rename_terminated_at_connection_last_seen'), + ] + + operations = [ + migrations.AlterField( + model_name='connection', + name='user', + field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL), + ), + ] diff --git a/osinaweb/osinacore/migrations/0092_rename_connected_connection_online.py b/osinaweb/osinacore/migrations/0092_rename_connected_connection_online.py new file mode 100644 index 00000000..4b25a33f --- /dev/null +++ b/osinaweb/osinacore/migrations/0092_rename_connected_connection_online.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.5 on 2024-07-08 08:57 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('osinacore', '0091_alter_connection_user'), + ] + + operations = [ + migrations.RenameField( + model_name='connection', + old_name='connected', + new_name='online', + ), + ] diff --git a/osinaweb/osinacore/migrations/0093_connection_disconnected.py b/osinaweb/osinacore/migrations/0093_connection_disconnected.py new file mode 100644 index 00000000..f2a45deb --- /dev/null +++ b/osinaweb/osinacore/migrations/0093_connection_disconnected.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.5 on 2024-07-08 09:06 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('osinacore', '0092_rename_connected_connection_online'), + ] + + operations = [ + migrations.AddField( + model_name='connection', + name='disconnected', + field=models.BooleanField(default=False), + ), + ] diff --git a/osinaweb/osinacore/migrations/__pycache__/0086_remove_connection_status_connection_terminated_at.cpython-310.pyc b/osinaweb/osinacore/migrations/__pycache__/0086_remove_connection_status_connection_terminated_at.cpython-310.pyc new file mode 100644 index 00000000..d52af772 Binary files /dev/null and b/osinaweb/osinacore/migrations/__pycache__/0086_remove_connection_status_connection_terminated_at.cpython-310.pyc differ diff --git a/osinaweb/osinacore/migrations/__pycache__/0087_remove_connection_date.cpython-310.pyc b/osinaweb/osinacore/migrations/__pycache__/0087_remove_connection_date.cpython-310.pyc new file mode 100644 index 00000000..2cd31808 Binary files /dev/null and b/osinaweb/osinacore/migrations/__pycache__/0087_remove_connection_date.cpython-310.pyc differ diff --git a/osinaweb/osinacore/migrations/__pycache__/0088_connection_date.cpython-310.pyc b/osinaweb/osinacore/migrations/__pycache__/0088_connection_date.cpython-310.pyc new file mode 100644 index 00000000..e707cfd7 Binary files /dev/null and b/osinaweb/osinacore/migrations/__pycache__/0088_connection_date.cpython-310.pyc differ diff --git a/osinaweb/osinacore/migrations/__pycache__/0089_connection_connected.cpython-310.pyc b/osinaweb/osinacore/migrations/__pycache__/0089_connection_connected.cpython-310.pyc new file mode 100644 index 00000000..033bfb10 Binary files /dev/null and b/osinaweb/osinacore/migrations/__pycache__/0089_connection_connected.cpython-310.pyc differ diff --git a/osinaweb/osinacore/migrations/__pycache__/0090_rename_terminated_at_connection_last_seen.cpython-310.pyc b/osinaweb/osinacore/migrations/__pycache__/0090_rename_terminated_at_connection_last_seen.cpython-310.pyc new file mode 100644 index 00000000..7ee8c542 Binary files /dev/null and b/osinaweb/osinacore/migrations/__pycache__/0090_rename_terminated_at_connection_last_seen.cpython-310.pyc differ diff --git a/osinaweb/osinacore/migrations/__pycache__/0091_alter_connection_user.cpython-310.pyc b/osinaweb/osinacore/migrations/__pycache__/0091_alter_connection_user.cpython-310.pyc new file mode 100644 index 00000000..2cae5dd5 Binary files /dev/null and b/osinaweb/osinacore/migrations/__pycache__/0091_alter_connection_user.cpython-310.pyc differ diff --git a/osinaweb/osinacore/migrations/__pycache__/0092_rename_connected_connection_online.cpython-310.pyc b/osinaweb/osinacore/migrations/__pycache__/0092_rename_connected_connection_online.cpython-310.pyc new file mode 100644 index 00000000..02a7f25b Binary files /dev/null and b/osinaweb/osinacore/migrations/__pycache__/0092_rename_connected_connection_online.cpython-310.pyc differ diff --git a/osinaweb/osinacore/migrations/__pycache__/0093_connection_disconnected.cpython-310.pyc b/osinaweb/osinacore/migrations/__pycache__/0093_connection_disconnected.cpython-310.pyc new file mode 100644 index 00000000..15ee9d83 Binary files /dev/null and b/osinaweb/osinacore/migrations/__pycache__/0093_connection_disconnected.cpython-310.pyc differ diff --git a/osinaweb/osinacore/models.py b/osinaweb/osinacore/models.py index 506623d2..9cdf8e40 100644 --- a/osinaweb/osinacore/models.py +++ b/osinaweb/osinacore/models.py @@ -388,12 +388,9 @@ class DailyReport(models.Model): class Connection(models.Model): - STATUS_CHOICES = ( - ('Online', 'Online'), - ('Offline', 'Offline'), - ) - status = models.CharField(max_length=200, choices=STATUS_CHOICES, null=True) date = models.DateTimeField(null=True) - user = models.ForeignKey(User, on_delete=models.CASCADE) - + 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) diff --git a/osinaweb/osinacore/routing.py b/osinaweb/osinacore/routing.py new file mode 100644 index 00000000..30b01d3f --- /dev/null +++ b/osinaweb/osinacore/routing.py @@ -0,0 +1,8 @@ +from django.urls import path +from .consumers import * + +websocket_urlpatterns = [ + path("ws/online-users/", OnlineUserConsumer.as_asgi()), + + +] \ No newline at end of file diff --git a/osinaweb/osinacore/templates/add-edit-main.html b/osinaweb/osinacore/templates/add-edit-main.html index 0e8babdc..77b5bb4d 100644 --- a/osinaweb/osinacore/templates/add-edit-main.html +++ b/osinaweb/osinacore/templates/add-edit-main.html @@ -944,6 +944,9 @@ + + + diff --git a/osinaweb/osinacore/templates/customer_main.html b/osinaweb/osinacore/templates/customer_main.html index 8d712ccb..b6fe0b79 100644 --- a/osinaweb/osinacore/templates/customer_main.html +++ b/osinaweb/osinacore/templates/customer_main.html @@ -610,6 +610,9 @@ + + + diff --git a/osinaweb/osinacore/templates/recent-activities.html b/osinaweb/osinacore/templates/details_templates/partials/recent-activities.html similarity index 97% rename from osinaweb/osinacore/templates/recent-activities.html rename to osinaweb/osinacore/templates/details_templates/partials/recent-activities.html index 41dc8156..3322d669 100644 --- a/osinaweb/osinacore/templates/recent-activities.html +++ b/osinaweb/osinacore/templates/details_templates/partials/recent-activities.html @@ -126,7 +126,7 @@ {% if latest_statuses_time_ago %} {% for latest in latest_statuses_time_ago %} -
Recently Online
+{{staff_connection.user.first_name}} + {{staff_connection.user.last_name}}
+Online
+