You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

301 line
7.2 KiB

  1. """
  2. Utilities for using modules
  3. """
  4. import webnotes
  5. transfer_types = ['Role', 'Print Format','DocType','Page','DocType Mapper','GL Mapper','Search Criteria', 'Patch']
  6. def scrub(txt):
  7. return txt.replace(' ','_').replace('-', '_').replace('/', '_').lower()
  8. def scrub_dt_dn(dt, dn):
  9. """
  10. Returns in lowercase and code friendly names of doctype and name for certain types
  11. """
  12. ndt, ndn = dt, dn
  13. if dt.lower() in ('doctype', 'search criteria', 'page'):
  14. ndt, ndn = scrub(dt), scrub(dn)
  15. return ndt, ndn
  16. def get_item_file(module, dt, dn):
  17. """
  18. Returns the path of the item file
  19. """
  20. import os
  21. ndt, ndn = scrub_dt_dn(dt, dn)
  22. return os.path.join(get_module_path(module), ndt, ndn, ndn + '.txt')
  23. def get_item_timestamp(module, dt, dn):
  24. """
  25. Return ths timestamp of the given item (if exists)
  26. """
  27. from webnotes.utils import get_file_timestamp
  28. return get_file_timestamp(get_item_file(module, dt, dn))
  29. def get_module_path(module):
  30. """
  31. Returns path of the given module (imports it and reads it from __file__)
  32. """
  33. return Module(module).get_path()
  34. def get_doc_path(dt, dn, module=None):
  35. """
  36. Return the path to a particular doc folder
  37. """
  38. import os
  39. if not module:
  40. if dt=='Module Def':
  41. module=dn
  42. else:
  43. module = webnotes.conn.get_value(dt, dn, 'module')
  44. ndt, ndn = scrub_dt_dn(dt, dn)
  45. return os.path.join(get_module_path(module), ndt, ndn)
  46. def reload_doc(module, dt, dn):
  47. """
  48. Sync a file from txt to module
  49. Alias for::
  50. Module(module).reload(dt, dn)
  51. """
  52. Module(module).reload(dt, dn)
  53. class ModuleManager:
  54. """
  55. Module manager class, used to run functions on all modules
  56. """
  57. def get_all_modules(self):
  58. """
  59. Return list of all modules
  60. """
  61. import webnotes.defs
  62. from webnotes.modules.utils import listfolders
  63. if hasattr(webnotes.defs, 'modules_path'):
  64. return listfolders(webnotes.defs.modules_path, 1)
  65. class Module:
  66. """
  67. Represents a module in the framework, has classes for syncing files
  68. """
  69. def __init__(self, name):
  70. self.name = name
  71. self.path = None
  72. self.sync_types = ['txt','sql']
  73. self.code_types = ['js','css','py','html','sql']
  74. def get_path(self):
  75. """
  76. Returns path of the module (imports it and reads it from __file__)
  77. """
  78. if not self.path:
  79. import webnotes.defs, os
  80. try:
  81. # by import
  82. exec ('import ' + scrub(self.name)) in locals()
  83. self.path = eval(scrub(self.name) + '.__file__')
  84. self.path = os.path.sep.join(self.path.split(os.path.sep)[:-1])
  85. except ImportError, e:
  86. # force
  87. self.path = os.path.join(webnotes.defs.modules_path, scrub(self.name))
  88. return self.path
  89. def get_doc_file(self, dt, dn, extn='.txt'):
  90. """
  91. Return file of a doc
  92. """
  93. dt, dn = scrub_dt_dn(dt, dn)
  94. return self.get_file(dt, dn, dn + extn)
  95. def get_file(self, *path):
  96. """
  97. Returns ModuleFile object, in path specifiy the package name and file name
  98. For example::
  99. Module('accounts').get_file('doctype','account','account.txt')
  100. """
  101. import os
  102. path = os.path.join(self.get_path(), os.path.join(*path))
  103. if path.endswith('.txt'):
  104. return TxtModuleFile(path)
  105. if path.endswith('.sql'):
  106. return SqlModuleFile(path)
  107. if path.endswith('.js'):
  108. return JsModuleFile(path)
  109. else:
  110. return ModuleFile(path)
  111. def reload(self, dt, dn):
  112. """
  113. Sync the file to the db
  114. """
  115. import os
  116. dt, dn = scrub_dt_dn(dt, dn)
  117. path = os.path.exists(os.path.join(self.get_path(), os.path.join(dt, dn, dn + '.txt')))
  118. if not path:
  119. webnotes.msgprint("%s not found" % path)
  120. else:
  121. self.get_file(dt, dn, dn + '.txt').sync(force=1)
  122. def sync_all_of_type(self, extn, verbose=0):
  123. """
  124. Walk through all the files in the modules and sync all files of
  125. a particular type
  126. """
  127. import os
  128. ret = []
  129. for walk_tuple in os.walk(self.get_path()):
  130. for f in walk_tuple[2]:
  131. if f.split('.')[-1] == extn:
  132. path = os.path.relpath(os.path.join(walk_tuple[0], f), self.get_path())
  133. self.get_file(path).sync()
  134. if verbose:
  135. print 'complete: ' + path
  136. def sync_all(self, verbose=0):
  137. """
  138. Walk through all the files in the modules and sync all files
  139. """
  140. import os
  141. self.sync_all_of_type('txt', verbose)
  142. self.sync_all_of_type('sql', verbose)
  143. class ModuleFile:
  144. """
  145. Module file class.
  146. Module files can be dynamically generated by specifying first line is "#!python"
  147. the output
  148. """
  149. def __init__(self, path):
  150. self.path = path
  151. def load_content(self):
  152. """
  153. returns file contents
  154. """
  155. import os
  156. if os.path.exists(self.path):
  157. f = open(self.path,'r')
  158. self.content = f.read()
  159. f.close()
  160. else:
  161. self.content = ''
  162. return self.content
  163. def read(self, do_execute = None):
  164. """
  165. Return the file content, if dynamic, execute it
  166. """
  167. self.load_content()
  168. if do_execute and self.content.startswith('#!python'):
  169. from webnotes.model.code import execute
  170. self.content = execute(self.content)
  171. return self.content
  172. class TxtModuleFile(ModuleFile):
  173. """
  174. Class for .txt files, sync the doclist in the txt file into the database
  175. """
  176. def __init__(self, path):
  177. ModuleFile.__init__(self, path)
  178. def sync(self, force=1):
  179. """
  180. import the doclist if new
  181. """
  182. from webnotes.model.utils import peval_doclist
  183. doclist = peval_doclist(self.read())
  184. if doclist:
  185. from webnotes.utils.transfer import set_doc
  186. set_doc(doclist, 1, 1, 1)
  187. # since there is a new timestamp on the file, update timestamp in
  188. # the record
  189. webnotes.conn.sql("update `tab%s` set modified=now() where name=%s" \
  190. % (doclist[0]['doctype'], '%s'), doclist[0]['name'])
  191. class SqlModuleFile(ModuleFile):
  192. def __init__(self, path):
  193. ModuleFile.__init__(self, path)
  194. def sync(self):
  195. """
  196. execute the sql if new
  197. The caller must either commit or rollback an open transaction
  198. """
  199. content = self.read()
  200. # execute everything but selects
  201. # theses are ddl statements, should either earlier
  202. # changes must be committed or rollbacked
  203. # by the caller
  204. if content.strip().split()[0].lower() in ('insert','update','delete','create','alter','drop'):
  205. webnotes.conn.sql(self.read())
  206. # start a new transaction, as we have to update
  207. # the timestamp table
  208. webnotes.conn.begin()
  209. class JsModuleFile(ModuleFile):
  210. """
  211. JS File. read method will read file and replace all $import() with relevant code
  212. Example::
  213. $import(accounts/common.js)
  214. """
  215. def __init__(self, path):
  216. ModuleFile.__init__(self, path)
  217. def get_js(self, match):
  218. """
  219. New style will expect file path or doctype
  220. """
  221. name = match.group('name')
  222. custom = ''
  223. import webnotes.defs, os
  224. if os.path.sep in name:
  225. module = name.split(os.path.sep)[0]
  226. path = os.path.join(Module(module).get_path(), os.path.sep.join(name.split(os.path.sep)[1:]))
  227. else:
  228. # its a doctype
  229. path = os.path.join(get_doc_path('DocType', name), scrub(name) + '.js')
  230. # add custom script if present
  231. from webnotes.model.code import get_custom_script
  232. custom = get_custom_script(name, 'Client') or ''
  233. return JsModuleFile(path).read() + '\n' + custom
  234. def read(self):
  235. """
  236. return js content (replace $imports if needed)
  237. """
  238. self.load_content()
  239. code = self.content
  240. if code and code.strip():
  241. import re
  242. p = re.compile('\$import\( (?P<name> [^)]*) \)', re.VERBOSE)
  243. code = p.sub(self.get_js, code)
  244. return code