Skip to content

Commit

Permalink
Merge pull request #6 from josnin/featureLoadSave
Browse files Browse the repository at this point in the history
save chat message using tortoise ORM
  • Loading branch information
josnin authored Oct 23, 2020
2 parents 22af504 + 126f31b commit 18ffcc1
Show file tree
Hide file tree
Showing 12 changed files with 169 additions and 9 deletions.
13 changes: 13 additions & 0 deletions chat/consumers.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import json
from channels.generic.websocket import AsyncWebsocketConsumer
#from asgiref.sync import await_to_sync
from chat.services import chat_save_message



class ChatConsumer(AsyncWebsocketConsumer):
""" handshake websocket front end """
Expand All @@ -20,6 +23,7 @@ async def connect(self):

await self.accept()


async def disconnect(self, code):
# Leave room group
if self.room_group_name and self.channel_name:
Expand Down Expand Up @@ -47,6 +51,15 @@ async def receive(self, text_data=None, bytes_data=None):
}
)

await chat_save_message(
username=self.scope['user'].username.title(),
room_id=self.room_name,
message=message,
message_type=message_type,
image_caption=image_caption
)


# Receive message from room group
async def chat_message(self, event):
""" exhange message here """
Expand Down
2 changes: 2 additions & 0 deletions chat/models.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
from django.db import models
from django.contrib.auth.models import Group



# Create your models here.
class ChatGroup(Group):
""" extend Group model to add extra info"""
Expand Down
20 changes: 20 additions & 0 deletions chat/services.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@

from tortoise import Tortoise, run_async
from django.conf import settings
from .tortoise_models import ChatMessage

async def chat_save_message(username, room_id, message, message_type, image_caption):

""" function to store chat message in sqlite """

await Tortoise.init(**settings.TORTOISE_INIT)
await Tortoise.generate_schemas()

await ChatMessage.create(room_id=room_id,
username=username,
message=message,
message_type=message_type,
image_caption=image_caption
)

await Tortoise.close_connections()
11 changes: 11 additions & 0 deletions chat/static/chat/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -581,3 +581,14 @@ svg {
font-size: .93rem;
}

.date_weekday {
display: grid;
justify-self: center;
padding: 5px 12px 6px;
font-size: 0.78rem;
background-color: #e1f3faff;
border-radius: 7.5px;
width: fit-content;
box-shadow: 0 1px .5px rgba(0,0,0,.13);
line-height: 21px;
}
80 changes: 73 additions & 7 deletions chat/templates/chat/room.html
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@
<script>

const roomId = '{{chatgroup.id}}'
var tempDaysWeekdays = [];

