Algorithm of Medical Chat App with Django (Version 2)
Chasfat Projects

Algorithm of Medical Chat App with Django (Version 2)

The middle ground between Medicine and Technology still basically deals with exploration, troubleshooting, solving problems and bringing about novel ideas into life and building upon (updating) existing knowledge. In view of this, this is what I call the second version of the medic chat app algorithm I thought to expose you to. You can check the first version below.

In this episode, I thought of exposing you to the updates that so far has been made to the application. Change remains the only constant thing.

Overview of the Application

This is a project which involves building a chat application for Medics. The app will have multiple rooms where authenticated medics can chat. Each room will have a list of currently connected medics.

The technologies employed in this project are primarily Django, Channels (a Django package with web socket capabilities, Bootstrap) among others.

STEP 6: Overhauling of the consumers.py file.

Importing the necessary files

consumers.py


Updating the basic asynchronous functions of connect, disconnect, receive in the consumer.


Updating the codes of functions called within the main connect, disconnect async functions such as: send_user_list_update() used to update the list of active users when they join the room or leave; create_message() used to dynamically create messages dynamically in the Message models, so they can be preloaded when users log in to the room at other times.

All other functions have their specific usage, most of which are glaring even from the names given them.
consumers.py

STEP 7: Updating the views.py file.

Reason 1: To allow access to the app by only authenticated users. The built in Django Authentication was utilized.

Reason 2: To ensure preloaded messages (and newly loaded messages) are grouped based on their timestamps.

Reason 3: To facilitate some styling with css and Bootstrap framework.

views.py

STEP 8: Updating the templates.

Making updates to room.html to allow preloaded messages to be seamlessly integrated with incoming messages.

Integrate the room.html with both the static files (JavaScript and CSS) that are to add dynamism and styling to it respectively.

room.html
index.html


base.html

STEP 9: Styling the templates.

When it comes to styling, of course the sky is your starting point. Meanwhile due to the mainly logic driven app, much emphasis was given to the logic side than the styling. So, feel free to style the HTML appropriately to your taste.

base.css
You can have separation of the HTML codes from the styling by following the tutorial on handling static files with Django below.

Here is the link to handling static files with Django.

Due to being unable to provide the snapshot of the room.css file (dimension issue) I will provide the codebase to the room.css external CSS file which styles the room.html template.

/***** room.css file *******/
:root{
    --sender-bg-color:#2c3e50;
    --receiver-bg-color: #34495e /* Darker blue for receiver messages */

}
  
body{
    background: radial-gradient(circle, rgba(25,95,25,1) 20%, rgb(204, 182, 56) 75%);
    color:#fff;
}

#chatLog{
    display:flex;
    flex-direction:column;
    max-height: 50vh; /* Adjust as needed */
    overflow-y: scroll;
    border:1px solid #ccc;
    padding: 10px; /* Add padding inside the chat log container */
    border-radius: 10px; /* Add rounded corners to the chat log container */
    background-color: transparent; /* Set a light background color */
    box-shadow: 0 2px 5px rgba(0, 0, 0, 0.8); /* Add a subtle box shadow */
    /* Linear gradient background */
    background: radial-gradient(circle, rgba(255,255,255,1) 0%, rgba(204,204,204,1) 100%);
  /* For older browsers */
  
  
  background: -moz-radial-gradient(circle, rgba(255,255,255,1) 0%, rgba(204,204,204,1) 100%);
  background: -o-radial-gradient(circle, rgba(255,255,255,1) 0%, rgba(204,204,204,1) 100%);
  background: -moz-radial-gradient(circle, rgba(255,255,255,1) 0%, rgba(204,204,204,1) 100%);
}


.message-box {
    max-width:60%;
    padding: 10px;
    border-radius: 10px;
    border:1px solid #ccc;
    word-wrap: break-word;
    margin-bottom: 5px;
    position: relative;
    
}

.sender-message {
    background-color:var(--sender-bg-color);
    /**background-color:rgb(20,24,130); **/
    color:#fff;
    padding:10px 10px;
    align-self:flex-end;
    

}
.receiver-message {
    background-color:var(--receiver-bg-color);
    color:#fff;
    padding:10px 10px;
    align-self:flex-start;
}


.sender-name {
    font-weight: bold;
    margin-bottom: 10px;
   font-size:large;
    position: absolute;
    top: 0;
    left: 0;
}
.message-content {
    text-align: center;
   font-size:large;
   margin:10px;
    min-width:20vh;
}
.date {
    position: absolute;
    bottom: 0;
    right: 0;
    font-size: smaller;
    
    align-self:flex-end;
    font-style:italic;
    
}
.input-group{
    display:flex;
    align-items:center;
}
#chatMessageInput{
    flex: 1;
    min-height: 60px; /* Adjust as needed */
    resize: none;
    padding: 10px;
    border-radius: 10px;
    border: 1px solid #ccc;
    margin-right: 10px;
    font-size: 16px;
}
#chatMessageSend {
    padding: 10px 20px;
    border: none;
    border-radius: 10px;
    background-color: #007bff; /* Change color as needed */
    color: white;
    font-size: 16px;
    cursor: pointer;
    transition: background-color 0.3s;
  }

