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.

__init__.py 8.3 KiB

14 jaren geleden
14 jaren geleden
14 jaren geleden
14 jaren geleden
14 jaren geleden
14 jaren geleden
14 jaren geleden
14 jaren geleden
14 jaren geleden
14 jaren geleden
14 jaren geleden
14 jaren geleden
14 jaren geleden
14 jaren geleden
14 jaren geleden
14 jaren geleden
14 jaren geleden
14 jaren geleden
14 jaren geleden
14 jaren geleden
14 jaren geleden
14 jaren geleden
14 jaren geleden
14 jaren geleden
14 jaren geleden
14 jaren geleden
14 jaren geleden
14 jaren geleden
14 jaren geleden
14 jaren geleden
14 jaren geleden
14 jaren geleden
14 jaren geleden
14 jaren geleden
14 jaren geleden
14 jaren geleden
14 jaren geleden
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341
  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