const chatSocket = new WebSocket(
`ws://${window.location.host}/ws/chat/${roomId}/`
Expand Down Expand Up @@ -116,7 +117,6 @@

document.querySelector('#chat-message-input').onpaste = function(e) {
let item = e.clipboardData.items[0];
console.log(item);
if (item.type.includes('image')) {
let blob = item.getAsFile();

Expand All @@ -139,18 +139,27 @@
msgbox.scrollTop = msgbox.scrollHeight
}

function getTime() {
let today = new Date();
function getTime(msg_time) {
if (msg_time) {
// define as Date so we can convert to acceptable date time format (with out letter T, ex. 2020-10-10T06:50:14.751 )
temp = new Date(msg_time);

// suffix UTC so it will render as local time when it use toLocalString
var today = new Date(`${temp.toLocaleString()} UTC`);
} else {
var today = new Date();
}

// format & render to local time
let time = today.toLocaleString([], { hour: '2-digit', minute: '2-digit' });
return time

}

function broadcastMessage(msg, user, msg_type, img_caption) {
function broadcastMessage(msg, user, msg_type, img_caption, msg_time) {
// create a new div element
let newDiv = document.createElement("div");
// and give it some content
console.log(msg_type, 'broadcastMessage')
if (msg_type == 'image') {
msg = `<img src="${msg}"> <br/> ${img_caption}`;
}
Expand All @@ -159,7 +168,7 @@
var msg1 = `<div class="right-msg-container">
<div class="s-message">
<div class="s-msg">${msg}</div>
<div class="s-time">${getTime()}</div>
<div class="s-time">${getTime(msg_time)}</div>
</div>
<div class="s-tail"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 8 13" preserveAspectRatio="none" width="8" height="13"><path opacity=".5" d="M5.188 1H0v11.193l6.467-8.625C7.526 2.156 6.958 1 5.188 1z"></path><path fill="#dcf8c6ff" d="M5.188 0H0v11.193l6.467-8.625C7.526 1.156 6.958 0 5.188 0z"></path></svg></div>
</div>`
Expand All @@ -169,10 +178,16 @@
<div class="r-message" >
<div class="r-user"><a href="#">${user}</a></div>
<div class="r-msg">${msg}</div>
<div class="r-time">${getTime()}</div>
<div class="r-time">${getTime(msg_time)}</div>
</div>
</div>`
}

if (msg_time) {
showDatesWeekDays(msg_time)
} else {
showDatesWeekDays(new Date())
}

newDiv.innerHTML = msg1;

Expand Down Expand Up @@ -232,5 +247,56 @@
}));
}


function showDatesWeekDays(date_created) {
// add the newly created element and its content into the DOM

dt = new Date(date_created)

if (!tempDaysWeekdays.includes(dt.toLocaleDateString())) {
let newDiv = document.createElement("div");
let currentDiv = document.getElementById("new-message-chat");
let parentDiv = currentDiv.parentNode;
let days = ['Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday'];

if (dt.toDateString() == new Date().toDateString()) {
// display TODAY in message
date_weekday = 'TODAY';
} else if(dt > getDateBefore()) {
// display week day in message
date_weekday = days[dt.getDay()].toUpperCase()
} else {
// display date in message
date_weekday = dt.toLocaleDateString();
}

newDiv.style.display = "grid";
newDiv.innerHTML = `<div class="date_weekday">${date_weekday}</div>`
parentDiv.insertBefore(newDiv, currentDiv);

tempDaysWeekdays.push(dt.toLocaleDateString())
}

}

function getDateBefore(days=7) {
// calculate the last 7 days date
// 7 (days) * 24 (hours) * 60 (minutes) * 60 (seconds) * 1000 (milliseconds ) = 604800000 or 7 days in milliseconds.
daysInMs= days * 24 * 60 * 60 * 1000
return new Date(Date.now() - daysInMs)
}

function loadMessage() {
fetch("{% url 'chat:history' chatgroup.id %}")
.then( response => response.json() )
.then( data => {
for (let msg of data) {
broadcastMessage(msg.message, msg.username, msg.message_type, msg.image_caption, msg.date_created)
}
scrollBottom()
})
}
loadMessage()

</script>
{% endblock content %}
21 changes: 21 additions & 0 deletions chat/tortoise_models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@

from tortoise import fields
from tortoise.models import Model

class ChatMessage(Model):
""" use to store chat history message
make used of tortoise ORM since its support asyncio ORM
"""
id = fields.IntField(pk=True)
room_id = fields.IntField(null=True)
username = fields.CharField(max_length=50, null=True)
message = fields.TextField()
message_type = fields.CharField(max_length=50, null=True)
image_caption = fields.CharField(max_length=50, null=True)
date_created = fields.DatetimeField(null=True, auto_now_add=True)

class Meta:
table = 'chat_chatmessage'

def __str__(self):
return self.message
1 change: 1 addition & 0 deletions chat/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

urlpatterns = [
path('', views.index, name='index'),
path('history/<str:room_id>/', views.history, name='history'),
path('unauthorized/', views.unauthorized, name='unauthorized'),
path('<str:group_id>/', views.room, name='room'),
]
17 changes: 15 additions & 2 deletions chat/views.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
from django.shortcuts import render
from django.contrib.auth.decorators import login_required
from django.urls import reverse
from django.http import HttpResponseRedirect
from django.http import HttpResponseRedirect, JsonResponse
from asgiref.sync import sync_to_async
from tortoise import Tortoise
from django.conf import settings

# Create your views here.

# chat/views.py
from django.shortcuts import render
from .models import ChatGroup
from .tortoise_models import ChatMessage

@login_required
def index(request):
Expand Down Expand Up @@ -46,4 +50,13 @@ def room(request, group_id):

@login_required
def unauthorized(request):
return render(request, 'chat/unauthorized.html', {})
return render(request, 'chat/unauthorized.html', {})


async def history(request, room_id):

await Tortoise.init(**settings.TORTOISE_INIT)
chat_message = await ChatMessage.filter(room_id=room_id).order_by('date_created').values()
await Tortoise.close_connections()

return await sync_to_async(JsonResponse)(chat_message, safe=False)
Binary file added db.sqlite3.tortoise
Binary file not shown.
8 changes: 8 additions & 0 deletions django_channel_tutorial/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -134,3 +134,11 @@

MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'django_channel_tutorial/media')


TORTOISE_INIT = {
"db_url": "sqlite://db.sqlite3.tortoise",
"modules" : {
"models": ["chat.tortoise_models"]
}
}
5 changes: 5 additions & 0 deletions requirements
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
aioredis==1.3.1
aiosqlite==0.15.0
asgiref==3.2.10
async-timeout==3.0.1
attrs==20.2.0
Expand All @@ -15,17 +16,21 @@ hiredis==1.1.0
hyperlink==20.0.1
idna==2.10
incremental==17.5.0
iso8601==0.1.13
msgpack==1.0.0
Pillow==7.2.0
pyasn1==0.4.8
pyasn1-modules==0.2.8
pycparser==2.20
PyHamcrest==2.0.2
pyOpenSSL==19.1.0
PyPika==0.42.1
pytz==2020.1
service-identity==18.1.0
six==1.15.0
sqlparse==0.3.1
tortoise-orm==0.16.16
Twisted==20.3.0
txaio==20.4.1
typing-extensions==3.7.4.3
zope.interface==5.1.0
Empty file removed test
Empty file.

0 comments on commit 18ffcc1

Please sign in to comment.