#chatMessageSend:hover {
    background-color: #0056b3; /* Change color as needed */
  }


.arrow {
    position: absolute;
    width: 0;
    height: 0;
    border-style: solid;
}

.arrow-left {
    border-width: 8px 12px 8px 0; /* Adjust border-width for arrow shape */
    border-color: #fff transparent #fff #fff; /* Arrow pointing left */
    top: calc(50% - 8px); /* Position arrow vertically at the middle */
    right: -12px; /* Adjust right position to align with message box */
    transform: rotate(180deg); /* Rotate arrow by 180 degrees to point outward */
    background-color: var(--receiver-bg-color); /* Match background color of sender message box */
   

}

.arrow-right {
    border-width: 8px 12px 8px 0; /* Adjust border-width for arrow shape */
    border-color: #fff transparent #fff #fff; /* Arrow pointing right */
    top: calc(50% - 8px); /* Position arrow vertically at the middle */
    left: -12px; /* Adjust left position to align with message box */
    transform: rotate(0deg); /* Rotate arrow by 180 degrees to point outward */
    background-color:var(--sender-bg-color);/* Match background color of receiver message box */
}
.group_date{
    align-self:center;
    color:#000;
    
}        


STEP 10: Adding Implementation with JavaScript on the front end.

Here we basically have two files viz: index.js and room.js

index.js

Due to finding it hard, to provide the picture of the room.js , I should provide the codebase directly as found below.

It contains the updated codes for the room.js that provides the websocket connections to intercept and handle various events such as message send, receiving messages etc.

/*** room.js file *****/
console.log("Room.js")

console.log(currentUser)

const roomName=JSON.parse(document.getElementById('roomName').textContent);

let chatLog=document.querySelector("#chatLog");
let userListElement = document.querySelector('#userList');

let chatMessageInput=document.querySelector("#chatMessageInput");


let chatMessageSend=document.querySelector("#chatMessageSend");
let onlineUsersSelector=document.querySelector("#onlineUsersSelector");


function onlineUsersSelectorAdd(value){
    if(document.querySelector("option[value='" +value+ "']")) return;
    let newOption=document.createElement("option");
    newOption.value=value;
    newOption.innerHTML=value;
    onlineUsersSelector.appendChild(newOption);

}

function onlineUsersSelectorRemove(value){
    let oldOption=document.querySelector("option[value='" + value + "']");
    if(oldOption !== null) oldOption.remove();

}


//Focus chat message when a user opens the page 
chatMessageInput.focus();

//Submit if the user presses the enter key 
chatMessageInput.onkeyup=function(e){
    if(e.keyCode===13){
        //Enter key 
       
        chatMessageSend.click();
        
    }
};

//Clear the 'chatMessageInput' and forward the message 
chatMessageSend.onclick=function(){
    console.log("Send has been clicked automatically ")
  
    if(chatMessageInput.value.length===0)return;
    chatSocket.send(JSON.stringify({
        "message":chatMessageInput.value,
        
    }));
    chatMessageInput.value=""; 
}


