diff --git a/admin/docker/requirements.pip3 b/admin/docker/requirements.pip3
index 07e82d0..c2479e8 100644
--- a/admin/docker/requirements.pip3
+++ b/admin/docker/requirements.pip3
@@ -23,4 +23,6 @@ diceware==0.9.6
#gevent==1.4.0
#greenlet==0.4.15
python-engineio==3.8.1
-python-socketio==4.1.0
\ No newline at end of file
+python-socketio==4.1.0
+
+minio==7.0.3
\ No newline at end of file
diff --git a/admin/src/admin/lib/admin.py b/admin/src/admin/lib/admin.py
index 26ffa09..ab3327d 100644
--- a/admin/src/admin/lib/admin.py
+++ b/admin/src/admin/lib/admin.py
@@ -18,13 +18,7 @@ options = diceware.handle_options(None)
options.wordlist = 'cat_ascii'
options.num = 3
-
-
-from flask_socketio import SocketIO, emit, join_room, leave_room, \
- close_room, rooms, disconnect, send
-socketio = SocketIO(app)
-from ..views.Socketio import socketio
-# socketio = SocketIO(app)
+from .events import Events
class Admin():
def __init__(self):
@@ -39,9 +33,6 @@ class Admin():
sleep(2)
log.warning('Keycloak connected.')
- # print(self.keycloak.get_user_groups_paths('320b0713-c480-4928-9105-041309d72191'))
- # exit(1)
-
ready=False
while not ready:
try:
@@ -73,7 +64,6 @@ class Admin():
self.default_setup()
self.internal={}
- # self.resync_data()
ready=False
while not ready:
@@ -87,24 +77,11 @@ class Admin():
self.external={'users':[],
'groups':[],
'roles':[]}
+ log.warning(' Updating missing user avatars with defaults')
+ av=Avatars()
+ # av.minio_delete_all_objects() # This will reset all avatars on usres
+ av.update_missing_avatars(self.internal['users'])
log.warning(' SYSTEM READY TO HANDLE CONNECTIONS')
- # self.test_cohorts()
- # self.delete_all_moodle_cohorts()
-
- ########## Testing: get user group
- # cids=[c['id'] for c in self.moodle.get_cohorts()]
- # relations=self.moodle.get_cohort_members(cids)
- # for r in relations:
- # print(self.get_internalgroup_from_moodlecohortid(r['cohortid'])['path'])
-
- # def get_internalgroup_from_moodlecohortid(self,cohort_id):
- # for g in self.internal['groups']:
- # if not g['moodle']: continue
- # if g['moodle_id'] == cohort_id: return g
- # return ''
-
-
-
## This function should be moved to postup.py
def default_setup(self):
@@ -122,9 +99,9 @@ class Admin():
self.keycloak_admin.group_user_add(admin_uid,gid)
log.warning('KEYCLOAK: OK')
except:
+ # print(traceback.format_exc())
log.warning('KEYCLOAK: Seems to be there already')
-
#### Add default groups
try:
log.warning('KEYCLOAK: Adding default groups')
@@ -240,37 +217,6 @@ class Admin():
except:
log.warning('MOODLE: Seems to be there already')
-
- ### testing
- # def test_cohorts(self):
- # cohorts=self.moodle.get_cohorts()
-
- # testc=[c for c in cohorts if c['name'] in ['teacher','/teacher','student','/student']]
- # pprint(testc)
-
- # groups=[]
- # for u in self.internal['users']:
- # groups=groups+u['keycloak_groups']
- # groups=list(dict.fromkeys(groups))
- # pprint(groups)
- # pprint([g for g in groups if 'teacher' in g['keycloak_groups']])
- # exit(1)
- # total=len(groups)
- # i=0
- # for g in groups:
- # parts=g.split('/')
- # subpath=''
- # for i in range(1,len(parts)):
- # try:
- # log.warning(' MOODLE GROUPS: Adding group as cohort ('+str(i)+'/'+str(total)+'): '+subpath)
- # subpath=subpath+'/'+parts[i]
- # self.moodle.add_system_cohort(subpath)
- # except:
- # log.error('probably exists')
- # i=i+1
-
- ### end testing
-
def resync_data(self):
self.internal={'users':self._get_mix_users(),
'groups':self._get_mix_groups(),
@@ -380,8 +326,7 @@ class Admin():
theuser['nextcloud_groups']=[]
del theuser['groups']
users.append(theuser)
-
- # pprint([u['moodle_groups'] for u in users])
+
return users
def get_roles(self):
@@ -465,27 +410,66 @@ class Admin():
def get_external_roles(self):
return self.external['roles']
- def upload_json(self,data):
+ def upload_csv_ug(self,data):
+ log.warning('Processing uploaded users...')
+ users=[]
+ total=len(data['data'])
+ item=1
+ ev=Events('Processing uploaded users',total=len(data['data']))
+ groups=[]
+ for u in data['data']:
+ log.warning('Processing ('+str(item)+'/'+str(total)+') uploaded user: '+u['username'])
+ user_groups=["/" + g.strip() for g in u['groups'].split(',')]
+
+ groups=groups+user_groups
+ users.append({'provider':'external',
+ 'id':u['id'].strip(),
+ 'email': u['email'].strip(),
+ 'first': u['firstname'].strip(),
+ 'last': u['lastname'].strip(),
+ 'username': u['username'].strip(),
+ 'groups':user_groups,
+ 'roles':[u['role'].strip()],
+ 'password':diceware.get_passphrase(options=options)})
+ item+=1
+ ev.increment({'name':u['username'].split('@')[0]})
+ self.external['users']=users
+
+ groups=list(dict.fromkeys(groups))
+
+ sysgroups=[]
+ for g in groups:
+ sysgroups.append({'provider':'external',
+ "id": g,
+ "mailid": g,
+ "name": g,
+ "description": 'Imported with csv'})
+ self.external['groups']=sysgroups
+ return True
+
+ def upload_json_ga(self,data):
groups=[]
log.warning('Processing uploaded groups...')
+ ev=Events('Processing uploaded groups',total=len(data['data']['groups']))
for g in data['data']['groups']:
- # for m in data['data']['d_members']:
-
- # data['provider']
- groups.append({'provider':'external',
- "id": g['id'],
- "mailid": g['email'].split('@')[0],
- "name": g['name'],
- "description": g['description']})
+ try:
+ ev.increment({'name':g['name']})
+ groups.append({'provider':'external',
+ "id": g['id'],
+ "mailid": g['email'].split('@')[0],
+ "name": g['name'],
+ "description": g['description']})
+ except:
+ pass
self.external['groups']=groups
log.warning('Processing uploaded users...')
users=[]
total=len(data['data']['users'])
- i=1
+ item=1
+ ev=Events('Processing uploaded users',total=len(data['data']['users']))
for u in data['data']['users']:
- log.warning('Processing ('+str(i)+'/'+str(total)+') uploaded user: '+u['primaryEmail'].split('@')[0])
- # data['provider']
+ log.warning('Processing ('+str(item)+'/'+str(total)+') uploaded user: '+u['primaryEmail'].split('@')[0])
users.append({'provider':'external',
'id':u['id'],
'email': u['primaryEmail'],
@@ -494,12 +478,9 @@ class Admin():
'username': u['primaryEmail'].split('@')[0],
'groups':[u['orgUnitPath']], ## WARNING: Removing the first
'roles':[],
- 'password':diceware.get_passphrase(options=options)})
- socketio.emit('update',
- json.dumps({'status':True,'item':'user','action':'delete','itemdata':''}),
- namespace='/isard-sso-admin/sio',
- room='admin')
- i=i+1
+ 'password': diceware.get_passphrase(options=options)})
+ item+=1
+ ev.increment({'name':u['primaryEmail'].split('@')[0]})
self.external['users']=users
## Add groups to users (now they only have their orgUnitPath)
@@ -513,6 +494,7 @@ class Admin():
def sync_external(self,ids):
log.warning('Starting sync to keycloak')
self.sync_to_keycloak()
+ ### Now we only sycn external to keycloak and then they can be updated to others with UI buttons
# log.warning('Starting sync to moodle')
# self.sync_to_moodle()
# log.warning('Starting sync to nextcloud')
@@ -527,18 +509,21 @@ class Admin():
total=len(groups)
i=0
+ ev=Events('Syncing import groups to keycloak',total=len(groups))
for g in groups:
i=i+1
- # print('ADDING FULL PATH: '+str(g))
log.warning(' KEYCLOAK GROUPS: Adding group ('+str(i)+'/'+str(total)+'): '+g)
+ ev.increment({'name':g})
self.keycloak.add_group_tree(g)
total=len(self.external['users'])
index=0
+ ev=Events('Syncing import users to keycloak',total=len(self.external['users']))
for u in self.external['users']:
index=index+1
# Add user
log.warning(' KEYCLOAK USERS: Adding user ('+str(index)+'/'+str(total)+'): '+u['username'])
+ ev.increment({'name':u['username']})
uid=self.keycloak.add_user(u['username'],u['first'],u['last'],u['email'],u['password'])
# Add user to role and group rolename
@@ -573,12 +558,14 @@ class Admin():
### Create all groups. Skip / in system groups
total=len(groups)
i=0
+ ev=Events('Syncing groups from keycloak to moodle',total=len(groups))
for g in groups:
parts=g.split('/')
subpath=''
for i in range(1,len(parts)):
try:
log.warning(' MOODLE GROUPS: Adding group as cohort ('+str(i)+'/'+str(total)+'): '+subpath)
+ ev.increment({'name':subpath})
if parts[i] in ['admin','manager','teacher','student']:
subpath=parts[i]
else:
@@ -592,19 +579,28 @@ class Admin():
cohorts=self.moodle.get_cohorts()
### Create users in moodle
+ ev=Events('Syncing users from keycloak to moodle',total=len(self.internal['users']))
for u in self.internal['users']:
if not u['moodle']:
- log.error('Creating moodle user: '+u['username'])
- user=self.moodle.create_user(u['email'],u['username'],'1Random 1String',u['first'],u['last'])[0]
+ log.warning('Creating moodle user: '+u['username'])
+ ev.increment({'name':u['username']})
+ if u['first'] == '': u['first']=' '
+ if u['last'] == '': u['last']=' '
+ try:
+ # pprint(u)
+ pprint(self.moodle.create_user(u['email'],u['username'],'1Random 1String',u['first'],u['last'])[0])
+ except:
+ log.error(' -->> Error creating on moodle the user: '+u['username'])
# user_id=user['id']
self.resync_data()
### Add user to their cohorts (groups)
+ ev=Events('Syncing users groups from keycloak to moodle cohorts',total=len(self.internal['users']))
for u in self.internal['users']:
total=len(u['keycloak_groups'])
index=0
+ ev.increment({'name':u['username']})
for g in u['keycloak_groups']:
- log.error('for user groups')
parts=g.split('/')
subpath=''
# pprint(parts)
@@ -617,7 +613,6 @@ class Admin():
try:
cohort=[c for c in cohorts if c['name']==subpath][0]
except:
- # pprint(subpath)
log.error(' MOODLE USER GROUPS: keycloak group '+subpath+' does not exist as moodle cohort. This should not happen. User '+u['username']+ ' not added.')
try:
@@ -640,25 +635,26 @@ class Admin():
total=len(groups)
i=0
+ ev=Events('Syncing groups from keycloak to nextcloud',total=len(groups))
for g in groups:
parts=g.split('/')
subpath=''
for i in range(1,len(parts)):
try:
log.warning(' NEXTCLOUD GROUPS: Adding group ('+str(i)+'/'+str(total)+'): '+subpath)
+ ev.increment({'name':subpath})
subpath=subpath+'/'+parts[i]
self.nextcloud.add_group(subpath)
except:
log.error('probably exists')
i=i+1
+ ev=Events('Syncing users from keycloak to nextcloud',total=len(self.internal['users']))
for u in self.internal['users']:
- # print('User '+u['username'])
if not u['nextcloud']:
- # print(' Is not in nextcloud')
log.warning(' NEXTCLOUD USERS: Creating nextcloud user: '+u['username']+' in groups '+str(u['keycloak_groups']))
- # group=u['keycloak_groups'][0] if len(u['keycloak_groups']) else False
try:
+ ev.increment({'name':u['username']})
self.nextcloud.add_user_with_groups(u['username'],'1Random 1String',500000000000,u['keycloak_groups'],u['email'],u['first']+' '+u['last'])
except ProviderItemExists:
log.warning('User '+u['username']+' already exists. Skipping...')
@@ -673,12 +669,11 @@ class Admin():
i=i+1
if not u['keycloak']: continue
# Do not remove admin users!!! What to do with managers???
- if 'admin' in u['roles']: continue
- # if 'manager' in u['roles']: continue
+ if ['admin'] in u['roles']: continue
log.info(' KEYCLOAK USERS: Removing user ('+str(i)+'/'+str(total)+'): '+u['username'])
try:
self.keycloak.delete_user(u['id'])
- socketio.emit('update',
+ app.socketio.emit('update',
json.dumps({'status':True,'item':'user','action':'delete','itemdata':u}),
namespace='/isard-sso-admin/sio',
room='admin')
@@ -692,7 +687,7 @@ class Admin():
log.info('Removing nextcloud user: '+u['username'])
try:
self.nextcloud.delete_user(u['nextcloud_id'])
- socketio.emit('update',
+ app.socketio.emit('update',
json.dumps({'status':True,'item':'user','action':'delete','itemdata':u}),
namespace='/isard-sso-admin/sio',
room='admin')
@@ -711,7 +706,7 @@ class Admin():
log.warning('Removing moodle users: '+','.join(usernames))
try:
self.moodle.delete_users(userids)
- socketio.emit('update',
+ app.socketio.emit('update',
json.dumps({'status':True,'item':'user','action':'delete','itemdata':u}),
namespace='/isard-sso-admin/sio',
room='admin')
diff --git a/admin/src/admin/lib/avatars.py b/admin/src/admin/lib/avatars.py
index 1db2318..36a0cf3 100644
--- a/admin/src/admin/lib/avatars.py
+++ b/admin/src/admin/lib/avatars.py
@@ -4,30 +4,52 @@ from admin import app
import logging as log
from pprint import pprint
import os
-from os import listdir
-from os.path import isfile, join
-from .postgres import Postgres
-
-# Module variables to connect to moodle api
+from minio import Minio
+from minio.commonconfig import REPLACE, CopySource
+from minio.deleteobjects import DeleteObject
class Avatars():
-
def __init__(self):
- self.keycloak_pg=Postgres('isard-apps-postgresql','keycloak',os.environ['KEYCLOAK_DB_USER'],os.environ['KEYCLOAK_DB_PASSWORD'])
+ self.mclient = Minio(
+ "isard-sso-avatars:9000",
+ access_key="AKIAIOSFODNN7EXAMPLE",
+ secret_key="wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY",
+ secure=False
+ )
+ self.bucket='master-avatars'
+ self._minio_set_realm()
+ # self.update_missing_avatars()
- def username2id(self,username):
- q = """select id, username from user_entity where username = '%s'""" % (username)
- try:
- return self.keycloak_pg.select(q)[0][0]
- except:
- pass
- return False
+ def update_missing_avatars(self,users):
+ sys_roles=['admin','manager','teacher','student']
+ for u in self.get_users_without_image(users):
+ try:
+ img=[r+'.jpg' for r in sys_roles if r in u['roles']][0]
+ except:
+ img='unknown.jpg'
- def get_files(self):
- path='avatars/master-avatars/'
- onlyfiles = [f for f in listdir(path) if isfile(join(path, f))]
- pprint(onlyfiles)
+ self.mclient.fput_object(
+ self.bucket, u['id'], os.path.join(app.root_path,"../custom/avatars/"+img),
+ content_type="image/jpeg ",
+ )
+ log.warning(' AVATARS: Updated avatar for user '+u['username']+' with role '+img.split('.')[0])
- # def generate_missing(self, users):
- # for u in users:
+ def _minio_set_realm(self):
+ if not self.mclient.bucket_exists(self.bucket):
+ self.mclient.make_bucket(self.bucket)
+
+ def minio_get_objects(self):
+ return [o.object_name for o in self.mclient.list_objects(self.bucket)]
+
+ def minio_delete_all_objects(self):
+ delete_object_list = map(
+ lambda x: DeleteObject(x.object_name),
+ self.mclient.list_objects(self.bucket),
+ )
+ errors=self.mclient.remove_objects(self.bucket, delete_object_list)
+ for error in errors:
+ log.error(" AVATARS: Error occured when deleting avatar object", error)
+
+ def get_users_without_image(self,users):
+ return [u for u in users if u['id'] not in self.minio_get_objects()]
\ No newline at end of file
diff --git a/admin/src/admin/lib/events.py b/admin/src/admin/lib/events.py
new file mode 100644
index 0000000..6f622e4
--- /dev/null
+++ b/admin/src/admin/lib/events.py
@@ -0,0 +1,73 @@
+#!flask/bin/python
+# coding=utf-8
+from admin import app
+import logging as log
+import traceback
+
+from uuid import uuid4
+import json
+from time import sleep
+import sys,os
+from flask import render_template, Response, request, redirect, url_for, jsonify
+from flask_socketio import SocketIO, emit, join_room, leave_room, \
+ close_room, rooms, disconnect, send
+import base64
+
+class Events():
+ def __init__(self,title,text='',total=0):
+ self.eid=str(base64.b64encode(os.urandom(32))[:8])
+ self.title=title
+ self.text=text
+ self.total=total
+ self.item=0
+ self.create()
+
+ def create(self):
+ app.socketio.emit('notify-create',
+ json.dumps({'id':self.eid,
+ 'title':self.title,
+ 'text':self.text}),
+ namespace='/isard-sso-admin/sio',
+ room='admin')
+ sleep(0.1)
+
+ def __del__(self):
+ app.socketio.emit('notify-destroy',
+ json.dumps({'id':self.eid}),
+ namespace='/isard-sso-admin/sio',
+ room='admin')
+ sleep(0.1)
+
+ def update_text(self,text):
+ self.text=text
+ app.socketio.emit('notify-update',
+ json.dumps({'id':self.eid,
+ 'text':self.text,}),
+ namespace='/isard-sso-admin/sio',
+ room='admin')
+ sleep(0.1)
+
+ def increment(self,data={}):
+ self.item+=1
+ app.socketio.emit('notify-update',
+ json.dumps({'id':self.eid,
+ 'text': '['+str(self.item)+'/'+str(self.total)+'] ',
+ 'item':self.item,
+ 'total':self.total,
+ 'data':data}),
+ namespace='/isard-sso-admin/sio',
+ room='admin')
+ sleep(0.1)
+
+ def decrement(self,data={}):
+ self.item-=1
+ app.socketio.emit('notify-update',
+ json.dumps({'id':self.eid,
+ 'text':'['+str(self.item)+'/'+str(self.total)+'] ',
+ 'item':self.item,
+ 'total':self.total,
+ 'data':data}),
+ namespace='/isard-sso-admin/sio',
+ room='admin')
+ sleep(0.1)
+
diff --git a/admin/src/admin/static/js/external.js b/admin/src/admin/static/js/external.js
index 932d44b..a01a8b1 100644
--- a/admin/src/admin/static/js/external.js
+++ b/admin/src/admin/static/js/external.js
@@ -17,7 +17,7 @@ $(document).ready(function() {
$.each(users_table.rows().data(),function(key, value){
ids[value['id']]=value['roles']
});
- console.log(ids)
+ // console.log(ids)
$.ajax({
type: "PUT",
url:"/isard-sso-admin/external",
@@ -46,25 +46,30 @@ $(document).ready(function() {
})
$("#modalImport #send").on('click', function(e){
- console.log(users_table.rows().data())
+ // console.log(users_table.rows().data())
var form = $('#modalImportForm');
form.parsley().validate();
if (form.parsley().isValid()){
formdata = form.serializeObject()
- formdata['data']=JSON.parse(filecontents)
+ if($('#format').val() == 'csv-ug'){
+ formdata['data']=parseCSV(filecontents)
+ }else{
+ formdata['data']=JSON.parse(filecontents)
+ }
$.ajax({
type: "POST",
url:"/isard-sso-admin/external",
data: JSON.stringify(formdata),
success: function(data)
{
- $("#modalImport").modal('hide');
- users_table.ajax.reload();
- groups_table.ajax.reload();
+ console.log('SUCCESS')
+ // $("#modalImport").modal('hide');
+ // users_table.ajax.reload();
+ // groups_table.ajax.reload();
},
error: function(data)
{
- alert('Something went wrong on our side...')
+ console.log('Ajax timeout')
}
});
}
@@ -194,20 +199,46 @@ $(document).ready(function() {
});
function readFile (evt) {
- path = "";
- items = [];
- var files = evt.target.files;
- var file = files[0];
- var reader = new FileReader();
- reader.onload = function(event) {
- filecontents=event.target.result;
- $.each(JSON.parse(filecontents), walker);
- populate_path(items)
+ if($('#format').val() == 'json-ga'){
+ path = "";
+ items = [];
+ var files = evt.target.files;
+ var file = files[0];
+ var reader = new FileReader();
+ reader.onload = function(event) {
+ filecontents=event.target.result;
+ $.each(JSON.parse(filecontents), walker);
+ populate_path(items)
+ }
+ reader.readAsText(file, 'UTF-8')
+ }
+ if($('#format').val() == 'csv-ug'){
+ var files = evt.target.files;
+ var file = files[0];
+ var reader = new FileReader();
+ reader.onload = function(event) {
+ filecontents=event.target.result;
+ // $.each(JSON.parse(filecontents), walker);
+ // populate_path(items)
+ }
+ reader.readAsText(file, 'UTF-8')
}
- reader.readAsText(file, 'UTF-8')
}
+function parseCSV(){
+ lines=filecontents.split('\n')
+ header=lines[0].split(';')
+ users=[]
+ $.each(lines, function(n, l){
+ if(n!=0 && l.length > 10){
+ usr=toObject(header,l.split(';'))
+ usr['id']=usr['username']
+ users.push(usr)
+ }
+ })
+ return users;
+}
function toObject(names, values) {
var result = {};
for (var i = 0; i < names.length; i++)
diff --git a/admin/src/admin/static/js/status_socket.js b/admin/src/admin/static/js/status_socket.js
index 7053b12..c710b7b 100644
--- a/admin/src/admin/static/js/status_socket.js
+++ b/admin/src/admin/static/js/status_socket.js
@@ -1,19 +1,86 @@
- // SocketIO
- socket = io.connect(location.protocol+'//' + document.domain +'/isard-sso-admin/sio');
- console.log(location.protocol+'//' + document.domain +'/isard-sso-admin/sio')
- socket.on('connect', function() {
- // connection_done();
- console.log('Listening status socket');
- });
+notice={}
+$lost=0;
- socket.on('connect_error', function(data) {
- // connection_lost();
- });
-
- socket.on('update', function(data) {
- var data = JSON.parse(data);
- console.log('Status update')
- console.log(data)
- // var data = JSON.parse(data);
- // drawUserQuota(data);
+socket = io.connect(location.protocol+'//' + document.domain +'/isard-sso-admin/sio');
+console.log(location.protocol+'//' + document.domain +'/isard-sso-admin/sio')
+socket.on('connect', function() {
+ if($lost){location.reload();}
+ console.log('Listening status socket');
+});
+
+socket.on('connect_error', function(data) {
+ $lost=$lost+1;
+ $('#modal-lostconnection').modal({
+ backdrop: 'static',
+ keyboard: false
+ }).modal('show');
+});
+
+socket.on('notify-create', function(data) {
+ var data = JSON.parse(data);
+ notice[data.id] = new PNotify({
+ title: data.title,
+ text: data.text,
+ hide: false
});
+});
+
+socket.on('notify-destroy', function(data) {
+ var data = JSON.parse(data);
+ notice[data.id].remove()
+});
+
+socket.on('notify-update', function(data) {
+ var data = JSON.parse(data);
+ // console.log(data.text)
+ notice[data.id].update({
+ text: data.text
+ })
+});
+
+ // new PNotify({
+ // title: "Quota for creating desktops full.",
+ // text: "Can't create another desktop, user quota full.",
+ // hide: true,
+ // delay: 3000,
+ // icon: 'fa fa-alert-sign',
+ // opacity: 1,
+ // type: 'error'
+ // });
+
+// socket.on('update', function(data) {
+// var data = JSON.parse(data);
+// console.log('Status update')
+// console.log(data)
+// // var data = JSON.parse(data);
+// // drawUserQuota(data);
+// });
+
+socket.on('update', function(data) {
+ var data = JSON.parse(data);
+ console.log('Status update')
+ console.log(data)
+ // var data = JSON.parse(data);
+ // drawUserQuota(data);
+});
+
+ // {'event':'traceback',
+ // 'id':u['id'],
+ // 'item':'group',
+ // 'action':'add'
+ // 'name':g['name'],
+ // 'progress':str(item)+'/'+str(total),
+ // 'status':False,
+ // 'msg':,
+ // 'payload':{'traceback':traceback.format_exc(),
+ // 'data':g})
+
+socket.on('progress', function(data) {
+ var data = JSON.parse(data);
+ console.log(data)
+ // $('.modal-progress #item').html(data.item)
+});
+
+
+
+////
diff --git a/admin/src/admin/static/js/users.js b/admin/src/admin/static/js/users.js
index efcea8b..63a5705 100644
--- a/admin/src/admin/static/js/users.js
+++ b/admin/src/admin/static/js/users.js
@@ -220,7 +220,7 @@ $(document).ready(function() {
"columnDefs": [ {
"targets": 1,
"render": function ( data, type, full, meta ) {
- return ''
+ return ''
}},
{
"targets": 6,
diff --git a/admin/src/admin/static/templates/base.html b/admin/src/admin/static/templates/base.html
index 2a2558c..92455a4 100644
--- a/admin/src/admin/static/templates/base.html
+++ b/admin/src/admin/static/templates/base.html
@@ -59,26 +59,9 @@
-