# -*- coding: utf-8 # $Id$ # # Copyright 2001, 2002 by IVA Team and contributors # # This file is part of IVA. # # IVA is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # IVA is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with IVA; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA """ Module collects statistics from all parts of IVA. """ __version__ = "$Revision$"[11:-2] import time import os, Globals from OFS.SimpleItem import SimpleItem from OFS.PropertyManager import PropertyManager from Globals import Persistent, PersistentMapping from Products.PageTemplates.PageTemplateFile import PageTemplateFile import AccessControl from common import perm_view,perm_manage, perm_edit from threading import Lock import stats2_engine as fscounter import zLOG USE_FS = 1 class StatisticsManager(PropertyManager, SimpleItem): """ store and serve statistics. Storing should be through session expiring """ security = AccessControl.ClassSecurityInfo() security.declareObjectPublic() meta_type = 'Statistics' _version = 1 security.declareProtected(perm_manage, 'stats_tab') stats_tab = PageTemplateFile('ui/stats.zpt',globals()) stats_tab._owner = None manage_options = ( {'label': 'Stats', 'action': 'stats_tab'}, ) + PropertyManager.manage_options+ SimpleItem.manage_options def __init__(self): """ ug_stat: username -> pagesOpenTotal - n sessions : [{'pagesOpened', 'start', 'end', 'urls'},] all_urls : [[url, time.time()],] lastAccessGlobal - time.time() c_stat: course_id -> (modBookshelf, modKnowledge, .. ) o_stat: url_to_object -> {'uname': {'count', 'times': time.time()} uc_stat: username -> {course_id: {postedArtefactAnnos, openPages, numTimesEntered}} """ self.id = 'Statistics' self.ug_stat = {} # not course spec. user stats self.uc_stat = {} # course spec. user stats self.c_stat = {} # course stats self.o_stat = {} # object views security.declareProtected(perm_manage, 'manage_afterAdd') def manage_afterAdd(self, item, container): """ manage after add storeAllURLs -- store ALL urls user goes, that can be very resource consuming. trackSessions -- most of the statistics machinery is switched off. it will also disable showAllViews. showAllViews -- if object is accessed then we write it down. if all access times are stored or only last """ try: self.manage_addProperty('storeAllURLs', 1, 'boolean') self.manage_addProperty('trackSessions', 1, 'boolean') self.manage_addProperty('showAllViews', 1, 'boolean') except: pass security.declarePrivate('get_temp') def get_temp(self): """ return temp_folder but before that check if temp_folder is there. if not then create """ res = None try: res = self.fle_root().aq_explicit.temp_folder.aq_explicit except AttributeError: self.fle_root().manage_addProduct['TemporaryFolder'].constructTemporaryFolder('temp_folder', 'Temporary Folder') if res == None: res = self.fle_root().aq_explicit.temp_folder.aq_explicit objs = res.objectIds() for x in ['ug_stat', 'uc_stat', 'c_stat', 'o_stat']: if x not in objs: from stats2_engine import load_stats load_stats(self, [self.fle_root(),]) # set get_temp to point to another function. Once we have created temp_folder and objects # inside, there's no need to recheck it again. Possible errors may occur. # self.get_temp = self.get_temp_func2 return res security.declareProtected(perm_view, 'active_users') def active_users(self): """ list of user who has session. """ sd = self.fle_root().aq_parent.temp_folder.session_data result = [] pp = self.fle_root().getPhysicalPath() iva_url = '/'.join(pp[1:]) for x in sd.keys(): o = sd.get(x) try: result.append(o[iva_url]['uname']) except KeyError: pass return result security.declareProtected(perm_manage, 'save_to_disc') def save_to_disc(self, interval, tick, prev_tick, next_tick): """ save stats to disc """ print interval, tick, prev_tick, next_tick import cPickle path = '_'.join(self.fle_root().getPhysicalPath()[1:]).lower()+'_' data_dir = os.path.join(Globals.INSTANCE_HOME,'var','iva_stat') ug = os.path.join(data_dir, path+'ug_stat.dat') uc = os.path.join(data_dir, path+'uc_stat.dat') c = os.path.join(data_dir, path+'c_stat.dat') o = os.path.join(data_dir, path+'o_stat.dat') urls = os.path.join(data_dir, path+'paths.dat') sess = os.path.join(data_dir, path+'sessions.dat') tf = self.get_temp() import gzip f = gzip.GzipFile(urls, 'a') # do dump for x in tf.ug_stat.ug_stat.keys(): if tf.ug_stat.ug_stat[x].has_key('all_urls'): for y, z in tf.ug_stat.ug_stat[x]['all_urls']: f.write(x+'\t'+y+'\t'+z+'\n') f.close() f = gzip.GzipFile(sess, 'a') for x in tf.ug_stat.ug_stat.keys(): if tf.ug_stat.ug_stat[x].has_key('sessions'): for y in tf.ug_stat.ug_stat[x]['sessions']: f.write(x+'\t'+str(y)+'\n') f.close() for x in tf.ug_stat.ug_stat.keys(): try: del tf.ug_stat.ug_stat[x]['all_urls'] pass except KeyError: pass try: del tf.ug_stat.ug_stat[x]['sessions'] pass except KeyError: pass tf.ug_stat._p_changed = True f = open(ug, "w") cPickle.dump(tf.ug_stat.ug_stat, f) f.close() f = open(uc, "w") cPickle.dump(tf.uc_stat.uc_stat, f) f.close() f = open(c, "w") cPickle.dump(tf.c_stat.c_stat, f) f.close() f = open(o, "w") cPickle.dump(tf.o_stat.o_stat, f) f.close() zLOG.LOG('IVA', zLOG.INFO, 'Statistics saved to disc ('+self.absolute_url()+')', '') return 0 security.declareProtected(perm_manage, 'Shutdown') def Shutdown(self): """ this method should be called before shuting down zope server. It will save all unsaved stats """ l = Lock() try: l.acquire() # Find temp_folder # get sessiondata # FIX: what if temp_folder isn't there sd = self.fle_root().aq_parent.temp_folder.session_data # get all sessions for x in sd.keys(): o = sd.get(x) pp = self.fle_root().getPhysicalPath() iva_url = '/'.join(pp[1:]) # take sessiondata and run then through saveStats method dat = o.get(iva_url, {}) self.saveStats(dat) o.invalidate() l.release() return "ready for shutdown" except: return "failed" return "failed" security.declareProtected(perm_manage, 'saveStats') def saveStats(self, data): """ save stats """ print "data received from delScript:", data l = Lock() l.acquire() uname = data.get('uname', None) if uname is None: return 0 if self.get_storeAllURLs(): self.setAllURLs(uname, data.get('all_urls', {})) self.setViewedObjects(uname, data.get('obj_history', {})) self.setUserGlobalData(uname, data) self.setUserCourseData(uname, data.get('courses', [])) l.release() return 1 security.declarePublic('index_html') def index_html(self): """ index """ return "no index here" security.declareProtected(perm_view, 'get_showAllViews') def get_showAllViews(self): """ return showAllViews """ return self.showAllViews security.declareProtected(perm_manage, 'get_trackSessions') def get_trackSessions(self): """ return trackSessions """ return self.trackSessions security.declareProtected(perm_manage, 'get_storeAllURLs') def get_storeAllURLs(self): """ return storeAllURLs """ return self.storeAllURLs security.declareProtected(perm_manage, 'setAllURLs') def setAllURLs(self, uname, data): """ saves all urls users have gone through """ ugstat = self.get_temp().ug_stat.ug_stat.get(uname, {}) all_urls = ugstat.get('all_urls', []) for x in data: all_urls.append(x) ugstat['all_urls'] = all_urls self.get_temp().ug_stat.ug_stat[uname] = ugstat self.get_temp().ug_stat._p_changed = True return 0 security.declareProtected(perm_manage, 'getAllURLs') def getAllURLs(self, uname): """ return all URLs """ user = self.get_temp().ug_stat.ug_stat.get(uname, []) if not user: return [] return user.get('all_urls', []) #return self.ug_stat.get(uname).get('all_urls', []) security.declareProtected(perm_manage, 'setViewedObjects') def setViewedObjects(self, uname, data): """ save viewed objects - webtop items mostly """ save_all = self.get_showAllViews() for obj in data: o_sta = self.get_temp().o_stat.o_stat.get(obj[0], {}) u_sta = o_sta.get(uname, {'times':[], 'count':0}) if not save_all: u_sta['times'] = [] u_sta['times'].append(obj[1]) if not save_all: u_sta['count'] += obj[2] else: u_sta['count'] = -1 o_sta[uname] = u_sta self.get_temp().o_stat.o_stat[obj[0]] = o_sta self.get_temp().o_stat._p_changed = True return 0 security.declarePrivate('delObjectHistory') def delObjectHistory(self, url): """ delete object history. This is triggered when object is deleted """ print "removing %s" % url try: del self.get_temp().o_stat.o_stat[url] except KeyError: print "oops... failed" pass # XXX: This can be expensive to_be_removed = [] for x in self.get_temp().o_stat.o_stat.keys(): if url in x: to_be_removed.append(x) for x in to_be_removed: print "removing %s" % x try: del self.get_temp().o_stat.o_stat[x] except KeyError: print "oops... failed" security.declareProtected(perm_manage, 'getViewedObjs') def getViewedObjs(self): """ get """ return self.get_temp().o_stat.o_stat security.declareProtected(perm_manage, 'getLastVisitTime') def getLastVisitTime(self, REQUEST, url, uname): """ url - relative url """ time = 0 #iva_url = self.fle_root().absolute_url(1) pp = self.fle_root().getPhysicalPath() iva_url = '/'.join(pp[1:]) sess = REQUEST.SESSION.get(iva_url, None) if sess is not None: obj_history = sess.get('obj_history', None) if obj_history is not None: for x in obj_history: if x[0] == url: if x[1] > time: time = x[1] tf = self.get_temp() if tf.o_stat.o_stat.has_key(url): if tf.o_stat.o_stat[url].has_key(uname): if tf.o_stat.o_stat[url][uname]['times'][-1] > time: time = tf.o_stat.o_stat[url][uname]['times'][-1] return time security.declareProtected(perm_manage, 'setUserGlobalData') def setUserGlobalData(self, uname, data): """ user global stats """ ugstat = self.get_temp().ug_stat.ug_stat.get(uname, {}) ugstat['lastAccessGlobal'] = data.get('lastAccessGlobal') pages = ugstat.get('pagesOpenTotal', 0) ugstat['pagesOpenTotal'] = pages + data.get('pagesOpenTotal') sessions = ugstat.get('sessions', []) if not sessions: ugstat['sessions'] = [] session = { 'start':data.get('sessionStart'), 'end':data.get('lastAccessGlobal'), 'pagesOpened': data.get('pagesOpenTotal'), 'urls':[] } if self.get_storeAllURLs(): session['urls'] = data.get('all_urls') sessions.append(session) ugstat['sessions'] = sessions #XXX: does this work? self.get_temp().ug_stat.ug_stat[uname] = ugstat self.get_temp().ug_stat._p_changed = True return 1 security.declareProtected(perm_manage, 'getUserSessions') def getUserSessions(self, uname): """ return a list of user sessions """ u = self.get_temp().ug_stat.ug_stat.get(uname, []) if not u: return u return u.get('sessions', []) security.declareProtected(perm_manage, 'setUserCourseData') def setUserCourseData(self, uname, data): """ self.setUserCourseData(uname, data.get('courses', [])) """ stat = self.get_temp().uc_stat.uc_stat.get(uname, {}) if not stat: self.get_temp().uc_stat.uc_stat[uname] = {} for x in data.keys(): cs = stat.get(x, {}) for y in ('openPages','lastAccess','notesRead','postedNotes','postedArtefacts','postedArtefactAnnos','uploadedFiles','createdWikis','memosAdded','foldersOpened','linksAdded','testsSolved','numTimesEntered'): if y in ('lastAccess',): cs[y] = data[x].get(y) else: cs[y] = cs.get(y, 0) # avoid KeyErrors cs[y] += data[x].get(y) vj = data[x].get('visited').get('visit_jamming') vk = data[x].get('visited').get('visit_knowledgebuilding') if vj > cs.get('visit_jamming',0): cs['visit_jamming'] = vj if vk > cs.get('visit_knowledgebuilding', 0): cs['visit_knowledgebuilding'] = vk #cs['openPages'] = data.get('openPages') #cs['lastAccess'] = data.get('lastAccess') #cs['notesRead'] = data.get('notesRead') #cs['postedNotes'] = data.get('postedNotes') #cs['postedArtefacts'] = data.get('postedArtefacts') #cs['postedArtefactAnnos'] = data.get('postedArtefactAnnos') #cs['uploadedFiles'] = data.get('uploadedFiles') #cs['createdWikis'] = data.get('createdWikis') #cs['memosAdded'] = data.get('memosAdded') #cs['foldersOpened'] = data.get('foldersOpened') #cs['linksAdded'] = data.get('linsAdded') #cs['testsSolved'] = data.get('testsSolved') #cs['numTimesEntered'] = "unimplemented" cs['addedEvents'] = "unimplemented" cs['addedQuizzes'] = "unimplemented" cs['addedAssignments'] = "unimplemented" stat[x] = cs csta = self.get_temp().c_stat.c_stat.get(x, {}) if data[x].get('modified').get('jamming') > csta.get('modJamming',0): csta['modJamming'] = data[x].get('modified').get('jamming') if data[x].get('modified').get('knowledgebuilding') > csta.get('modKnowledge',0): csta['modKnowledge'] = data[x].get('modified').get('knowledgebuilding') if data[x].get('modified').get('bookshelf') > csta.get('modBookshelf',0): csta['modBookshelf'] = data[x].get('modified').get('bookshelf') csta['modAssignments'] = "unimplemented" csta['modQuizes'] = "unimplemented" csta['modEvents'] = "unimplemented" self.get_temp().c_stat.c_stat[x] = csta self.get_temp().uc_stat.uc_stat[uname] = stat self.get_temp().uc_stat._p_changed = True self.get_temp().c_stat._p_changed = True return 1 security.declareProtected(perm_view, 'getObjVisitors') def getObjVisitors(self, url): """ return visitors for object """ return self.get_temp().o_stat.o_stat.get(url, None) security.declareProtected(perm_manage, 'getUserCourseStat') def getUserCourseStat(self, course, stat, uname): """ return stat for user on given course """ o = self.get_temp().uc_stat.uc_stat.get(uname, None) if o is None: return o c = o.get(course, None) if c is None: return c result = c.get(stat, None) return result security.declareProtected(perm_manage, 'getCourseStat') def getCourseStat(self, course, stat): """ return stat for course """ o = self.get_temp().c_stat.c_stat.get(course, None) if o is None: return o return o.get(stat, None) def delCourseHistory(self, course): """ delete course stats. triggered from manage_beforeDelete """ try: del self.get_temp().c_stat.c_stat[course] except KeyError: print "key error" pass security.declareProtected(perm_manage, 'getUserGlobalStat') def getUserGlobalStat(self, uname, stat): """ return stat for user """ ugstat = self.get_temp().ug_stat.ug_stat.get(uname, {}) if not ugstat: return 0 #ugstat['lastAccessGlobal'] = data.get('lastAccessGlobal') #pages = ugstat.get('pagesOpenTotal', 0) #ugstat['pagesOpenTotal'] = pages + data.get('pagesOpenTotal') return ugstat.get(stat, 0) # Methods for drawing graphs security.declareProtected(perm_edit, 'users_with_record') def users_with_record(self): """ a list of users how have usage record """ return self.get_temp().ug_stat.ug_stat.keys() security.declareProtected(perm_manage, 'get_session_times') def get_session_times(self): """ return a list of session start and end times from all users """ import time data = self.get_temp().ug_stat.ug_stat results = [] for x in data.keys(): sessions = data[x]['sessions'] for sess in sessions: s = float(sess['start']) e = float(sess['end']) results.append([int(s),int(e)]) results.sort() return results security.declareProtected(perm_manage, 'draw_range_graph') def draw_range_graph(self, range_start, range_end=None, opt=-2, unix_timestamps=0, accuracy=-2): """ draw range graph """ import time if not unix_timestamps: tmp = range_start.split('-') range_start = time.mktime((int(tmp[0]), int(tmp[1]), int(tmp[2]), 0,0,0,0,0,0 )) if not range_end: range_end = range_start+86400 # 24h else: tmp = range_end.split('-') range_end = time.mktime((int(tmp[0]), int(tmp[1]), int(tmp[2]), 0,0,0,0,0,0 )) range_start = int(range_start) range_end = int(range_end) end = {} res_tmp = self.get_session_times() print "kokku tsükleid:", len(range(range_start, range_end+1)) highest_count = 0 j = 0 for i in range(range_start, range_end+1): j = j + 1 count = 0 for x in res_tmp: if x[0] > i: break if x[0] < i < x[1]: count = count + 1 if i > x[1]: res_tmp.remove(x) end[i] = count if count > highest_count: highest_count = count if j % 1000000 == 0: print j, i e_time = time.time() print "pikkus:", len(res_tmp) s_time = time.time() print "highest count:", highest_count new_res = self.pack_results(end, accuracy=accuracy) self.draw_graph(new_res, accuracy); return "done" security.declarePrivate('draw_graph') def draw_graph(self, data, detail_level): """ draw graph """ from PIL import Image, ImageDraw def calc_rel_pos(zero_pos, x): dl = 100 if detail_level == -4: dl = 1000 if detail_level == -3: dl = 100 if detail_level == -2: dl = 10 if detail_level == -1: dl = 5 return (x - zero_pos + 15)/dl name = "graph.jpg" sorted_data = data.keys() sorted_data.sort() end_pos = sorted_data[-1] + 2000 zero_pos = sorted_data[0] - 1000 max = 0 for x in sorted_data: if data[x]['max'] > max: max = data[x]['max'] y_zero = max*10+50 img = Image.new('RGB', (calc_rel_pos(zero_pos, end_pos), max*10+75), (255,255,255)) draw = ImageDraw.Draw(img) for x in range(0,y_zero , 10): draw.line((1,y_zero-x,calc_rel_pos(zero_pos, end_pos),y_zero-x), fill=(230,230,230)) draw.line( (15,1,15,y_zero+15), fill='black' ) draw.line( (1,y_zero,8700,y_zero), fill='black' ) draw.line( (1,y_zero+1,8700,y_zero+1), fill='black' ) for u in sorted_data: print "line1:",calc_rel_pos(zero_pos, u), y_zero-(data[u]['min']*10), calc_rel_pos(zero_pos, u), y_zero draw.line( (calc_rel_pos(zero_pos, u)+16, y_zero-(data[u]['min']*10), calc_rel_pos(zero_pos, u)+16, y_zero), fill='red') draw.line( (calc_rel_pos(zero_pos, u)+2+16, y_zero-(data[u]['max']*10), calc_rel_pos(zero_pos, u)+2+16, y_zero), fill='green') draw.line( (calc_rel_pos(zero_pos, u)+4+16, y_zero-(data[u]['load']/data[u]['count']*10), calc_rel_pos(zero_pos, u)+4+16, y_zero), fill='blue') path = os.path.join(Globals.INSTANCE_HOME,'Products','iva','ui','stats',name) f = open(path, "w+b") img.save(f, "JPEG", quality=100) f.close() return 0 security.declarePrivate('pack_results') def pack_results(self, data, accuracy): """ rounds time.time """ import copy res = {} for x in data.keys(): r = round(x, accuracy) prev = res.get(r, None) if not prev: prev = {'load':0, 'count':0, 'min':999999, 'max':0} j = prev['count']+1 l = prev['load']+data.get(x) min = prev['min'] max = prev['max'] if prev['min'] > data.get(x): min = data.get(x) if prev['max'] < data.get(x): max = data.get(x) tmp = {'count': j, 'load': l, 'max':max, 'min':min} res[r] = copy.copy(tmp) # TODO: cal average here return res Globals.InitializeClass(StatisticsManager) class StatisticsRegister: """ statistics module that all other parts speak global parameters to stat: uname - for recognizing user lastAccessGlobal pagesOpenTotal url - optional !? requires space in session data holding folder and is resource consuming course specific parameters: openPages modified - modified block ( dict ) teadmuspaja - str(time.time) meediapaja - str(time.time) bookshelf - str(time.time) visited - visited block ( dict ) jamming knowledge lastAccess notesRead postedNotes postedArtefacts postedArtefactAnnos uploadedFiles createdWikis memosAdded foldersOpened linksAdded testsSolved numTimesEntered """ security = AccessControl.ClassSecurityInfo() def __init__(self): """ init method """ self.registerVariables() security.declarePublic('registerVariables') def registerVariables(self): """ default value for user_course_default variable. """ self.user_course_default = {'openPages':0, 'modified':{'jamming':0, 'knowledgebuilding':0, 'bookshelf':0}, 'visited':{'visit_jamming':0, 'visit_knowledgebuilding':0}, 'lastAccess': str(time.time()), 'notesRead':0, 'postedNotes':0, 'postedArtefacts':0, 'postedArtefactAnnos':0, 'uploadedFiles':0, 'createdWikis':0, 'memosAdded':0, 'foldersOpened':0, 'linksAdded':0, 'testsSolved':0, 'numTimesEntered':0 } security.declareProtected(perm_view,'registerVisit') def registerVisit(self, REQUEST, course_id, userLocation): """ registers visit, page open, note reading etc. """ uname = str(REQUEST.AUTHENTICATED_USER) pp = self.fle_root().getPhysicalPath() my_path = '/'.join(pp[1:]) sd = REQUEST.SESSION.get(my_path, { 'pagesOpenTotal':0, 'uname':uname, 'courses':{}, 'lastAccessGlobal': str(time.time()), 'sessionStart': str(time.time()), 'all_urls': [] } ) paths = REQUEST.SESSION.get('iva_paths', []) sd['pagesOpenTotal'] += 1 sd['lastAccessGlobal'] = str(time.time()) store_all = getattr(self, 'Statistics').get_storeAllURLs() if store_all: s_url = REQUEST.get('URL0', 'URL POLE') q_string = REQUEST.get('QUERY_STRING', 'POLE') if q_string: s_url += '?'+q_string sd['all_urls'].append([s_url, str(time.time())]) course_id = int(course_id) if course_id and userLocation!='Organizer': ci = sd['courses'].get(course_id, None) if ci is None: import copy ci = copy.copy(self.user_course_default) ci['openPages'] += 1 ci['lastAccess'] = str(time.time()) sd['courses'][course_id] = ci REQUEST.SESSION.set(my_path, sd) if my_path not in paths: paths.append(my_path) REQUEST.SESSION.set('iva_paths', paths) return 1 security.declareProtected(perm_view,'updateStat') def updateStat(self, REQUEST, field, course_id=None): """ updates one statistics field. course specific only. """ sta_obj = getattr(self, 'Statistics') if not sta_obj.get_trackSessions(): return 0 if course_id is None: user = getattr(self.fle_root().fle_users, str(REQUEST.AUTHENTICATED_USER), None) #XXX: maybe we should check user here too ? course_id = int(user.get_jooksev_kursus()) #my_path = self.fle_root().absolute_url(1) pp = self.fle_root().getPhysicalPath() my_path = '/'.join(pp[1:]) base = REQUEST.SESSION.get(my_path, []) if not base: return ci = base['courses'].get(course_id) if ci is None: import copy ci = copy.copy(self.user_course_default) if field in ('jamming', 'knowledgebuilding', 'bookshelf'): ci['modified'][field] = str(time.time()) #update time elif field in ('visit_jamming', 'visit_knowledgebuilding'): ci['visited'][field] = str(time.time()) else: ci[field] += 1 base['courses'][course_id] = ci REQUEST.SESSION.set(my_path, base) return 1 def viewedObject(self, REQUEST, url): """ user is viewing object. url is basically an identifier, bare in mind that url might change """ sta_obj = getattr(self, 'Statistics') if not sta_obj.get_trackSessions(): return 0 pp = self.fle_root().getPhysicalPath() my_path = '/'.join(pp[1:]) #my_path = self.fle_root().absolute_url(1) base = REQUEST.SESSION.get(my_path, []) if not base: #session already expired? return store_all = sta_obj.get_showAllViews() hist = base.get('obj_history', []) count = 1 if not store_all: tmp = [] for x in hist: if x[0] != url: tmp.append([x[0], x[1], x[2]]) else: count += x[2] hist = tmp hist.append([url,str(time.time()), count]) base['obj_history'] = hist REQUEST.SESSION.set(my_path, base) return 1 def getStatField(self, REQUEST, field, course_id=None): """ return one field from session. shoudn't pass course_id when accessing not course specific data""" #my_path = self.fle_root().absolute_url(1) pp = self.fle_root().getPhysicalPath() my_path = '/'.join(pp[1:]) data = REQUEST.SESSION.get(my_path, []) if course_id: # do something cd = data.get(course_id, 0) if cd: return cd.get(field, -1) else: return 0 else: return data.get(field, -1) Globals.InitializeClass(StatisticsRegister)