Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.

11 роки тому
11 роки тому
11 роки тому
11 роки тому
11 роки тому
11 роки тому
11 роки тому
11 роки тому
11 роки тому
11 роки тому
11 роки тому
11 роки тому
11 роки тому
11 роки тому
11 роки тому
11 роки тому
11 роки тому
11 роки тому
11 роки тому
11 роки тому
11 роки тому
11 роки тому
11 роки тому
11 роки тому
11 роки тому
11 роки тому
11 роки тому
11 роки тому
11 роки тому
11 роки тому
11 роки тому
11 роки тому
11 роки тому
11 роки тому
11 роки тому
11 роки тому
11 роки тому
11 роки тому
11 роки тому
11 роки тому
11 роки тому
11 роки тому
11 роки тому
11 роки тому
11 роки тому
11 роки тому
11 роки тому
11 роки тому
11 роки тому
11 роки тому
11 роки тому
11 роки тому
11 роки тому
11 роки тому
11 роки тому
11 роки тому
11 роки тому
13 роки тому
11 роки тому
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534
  1. #!/usr/bin/env python
  2. # Copyright (c) 2013, Web Notes Technologies Pvt. Ltd.
  3. # MIT License. See license.txt
  4. from __future__ import unicode_literals
  5. import sys
  6. if __name__=="__main__":
  7. sys.path = [".", "lib", "app"] + sys.path
  8. import webnotes
  9. def main():
  10. parsed_args = webnotes._dict(vars(setup_parser()))
  11. fn = get_function(parsed_args)
  12. if parsed_args.get("site")=="all":
  13. for site in get_sites():
  14. args = parsed_args.copy()
  15. args["site"] = site
  16. run(fn, args)
  17. else:
  18. run(fn, parsed_args)
  19. def cmd(fn):
  20. def new_fn(*args, **kwargs):
  21. import inspect
  22. fnargs, varargs, varkw, defaults = inspect.getargspec(fn)
  23. new_kwargs = {}
  24. for a in fnargs:
  25. if a in kwargs:
  26. new_kwargs[a] = kwargs.get(a)
  27. return fn(*args, **new_kwargs)
  28. return new_fn
  29. def run(fn, args):
  30. if isinstance(args.get(fn), (list, tuple)):
  31. out = globals().get(fn)(*args.get(fn), **args)
  32. else:
  33. out = globals().get(fn)(**args)
  34. webnotes.destroy()
  35. return out
  36. def get_function(args):
  37. for fn, val in args.items():
  38. if (val or isinstance(val, list)) and globals().get(fn):
  39. return fn
  40. def get_sites():
  41. import os
  42. import conf
  43. return [site for site in os.listdir(conf.sites_dir)
  44. if not os.path.islink(os.path.join(conf.sites_dir, site))]
  45. def setup_parser():
  46. import argparse
  47. parser = argparse.ArgumentParser(description="Run webnotes utility functions")
  48. setup_install(parser)
  49. setup_utilities(parser)
  50. setup_translation(parser)
  51. setup_git(parser)
  52. # common
  53. parser.add_argument("-f", "--force", default=False, action="store_true",
  54. help="Force execution where applicable (look for [-f] in help)")
  55. parser.add_argument("--quiet", default=True, action="store_false", dest="verbose",
  56. help="Show verbose output where applicable")
  57. parser.add_argument("--site", nargs="?", metavar="SITE-NAME or all",
  58. help="Run for a particular site")
  59. return parser.parse_args()
  60. def setup_install(parser):
  61. parser.add_argument("--install", metavar="DB-NAME", nargs=1,
  62. help="Install a new app")
  63. parser.add_argument("--root-password", nargs=1,
  64. help="Root password for new app")
  65. parser.add_argument("--reinstall", default=False, action="store_true",
  66. help="Install a fresh app in db_name specified in conf.py")
  67. parser.add_argument("--restore", metavar=("DB-NAME", "SQL-FILE"), nargs=2,
  68. help="Restore from an sql file")
  69. parser.add_argument("--install_fixtures", default=False, action="store_true",
  70. help="(Re)Install install-fixtures from app/startup/install_fixtures")
  71. parser.add_argument("--make_demo", default=False, action="store_true",
  72. help="Install demo in demo_db_name specified in conf.py")
  73. parser.add_argument("--make_demo_fresh", default=False, action="store_true",
  74. help="(Re)Install demo in demo_db_name specified in conf.py")
  75. def setup_utilities(parser):
  76. # update
  77. parser.add_argument("-u", "--update", nargs="*", metavar=("REMOTE", "BRANCH"),
  78. help="Perform git pull, run patches, sync schema and rebuild files/translations")
  79. parser.add_argument("--patch", nargs=1, metavar="PATCH-MODULE",
  80. help="Run a particular patch [-f]")
  81. parser.add_argument("-l", "--latest", default=False, action="store_true",
  82. help="Run patches, sync schema and rebuild files/translations")
  83. parser.add_argument("--sync_all", default=False, action="store_true",
  84. help="Reload all doctypes, pages, etc. using txt files [-f]")
  85. parser.add_argument("--update_all_sites", nargs="*", metavar=("REMOTE", "BRANCH"),
  86. help="Perform git pull, run patches, sync schema and rebuild files/translations")
  87. parser.add_argument("--reload_doc", nargs=3,
  88. metavar=('"MODULE"', '"DOCTYPE"', '"DOCNAME"'))
  89. # build
  90. parser.add_argument("-b", "--build", default=False, action="store_true",
  91. help="Minify + concatenate JS and CSS files, build translations")
  92. parser.add_argument("-w", "--watch", default=False, action="store_true",
  93. help="Watch and concatenate JS and CSS files as and when they change")
  94. # misc
  95. parser.add_argument("--backup", default=False, action="store_true",
  96. help="Take backup of database in backup folder [--with_files]")
  97. parser.add_argument("--move", default=False, action="store_true",
  98. help="Move site to different directory defined by --dest_dir")
  99. parser.add_argument("--dest_dir", nargs=1, metavar="DEST-DIR",
  100. help="Move site to different directory")
  101. parser.add_argument("--with_files", default=False, action="store_true",
  102. help="Also take backup of files")
  103. parser.add_argument("--docs", default=False, action="store_true",
  104. help="Build docs")
  105. parser.add_argument("--domain", nargs="*",
  106. help="Get or set domain in Website Settings")
  107. parser.add_argument("--make_conf", nargs="*", metavar=("DB-NAME", "DB-PASSWORD"),
  108. help="Create new conf.py file")
  109. parser.add_argument("--set_admin_password", metavar='ADMIN-PASSWORD', nargs=1,
  110. help="Set administrator password")
  111. # clear
  112. parser.add_argument("--clear_web", default=False, action="store_true",
  113. help="Clear website cache")
  114. parser.add_argument("--clear_cache", default=False, action="store_true",
  115. help="Clear cache, doctype cache and defaults")
  116. parser.add_argument("--reset_perms", default=False, action="store_true",
  117. help="Reset permissions for all doctypes")
  118. # scheduler
  119. parser.add_argument("--run_scheduler", default=False, action="store_true",
  120. help="Trigger scheduler")
  121. parser.add_argument("--run_scheduler_event", nargs=1,
  122. metavar="all | daily | weekly | monthly",
  123. help="Run a scheduler event")
  124. # replace
  125. parser.add_argument("--replace", nargs=3,
  126. metavar=("SEARCH-REGEX", "REPLACE-BY", "FILE-EXTN"),
  127. help="Multi-file search-replace [-f]")
  128. # import/export
  129. parser.add_argument("--export_doc", nargs=2, metavar=('"DOCTYPE"', '"DOCNAME"'))
  130. parser.add_argument("--export_doclist", nargs=3, metavar=("DOCTYPE", "NAME", "PATH"),
  131. help="""Export doclist as json to the given path, use '-' as name for Singles.""")
  132. parser.add_argument("--export_csv", nargs=2, metavar=("DOCTYPE", "PATH"),
  133. help="""Dump DocType as csv""")
  134. parser.add_argument("--import_doclist", nargs=1, metavar="PATH",
  135. help="""Import (insert/update) doclist. If the argument is a directory, all files ending with .json are imported""")
  136. def setup_git(parser):
  137. parser.add_argument("--pull", nargs="*", metavar=("REMOTE", "BRANCH"),
  138. help="Run git pull for both repositories")
  139. parser.add_argument("-p", "--push", nargs="*", metavar=("REMOTE", "BRANCH"),
  140. help="Run git push for both repositories")
  141. parser.add_argument("--status", default=False, action="store_true",
  142. help="Run git status for both repositories")
  143. parser.add_argument("--commit", nargs=1, metavar="COMMIT-MSG",
  144. help="Run git commit COMMIT-MSG for both repositories")
  145. parser.add_argument("--checkout", nargs=1, metavar="BRANCH",
  146. help="Run git checkout BRANCH for both repositories")
  147. parser.add_argument("--git", nargs="*", metavar="OPTIONS",
  148. help="Run git command for both repositories")
  149. def setup_translation(parser):
  150. parser.add_argument("--build_message_files", default=False, action="store_true",
  151. help="Build message files for translation")
  152. parser.add_argument("--export_messages", nargs=2, metavar=("LANG-CODE", "FILENAME"),
  153. help="""Export all messages for a language to translation in a csv file.
  154. Example, lib/wnf.py --export_messages hi hindi.csv""")
  155. parser.add_argument("--import_messages", nargs=2, metavar=("LANG-CODE", "FILENAME"),
  156. help="""Import messages for a language and make language files.
  157. Example, lib/wnf.py --import_messages hi hindi.csv""")
  158. parser.add_argument("--google_translate", nargs=3,
  159. metavar=("LANG-CODE", "INFILE", "OUTFILE"),
  160. help="Auto translate using Google Translate API")
  161. parser.add_argument("--translate", nargs=1, metavar="LANG-CODE",
  162. help="""Rebuild translation for the given langauge and
  163. use Google Translate to tranlate untranslated messages. use "all" """)
  164. # methods
  165. # install
  166. @cmd
  167. def install(db_name, source_sql=None, site=None, verbose=True, force=False, root_password=None):
  168. from webnotes.install_lib.install import Installer
  169. inst = Installer('root', db_name=db_name, site=site, root_password=root_password)
  170. inst.install(db_name, source_sql=source_sql, verbose=verbose, force=force)
  171. @cmd
  172. def reinstall(site=None, verbose=True):
  173. webnotes.init(site=site)
  174. install(webnotes.conf.db_name, site=site, verbose=verbose, force=True)
  175. @cmd
  176. def restore(db_name, source_sql, site=None, verbose=True, force=False):
  177. install(db_name, source_sql, site=site, verbose=verbose, force=force)
  178. @cmd
  179. def install_fixtures(site=None):
  180. webnotes.init(site=site)
  181. from webnotes.install_lib.install import install_fixtures
  182. install_fixtures()
  183. @cmd
  184. def make_demo(site=None):
  185. import utilities.demo.make_demo
  186. webnotes.init(site=site)
  187. utilities.demo.make_demo.make()
  188. @cmd
  189. def make_demo_fresh(site=None):
  190. import utilities.demo.make_demo
  191. webnotes.init(site=site)
  192. utilities.demo.make_demo.make(reset=True)
  193. # utilities
  194. @cmd
  195. def update(remote=None, branch=None, site=None):
  196. pull(remote=remote, branch=branch, site=site)
  197. # maybe there are new framework changes, any consequences?
  198. reload(webnotes)
  199. latest(site=site)
  200. @cmd
  201. def latest(site=None, verbose=True):
  202. import webnotes.modules.patch_handler
  203. import webnotes.model.sync
  204. webnotes.connect(site=site)
  205. try:
  206. # run patches
  207. webnotes.modules.patch_handler.log_list = []
  208. webnotes.modules.patch_handler.run_all()
  209. if verbose:
  210. print "\n".join(webnotes.modules.patch_handler.log_list)
  211. # sync
  212. webnotes.model.sync.sync_all()
  213. except webnotes.modules.patch_handler.PatchError, e:
  214. print "\n".join(webnotes.modules.patch_handler.log_list)
  215. raise e
  216. @cmd
  217. def sync_all(site=None, force=False):
  218. import webnotes.model.sync
  219. webnotes.connect(site=site)
  220. webnotes.model.sync.sync_all(force=force)
  221. @cmd
  222. def patch(patch_module, site=None, force=False):
  223. import webnotes.modules.patch_handler
  224. webnotes.connect(site=site)
  225. webnotes.modules.patch_handler.log_list = []
  226. webnotes.modules.patch_handler.run_single(patch_module, force=force)
  227. print "\n".join(webnotes.modules.patch_handler.log_list)
  228. @cmd
  229. def update_all_sites(remote=None, branch=None, verbose=True):
  230. pull(remote, branch)
  231. build()
  232. for site in get_sites():
  233. latest(site=site, verbose=verbose)
  234. @cmd
  235. def reload_doc(module, doctype, docname, site=None, force=False):
  236. webnotes.connect(site=site)
  237. webnotes.reload_doc(module, doctype, docname, force=force)
  238. @cmd
  239. def build():
  240. import webnotes.build
  241. webnotes.build.bundle(False)
  242. @cmd
  243. def watch():
  244. import webnotes.build
  245. webnotes.build.watch(True)
  246. @cmd
  247. def backup(site=None, with_files=False):
  248. from webnotes.utils.backups import scheduled_backup
  249. webnotes.connect(site=site)
  250. odb = scheduled_backup(ignore_files=not with_files)
  251. if __name__ == "__main__":
  252. print "backup taken -", odb.backup_path_db, "- on", now()
  253. return odb
  254. @cmd
  255. def move(site=None, dest_dir=None):
  256. import os
  257. import shutil
  258. if not dest_dir:
  259. raise Exception, "--dest_dir is required for --move"
  260. dest_dir = dest_dir[0]
  261. if not os.path.isdir(dest_dir):
  262. raise Exception, "destination is not a directory or does not exist"
  263. webnotes.init(site=site)
  264. old_path = webnotes.utils.get_site_path()
  265. new_path = os.path.join(dest_dir, site)
  266. shutil.move(old_path, new_path)
  267. webnotes.destroy()
  268. @cmd
  269. def docs():
  270. from core.doctype.documentation_tool.documentation_tool import write_static
  271. write_static()
  272. @cmd
  273. def domain(host_url=None, site=None):
  274. webnotes.connect(site=site)
  275. if host_url:
  276. webnotes.conn.set_value("Website Settings", None, "subdomain", host_url)
  277. webnotes.conn.commit()
  278. else:
  279. print webnotes.conn.get_value("Website Settings", None, "subdomain")
  280. @cmd
  281. def make_conf(db_name=None, db_password=None, site=None, site_config=None):
  282. from webnotes.install_lib.install import make_conf
  283. make_conf(db_name=db_name, db_password=db_password, site=site, site_config=site_config)
  284. # clear
  285. @cmd
  286. def clear_cache(site=None):
  287. import webnotes.sessions
  288. webnotes.connect(site=site)
  289. webnotes.sessions.clear_cache()
  290. @cmd
  291. def clear_web(site=None):
  292. import webnotes.webutils
  293. webnotes.connect(site=site)
  294. webnotes.webutils.clear_cache()
  295. @cmd
  296. def reset_perms(site=None):
  297. webnotes.connect(site=site)
  298. for d in webnotes.conn.sql_list("""select name from `tabDocType`
  299. where ifnull(istable, 0)=0 and ifnull(custom, 0)=0"""):
  300. webnotes.clear_cache(doctype=d)
  301. webnotes.reset_perms(d)
  302. # scheduler
  303. @cmd
  304. def run_scheduler(site=None):
  305. import webnotes.utils.scheduler
  306. webnotes.connect(site=site)
  307. print webnotes.utils.scheduler.execute()
  308. @cmd
  309. def run_scheduler_event(event, site=None):
  310. import webnotes.utils.scheduler
  311. webnotes.connect(site=site)
  312. print webnotes.utils.scheduler.trigger("execute_" + event)
  313. # replace
  314. @cmd
  315. def replace(search_regex, replacement, extn, force=False):
  316. print search_regex, replacement, extn
  317. replace_code('.', search_regex, replacement, extn, force=force)
  318. # import/export
  319. @cmd
  320. def export_doc(doctype, docname, site=None):
  321. import webnotes.modules
  322. webnotes.connect(site=site)
  323. webnotes.modules.export_doc(doctype, docname)
  324. @cmd
  325. def export_doclist(doctype, name, path, site=None):
  326. from core.page.data_import_tool import data_import_tool
  327. webnotes.connect(site=site)
  328. data_import_tool.export_json(doctype, name, path)
  329. @cmd
  330. def export_csv(doctype, path, site=None):
  331. from core.page.data_import_tool import data_import_tool
  332. webnotes.connect(site=site)
  333. data_import_tool.export_csv(doctype, path)
  334. @cmd
  335. def import_doclist(path, site=None):
  336. from core.page.data_import_tool import data_import_tool
  337. webnotes.connect(site=site)
  338. data_import_tool.import_doclist(path)
  339. # translation
  340. @cmd
  341. def build_message_files(site=None):
  342. import webnotes.translate
  343. webnotes.connect(site=site)
  344. webnotes.translate.build_message_files()
  345. @cmd
  346. def export_messages(lang, outfile, site=None):
  347. import webnotes.translate
  348. webnotes.connect(site=site)
  349. webnotes.translate.export_messages(lang, outfile)
  350. @cmd
  351. def import_messages(lang, infile, site=None):
  352. import webnotes.translate
  353. webnotes.connect(site=site)
  354. webnotes.translate.import_messages(lang, infile)
  355. @cmd
  356. def google_translate(lang, infile, outfile, site=None):
  357. import webnotes.translate
  358. webnotes.connect(site=site)
  359. webnotes.translate.google_translate(lang, infile, outfile)
  360. @cmd
  361. def translate(lang, site=None):
  362. import webnotes.translate
  363. webnotes.connect(site=site)
  364. webnotes.translate.translate(lang)
  365. # git
  366. @cmd
  367. def git(param):
  368. if isinstance(param, (list, tuple)):
  369. param = " ".join(param)
  370. import os
  371. os.system("""cd lib && git %s""" % param)
  372. os.system("""cd app && git %s""" % param)
  373. def get_remote_and_branch(remote=None, branch=None):
  374. if not (remote and branch):
  375. webnotes.init()
  376. if not webnotes.conf.branch:
  377. raise Exception("Please specify remote and branch")
  378. remote = remote or "origin"
  379. branch = branch or webnotes.conf.branch
  380. webnotes.destroy()
  381. return remote, branch
  382. @cmd
  383. def pull(remote=None, branch=None):
  384. remote, branch = get_remote_and_branch(remote, branch)
  385. git(("pull", remote, branch))
  386. @cmd
  387. def push(remote=None, branch=None):
  388. remote, branch = get_remote_and_branch(remote, branch)
  389. git(("push", remote, branch))
  390. @cmd
  391. def status():
  392. git("status")
  393. @cmd
  394. def commit(message):
  395. git("""commit -a -m "%s" """ % message.replace('"', '\"'))
  396. @cmd
  397. def checkout(branch):
  398. git(("checkout", branch))
  399. @cmd
  400. def set_admin_password(admin_password, site=None):
  401. import webnotes
  402. webnotes.connect(site=site)
  403. webnotes.conn.sql("""update __Auth set `password`=password(%s)
  404. where user='Administrator'""", (admin_password,))
  405. webnotes.conn.commit()
  406. webnotes.destroy()
  407. def replace_code(start, txt1, txt2, extn, search=None, force=False):
  408. """replace all txt1 by txt2 in files with extension (extn)"""
  409. import webnotes.utils
  410. import os, re
  411. esc = webnotes.utils.make_esc('[]')
  412. if not search: search = esc(txt1)
  413. for wt in os.walk(start, followlinks=1):
  414. for fn in wt[2]:
  415. if fn.split('.')[-1]==extn:
  416. fpath = os.path.join(wt[0], fn)
  417. with open(fpath, 'r') as f:
  418. content = f.read()
  419. if re.search(search, content):
  420. res = search_replace_with_prompt(fpath, txt1, txt2, force)
  421. if res == 'skip':
  422. return 'skip'
  423. def search_replace_with_prompt(fpath, txt1, txt2, force=False):
  424. """ Search and replace all txt1 by txt2 in the file with confirmation"""
  425. from termcolor import colored
  426. with open(fpath, 'r') as f:
  427. content = f.readlines()
  428. tmp = []
  429. for c in content:
  430. if c.find(txt1) != -1:
  431. print fpath
  432. print colored(txt1, 'red').join(c[:-1].split(txt1))
  433. a = ''
  434. if force:
  435. c = c.replace(txt1, txt2)
  436. else:
  437. while a.lower() not in ['y', 'n', 'skip']:
  438. a = raw_input('Do you want to Change [y/n/skip]?')
  439. if a.lower() == 'y':
  440. c = c.replace(txt1, txt2)
  441. elif a.lower() == 'skip':
  442. return 'skip'
  443. tmp.append(c)
  444. with open(fpath, 'w') as f:
  445. f.write(''.join(tmp))
  446. print colored('Updated', 'green')
  447. if __name__=="__main__":
  448. main()