|
- """
- Utilities for using modules
- """
- import webnotes
-
- transfer_types = ['Role', 'Print Format','DocType','Page','DocType Mapper','GL Mapper','Search Criteria', 'Patch']
-
- def scrub(txt):
- return txt.replace(' ','_').replace('-', '_').replace('/', '_').lower()
-
- def scrub_dt_dn(dt, dn):
- """
- Returns in lowercase and code friendly names of doctype and name for certain types
- """
- ndt, ndn = dt, dn
- if dt.lower() in ('doctype', 'search criteria', 'page'):
- ndt, ndn = scrub(dt), scrub(dn)
-
- return ndt, ndn
-
- def get_item_file(module, dt, dn):
- """
- Returns the path of the item file
- """
- import os
- ndt, ndn = scrub_dt_dn(dt, dn)
-
- return os.path.join(get_module_path(module), ndt, ndn, ndn + '.txt')
-
- def get_item_timestamp(module, dt, dn):
- """
- Return ths timestamp of the given item (if exists)
- """
- from webnotes.utils import get_file_timestamp
- return get_file_timestamp(get_item_file(module, dt, dn))
-
-
- def get_module_path(module):
- """
- Returns path of the given module (imports it and reads it from __file__)
- """
- return Module(module).get_path()
-
- def get_doc_path(dt, dn, module=None):
- """
- Return the path to a particular doc folder
- """
- import os
-
- if not module:
- if dt=='Module Def':
- module=dn
- else:
- module = webnotes.conn.get_value(dt, dn, 'module')
-
- ndt, ndn = scrub_dt_dn(dt, dn)
-
- return os.path.join(get_module_path(module), ndt, ndn)
-
- def reload_doc(module, dt, dn):
- """
- Sync a file from txt to module
- Alias for::
- Module(module).reload(dt, dn)
- """
- Module(module).reload(dt, dn)
-
-
- class ModuleManager:
- """
- Module manager class, used to run functions on all modules
- """
-
- def get_all_modules(self):
- """
- Return list of all modules
- """
- import webnotes.defs
- from webnotes.modules.utils import listfolders
-
- if hasattr(webnotes.defs, 'modules_path'):
- return listfolders(webnotes.defs.modules_path, 1)
-
-
- class Module:
- """
- Represents a module in the framework, has classes for syncing files
- """
- def __init__(self, name):
- self.name = name
- self.path = None
- self.sync_types = ['txt','sql']
- self.code_types = ['js','css','py','html','sql']
-
- def get_path(self):
- """
- Returns path of the module (imports it and reads it from __file__)
- """
- if not self.path:
-
- import webnotes.defs, os
-
- try:
- # by import
- exec ('import ' + scrub(self.name)) in locals()
- self.path = eval(scrub(self.name) + '.__file__')
- self.path = os.path.sep.join(self.path.split(os.path.sep)[:-1])
- except ImportError, e:
- # force
- self.path = os.path.join(webnotes.defs.modules_path, scrub(self.name))
-
- return self.path
-
- def get_doc_file(self, dt, dn, extn='.txt'):
- """
- Return file of a doc
- """
- dt, dn = scrub_dt_dn(dt, dn)
- return self.get_file(dt, dn, dn + extn)
-
- def get_file(self, *path):
- """
- Returns ModuleFile object, in path specifiy the package name and file name
- For example::
- Module('accounts').get_file('doctype','account','account.txt')
- """
- import os
- path = os.path.join(self.get_path(), os.path.join(*path))
- if path.endswith('.txt'):
- return TxtModuleFile(path)
- if path.endswith('.sql'):
- return SqlModuleFile(path)
- if path.endswith('.js'):
- return JsModuleFile(path)
- else:
- return ModuleFile(path)
-
- def reload(self, dt, dn):
- """
- Sync the file to the db
- """
- import os
- dt, dn = scrub_dt_dn(dt, dn)
- path = os.path.exists(os.path.join(self.get_path(), os.path.join(dt, dn, dn + '.txt')))
- if not path:
- webnotes.msgprint("%s not found" % path)
- else:
- self.get_file(dt, dn, dn + '.txt').sync(force=1)
-
- def sync_all_of_type(self, extn, verbose=0):
- """
- Walk through all the files in the modules and sync all files of
- a particular type
- """
- import os
- ret = []
- for walk_tuple in os.walk(self.get_path()):
- for f in walk_tuple[2]:
- if f.split('.')[-1] == extn:
- path = os.path.relpath(os.path.join(walk_tuple[0], f), self.get_path())
- self.get_file(path).sync()
- if verbose:
- print 'complete: ' + path
-
- def sync_all(self, verbose=0):
- """
- Walk through all the files in the modules and sync all files
- """
- import os
- self.sync_all_of_type('txt', verbose)
- self.sync_all_of_type('sql', verbose)
-
- class ModuleFile:
- """
- Module file class.
-
- Module files can be dynamically generated by specifying first line is "#!python"
- the output
- """
- def __init__(self, path):
- self.path = path
-
- def is_new(self):
- """
- Returns true if file does not match with last updated timestamp
- """
- import webnotes.utils
- self.timestamp = webnotes.utils.get_file_timestamp(self.path)
- if self.timestamp != self.get_db_timestamp():
- return True
-
- def get_db_timestamp(self):
- """
- Returns the timestamp of the file
- """
- try:
- ts = webnotes.conn.sql("select tstamp from __file_timestamp where file_name=%s", self.path)
- if ts:
- return ts[0][0]
- except Exception, e:
- if e.args[0]==1146:
- # create the table
- webnotes.conn.commit()
- webnotes.conn.sql("""
- create table __file_timestamp (
- file_name varchar(180) primary key,
- tstamp varchar(40)) engine=InnoDB""")
- webnotes.conn.begin()
- else:
- raise e
-
- def update(self):
- """
- Update the timestamp into the database
- (must be called after is_new)
- """
- webnotes.conn.sql("""
- insert into __file_timestamp(file_name, tstamp)
- values (%s, %s) on duplicate key update tstamp=%s""", (self.path, self.timestamp, self.timestamp))
-
- def load_content(self):
- """
- returns file contents
- """
- import os
- if os.path.exists(self.path):
- f = open(self.path,'r')
- self.content = f.read()
- f.close()
- else:
- self.content = ''
-
- return self.content
-
- def read(self, do_execute = None):
- """
- Return the file content, if dynamic, execute it
- """
- self.load_content()
- if do_execute and self.content.startswith('#!python'):
- from webnotes.model.code import execute
- self.content = execute(self.content)
-
- return self.content
-
-
- class TxtModuleFile(ModuleFile):
- """
- Class for .txt files, sync the doclist in the txt file into the database
- """
- def __init__(self, path):
- ModuleFile.__init__(self, path)
-
- def sync(self, force=1):
- """
- import the doclist if new
- """
- if self.is_new():
- from webnotes.model.utils import peval_doclist
- doclist = peval_doclist(self.read())
- if doclist:
- from webnotes.utils.transfer import set_doc
- set_doc(doclist, 1, 1, 1)
-
- # since there is a new timestamp on the file, update timestamp in
- # the record
- webnotes.conn.sql("update `tab%s` set modified=now() where name=%s" \
- % (doclist[0]['doctype'], '%s'), doclist[0]['name'])
-
- self.update()
-
- class SqlModuleFile(ModuleFile):
- def __init__(self, path):
- ModuleFile.__init__(self, path)
-
- def sync(self):
- """
- execute the sql if new
- The caller must either commit or rollback an open transaction
- """
- if self.is_new():
- content = self.read()
-
- # execute everything but selects
- # theses are ddl statements, should either earlier
- # changes must be committed or rollbacked
- # by the caller
- if content.strip().split()[0].lower() in ('insert','update','delete','create','alter','drop'):
- webnotes.conn.sql(self.read())
-
- # start a new transaction, as we have to update
- # the timestamp table
- webnotes.conn.begin()
- self.update()
-
- class JsModuleFile(ModuleFile):
- """
- JS File. read method will read file and replace all $import() with relevant code
- Example::
- $import(accounts/common.js)
- """
- def __init__(self, path):
- ModuleFile.__init__(self, path)
-
- def get_js(self, match):
- """
- New style will expect file path or doctype
-
- """
- name = match.group('name')
- custom = ''
-
- import webnotes.defs, os
-
- if os.path.sep in name:
- module = name.split(os.path.sep)[0]
- path = os.path.join(Module(module).get_path(), os.path.sep.join(name.split(os.path.sep)[1:]))
- else:
- # its a doctype
- path = os.path.join(get_doc_path('DocType', name), scrub(name) + '.js')
-
- # add custom script if present
- from webnotes.model.code import get_custom_script
- custom = get_custom_script(name, 'Client') or ''
-
- return JsModuleFile(path).read() + '\n' + custom
-
- def read(self):
- """
- return js content (replace $imports if needed)
- """
- self.load_content()
- code = self.content
-
- if code and code.strip():
- import re
- p = re.compile('\$import\( (?P<name> [^)]*) \)', re.VERBOSE)
-
- code = p.sub(self.get_js, code)
-
- return code
|