Bladeren bron

fixes in versions and making startup versionable

version-14
Rushabh Mehta 14 jaren geleden
bovenliggende
commit
a7a09867ed
13 gewijzigde bestanden met toevoegingen van 510 en 120 verwijderingen
  1. +5
    -4
      js/core.min.js
  2. +4
    -4
      js/legacy/widgets/dialog.js
  3. +4
    -4
      js/legacy/wnf.compressed.js
  4. +10
    -0
      js/wn/assets.js
  5. +5
    -1
      js/wn/require.js
  6. +8
    -1
      js/wn/xmlhttp.js
  7. +107
    -62
      py/build/version.py
  8. +1
    -2
      py/webnotes/handler.py
  9. +8
    -2
      py/webnotes/model/doc.py
  10. +171
    -0
      py/webnotes/startup.py
  11. +1
    -1
      py/webnotes/tests.py
  12. +158
    -19
      py/webnotes/utils/nestedset.py
  13. +28
    -20
      wnf.py

+ 5
- 4
js/core.min.js Bestand weergeven

@@ -5,19 +5,20 @@ parent=parent[n];}}
wn.provide('wn.settings');wn.provide('wn.ui');wn.xmlhttp={request:function(){if(window.XMLHttpRequest)
return new XMLHttpRequest();else if(window.ActiveXObject)
return new ActiveXObject("MsXml2.XmlHttp");},complete:function(req,callback,url){if(req.status==200||req.status==304){callback(req.responseText);}else{alert(url+' request error: '+req.statusText+' ('+req.status+')');}},get:function(url,callback,args,async){if(async===null)async=true;var req=wn.xmlhttp.request();req.onreadystatechange=function(){if(req.readyState==4){wn.xmlhttp.complete(req,callback,url)}}
var u=args?(url+'?'+args):url;req.open('GET',u,async);req.send(null);if(!async){wn.xmlhttp.complete(req,callback,url)}}}
var sep=(args.indexOf('?')==-1?'?':'&');var u=args?(url+sep+args):url;req.open('GET',u,async);req.send(null);if(!async){wn.xmlhttp.complete(req,callback,url)}}}
wn.versions={is_latest:function(){if(window._version_number==(localStorage?localStorage['_version_number']:null)){return true;}
return false;},get_diff:function(){if(!localStorage)return;wn.xmlhttp.get('index.cgi',function(txt){r=JSON.parse(txt);if(r.exc){alert(r.exc);}
wn.versions.set(r.message);},'cmd=get_diff&version_number='+localStorage['_version_number'],false);},set:function(diff){for(var i=0;i<diff.length;i++){localStorage.removeItem(diff[i]);}
localStorage['_version_number']=_version_number;},check:function(){if(localStorage&&!localStorage['_version_number']){localStorage['_version_number']=_version_number;return;}
if(!wn.versions.is_latest())wn.versions.get_diff();}}
wn.assets={executed_:{},exists:function(src){if('localStorage'in window&&localStorage.getItem(src))
return true},add:function(src,txt){if('localStorage'in window){localStorage.setItem(src,txt);}},get:function(src){return localStorage.getItem(src);},extn:function(src){return src.split('.').slice(-1)[0];},html_src:function(src){if(src.indexOf('/')!=-1){var t=src.split('/').slice(0,-1);t.push('src');t=t.join('/')+'/'+a.split('/').slice(-1)[0];}else{var t='src/'+src;}
return true},add:function(src,txt){if('localStorage'in window){localStorage.setItem(src,txt);}},get:function(src){return localStorage.getItem(src);},extn:function(src){if(src.indexOf('?')!=-1){src=src.split('?').slice(-1)[0];}
return src.split('.').slice(-1)[0];},html_src:function(src){if(src.indexOf('/')!=-1){var t=src.split('/').slice(0,-1);t.push('src');t=t.join('/')+'/'+a.split('/').slice(-1)[0];}else{var t='src/'+src;}
return t;},load:function(src){var t=wn.assets.extn(src)=='html'?wn.assets.html_src(src):src;wn.xmlhttp.get(t,function(txt){wn.assets.add(src,txt);},'q='&Math.floor(Math.random()*1000),false)},execute:function(src){if(!wn.assets.exists(src)){wn.assets.load(src);}
var type=wn.assets.extn(src);if(wn.assets.handler[type]){wn.assets.handler[type](wn.assets.get(src),src);wn.assets.executed_[src]=1;}},handler:{js:function(txt,src){wn.dom.eval(txt);},css:function(txt,src){var se=document.createElement('style');se.type="text/css";if(se.styleSheet){se.styleSheet.cssText=txt;}else{se.appendChild(document.createTextNode(txt));}
document.getElementsByTagName('head')[0].appendChild(se);},html:function(txt,src){var page=wn.dom.add($('.outer .inner').get(0),'div','content',null,txt);page.setAttribute("_src",src);}}}
document.getElementsByTagName('head')[0].appendChild(se);},html:function(txt,src){var page=wn.dom.add($('.outer .inner').get(0),'div','content',null,txt);page.setAttribute("_src",src);},cgi:function(txt,src){wn.dom.eval(txt)}}}
wn.require=function(items){if(typeof items==="string"){items=[items];}
var l=items.length;for(var i=0;i<l;i++){var src=items[i];if(!(src in wn.assets.executed_)){wn.assets.execute(src)}}}
var l=items.length;for(var i=0;i<l;i++){var src=items[i];if(!(src in wn.assets.executed_)){wn.assets.execute(src);}}}
wn.provide('wn.dom');wn.dom.by_id=function(id){return document.getElementById(id);}
wn.dom.eval=function(txt){var el=document.createElement('script');el.appendChild(document.createTextNode(txt));document.getElementsByTagName('head')[0].appendChild(el);}
wn.dom.add=function(parent,newtag,className,cs,innerHTML,onclick){if(parent&&parent.substr)parent=wn.dom.by_id(parent);var c=document.createElement(newtag);if(parent)


+ 4
- 4
js/legacy/widgets/dialog.js Bestand weergeven

@@ -71,7 +71,7 @@ Dialog.prototype.make_row = function(d) {
c1.innerHTML = d[1];
c2.style.overflow = 'auto';
this.widgets[d[1]] = $a_input(c2, 'text');
if(d[2])$a(c2, 'div', 'comment').innerHTML = d[2];
if(d[2])$a(c2, 'div', 'field_description').innerHTML = d[2];
}
else if(d[0]=='Link') {
c1.innerHTML = d[1];
@@ -93,20 +93,20 @@ Dialog.prototype.make_row = function(d) {
c1.innerHTML = d[1];
c2.style.overflow = 'auto';
this.widgets[d[1]] = $a_input(c2, 'password');
if(d[3])$a(c2, 'div', 'comment').innerHTML = d[3];
if(d[3])$a(c2, 'div', 'field_description').innerHTML = d[3];
}
else if(d[0]=='Select') {
c1.innerHTML = d[1];
this.widgets[d[1]] = $a(c2, 'select', '', {width:'160px'})
if(d[2])$a(c2, 'div', 'comment').innerHTML = d[2];
if(d[2])$a(c2, 'div', 'field_description').innerHTML = d[2];
if(d[3])add_sel_options(this.widgets[d[1]], d[3], d[3][0]);
}
else if(d[0]=='Text') {
c1.innerHTML = d[1];
c2.style.overflow = 'auto';
this.widgets[d[1]] = $a(c2, 'textarea');
if(d[2])$a(c2, 'div', 'comment').innerHTML = d[2];
if(d[2])$a(c2, 'div', 'field_description').innerHTML = d[2];
}
else if(d[0]=='Button') {
c2.style.height = '32px';


+ 4
- 4
js/legacy/wnf.compressed.js Bestand weergeven

@@ -329,13 +329,13 @@ Dialog.prototype.make_row=function(d){var me=this;this.rows[d[1]]=$a(this.body,'
$t(c1,d[1]);}
if(d[0]=='HTML'){if(d[2])row.innerHTML=d[2];this.widgets[d[1]]=row;}
else if(d[0]=='Check'){var i=$a_input(c2,'checkbox','',{width:'20px'});c1.innerHTML=d[1];this.widgets[d[1]]=i;}
else if(d[0]=='Data'){c1.innerHTML=d[1];c2.style.overflow='auto';this.widgets[d[1]]=$a_input(c2,'text');if(d[2])$a(c2,'div','comment').innerHTML=d[2];}
else if(d[0]=='Data'){c1.innerHTML=d[1];c2.style.overflow='auto';this.widgets[d[1]]=$a_input(c2,'text');if(d[2])$a(c2,'div','field_description').innerHTML=d[2];}
else if(d[0]=='Link'){c1.innerHTML=d[1];var f=make_field({fieldtype:'Link','label':d[1],'options':d[2]},'',c2,this,0,1);f.not_in_form=1;f.dialog=this;f.refresh();this.widgets[d[1]]=f.input;}
else if(d[0]=='Date'){c1.innerHTML=d[1];var f=make_field({fieldtype:'Date','label':d[1],'options':d[2]},'',c2,this,0,1);f.not_in_form=1;f.refresh();f.dialog=this;this.widgets[d[1]]=f.input;}
else if(d[0]=='Password'){c1.innerHTML=d[1];c2.style.overflow='auto';this.widgets[d[1]]=$a_input(c2,'password');if(d[3])$a(c2,'div','comment').innerHTML=d[3];}
else if(d[0]=='Password'){c1.innerHTML=d[1];c2.style.overflow='auto';this.widgets[d[1]]=$a_input(c2,'password');if(d[3])$a(c2,'div','field_description').innerHTML=d[3];}
else if(d[0]=='Select'){c1.innerHTML=d[1];this.widgets[d[1]]=$a(c2,'select','',{width:'160px'})
if(d[2])$a(c2,'div','comment').innerHTML=d[2];if(d[3])add_sel_options(this.widgets[d[1]],d[3],d[3][0]);}
else if(d[0]=='Text'){c1.innerHTML=d[1];c2.style.overflow='auto';this.widgets[d[1]]=$a(c2,'textarea');if(d[2])$a(c2,'div','comment').innerHTML=d[2];}
if(d[2])$a(c2,'div','field_description').innerHTML=d[2];if(d[3])add_sel_options(this.widgets[d[1]],d[3],d[3][0]);}
else if(d[0]=='Text'){c1.innerHTML=d[1];c2.style.overflow='auto';this.widgets[d[1]]=$a(c2,'textarea');if(d[2])$a(c2,'div','field_description').innerHTML=d[2];}
else if(d[0]=='Button'){c2.style.height='32px';c2.style.textAlign='right';var b=$btn(c2,d[1],function(btn){if(btn._onclick)btn._onclick(me)},null,null,1);b.dialog=me;if(d[2]){b._onclick=d[2];}
this.widgets[d[1]]=b;}}
list_opts={cell_style:{padding:'3px 2px'},alt_cell_style:{},head_style:{height:'20px',overflow:'hidden',verticalAlign:'middle',fontWeight:'bold',padding:'1px',fontSize:'13px'},head_main_style:{padding:'0px'},hide_export:1,hide_print:1,hide_refresh:0,hide_rec_label:0,show_calc:1,show_empty_tab:0,no_border:1,append_records:1,table_width:null};function Listing(head_text,no_index,no_loading){wn.require('lib/js/legacy/widgets/form/fields.js');this.start=0;this.page_len=20;this.filters_per_line=7;this.cell_idx=0;this.head_text=head_text?head_text:'Result';this.keyword='records';this.no_index=no_index;this.underline=1;this.no_rec_message='No Result';this.show_cell=null;this.show_result=null;this.colnames=null;this.colwidths=null;this.coltypes=null;this.coloptions=null;this.filters={};this.sort_list={};this.sort_order_dict={};this.sort_heads={};this.is_std_query=false;this.server_call=null;this.no_loading=no_loading;this.opts=copy_dict(list_opts);}


+ 10
- 0
js/wn/assets.js Bestand weergeven

@@ -28,6 +28,9 @@ wn.assets = {
},
extn: function(src) {
if(src.indexOf('?')!=-1) {
src = src.split('?').slice(-1)[0];
}
return src.split('.').slice(-1)[0];
},

@@ -45,6 +48,8 @@ wn.assets = {
// load an asset via
// xmlhttp
load: function(src) {
// this is virtual page load, only get the the source
// *without* the template
var t = wn.assets.extn(src)=='html' ? wn.assets.html_src(src) : src;

wn.xmlhttp.get(t, function(txt) {
@@ -86,6 +91,11 @@ wn.assets = {
// make the html content page
var page = wn.dom.add($('.outer .inner').get(0), 'div', 'content', null, txt);
page.setAttribute("_src", src);
},
cgi: function(txt, src) {
// dynamic content, will return content as
// javascript
wn.dom.eval(txt)
}
}
}

+ 5
- 1
js/wn/require.js Bestand weergeven

@@ -1,4 +1,8 @@
// require js file
// items to be called by their direct names
// for handler functions
// wn.require('index.cgi?cmd=startup')

wn.require = function(items) {
if(typeof items === "string") {
items = [items];
@@ -9,7 +13,7 @@ wn.require = function(items) {
var src = items[i];
if(!(src in wn.assets.executed_)) {
// check if available in localstorage
wn.assets.execute(src)
wn.assets.execute(src);
}
}
}

+ 8
- 1
js/wn/xmlhttp.js Bestand weergeven

@@ -25,7 +25,14 @@ wn.xmlhttp = {
wn.xmlhttp.complete(req, callback, url)
}
}
var u = args ? (url + '?' + args) : url;
// separator can be & or ?
// based on if there are already arguments
var sep = (args.indexOf('?')==-1 ? '?' : '&');
// add arguments to url
var u = args ? (url + sep + args) : url;

// call the server
req.open('GET', u, async);
req.send(null);


+ 107
- 62
py/build/version.py Bestand weergeven

@@ -17,19 +17,13 @@
must pull the latest .wnf db and merge
versions-local.db is never commited in the global repository
TODO

- walk
- merge
- client
"""

import unittest
import os

test_file = {'fname':'test.js', 'ftype':'js', 'content':'test_code', 'timestamp':'1100'}
root_path = os.path.abspath(os.curdir)
root_path = os.curdir

def edit_file():
# edit a file
@@ -50,18 +44,18 @@ verbose = False

class TestVC(unittest.TestCase):
def setUp(self):
self.vc = VersionControl()
self.vc = VersionControl(root_path, True)
self.vc.repo.setup()
def test_add(self):
self.vc.repo.add(**test_file)
ret = self.vc.repo.sql('select * from uncommitted', as_dict=1)[0]
self.assertTrue(ret==test_file)
self.vc.add(**test_file)
ret = self.vc.repo.sql('select * from uncommitted', as_dict=1)[0]
self.assertTrue(ret['content']==test_file['content'])
def test_commit(self):
last_number = self.vc.repo.get_value('last_version_number')
self.vc.repo.add(**test_file)
self.vc.repo.commit()
self.vc.add(**test_file)
self.vc.commit()

# test version
number = self.vc.repo.get_value('last_version_number')
@@ -78,8 +72,8 @@ class TestVC(unittest.TestCase):
self.assertTrue(self.vc.repo.sql("select * from log where version=?", (version,)))

def test_diff(self):
self.vc.repo.add(**test_file)
self.vc.repo.commit()
self.vc.add(**test_file)
self.vc.commit()
self.assertTrue(self.vc.repo.diff(None), ['test.js'])

def test_walk(self):
@@ -90,7 +84,7 @@ class TestVC(unittest.TestCase):
ret = self.vc.repo.sql("select * from uncommitted", as_dict=1)
self.assertTrue(len(ret)>0)

self.vc.repo.commit()
self.vc.commit()

p = edit_file()
# add
@@ -103,19 +97,18 @@ class TestVC(unittest.TestCase):
def test_merge(self):
self.vc.add_all()
self.vc.repo.commit()
self.vc.commit()
# write the file
self.vc.repo.conn.commit()

# make master (copy)
os.system('cp %s %s' % (os.path.join(root_path, 'versions-local.db'), os.path.join(root_path, 'versions-master.db')))
self.vc.setup_master()

p = edit_file()
self.vc.add_all()
self.vc.repo.commit()
self.vc.commit()
self.vc.merge(self.vc.repo, self.vc.master)
@@ -124,39 +117,66 @@ class TestVC(unittest.TestCase):
def tearDown(self):
self.vc.close()
os.remove(self.vc.repo.db_path)
if os.path.exists(self.vc.local_db_name()):
os.remove(self.vc.local_db_name())
if os.path.exists(self.vc.master_db_name()):
os.remove(self.vc.master_db_name())

class VersionControl:
def __init__(self, root=None):
#self.master = Repository(self, 'versions-master.db')
def __init__(self, root=None, testing=False):
self.testing = testing

self.set_root(root)

self.repo = Repository(self, 'versions-local.db')
self.repo = Repository(self, self.local_db_name())
self.ignore_folders = ['.git', '.', '..']
self.ignore_files = ['py', 'pyc', 'DS_Store', 'txt', 'db-journal', 'db']
def local_db_name(self):
"""return local db name"""
return os.path.join(self.root_path, 'versions-local.db' + (self.testing and '.test' or ''))

def master_db_name(self):
"""return master db name"""
return os.path.join(self.root_path, 'versions-master.db' + (self.testing and '.test' or ''))
def setup_master(self):
self.master = Repository(self, 'versions-master.db')
"""
setup master db from local (if not present)
"""
import os
if not os.path.exists(self.master_db_name()):
os.system('cp %s %s' % (self.local_db_name(), self.master_db_name()))

self.master = Repository(self, self.master_db_name())
def set_root(self, path=None):
"""
set / reset root and connect
(the root path is the path of the folder)
"""
import os
if not path:
raise Exception, 'path must be given'
path = os.path.abspath(os.path.curdir)
self.root_path = path
def relpath(self, fname):
"""
get relative path from root path
"""
import os
return os.path.relpath(fname, self.root_path)
def timestamp(self, path):
"""
returns timestamp
"""
import os
return int(os.stat(path).st_mtime)
if os.path.exists(path):
return int(os.stat(path).st_mtime)
else:
return 0

def add_all(self):
"""
@@ -182,7 +202,7 @@ class VersionControl:
continue
# file does not exist
if not self.repo.exists(fpath):
if not self.exists(fpath):
if verbose:
print "%s added" % fpath
self.repo.add(fpath)
@@ -217,16 +237,44 @@ class VersionControl:
target.add(**f)
target.commit(d[0])
"""
short hand
"""
def commit(self, version=None):
"""commit to local"""
self.repo.commit(version)
def add(self, **args):
"""add to local"""
self.repo.add(**args)

def remove(self, fname):
"""remove from local"""
self.repo.add(fname=fname, action='remove')
def exists(self, fname):
"""exists in local"""
return len(self.repo.sql("select fname from files where fname=?", (self.relpath(fname),)))
def get_file(self, fname):
"""return file"""
return self.repo.sql("select * from files where fname=?", (self.relpath(fname),), as_dict=1)[0]
def close(self):
self.repo.conn.commit()
self.repo.conn.close()
class Repository:
def __init__(self, vc, fname = 'versions-local.db'):
def __init__(self, vc, fname):
self.vc = vc

import sqlite3
self.db_path = os.path.join(self.vc.root_path, fname)
self.conn = sqlite3.connect(self.db_path)
self.cur = self.conn.cursor()
@@ -238,7 +286,7 @@ class Repository:
print "setting up %s..." % self.db_path
self.cur.executescript("""
create table properties(pkey primary key, value);
create table uncommitted(fname primary key, ftype, content, timestamp);
create table uncommitted(fname primary key, ftype, content, timestamp, action);
create table files (fname primary key, ftype, content, timestamp, version);
create table log (fname, ftype, version);
create table versions (number integer primary key, version);
@@ -277,22 +325,26 @@ class Repository:
self.sql("insert or replace into properties(pkey, value) values (?, ?)", (key,value))

def add(self, fname, ftype=None, timestamp=None, content=None, version=None):
def add(self, fname, ftype=None, timestamp=None, content=None, version=None, action=None):
"""
add to uncommitted
"""
import os

if not timestamp:
timestamp = self.vc.timestamp(fname)

# commit relative path
fname = os.path.relpath(fname, self.vc.root_path)
fname = self.vc.relpath(fname)
if not action:
action = 'add'
if not ftype:
ftype = fname.split('.')[-1]
if not timestamp:
timestamp = self.vc.timestamp(fname)
self.sql("insert or replace into uncommitted(fname, ftype, timestamp, content) values (?, ?, ?, ?)" \
, (fname, ftype, timestamp, content))
self.sql("insert or replace into uncommitted(fname, ftype, timestamp, content, action) values (?, ?, ?, ?, ?)" \
, (fname, ftype, timestamp, content, action))
def new_version(self):
"""
@@ -340,29 +392,28 @@ class Repository:
added = self.sql("select * from uncommitted", as_dict=1)
for f in added:
if f['action']=='add':
# move them to "files"
self.sql("""
insert or replace into files
(fname, ftype, timestamp, content, version)
values (?,?,?,?,?)
""", (f['fname'], f['ftype'], f['timestamp'], f['content'], version))

elif f['action']=='remove':
self.sql("""delete from files where fname=?""", (f['fname'],))
else:
raise Exception, 'bad action %s' % action
# move them to "files"
self.sql("""
insert or replace into files
(fname, ftype, timestamp, content, version)
values (?,?,?,?,?)
""", (f['fname'], f['ftype'], f['timestamp'], f['content'], version))
# update log
self.add_log(f['fname'], f['ftype'], version)
def exists(self, fname):
"""
true if exists
"""
fname = os.path.relpath(fname, self.vc.root_path)
return self.sql("select fname from files where fname=?", (fname,))

def timestamp(self, fname):
"""
get timestamp
"""
fname = os.path.relpath(fname, self.vc.root_path)
fname = self.vc.relpath(fname)
return int(self.sql("select timestamp from files where fname=?", (fname,))[0][0] or 0)

def diff(self, number):
@@ -383,12 +434,6 @@ class Repository:
"""
return [f[0] for f in self.sql("select fname from uncommitted")]
def get_file(self, fname):
"""
return file info as dict
"""
return self.sql("select * from files where fname=?", (fname,), as_dict=1)[0]
def add_log(self, fname, ftype, version):
"""
add file to log


+ 1
- 2
py/webnotes/handler.py Bestand weergeven

@@ -44,10 +44,9 @@ def logout():
# --------

def get_diff():
import os
v = webnotes.form_dict.get('version_number')
from build.version import VersionControl
webnotes.response['message'] = VersionControl(os.path.abspath(os.path.curdir)).repo.diff(v)
webnotes.response['message'] = VersionControl().repo.diff(v)

# DocType Mapper
# ------------------------------------------------------------------------------------


+ 8
- 2
py/webnotes/model/doc.py Bestand weergeven

@@ -95,8 +95,14 @@ class Document:
def _loadfromdb(self, doctype = None, name = None):
if name: self.name = name
if doctype: self.doctype = doctype
if webnotes.model.meta.is_single(self.doctype):
is_single = False
try:
is_single = webnotes.model.meta.is_single(self.doctype)
except Exception, e:
pass
if is_single:
self._loadsingle()
else:
dataset = webnotes.conn.sql('select * from `%s%s` where name="%s"' % (self._prefix, self.doctype, self.name.replace('"', '\"')))


+ 171
- 0
py/webnotes/startup.py Bestand weergeven

@@ -0,0 +1,171 @@
"""
startup info for the app
client needs info that is static across all users
and user specific info like roles and defaults
so calling will be:
index.cgi?cmd=webnotes.startup.common_info
index.cgi?cmd=webnotes.startup.user_info&user=x@y.com
to clear startup,
you must clear all files in the vcs starting with index.cgi?cmd=webnotes.startup
"""

import webnotes


def get_letter_heads():
"""
get letter head
"""
import webnotes
try:
lh = {}
ret = webnotes.conn.sql("select name, content from `tabLetter Head` where ifnull(disabled,0)=0")
for r in ret:
lh[r[0]] = r[1]
return lh
except Exception, e:
if e.args[0]==1146:
return {}
else:
raise Exception, e



def get_content_user():
"""
get user specific content
"""
import webnotes
import webnotes.utils
import webnotes.widgets.page
import webnotes.widgets.menus
user = webnotes.form_dict['user']
doclist, ret = [], {}

webnotes.conn.begin()
ret['profile'] = webnotes.user.load_profile()
home_page = webnotes.user.get_home_page()
if home_page:
doclist += webnotes.widgets.page.get(home_page)
ret['sysdefaults'] = webnotes.utils.get_defaults()
ret['home_page'] = home_page or ''
# role-wise menus
ret['start_items'] = webnotes.widgets.menus.get_menu_items()
# bundle
webnotes.session['data']['profile'] = ret['profile']
if webnotes.session['data'].get('ipinfo'):
ret['ipinfo'] = webnotes.session['data']['ipinfo']

webnotes.conn.commit()
webnotes.response['docs'] = doclist
return ret

def get_content_common():
"""
build common startup info
"""

import webnotes
import webnotes.model.doc
import webnotes.model.doctype
import webnotes.model
doclist, ret = [], {}
doclist += webnotes.model.doc.get('Control Panel')
doclist += webnotes.model.doctype.get('Event')
doclist += webnotes.model.doctype.get('Search Criteria')

cp = doclist[0]
ret['account_name'] = cp.account_id or ''
ret['letter_heads'] = get_letter_heads()
ret['dt_labels'] = webnotes.model.get_dt_labels()

webnotes.response['docs'] = doclist

return ret


def common_info():
"""
get common startup info (from version or live)
"""
get_info('index.cgi?cmd=webnotes.startup.common_info', 'common')

def user_info():
"""
get user info
"""
user = webnotes.form_dict['user']
get_info('index.cgi?cmd=webnotes.startup.user_info&user='+user, 'user')

def get_info(fname, key):
"""
get info from version or re-build
"""
from build.version import VersionControl

vc = VersionControl()

# from versions (same static)
if vc.exists(fname):
content = vc.get_file(fname)['content']
else:
content = globals().get('get_content_'+key)()
import json
content = json.dumps(content)

# add in vcs
vc.add(fname=fname, content=content)
vc.commit()
vc.close()
webnotes.response['content'] = content
return





def clear_info(info_type=None):
"""
clear startup info and force a new version
parameter: info_type = 'user' or 'common' or 'all'
"""
if not info_type:
info_type = webnotes.form_dict.get('info_type')
from build.version import VersionControl
vc = VersionControl()

flist = []

if info_type=='common':
flist = ['index.cgi?cmd=webnotes.startup.common_info']
elif info_type=='user':
flist = [f[0] for f in vc.repo.sql("""select fname from files where fname like ?""",\
('index.cgi?cmd=webnotes.startup.user_info%',))]
elif info_type=='all':
flist = [f[0] for f in vc.repo.sql("""select fname from files where fname like ?""",\
('index.cgi?cmd=webnotes.startup%',))]
else:
webnotes.msgprint("info_type not found: %s" % info_type)
for f in flist:
print 'clearing %s' % f
vc.remove(f)
vc.commit()
vc.close()

+ 1
- 1
py/webnotes/tests.py Bestand weergeven

@@ -15,7 +15,7 @@ import sys, os
import unittest

# webnotes path
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
sys.path.append('lib/py')

# modules path
import webnotes


+ 158
- 19
py/webnotes/utils/nestedset.py Bestand weergeven

@@ -8,29 +8,149 @@

# ------------------------------------------

import webnotes
import webnotes, unittest

class TestNSM(unittest.TestCase):
def setUp(self):
from webnotes.model.doc import Document
self.root = Document(fielddata={'doctype':'nsmtest', 'name':'T001', 'parent':None})
self.first_child = Document(fielddata={'doctype':'nsmtest', 'name':'C001', 'parent_node':'T001'})
self.first_sibling = Document(fielddata={'doctype':'nsmtest', 'name':'C002', 'parent_node':'T001'})
self.grand_child = Document(fielddata={'doctype':'nsmtest', 'name':'GC001', 'parent_node':'C001'})

webnotes.conn.sql("""
create table `tabnsmtest` (
name varchar(120) not null primary key,
creation datetime,
modified datetime,
modified_by varchar(40),
owner varchar(40),
docstatus int(1) default '0',
parent varchar(120),
parentfield varchar(120),
parenttype varchar(120),
idx int(8),
parent_node varchar(180),
old_parent varchar(180),
lft int,
rgt int) ENGINE=InnoDB""")
def test_root(self):
self.root.save(1)
update_nsm(self.root)
self.assertTrue(self.root.lft==1)
self.assertTrue(self.root.rgt==2)
def test_first_child(self):
self.root.save(1)
update_nsm(self.root)

self.first_child.save(1)
update_nsm(self.first_child)

self.root._loadfromdb()
self.assertTrue(self.root.lft==1)
self.assertTrue(self.first_child.lft==2)
self.assertTrue(self.first_child.rgt==3)
self.assertTrue(self.root.rgt==4)
def test_sibling(self):
self.test_first_child()
self.first_sibling.save(1)
update_nsm(self.first_sibling)
self.root._loadfromdb()
self.first_child._loadfromdb()
self.assertTrue(self.root.lft==1)
self.assertTrue(self.first_child.lft==2)
self.assertTrue(self.first_child.rgt==3)
self.assertTrue(self.first_sibling.lft==4)
self.assertTrue(self.first_sibling.rgt==5)
self.assertTrue(self.root.rgt==6)
def test_remove_sibling(self):
self.test_sibling()
self.first_sibling.parent_node = ''
update_nsm(self.first_sibling)

self.root._loadfromdb()
self.first_child._loadfromdb()

self.assertTrue(self.root.lft==1)
self.assertTrue(self.first_child.lft==2)
self.assertTrue(self.first_child.rgt==3)
self.assertTrue(self.root.rgt==4)
self.assertTrue(self.first_sibling.lft==5)
self.assertTrue(self.first_sibling.rgt==6)
def test_change_parent(self):
self.test_sibling()
# add grand child
self.grand_child.save(1)
update_nsm(self.grand_child)
# check lft rgt
self.assertTrue(self.grand_child.lft==3)
self.assertTrue(self.grand_child.rgt==4)

# change parent
self.grand_child.parent_node = 'C002'
self.grand_child.save()
# update
update_nsm(self.grand_child)

# check lft rgt
self.assertTrue(self.grand_child.lft==5)
self.assertTrue(self.grand_child.rgt==6)

def tearDown(self):
webnotes.conn.sql("drop table tabnsmtest")



# called in the on_update method
def update_nsm(doc_obj):
# get fields, data from the DocType
d = doc_obj.doc
pf, opf = 'parent_node', 'old_parent'
if hasattr(doc_obj,'nsm_parent_field'):
pf = doc_obj.nsm_parent_field
if hasattr(doc_obj,'nsm_oldparent_field'):
opf = doc_obj.nsm_oldparent_field
p, op = d.fields[pf], d.fields.get(opf, '')

if str(doc_obj.__class__)=='webnotes.model.doc.Document':
# passed as a Document object
d = doc_obj
else:
# passed as a DocType object
d = doc_obj.doc
if hasattr(doc_obj,'nsm_parent_field'):
pf = doc_obj.nsm_parent_field
if hasattr(doc_obj,'nsm_oldparent_field'):
opf = doc_obj.nsm_oldparent_field

p, op = d.fields.get(pf, ''), d.fields.get(opf, '')

# has parent changed (?) or parent is None (root)
if not doc_obj.doc.lft and not doc_obj.doc.rgt:
update_add_node(doc_obj.doc.doctype, doc_obj.doc.name, p or '', pf)
if not d.lft and not d.rgt:
update_add_node(d.doctype, d.name, p or '', pf)
elif op != p:
update_remove_node(doc_obj.doc.doctype, doc_obj.doc.name)
update_add_node(doc_obj.doc.doctype, doc_obj.doc.name, p or '', pf)
update_remove_node(d.doctype, d.name)
update_add_node(d.doctype, d.name, p or '', pf)
# set old parent
webnotes.conn.set(d, opf, p or '')

# reload
d._loadfromdb()
def rebuild_tree(doctype, parent_field):
"""
call rebuild_node for all root nodes
"""
# get all roots
right = 1
result = webnotes.conn.sql("SELECT name FROM `tab%s` WHERE `%s`='' or `%s` IS NULL" % (doctype, parent_field, parent_field))
@@ -38,6 +158,12 @@ def rebuild_tree(doctype, parent_field):
right = rebuild_node(doctype, r[0], right, parent_field)
def rebuild_node(doctype, parent, left, parent_field):
"""
reset lft, rgt and recursive call for all children
"""
from webnotes.utils import now
n = now()
# the right value of this node is the left value + 1
right = left+1

@@ -48,37 +174,50 @@ def rebuild_node(doctype, parent, left, parent_field):

# we've got the left value, and now that we've processed
# the children of this node we also know the right value
webnotes.conn.sql('UPDATE `tab%s` SET lft=%s, rgt=%s WHERE name="%s"' % (doctype,left,right,parent))
webnotes.conn.sql("UPDATE `tab%s` SET lft=%s, rgt=%s, modified='%s' WHERE name='%s'" % (doctype,left,right,n,parent))

#return the right value of this node + 1
return right+1
def update_add_node(doctype, name, parent, parent_field):
"""
insert a new node
"""
from webnotes.utils import now
n = now()

# get the last sibling of the parent
if parent:
right = webnotes.conn.sql("select rgt from `tab%s` where name='%s'" % (doctype, parent))[0][0]
else: # root
right = webnotes.conn.sql("select ifnull(max(rgt),0)+1 from `tab%s` where ifnull(`%s`,'') =''" % (doctype, parent_field))[0][0]
right = right or 1
# update all on the right
webnotes.conn.sql("update `tab%s` set rgt = rgt+2 where rgt >= %s" %(doctype,right))
webnotes.conn.sql("update `tab%s` set lft = lft+2 where lft >= %s" %(doctype,right))
webnotes.conn.sql("update `tab%s` set rgt = rgt+2, modified='%s' where rgt >= %s" %(doctype,n,right))
webnotes.conn.sql("update `tab%s` set lft = lft+2, modified='%s' where lft >= %s" %(doctype,n,right))
# update index of new node
if webnotes.conn.sql("select * from `tab%s` where lft=%s or rgt=%s"% (doctype, right, right+1)):
webnotes.msgprint("Nested set error. Please send mail to support")
raise Exception

webnotes.conn.sql("update `tab%s` set lft=%s, rgt=%s where name='%s'" % (doctype,right,right+1,name))
webnotes.conn.sql("update `tab%s` set lft=%s, rgt=%s, modified='%s' where name='%s'" % (doctype,right,right+1,n,name))
return right

def update_remove_node(doctype, name):
"""
remove a node
"""
from webnotes.utils import now
n = now()

left = webnotes.conn.sql("select lft from `tab%s` where name='%s'" % (doctype,name))
if left[0][0]:
# reset this node
webnotes.conn.sql("update `tab%s` set lft=0, rgt=0 where name='%s'" % (doctype,name))
webnotes.conn.sql("update `tab%s` set lft=0, rgt=0, modified='%s' where name='%s'" % (doctype,n,name))

# update all on the right
webnotes.conn.sql("update `tab%s` set rgt = rgt-2 where rgt > %s" %(doctype,left[0][0]))
webnotes.conn.sql("update `tab%s` set lft = lft-2 where lft > %s" %(doctype,left[0][0]))
webnotes.conn.sql("update `tab%s` set rgt = rgt-2, modified='%s' where rgt > %s" %(doctype,n,left[0][0]))
webnotes.conn.sql("update `tab%s` set lft = lft-2, modified='%s' where lft > %s" %(doctype,n,left[0][0]))


+ 28
- 20
wnf.py Bestand weergeven

@@ -9,7 +9,6 @@ version.verbose = True
def run():
sys.path.append('lib')
sys.path.append('lib/py')
vc = version.VersionControl(os.path.abspath(os.curdir))

if len(sys.argv)<2:
print "wnframework version control utility"
@@ -20,25 +19,13 @@ def run():

if cmd=='build':
from py import build
build.run(os.path.abspath(os.curdir))
if cmd=='add':
if not len(sys.argv)>1:
print 'usage: wnf add path/to/file'
return
vc.repo.add(sys.argv[2])
if cmd=='commit':
if len(sys.argv>2) and sys.argv[2]=='-a':
vc.add_all()
vc.repo.commit()
if cmd=='diff':
vc.repo.uncommitted()
build.run()

vc = version.VersionControl()
print 'version %s' % vc.repo.get_value('last_version_number')
if cmd=='merge':
vc = version.VersionControl()
vc.setup_master()
if sys.argv[2]=='local':
vc.merge(vc.repo, vc.master)
@@ -47,11 +34,32 @@ def run():
else:
print "usage: wnf merge local|master"
print "help: parameter (local or master) is the source"
vc.close()

if cmd=='setup':
vc = version.VersionControl()
vc.repo.setup()
vc.close()
if cmd=='clear_startup':
from webnotes import startup
startup.clear_info('all')

vc.close()
vc = version.VersionControl()
print 'version %s' % vc.repo.get_value('last_version_number')
if cmd=='log':
vc = version.VersionControl()
for l in vc.repo.sql("select * from log order by rowid desc limit 10 ", as_dict =1):
print 'file:'+ l['fname'] + ' | version: ' + l['version']
print 'version %s' % vc.repo.get_value('last_version_number')
vc.close()
if cmd=='files':
vc = version.VersionControl()
for f in vc.repo.sql("select fname from files where fname like ?", ((sys.argv[2] + '%'),)):
print f[0]
vc.close()

if __name__=='__main__':
run()

Laden…
Annuleren
Opslaan