[Tutorial] Realtime (socket.io)

Summary

This section going to explain how socket.io is used in frappe and we can use the frappe Realtime Feature in ERPNext.

Reference Document


Get Started

You can code from the Custom App, Server Script & Client Script its depends on the requirement.

Here is the scenario Assume you have a doctype with the name of Less Hour Request from that context the below code will be there. you can implement this any doctype have to change the values accordingly.

frappe.publish_progress

We have to use this feature in Python file or Server Script in the site.

This progress will published while save but this code writen in Python file in custom app. please adjust the code for the Server Script.

less_hour_request.py

import frappe
import time
from frappe.model.document import Document

class LessHourRequest(Document):
	

	def validate(self):
		frappe.enqueue_doc(
			self.doctype,
			self.name,
			"set_publish_progress_bar", # name of the controller method
			queue="long",
			timeout=4000,
			param="My Discription"
		)

  
	def set_publish_progress_bar(self, **kwargs):
		for i in range(1,101):
			time.sleep(1)
			description = f"frappe.publish_realtime {i} and Keyword Argument {kwargs}"
			frappe.publish_progress(i, title='Some title', description=description, doctype=self.doctype)

output

frappe.realtime.on

The frappe.publish_progress is completely different we are updating the realtime progress directly to the Client from the Server but frappe.realtime.on is completely different we can use this for lots of purposes. it will send the data to the front end periodically and we can use for different scenario like User Tying, Some One Seeing …etc.

In this section we have to code in Python & Javascript

in javascript

less_hour_request.js

frappe.ui.form.on('Less Hour Request', {
    refresh: function(frm) {
		frm.trigger("set_progress_bar_1")
    },
   set_progress_bar_1(frm){
        frappe.realtime.on('custom_event', (data) => {
			let percent = Math.floor((data.current * 100) / data.total);
			let message = data.message
			frm.dashboard.show_progress(__('Import Progress'), percent, message);
	})
   }
});

In Python

less_hour_request.py

import frappe
import time
from frappe.model.document import Document

class LessHourRequest(Document):
	
	def validate(self):
		frappe.enqueue_doc(
			self.doctype,
			self.name,
			"set_publish_progress_bar", # name of the controller method
			queue="long",
			timeout=4000,
			param="My Discription"
		)

  
	def set_publish_progress_bar(self, **kwargs):
		for i in range(1,101):
			time.sleep(1)
			description = f"frappe.publish_realtime {i} and Keyword Argument {kwargs}"
			frappe.publish_realtime('custom_event', {'message': f'{i}% of the preocess done from 100%', 'current':i, 'total':100}, user=frappe.session.user)		

Everything is same as in the frappe.publish_progress but here we used the frappe.publish_realtime this is the only diffrence. below line is only changed

frappe.publish_realtime('custom_event', {'message': f'{i}% of the preocess done from 100%', 'current':i, 'total':100}, user=frappe.session.user)

Output

Recording 2023-12-14 at 13.17.57

frappe.realtime.off(‘event_name’)

Stop listening to an event you have subscribed to. You have to code in the Javascript or Client Script


Source Code:

Contact

Github : Antony-M1 · GitHub
Linkedin : Antony

Please support with your likes and command

12 Likes

Thanks!

1 Like

SetInterval

Assume we want to update something to get from the database or some other operation should happen periodically you can use setInterval funtion. for example. I want to do a operation after the save. here the code

Javascript

frappe.ui.form.on('Less Hour Request', {
     after_save(frm){
          const IntervalID = setInterval(function (){
              frm.call("get_items_created_data")
                   .then(r => {
                        if (r.message) {
                            let data = r.message;
                            let data = linked_doc;
			                let percent = Math.floor((data.current * 100) / data.total);
			                let message = data.message
                            frm.dashboard.show_progress(__('Import Progress'), percent, message);
                            if (data.current >= data.total){
                                // Kill the Interval
                                clearInterval(IntervalID)
                            }
                        }
                    })             
                     
          }, 2000)
    }
})

Python

import frappe
import time
from frappe.model.document import Document
import random

class LessHourRequest(Document):

    @frappe.whitelist()
    def set_publish_progress_bar(self, **kwargs):
        gen_count = random.randint(0,200)
        return {'message': f'{gen_count}% of the preocess done from 100%', 'current':gen_count, 'total':100}

If the current value is greater that 100 it will kill the setInterval after that there is no more further call.

I do believe that sometimes realtime updates can be seen all online users. It should be seen just on the user who started the job. Do we have any update or parameter for that in newer versions?

Great tutorial by the way.

I checked that its only showing who triggered if using frappe.publish_progress.
In the frappe.publish_realtime you can pass the user list. there is user argument is there

1 Like

Can you share what changes have to be done to suit this code to be added in the Server Script .?
I have noticed that frappe.publish_realtime is not available as a method call in server scripts.

Is there a way to achieve what you have done in server script?

Actually I haven’t explored that one. I thought it will work in the Server Script

I am having a particularly weird issue. I created this function:

            frappe.realtime.on('branch_control_center_setup_progress', function(data) {      
                let progress_area = $(frm.dashboard.progress_area).find('.progress');
                let progress_bar = progress_area.find('.progress-bar[data-key="setup_progress"]');
                        
                if (progress_bar.length === 0) {
                    frm.dashboard.add_progress('Setup Progress', data.progress, __(data.description), {
                        progress_key: 'setup_progress'
                    });
                } else {
                    progress_bar.css('width', data.progress + '%').attr('aria-valuenow', data.progress);
                    progress_area.find('.progress-label').text(__(data.description));
                }
            
                if (data.progress === 100) {
                    frm.set_value('is_setup_complete', true).save().then(() => {
                        frm.reload_doc();
                    });
                }
            });  

But i am getting this

Instead of increasing the progress bar it is duplicating it. What am i missing?

Found the solution. See here: