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.
304 lines
11 KiB
Python
304 lines
11 KiB
Python
from django.db import models
|
|
from osinacore.models import *
|
|
import mimetypes
|
|
import os
|
|
from channels.layers import get_channel_layer
|
|
from asgiref.sync import async_to_sync
|
|
from fcm_django.models import FCMDevice
|
|
from firebase_admin.messaging import Message,AndroidConfig,APNSConfig,APNSPayload,ApsAlert,Aps,AndroidNotification, Notification as NotificationFB
|
|
from django.utils.safestring import mark_safe
|
|
|
|
|
|
|
|
def send_notification(notification):
|
|
notification_data = {
|
|
'title': notification.title,
|
|
'body': mark_safe(notification.message),
|
|
}
|
|
|
|
sound = 'default'
|
|
if notification.type == 'Visitor':
|
|
sound = 'https://osina.ositcom.com/static/notifications/new-visitor.mp3'
|
|
|
|
android_config = AndroidConfig(
|
|
notification=AndroidNotification(
|
|
title=notification_data['title'],
|
|
body=notification_data['body'],
|
|
sound=sound
|
|
)
|
|
)
|
|
|
|
apns_config = APNSConfig(
|
|
payload=APNSPayload(
|
|
aps=Aps(
|
|
alert=ApsAlert(
|
|
title=notification_data['title'],
|
|
body=notification_data['body'],
|
|
),
|
|
sound=sound,
|
|
)
|
|
)
|
|
)
|
|
|
|
if notification.image:
|
|
FCMDevice.objects.send_message(
|
|
Message(
|
|
notification=NotificationFB(
|
|
title=notification_data['title'],
|
|
body=notification_data['body'],
|
|
image=notification.image,
|
|
),
|
|
data={"image": notification.image},
|
|
android=android_config,
|
|
apns=apns_config
|
|
)
|
|
)
|
|
else:
|
|
FCMDevice.objects.send_message(
|
|
Message(
|
|
notification=NotificationFB(
|
|
title=notification_data['title'],
|
|
body=notification_data['body'],
|
|
),
|
|
android=android_config,
|
|
apns=apns_config
|
|
)
|
|
)
|
|
|
|
# Create your models here.
|
|
|
|
class ChatNotification(models.Model):
|
|
TYPES = (
|
|
('Visitor', 'Visitor'),
|
|
('Chat', 'Chat')
|
|
)
|
|
title = models.CharField(max_length=255)
|
|
message = models.TextField()
|
|
image = models.TextField(blank=True,null=True)
|
|
created_at = models.DateTimeField(auto_now_add=True)
|
|
type = models.CharField(max_length=8, choices=TYPES, null=True)
|
|
def save(self, *args, **kwargs):
|
|
is_new = not self.pk
|
|
super().save(*args, **kwargs)
|
|
if is_new:
|
|
send_notification(self)
|
|
|
|
|
|
class Visitor(models.Model):
|
|
session_id = models.CharField(max_length=300)
|
|
ip_address = models.CharField(max_length=300)
|
|
country = models.CharField(max_length=15, null=True)
|
|
region = models.CharField(max_length=25, null=True)
|
|
name = models.CharField(max_length=200, blank=True, null=True)
|
|
mobile_number = models.CharField(max_length=10, null=True, blank=True)
|
|
email = models.CharField(max_length=100, null=True, blank=True)
|
|
browser_name = models.CharField(max_length=100, null=True, blank=True)
|
|
os_name = models.CharField(max_length=100, null=True, blank=True)
|
|
@property
|
|
def flag_image_url(self):
|
|
flag_url = f"https://osina.ositcom.com/static/images/flags/{self.country.lower()}.svg"
|
|
return flag_url
|
|
@property
|
|
def notification_flag_image_url(self):
|
|
flag_url = f"https://flagcdn.com/w640/{self.country.lower()}.png"
|
|
return flag_url
|
|
@property
|
|
def is_online(self):
|
|
latest_log = self.visitorlog_set.order_by('-visit_date').first()
|
|
if latest_log and latest_log.left_date is None:
|
|
return True
|
|
return False
|
|
@property
|
|
def total_duration(self):
|
|
total_seconds = 0
|
|
for log in self.visitorlog_set.all():
|
|
end_time = log.left_date if log.left_date else timezone.now()
|
|
total_seconds += (end_time - log.visit_date).total_seconds()
|
|
|
|
duration = timedelta(seconds=total_seconds)
|
|
hours, remainder = divmod(duration.total_seconds(), 3600)
|
|
minutes, seconds = divmod(remainder, 60)
|
|
|
|
if hours >= 1:
|
|
return f"{int(hours):02}:{int(minutes):02}:{int(seconds):02}"
|
|
else:
|
|
return f"{int(minutes):02}:{int(seconds):02}"
|
|
|
|
def save(self, *args, **kwargs):
|
|
super().save(*args, **kwargs)
|
|
channel_layer = get_channel_layer()
|
|
event = {
|
|
'type': 'new_visitor_update_handler',
|
|
'visitor_id': self.id,
|
|
'action': 'new_visitor'
|
|
}
|
|
async_to_sync(channel_layer.group_send)("osichat", event)
|
|
|
|
|
|
|
|
|
|
|
|
class VisitorLog(models.Model):
|
|
visitor = models.ForeignKey(Visitor, on_delete=models.CASCADE, null=True)
|
|
url = models.URLField()
|
|
title = models.CharField(null=True, blank=True, max_length=500)
|
|
referrer = models.URLField(null=True, blank=True)
|
|
visit_date = models.DateTimeField(null=True)
|
|
left_date = models.DateTimeField(null=True)
|
|
@property
|
|
def log_duration(self):
|
|
end_time = self.left_date if self.left_date else timezone.now()
|
|
total_seconds = (end_time - self.visit_date).total_seconds()
|
|
|
|
duration = timedelta(seconds=total_seconds)
|
|
hours, remainder = divmod(duration.total_seconds(), 3600)
|
|
minutes, seconds = divmod(remainder, 60)
|
|
|
|
if hours >= 1:
|
|
return f"{int(hours):02}:{int(minutes):02}:{int(seconds):02}"
|
|
else:
|
|
return f"{int(minutes):02}:{int(seconds):02}"
|
|
|
|
def save(self, *args, **kwargs):
|
|
is_first_log = self.visitor.visitorlog_set.count() == 0
|
|
if self.left_date:
|
|
action = 'end_log'
|
|
else:
|
|
action = 'new_log'
|
|
super().save(*args, **kwargs)
|
|
channel_layer = get_channel_layer()
|
|
event = {
|
|
'type': 'new_visitor_update_handler',
|
|
'visitor_id': self.visitor.id,
|
|
'action': action
|
|
}
|
|
async_to_sync(channel_layer.group_send)("osichat", event)
|
|
if is_first_log:
|
|
self.send_visitor_notification()
|
|
else:
|
|
last_log = self.visitor.visitorlog_set.exclude(id=self.id).order_by('-visit_date').first()
|
|
if last_log:
|
|
time_difference = self.visit_date - last_log.visit_date
|
|
if time_difference > timedelta(minutes=5):
|
|
self.send_visitor_notification(is_repeat=True)
|
|
|
|
def send_visitor_notification(self, is_repeat=False):
|
|
if is_repeat:
|
|
title = "Existing visitor new acitivity on Ositcom!"
|
|
if self.title:
|
|
body = f"Visitor navigated to {self.title}"
|
|
elif self.url:
|
|
body = f"Visitor returned to {self.url}"
|
|
else:
|
|
title = "New visitor on Ositcom!"
|
|
if self.title:
|
|
body = f"New visitor navigated to {self.title}"
|
|
elif self.url:
|
|
body = f"New visitor navigated to {self.url}"
|
|
notification = ChatNotification.objects.create(
|
|
title=title,
|
|
message = body,
|
|
image = self.visitor.notification_flag_image_url,
|
|
type = "Visitor"
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
class ChatRoom(models.Model):
|
|
name = models.CharField(max_length=300)
|
|
created_by = models.ForeignKey(User, null=True, on_delete=models.SET_NULL, blank=True)
|
|
date_created = models.DateTimeField()
|
|
terminated_by = models.ForeignKey(User, null=True, on_delete=models.SET_NULL, blank=True, related_name='terminated_chatrooms')
|
|
date_terminated = models.DateTimeField(null=True, blank=True)
|
|
@property
|
|
def last_updated(self):
|
|
last_message = ChatMessage.objects.filter(room=self).order_by('-date_sent').first()
|
|
if last_message:
|
|
last_updated_time = last_message.date_sent
|
|
else:
|
|
last_updated_time = self.date_created
|
|
|
|
now = datetime.now()
|
|
if last_updated_time.date() == now.date():
|
|
return last_updated_time.strftime('%I:%M %p')
|
|
else:
|
|
return last_updated_time.strftime('%d-%m-%Y')
|
|
def unread_messages(self, user):
|
|
return ChatMessage.objects.filter(room=self).exclude(member=user).exclude(chatmessageseen__member=user).count()
|
|
def save(self, *args, **kwargs):
|
|
super().save(*args, **kwargs)
|
|
channel_layer = get_channel_layer()
|
|
event = {
|
|
'type': 'new_chat_update_handler',
|
|
'chatroom_id': self.id,
|
|
}
|
|
async_to_sync(channel_layer.group_send)("osichat", event)
|
|
|
|
|
|
|
|
class ChatRoomGuest(models.Model):
|
|
room = models.OneToOneField(ChatRoom, on_delete=models.CASCADE, null=True)
|
|
visitor = models.ForeignKey(Visitor, null=True, on_delete=models.CASCADE)
|
|
|
|
|
|
class ChatRoomReview(models.Model):
|
|
REACTION_CHOICES = (
|
|
('Happy', 'Happy'),
|
|
('Indifferent', 'Indifferent'),
|
|
('Sad', 'Sad'),
|
|
)
|
|
room = models.OneToOneField(ChatRoom, on_delete=models.CASCADE, null=True)
|
|
reaction = models.CharField(max_length=50, choices=REACTION_CHOICES, null=True)
|
|
details = models.TextField(null=True, blank=True)
|
|
|
|
class ChatMember(models.Model):
|
|
member = models.ForeignKey(User, on_delete=models.CASCADE)
|
|
room = models.ForeignKey(ChatRoom, on_delete=models.CASCADE)
|
|
date_joined = models.DateTimeField()
|
|
|
|
|
|
class ChatProject(models.Model):
|
|
room = models.OneToOneField(ChatRoom, on_delete=models.CASCADE)
|
|
project = models.ForeignKey(Project, on_delete=models.CASCADE)
|
|
public = models.BooleanField()
|
|
|
|
|
|
class ChatMessage(models.Model):
|
|
room = models.ForeignKey(ChatRoom, on_delete=models.CASCADE, null=True)
|
|
member = models.ForeignKey(User, null=True, on_delete=models.SET_NULL, blank=True)
|
|
content = models.TextField(null=True, blank=True)
|
|
date_sent = models.DateTimeField()
|
|
def save(self, *args, **kwargs):
|
|
super().save(*args, **kwargs)
|
|
channel_layer = get_channel_layer()
|
|
event = {
|
|
'type': 'new_chat_update_handler',
|
|
'chatroom_id': self.room.id,
|
|
}
|
|
async_to_sync(channel_layer.group_send)("osichat", event)
|
|
|
|
|
|
class ChatMessageAttachment(models.Model):
|
|
message = models.OneToOneField(ChatMessage, on_delete=models.CASCADE)
|
|
attachment = models.TextField()
|
|
def is_image(self):
|
|
mime_type, _ = mimetypes.guess_type(self.attachment)
|
|
return mime_type and mime_type.startswith('image')
|
|
@property
|
|
def file_name(self):
|
|
return os.path.basename(self.attachment)
|
|
|
|
|
|
class ChatMessageReaction(models.Model):
|
|
message = models.ForeignKey(ChatMessage, on_delete=models.CASCADE)
|
|
member = models.ForeignKey(User, on_delete=models.CASCADE)
|
|
reaction = models.CharField(max_length=200)
|
|
|
|
|
|
class ChatMessageSeen(models.Model):
|
|
message = models.ForeignKey(ChatMessage, on_delete=models.CASCADE)
|
|
member = models.ForeignKey(User, on_delete=models.CASCADE, null=True, blank=True)
|
|
guest = models.ForeignKey(ChatRoomGuest, on_delete=models.CASCADE, null=True, blank=True)
|
|
seen_date = models.DateTimeField(null=True) |