added docfield utility
This commit is contained in:
parent
68330eb88d
commit
4ef06e8bf0
9 changed files with 308 additions and 167 deletions
BIN
css/fonts/cabin.woff
Normal file
BIN
css/fonts/cabin.woff
Normal file
Binary file not shown.
69
py/webnotes/model/docfield.py
Normal file
69
py/webnotes/model/docfield.py
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
# Copyright (c) 2012 Web Notes Technologies Pvt Ltd (http://erpnext.com)
|
||||
#
|
||||
# MIT License (MIT)
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a
|
||||
# copy of this software and associated documentation files (the "Software"),
|
||||
# to deal in the Software without restriction, including without limitation
|
||||
# the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
# and/or sell copies of the Software, and to permit persons to whom the
|
||||
# Software is furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
# INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||
# PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
|
||||
# CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
|
||||
# OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
#
|
||||
|
||||
"""docfield utililtes"""
|
||||
|
||||
import webnotes
|
||||
|
||||
def rename(doctype, fieldname, newname):
|
||||
"""rename docfield"""
|
||||
df = webnotes.conn.sql("""select * from tabDocField where parent=%s and fieldname=%s""",
|
||||
(doctype, fieldname), as_dict=1)
|
||||
if not df:
|
||||
return
|
||||
|
||||
df = df[0]
|
||||
|
||||
if webnotes.conn.get_value('DocType', doctype, 'issingle'):
|
||||
update_single(df, newname)
|
||||
else:
|
||||
update_table(df, newname)
|
||||
update_parent_field(df, newname)
|
||||
|
||||
def update_single(f, new):
|
||||
"""update in tabSingles"""
|
||||
webnotes.conn.begin()
|
||||
webnotes.conn.sql("""update tabSingles set field=%s where doctype=%s and field=%s""",
|
||||
(new, f['parent'], f['fieldname']))
|
||||
webnotes.conn.commit()
|
||||
|
||||
def update_table(f, new):
|
||||
"""update table"""
|
||||
query = get_change_column_query(f, new)
|
||||
if query:
|
||||
webnotes.conn.sql(query)
|
||||
|
||||
def update_parent_field(f, new):
|
||||
"""update 'parentfield' in tables"""
|
||||
if f['fieldtype']=='Table':
|
||||
webnotes.conn.begin()
|
||||
webnotes.conn.sql("""update `tab%s` set parentfield=%s where parentfield=%s""" \
|
||||
% (f['options'], '%s', '%s'), (new, f['fieldname']))
|
||||
webnotes.conn.commit()
|
||||
|
||||
def get_change_column_query(f, new):
|
||||
"""generate change fieldname query"""
|
||||
desc = webnotes.conn.sql("desc `tab%s`" % f['parent'])
|
||||
for d in desc:
|
||||
if d[0]== f['fieldname']:
|
||||
return 'alter table `tab%s` change `%s` `%s` %s' % \
|
||||
(f['parent'], f['fieldname'], new, d[1])
|
||||
|
|
@ -198,7 +198,6 @@ class DocList:
|
|||
if hasattr(self.obj, 'custom_' + method):
|
||||
getattr(self.obj, 'custom_' + method)()
|
||||
|
||||
from webnotes.model.events import trigger
|
||||
trigger(method, self.doc)
|
||||
|
||||
def save_main(self):
|
||||
|
|
@ -313,6 +312,19 @@ def clone(source_doclist):
|
|||
return doclistobj
|
||||
|
||||
|
||||
def trigger(method, doc):
|
||||
"""trigger doctype events"""
|
||||
try:
|
||||
import startup.event_handlers
|
||||
except ImportError:
|
||||
return
|
||||
|
||||
if hasattr(startup.event_handlers, method):
|
||||
getattr(startup.event_handlers, method)(doc)
|
||||
|
||||
if hasattr(startup.event_handlers, 'doclist_all'):
|
||||
startup.event_handlers.doclist_all(doc, method)
|
||||
|
||||
# for bc
|
||||
def getlist(doclist, parentfield):
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -1,36 +0,0 @@
|
|||
# Copyright (c) 2012 Web Notes Technologies Pvt Ltd (http://erpnext.com)
|
||||
#
|
||||
# MIT License (MIT)
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a
|
||||
# copy of this software and associated documentation files (the "Software"),
|
||||
# to deal in the Software without restriction, including without limitation
|
||||
# the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
# and/or sell copies of the Software, and to permit persons to whom the
|
||||
# Software is furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
# INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||
# PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
|
||||
# CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
|
||||
# OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
#
|
||||
|
||||
"""trigger doctype events"""
|
||||
|
||||
def trigger(method, doc):
|
||||
try:
|
||||
import startup.event_handlers
|
||||
except ImportError:
|
||||
return
|
||||
|
||||
if hasattr(startup.event_handlers, method):
|
||||
getattr(startup.event_handlers, method)(doc)
|
||||
|
||||
if hasattr(startup.event_handlers, 'doclist_all'):
|
||||
startup.event_handlers.doclist_all(doc, method)
|
||||
|
||||
|
|
@ -1,48 +0,0 @@
|
|||
# Copyright (c) 2012 Web Notes Technologies Pvt Ltd (http://erpnext.com)
|
||||
#
|
||||
# MIT License (MIT)
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a
|
||||
# copy of this software and associated documentation files (the "Software"),
|
||||
# to deal in the Software without restriction, including without limitation
|
||||
# the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
# and/or sell copies of the Software, and to permit persons to whom the
|
||||
# Software is furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
# INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||
# PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
|
||||
# CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
|
||||
# OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
#
|
||||
|
||||
# Modules
|
||||
# -----------
|
||||
|
||||
def get_module_items(mod, only_dt=0):
|
||||
dl = []
|
||||
if only_dt:
|
||||
transfer_types = ['DocType']
|
||||
else:
|
||||
transfer_types = ['Role', 'Page', 'DocType', 'DocType Mapper', 'Search Criteria']
|
||||
dl = ['Module Def,'+mod]
|
||||
|
||||
for dt in transfer_types:
|
||||
try:
|
||||
dl2 = sql('select name from `tab%s` where module="%s"' % (dt,mod))
|
||||
dl += [(dt+','+e[0]) for e in dl2]
|
||||
except:
|
||||
pass
|
||||
|
||||
if not only_dt:
|
||||
dl1 = sql('select doctype_list from `tabModule Def` where name=%s', mod)
|
||||
dl += dl1[0][0].split('\n')
|
||||
|
||||
# build finally
|
||||
dl = [e.split(',') for e in dl]
|
||||
dl = [[e[0].strip(), e[1].strip()] for e in dl] # remove blanks
|
||||
return dl
|
||||
|
|
@ -1,80 +0,0 @@
|
|||
# Copyright (c) 2012 Web Notes Technologies Pvt Ltd (http://erpnext.com)
|
||||
#
|
||||
# MIT License (MIT)
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a
|
||||
# copy of this software and associated documentation files (the "Software"),
|
||||
# to deal in the Software without restriction, including without limitation
|
||||
# the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
# and/or sell copies of the Software, and to permit persons to whom the
|
||||
# Software is furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
# INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||
# PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
|
||||
# CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
|
||||
# OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
#
|
||||
|
||||
"""
|
||||
Sync sql files (that contain triggers, stored procs etc) into the database
|
||||
calling sync will walk through all .sql files in the modules file structure
|
||||
and execute them if they are not executed or their timestamps have changed
|
||||
|
||||
All sql timestamps will be saved in a table '__sql_timestamps'
|
||||
"""
|
||||
|
||||
# modules path
|
||||
import webnotes
|
||||
import webnotes.defs
|
||||
|
||||
def get_sql_files():
|
||||
"""
|
||||
Returns list of .sql files from
|
||||
"""
|
||||
import os
|
||||
ret = []
|
||||
for walk_tuple in os.walk(webnotes.defs.modules_path):
|
||||
if os.path.split(walk_tuple[0])[-1]=='doctype':
|
||||
for sql_file in filter(lambda x: x.endswith('.sql'), walk_tuple[2]):
|
||||
ret.append[os.path.join(walk_tuple[0], sql_file)]
|
||||
return ret
|
||||
|
||||
def run_sql_file(fn):
|
||||
"""
|
||||
Checks if timestamp matches, if not runs it
|
||||
"""
|
||||
from webnotes.modules import ModuleFile
|
||||
mf = ModuleFile(fn)
|
||||
if mf.is_new():
|
||||
webnotes.conn.sql(mf.read())
|
||||
mf.update()
|
||||
|
||||
def get_sql_timestamp(fn):
|
||||
"""
|
||||
Returns the last updated timestamp of the sql file
|
||||
from the __sql_timestamps table. If the table does not
|
||||
exist, it will create it
|
||||
"""
|
||||
try:
|
||||
ts = webnotes.conn.sql("select tstamp from __sql_timestamp where file_name=%s", fn)
|
||||
if ts:
|
||||
return ts[0][0]
|
||||
except Exception, e:
|
||||
if e.args[0]==1147:
|
||||
# create the table
|
||||
webnotes.conn.commit()
|
||||
webnotes.conn.sql("""
|
||||
create table __sql_timestamp (
|
||||
file_name varchar(320) primary key,
|
||||
tstamp varchar(40))""")
|
||||
webnotes.conn.begin()
|
||||
else:
|
||||
raise e
|
||||
|
||||
def update_sql_timestamp(fn, ts):
|
||||
pass
|
||||
29
py/webnotes/tests/test_docfield.py
Normal file
29
py/webnotes/tests/test_docfield.py
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
import unittest, sys
|
||||
|
||||
sys.path.append('lib/py')
|
||||
|
||||
import webnotes
|
||||
from webnotes.model import docfield
|
||||
webnotes.connect()
|
||||
|
||||
class TestDocField(unittest.TestCase):
|
||||
def test_rename(self):
|
||||
docfield.rename('Event', 'notes', 'notes1')
|
||||
|
||||
# check in table
|
||||
tf = webnotes.conn.sql("""desc tabEvent""")
|
||||
|
||||
self.assertTrue('notes' not in [d[0] for d in tf])
|
||||
self.assertTrue('notes1' in [d[0] for d in tf])
|
||||
|
||||
docfield.rename('Event', 'notes1', 'notes')
|
||||
|
||||
def test_table_rename(self):
|
||||
docfield.rename('Event', 'event_individuals', 'event_users')
|
||||
|
||||
self.assertFalse(webnotes.conn.sql("""select parent from `tabEvent User` where parentfield='event_individuals'"""))
|
||||
self.assertTrue(webnotes.conn.sql("""select parent from `tabEvent User` where parentfield='event_users'"""))
|
||||
|
||||
|
||||
if __name__=='__main__':
|
||||
unittest.main()
|
||||
|
|
@ -438,8 +438,7 @@ def in_words(integer):
|
|||
Returns string in words for the given integer.
|
||||
"""
|
||||
|
||||
in_million = webnotes.conn.get_value('Control Panel',None,'currency_format')=='Millions' and 1 or 0
|
||||
|
||||
in_million = webnotes.conn.get_default('currency_format')=='Millions' and 1 or 0
|
||||
|
||||
n=int(integer)
|
||||
known = {0: 'zero', 1: 'one', 2: 'two', 3: 'three', 4: 'four', 5: 'five', 6: 'six', 7: 'seven', 8: 'eight', 9: 'nine', 10: 'ten',
|
||||
|
|
|
|||
196
py/webnotes/utils/editdoctype.py
Executable file
196
py/webnotes/utils/editdoctype.py
Executable file
|
|
@ -0,0 +1,196 @@
|
|||
#!/usr/bin/python
|
||||
|
||||
"""
|
||||
Utilty to review DocType fields:
|
||||
|
||||
- Will prompt each field and allow to change properties and fieldnames
|
||||
- Run from command line of project home
|
||||
- set fieldname_patch_file in defs.py
|
||||
|
||||
# todo
|
||||
|
||||
- update db
|
||||
- parent field for table fields
|
||||
"""
|
||||
|
||||
import os, sys
|
||||
|
||||
curpath = os.path.dirname(__file__)
|
||||
|
||||
sys.path.append(os.path.join(curpath, '..', '..'))
|
||||
|
||||
import webnotes
|
||||
import webnotes.defs
|
||||
from webnotes.modules.export_module import export_to_files
|
||||
from termcolor import colored
|
||||
|
||||
sys.path.append(webnotes.defs.modules_path)
|
||||
|
||||
def update_field_property(f, property):
|
||||
import webnotes
|
||||
|
||||
new = raw_input('New %s: ' % property)
|
||||
if new:
|
||||
webnotes.conn.begin()
|
||||
webnotes.conn.set_value('DocField', f['name'], property, new)
|
||||
webnotes.conn.commit()
|
||||
export_to_files(record_list=[['DocType', f['parent']]])
|
||||
|
||||
return new
|
||||
|
||||
def remove_field(f):
|
||||
webnotes.conn.begin()
|
||||
webnotes.conn.sql("""delete from tabDocField where name=%s""", f['name'])
|
||||
webnotes.conn.commit()
|
||||
export_to_files(record_list=[['DocType', f['parent']]])
|
||||
|
||||
def replace_code(old, new):
|
||||
"""find files with fieldname using grep and pass fieldnames to replace code"""
|
||||
import subprocess
|
||||
|
||||
# js
|
||||
proc = subprocess.Popen(['grep', '-rl', '--include=*.js', old, '.'],
|
||||
shell=False, stdout = subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
stdout, stderr = proc.communicate()
|
||||
for fpath in stdout.split():
|
||||
ret = search_replace_with_prompt(fpath, old, new)
|
||||
if ret == 'quit':
|
||||
break
|
||||
|
||||
# py
|
||||
proc = subprocess.Popen(['grep', '-rl', '--include=*.py', old, '.'],
|
||||
shell=False, stdout = subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
stdout, stderr = proc.communicate()
|
||||
for fpath in stdout.split():
|
||||
ret = search_replace_with_prompt(fpath, old, new)
|
||||
if ret == 'quit':
|
||||
break
|
||||
|
||||
def update_fieldname_patch_file(fdata):
|
||||
"""update patch file with list"""
|
||||
with open(webnotes.defs.fieldname_patch_file, 'a') as patchfile:
|
||||
patchfile.write(str(fdata) + '\n')
|
||||
|
||||
def search_replace_with_prompt(fpath, txt1, txt2):
|
||||
""" Search and replace all txt1 by txt2 in the file with confirmation"""
|
||||
|
||||
from termcolor import colored
|
||||
with open(fpath, 'r') as f:
|
||||
content = f.readlines()
|
||||
|
||||
tmp = []
|
||||
for c in content:
|
||||
if c.find(txt1) != -1:
|
||||
print '\n', fpath
|
||||
print colored(txt1, 'red').join(c[:-1].split(txt1))
|
||||
a = ''
|
||||
while a not in ['y', 'n', 'Y', 'N', 's', 'q']:
|
||||
a = raw_input('Do you want to Change [y/n/s/q]?')
|
||||
if a.lower() == 'y':
|
||||
c = c.replace(txt1, txt2)
|
||||
if a.lower() == 's':
|
||||
return
|
||||
if a.lower() == 'q':
|
||||
return 'quit'
|
||||
tmp.append(c)
|
||||
|
||||
with open(fpath, 'w') as f:
|
||||
f.write(''.join(tmp))
|
||||
print colored('Updated', 'green')
|
||||
|
||||
def review():
|
||||
"""review fields"""
|
||||
start = 0
|
||||
|
||||
flist = webnotes.conn.sql("""select t1.name, t1.parent, t1.fieldtype, t1.label, t1.fieldname,
|
||||
t1.options, t1.description from tabDocField t1, tabDocType t2 where
|
||||
t1.fieldtype not in ('Section Break', 'Column Break') and
|
||||
t1.fieldname not in ('address_display', 'contact_display') and
|
||||
t1.parent = t2.name and
|
||||
t2.module != 'Core' and
|
||||
replace(replace(replace(replace(lower(label), ' ', '_'), '-', '_'), ')', ''), '(', '') != fieldname
|
||||
order by parent, label""", as_dict=1)
|
||||
|
||||
for f in flist[start:]:
|
||||
os.system('clear')
|
||||
print f['parent']
|
||||
print 'Fieldname: ' + colored(f['fieldname'], 'red')
|
||||
print 'Label:' + f['label']
|
||||
print 'Description: ' + str(f['description'])
|
||||
print 'Type: ' + str(f['fieldtype'])
|
||||
print 'Options: ' + str(f['options'])
|
||||
print str(start) + '/' + str(len(flist))
|
||||
|
||||
action = ''
|
||||
while action != 'n':
|
||||
print '-----------------'
|
||||
action = raw_input("""[n]ext, [f]ieldname, [l]abel, [d]escription, [r]emove, view [c]ode, [q]uit:""")
|
||||
if action=='l':
|
||||
old = f['label']
|
||||
new = update_field_property(f, 'label')
|
||||
|
||||
# replace code for search criteria
|
||||
replace_code(old, new)
|
||||
|
||||
elif action=='d':
|
||||
update_field_property(f, 'description')
|
||||
elif action=='c':
|
||||
print colored('js:', 'green')
|
||||
os.system('grep -r --color --include=*.js "%s" ./' % f['fieldname'])
|
||||
print colored('py:', 'green')
|
||||
os.system('grep -r --color --include=*.py "%s" ./' % f['fieldname'])
|
||||
elif action=='f':
|
||||
old = f['fieldname']
|
||||
new = update_field_property(f, 'fieldname')
|
||||
|
||||
if new:
|
||||
# rename in table and parentfield
|
||||
from webnotes.model import docfield
|
||||
docfield.rename(f['parent'], f['fieldname'], new)
|
||||
|
||||
# replace code
|
||||
replace_code(old, new)
|
||||
|
||||
# write in fieldname patch file
|
||||
update_fieldname_patch_file([f['parent'], f['fieldname'], new])
|
||||
|
||||
elif action=='r':
|
||||
remove_field(f)
|
||||
action = 'n'
|
||||
elif action=='q':
|
||||
return
|
||||
|
||||
start += 1
|
||||
|
||||
def setup_options():
|
||||
from optparse import OptionParser
|
||||
parser = OptionParser()
|
||||
|
||||
parser.add_option("-a", "--all",
|
||||
action="store_true", default=False,
|
||||
help="Review all fields")
|
||||
|
||||
parser.add_option("-d", "--doctype",
|
||||
nargs=1, default=False, metavar='DOCTYPE',
|
||||
help="Review fields of a doctype")
|
||||
|
||||
parser.add_option("-f", "--field",
|
||||
nargs=2, default=False, metavar='DOCTYPE FIELD',
|
||||
help="Review a particular field")
|
||||
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
if __name__=='__main__':
|
||||
webnotes.connect()
|
||||
options, args = setup_options()
|
||||
|
||||
if options.all:
|
||||
review()
|
||||
|
||||
if options.doctype:
|
||||
review(options.doctype)
|
||||
|
||||
if options.field:
|
||||
review(options.field[0], options.field[1])
|
||||
|
||||
Loading…
Add table
Reference in a new issue