Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.
 
 
 
 
 
 

342 строки
8.3 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 is_new(self):
  152. """
  153. Returns true if file does not match with last updated timestamp
  154. """
  155. import webnotes.utils
  156. self.timestamp = webnotes.utils.get_file_timestamp(self.path)
  157. if self.timestamp != self.get_db_timestamp():
  158. return True
  159. def get_db_timestamp(self):
  160. """
  161. Returns the timestamp of the file
  162. """
  163. try:
  164. ts = webnotes.conn.sql("select tstamp from __file_timestamp where file_name=%s", self.path)
  165. if ts:
  166. return ts[0][0]
  167. except Exception, e:
  168. if e.args[0]==1146:
  169. # create the table
  170. webnotes.conn.commit()
  171. webnotes.conn.sql("""
  172. create table __file_timestamp (
  173. file_name varchar(180) primary key,
  174. tstamp varchar(40)) engine=InnoDB""")
  175. webnotes.conn.begin()
  176. else:
  177. raise e
  178. def update(self):
  179. """
  180. Update the timestamp into the database
  181. (must be called after is_new)
  182. """
  183. webnotes.conn.sql("""
  184. insert into __file_timestamp(file_name, tstamp)
  185. values (%s, %s) on duplicate key update tstamp=%s""", (self.path, self.timestamp, self.timestamp))
  186. def load_content(self):
  187. """
  188. returns file contents
  189. """
  190. import os
  191. if os.path.exists(self.path):
  192. f = open(self.path,'r')
  193. self.content = f.read()
  194. f.close()
  195. else:
  196. self.content = ''
  197. return self.content
  198. def read(self, do_execute = None):
  199. """
  200. Return the file content, if dynamic, execute it
  201. """
  202. self.load_content()
  203. if do_execute and self.content.startswith('#!python'):
  204. from webnotes.model.code import execute
  205. self.content = execute(self.content)
  206. return self.content
  207. class TxtModuleFile(ModuleFile):
  208. """
  209. Class for .txt files, sync the doclist in the txt file into the database
  210. """
  211. def __init__(self, path):
  212. ModuleFile.__init__(self, path)
  213. def sync(self, force=1):
  214. """
  215. import the doclist if new
  216. """
  217. if self.is_new():
  218. from webnotes.model.utils import peval_doclist
  219. doclist = peval_doclist(self.read())
  220. if doclist:
  221. from webnotes.utils.transfer import set_doc
  222. set_doc(doclist, 1, 1, 1)
  223. # since there is a new timestamp on the file, update timestamp in
  224. # the record
  225. webnotes.conn.sql("update `tab%s` set modified=now() where name=%s" \
  226. % (doclist[0]['doctype'], '%s'), doclist[0]['name'])
  227. self.update()
  228. class SqlModuleFile(ModuleFile):
  229. def __init__(self, path):
  230. ModuleFile.__init__(self, path)
  231. def sync(self):
  232. """
  233. execute the sql if new
  234. The caller must either commit or rollback an open transaction
  235. """
  236. if self.is_new():
  237. content = self.read()
  238. # execute everything but selects
  239. # theses are ddl statements, should either earlier
  240. # changes must be committed or rollbacked
  241. # by the caller
  242. if content.strip().split()[0].lower() in ('insert','update','delete','create','alter','drop'):
  243. webnotes.conn.sql(self.read())
  244. # start a new transaction, as we have to update
  245. # the timestamp table
  246. webnotes.conn.begin()
  247. self.update()
  248. class JsModuleFile(ModuleFile):
  249. """
  250. JS File. read method will read file and replace all $import() with relevant code
  251. Example::
  252. $import(accounts/common.js)
  253. """
  254. def __init__(self, path):
  255. ModuleFile.__init__(self, path)
  256. def get_js(self, match):
  257. """
  258. New style will expect file path or doctype
  259. """
  260. name = match.group('name')
  261. custom = ''
  262. import webnotes.defs, os
  263. if os.path.sep in name:
  264. module = name.split(os.path.sep)[0]
  265. path = os.path.join(Module(module).get_path(), os.path.sep.join(name.split(os.path.sep)[1:]))
  266. else:
  267. # its a doctype
  268. path = os.path.join(get_doc_path('DocType', name), scrub(name) + '.js')
  269. # add custom script if present
  270. from webnotes.model.code import get_custom_script
  271. custom = get_custom_script(name, 'Client') or ''
  272. return JsModuleFile(path).read() + '\n' + custom
  273. def read(self):
  274. """
  275. return js content (replace $imports if needed)
  276. """
  277. self.load_content()
  278. code = self.content
  279. if code and code.strip():
  280. import re
  281. p = re.compile('\$import\( (?P<name> [^)]*) \)', re.VERBOSE)
  282. code = p.sub(self.get_js, code)
  283. return code