//Making our WebSocket connection 
let chatSocket=null
function connect(){
    chatSocket=new WebSocket("ws://"+window.location.host + "/ws/chat/" + roomName + "/")
    
    

    chatSocket.onopen=function(e){
        console.log("Successfully connected to the web socket!!!");
    }

    chatSocket.onclose=function(e){
        console.log('WebSocket connection closed, reconnecting ...')
        setTimeout(function(){
            console.log("Reconnecting...");
            connect();
        },2000);
    }

    chatSocket.onmessage=function(e){
        const data=JSON.parse(e.data);
        console.log(data);

        switch(data.type){
            case "chat_message":
                displayChatMessage(data.message,data.user);
                break;
            case "user_list_update":
                const userList = data.user_list;
                if(userList){
                    console.log(userList)
                }else{
                    console.log("Nothing to show")
                }
                
                displayUserList(userList);
                break;

                
            default:
                console.error("Unknown message type!!!")
                break;
        }
        //Automatically scrolling chat to the bottom 
        chatLog.scrollTop=chatLog.scrollHeight;

    };

   
   
    function displayChatMessage(message, user) {
        var messageClass = user === currentUser ? "sender-message" : "receiver-message";
        var arrowClass = user === currentUser ? "arrow-right" : "arrow-left"; // Adjust arrow orientation based on message sender
        var htmlMessage = '<div class="message-box ' + messageClass + '">' +
            '<div class="sender-name">' + user + '</div>' +
            '<div class="message-content">' + message + '</div>' +
            '<div class="date">' + getFormattedDateTime() + '</div>' +
           // '<div class="arrow"></div>' + //Include the arrow class here 
            '<div class="arrow ' + arrowClass + '"></div>'
            '</div>';
        chatLog.innerHTML += htmlMessage;
        chatLog.scrollTop = chatLog.scrollHeight;
    }
    
    function displayUserList(userList) {
        // Clear existing user list
        //const userListElement = document.getElementById('userList');
        userListElement.innerHTML = '';
    
        // Add users to the list
        userList.forEach(function(user) {
            const listItem = document.createElement('li');
            listItem.textContent = user;
            userListElement.appendChild(listItem);
        });
    
        // Update select options (if needed)
        const selectElement = document.getElementById('onlineusersSelector');
        selectElement.innerHTML = '';
        userList.forEach(function(user) {
            const option = document.createElement('option');
            option.value = user;
            option.textContent = user;
            selectElement.appendChild(option);
        });
    }
    

    
    function getFormattedDateTime() {
        var months = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];
        
        var currentDate = new Date();
        var year = currentDate.getFullYear();
        var month = months[currentDate.getMonth()];
        var day = currentDate.getDate();
        var hours = currentDate.getHours();
        var minutes = currentDate.getMinutes();
        var ampm = hours >= 12 ? 'p.m.' : 'a.m.';
        hours = hours % 12;
        hours = hours ? hours : 12; // Handle midnight (0 hours)
        var formattedHours = (hours < 10 ? '0' : '') + hours; // Add leading zero if needed
        var formattedMinutes = (minutes < 10 ? '0' : '') + minutes; // Add leading zero if needed
        var formattedDateTime = month + ' ' + day + ', ' + year + ' ' + formattedHours + ':' + formattedMinutes + ' ' + ampm;
        return formattedDateTime;
    }
    
    chatSocket.onerror=function(err){
        console.log("Ooops!!! An error occurred  " + err.message);
        console.log("Closing Web socket ");
        chatSocket.close();
    }
};
connect();        


Room model represents a chat room and laden with several functionalities of a typical chat room such as join, leave etc. Message represents the chat contents being exchanged.

STEP 11: Creating a superuser.

At this point, having added authenticated codes in the backend, we can only have access by creating a superuser account and some other account to test the functionalities of the app.

You create a superuser account with the command line below and follow the prompts, it's a very simple thing to do.

>python manage.py createsuperuser

#When you are done, run your server
>python manage.py runserver        


You typically have access to the Django admin GUI by typing the url in your browser while your server is running. localhost:8000/admin. The admin is quite easy to use and can be customized.





Now at this point I have boosted the application with authentication and further enhancement as promised. The world of software and application building is a continuous thing. You keep having ideas which you can improve on, hence the existence of new patches and updates to the popular softwares out there.
Feel free to link up with me for freelancing, partnership and positive collaborations.

Check my online portfolio here

Olumide Adeola

Medicine(???) | Machines </> | Music ?? | Ministry (Teaching) ????

10 个月

Hello, Yes, I have worked with Django channels to facilitate real-time chat in a telemedicine app I am building(which you referenced here). It's nice knowing you are learning channels, keep it up. When using Redis as the channel layer in Django channels, the connection may terminate in a short period of them(as you did state ), it's a common issue and these are potential causes and solution I could proffer. 1. Check your Redis Configuration: You may need to adjust Redis settings such as 'timeout', 'maxclients , 'maxmemory' based on your requirements 2. Check Resource Limitations : Verify if there are any resource limitations on the server where Redis is running , for instance memory constraints can lead to connection drops 3. Ensure the Redis version you are using with Django matches , and is not somewhat outdated. It's often advises to use the most stable release of Redis. Django channels 3.x is compatible with Django 2.2,3.0,3.1 , 3.2 etc 4. Ensure your Channel layer settings is correct within the settings.py file 5. I also suggest implementing monitoring and logging 6. Ensure there isn't premature networks too. With regard to the in-memory layer (often advised for development purpose) , I hope it's working fine.

回复
Harsh Sahu

Python Developer| Docker |Dsa with python| Full Stack Developer { Django | Django Channels | Django REST Framework | HTML | CSS | JavaScript | Bootstrap } + Data Visualization with Chart.js & Django

10 个月

Hello Olumide Adeola I have a question for you: Have you worked with Django Channels? Currently, I'm learning it. I've successfully implemented real-time send/receive functionality, but when I use Redis as the channel layer, my connection automatically gets terminated after 2-3 seconds. Have you ever encountered the same problem? I'm currently using In-memory as the channel layer.

回复

要查看或添加评论,请登录

Olumide Adeola的更多文章

社区洞察

其他会员也浏览了