Du kannst nicht mehr als 25 Themen auswählen Themen müssen entweder mit einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.
 
 
 
 
 
 

605 Zeilen
19 KiB

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