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.

cli.py 24 KiB

11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
13 years ago
11 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789
  1. #!/usr/bin/env python2.7
  2. # Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
  3. # MIT License. See license.txt
  4. from __future__ import unicode_literals
  5. import sys, os
  6. import webnotes
  7. site_arg_optional = ['serve']
  8. def main():
  9. parsed_args = webnotes._dict(vars(setup_parser()))
  10. fn = get_function(parsed_args)
  11. if not parsed_args.get("sites_path"):
  12. parsed_args["sites_path"] = "."
  13. if not parsed_args.get("make_app"):
  14. if not parsed_args.get("site") and not fn in site_arg_optional:
  15. print "Site argument required"
  16. exit(1)
  17. if fn not in site_arg_optional and (fn != 'install' and not os.path.exists(parsed_args.get("site"))):
  18. print "Did not find folder '{}'. Are you in sites folder?".format(parsed_args.get("site"))
  19. exit(1)
  20. if parsed_args.get("site")=="all":
  21. for site in get_sites(parsed_args["sites_path"]):
  22. args = parsed_args.copy()
  23. args["site"] = site
  24. webnotes.init(site)
  25. run(fn, args)
  26. else:
  27. if not fn in site_arg_optional:
  28. webnotes.init(parsed_args.get("site"))
  29. run(fn, parsed_args)
  30. else:
  31. run(fn, parsed_args)
  32. def cmd(fn):
  33. def new_fn(*args, **kwargs):
  34. import inspect
  35. fnargs, varargs, varkw, defaults = inspect.getargspec(fn)
  36. new_kwargs = {}
  37. for i, a in enumerate(fnargs):
  38. # should not pass an argument more than once
  39. if i >= len(args) and a in kwargs:
  40. new_kwargs[a] = kwargs.get(a)
  41. return fn(*args, **new_kwargs)
  42. return new_fn
  43. def run(fn, args):
  44. if isinstance(args.get(fn), (list, tuple)):
  45. out = globals().get(fn)(*args.get(fn), **args)
  46. else:
  47. out = globals().get(fn)(**args)
  48. return out
  49. def get_function(args):
  50. for fn, val in args.items():
  51. if (val or isinstance(val, list)) and globals().get(fn):
  52. return fn
  53. def get_sites(sites_path=None):
  54. import os
  55. if not sites_path:
  56. sites_path = '.'
  57. return [site for site in os.listdir(sites_path)
  58. if not os.path.islink(os.path.join(sites_path, site))
  59. and os.path.isdir(os.path.join(sites_path, site))
  60. and not site in ('assets',)]
  61. def setup_parser():
  62. import argparse
  63. parser = argparse.ArgumentParser(description="Run webnotes utility functions")
  64. setup_install(parser)
  65. setup_utilities(parser)
  66. setup_translation(parser)
  67. setup_test(parser)
  68. parser.add_argument("site", nargs="?")
  69. # common
  70. parser.add_argument("-f", "--force", default=False, action="store_true",
  71. help="Force execution where applicable (look for [-f] in help)")
  72. parser.add_argument("-v", "--verbose", default=False, action="store_true", dest="verbose",
  73. help="Show verbose output where applicable")
  74. return parser.parse_args()
  75. def setup_install(parser):
  76. parser.add_argument("--make_app", default=False, action="store_true",
  77. help="Make a new application with boilerplate")
  78. parser.add_argument("--install", metavar="DB-NAME", nargs=1,
  79. help="Install a new db")
  80. parser.add_argument("--install_app", metavar="APP-NAME", nargs=1,
  81. help="Install a new app")
  82. parser.add_argument("--root-password", nargs=1,
  83. help="Root password for new app")
  84. parser.add_argument("--reinstall", default=False, action="store_true",
  85. help="Install a fresh app in db_name specified in conf.py")
  86. parser.add_argument("--restore", metavar=("DB-NAME", "SQL-FILE"), nargs=2,
  87. help="Restore from an sql file")
  88. parser.add_argument("--add_system_manager", nargs="+",
  89. metavar=("EMAIL", "[FIRST-NAME] [LAST-NAME]"), help="Add a user with all roles")
  90. def setup_test(parser):
  91. parser.add_argument("--run_tests", default=False, action="store_true",
  92. help="Run tests options [-d doctype], [-m module]")
  93. parser.add_argument("--app", metavar="APP-NAME", nargs=1,
  94. help="Run command for specified app")
  95. parser.add_argument("-d", "--doctype", metavar="DOCTYPE", nargs=1,
  96. help="Run command for specified doctype")
  97. parser.add_argument("-m", "--module", metavar="MODULE", nargs=1,
  98. help="Run command for specified module")
  99. def setup_utilities(parser):
  100. # update
  101. parser.add_argument("-u", "--update", nargs="*", metavar=("REMOTE", "BRANCH"),
  102. help="Perform git pull, run patches, sync schema and rebuild files/translations")
  103. parser.add_argument("--reload_gunicorn", default=False, action="store_true", help="reload gunicorn on update")
  104. parser.add_argument("--patch", nargs=1, metavar="PATCH-MODULE",
  105. help="Run a particular patch [-f]")
  106. parser.add_argument("-l", "--latest", default=False, action="store_true",
  107. help="Run patches, sync schema and rebuild files/translations")
  108. parser.add_argument("--sync_all", default=False, action="store_true",
  109. help="Reload all doctypes, pages, etc. using txt files [-f]")
  110. parser.add_argument("--update_all_sites", nargs="*", metavar=("REMOTE", "BRANCH"),
  111. help="Perform git pull, run patches, sync schema and rebuild files/translations")
  112. parser.add_argument("--reload_doc", nargs=3,
  113. metavar=('"MODULE"', '"DOCTYPE"', '"DOCNAME"'))
  114. # build
  115. parser.add_argument("-b", "--build", default=False, action="store_true",
  116. help="Minify + concatenate JS and CSS files, build translations")
  117. parser.add_argument("-w", "--watch", default=False, action="store_true",
  118. help="Watch and concatenate JS and CSS files as and when they change")
  119. # misc
  120. parser.add_argument("--backup", default=False, action="store_true",
  121. help="Take backup of database in backup folder [--with_files]")
  122. parser.add_argument("--move", default=False, action="store_true",
  123. help="Move site to different directory defined by --dest_dir")
  124. parser.add_argument("--dest_dir", nargs=1, metavar="DEST-DIR",
  125. help="Move site to different directory")
  126. parser.add_argument("--with_files", default=False, action="store_true",
  127. help="Also take backup of files")
  128. parser.add_argument("--domain", nargs="*",
  129. help="Get or set domain in Website Settings")
  130. parser.add_argument("--make_conf", nargs="*", metavar=("DB-NAME", "DB-PASSWORD"),
  131. help="Create new conf.py file")
  132. parser.add_argument("--make_custom_server_script", nargs=1, metavar="DOCTYPE",
  133. help="Create new conf.py file")
  134. parser.add_argument("--set_admin_password", metavar='ADMIN-PASSWORD', nargs=1,
  135. help="Set administrator password")
  136. parser.add_argument("--request", metavar='URL-ARGS', nargs=1, help="Run request as admin")
  137. parser.add_argument("--mysql", action="store_true", help="get mysql shell for a site")
  138. parser.add_argument("--serve", action="store_true", help="Run development server")
  139. parser.add_argument("--profile", action="store_true", help="enable profiling in development server")
  140. parser.add_argument("--smtp", action="store_true", help="Run smtp debug server",
  141. dest="smtp_debug_server")
  142. parser.add_argument("--python", action="store_true", help="get python shell for a site")
  143. parser.add_argument("--flush_memcache", action="store_true", help="flush memcached")
  144. parser.add_argument("--ipython", action="store_true", help="get ipython shell for a site")
  145. parser.add_argument("--get_site_status", action="store_true", help="Get site details")
  146. parser.add_argument("--update_site_config", nargs=1,
  147. metavar="site-CONFIG-JSON",
  148. help="Update site_config.json for a given site")
  149. parser.add_argument("--port", default=8000, type=int, help="port for development server")
  150. # clear
  151. parser.add_argument("--clear_web", default=False, action="store_true",
  152. help="Clear website cache")
  153. parser.add_argument("--build_sitemap", default=False, action="store_true",
  154. help="Build Website Sitemap")
  155. parser.add_argument("--clear_cache", default=False, action="store_true",
  156. help="Clear cache, doctype cache and defaults")
  157. parser.add_argument("--reset_perms", default=False, action="store_true",
  158. help="Reset permissions for all doctypes")
  159. # scheduler
  160. parser.add_argument("--run_scheduler", default=False, action="store_true",
  161. help="Trigger scheduler")
  162. parser.add_argument("--run_scheduler_event", nargs=1,
  163. metavar="all | daily | weekly | monthly",
  164. help="Run a scheduler event")
  165. # replace
  166. parser.add_argument("--replace", nargs=3,
  167. metavar=("SEARCH-REGEX", "REPLACE-BY", "FILE-EXTN"),
  168. help="Multi-file search-replace [-f]")
  169. # import/export
  170. parser.add_argument("--export_doc", nargs=2, metavar=('"DOCTYPE"', '"DOCNAME"'))
  171. parser.add_argument("--export_doclist", nargs=3, metavar=("DOCTYPE", "NAME", "PATH"),
  172. help="""Export doclist as json to the given path, use '-' as name for Singles.""")
  173. parser.add_argument("--export_csv", nargs=2, metavar=("DOCTYPE", "PATH"),
  174. help="""Dump DocType as csv""")
  175. parser.add_argument("--import_doclist", nargs=1, metavar="PATH",
  176. help="""Import (insert/update) doclist. If the argument is a directory, all files ending with .json are imported""")
  177. def setup_translation(parser):
  178. parser.add_argument("--build_message_files", default=False, action="store_true",
  179. help="Build message files for translation.")
  180. parser.add_argument("--get_untranslated", nargs=2, metavar=("LANG-CODE", "TARGET-FILE-PATH"),
  181. help="""Get untranslated strings for lang.""")
  182. parser.add_argument("--update_translations", nargs=3,
  183. metavar=("LANG-CODE", "UNTRANSLATED-FILE-PATH", "TRANSLATED-FILE-PATH"),
  184. help="""Update translated strings.""")
  185. # methods
  186. @cmd
  187. def make_app():
  188. from webnotes.utils.boilerplate import make_boilerplate
  189. make_boilerplate()
  190. # install
  191. @cmd
  192. def install(db_name, root_login="root", root_password=None, source_sql=None,
  193. admin_password = 'admin', verbose=True, force=False, site_config=None, reinstall=False):
  194. from webnotes.installer import install_db, install_app, make_site_dirs
  195. install_db(root_login=root_login, root_password=root_password, db_name=db_name, source_sql=source_sql,
  196. admin_password = admin_password, verbose=verbose, force=force, site_config=site_config, reinstall=reinstall)
  197. make_site_dirs()
  198. install_app("webnotes", verbose=verbose)
  199. webnotes.destroy()
  200. @cmd
  201. def install_app(app_name, verbose=False):
  202. from webnotes.installer import install_app
  203. webnotes.connect()
  204. install_app(app_name, verbose=verbose)
  205. webnotes.destroy()
  206. @cmd
  207. def reinstall(verbose=True):
  208. install(db_name=webnotes.conf.db_name, verbose=verbose, force=True, reinstall=True)
  209. @cmd
  210. def restore(db_name, source_sql, verbose=True, force=False):
  211. install(db_name, source_sql=source_sql, verbose=verbose, force=force)
  212. @cmd
  213. def install_fixtures():
  214. from webnotes.install_lib.install import install_fixtures
  215. install_fixtures()
  216. webnotes.destroy()
  217. @cmd
  218. def add_system_manager(email, first_name=None, last_name=None):
  219. webnotes.connect()
  220. webnotes.profile.add_system_manager(email, first_name, last_name)
  221. webnotes.conn.commit()
  222. webnotes.destroy()
  223. # utilities
  224. @cmd
  225. def update(remote=None, branch=None, reload_gunicorn=False):
  226. pull(remote=remote, branch=branch)
  227. # maybe there are new framework changes, any consequences?
  228. reload(webnotes)
  229. build()
  230. latest()
  231. if reload_gunicorn:
  232. import subprocess
  233. subprocess.check_output("killall -HUP gunicorn".split())
  234. @cmd
  235. def latest(verbose=True):
  236. import webnotes.modules.patch_handler
  237. import webnotes.model.sync
  238. from webnotes.website import rebuild_config
  239. webnotes.connect()
  240. try:
  241. # run patches
  242. webnotes.local.patch_log_list = []
  243. webnotes.modules.patch_handler.run_all()
  244. if verbose:
  245. print "\n".join(webnotes.local.patch_log_list)
  246. # sync
  247. webnotes.model.sync.sync_all()
  248. # build website config if any changes in templates etc.
  249. rebuild_config()
  250. except webnotes.modules.patch_handler.PatchError, e:
  251. print "\n".join(webnotes.local.patch_log_list)
  252. raise
  253. finally:
  254. webnotes.destroy()
  255. @cmd
  256. def sync_all(force=False):
  257. import webnotes.model.sync
  258. webnotes.connect()
  259. webnotes.model.sync.sync_all(force=force)
  260. webnotes.destroy()
  261. @cmd
  262. def patch(patch_module, force=False):
  263. import webnotes.modules.patch_handler
  264. webnotes.connect()
  265. webnotes.local.patch_log_list = []
  266. webnotes.modules.patch_handler.run_single(patch_module, force=force)
  267. print "\n".join(webnotes.local.patch_log_list)
  268. webnotes.destroy()
  269. @cmd
  270. def update_all_sites(remote=None, branch=None, verbose=True):
  271. pull(remote, branch)
  272. # maybe there are new framework changes, any consequences?
  273. reload(webnotes)
  274. build()
  275. for site in get_sites():
  276. webnotes.init(site)
  277. latest(verbose=verbose)
  278. @cmd
  279. def reload_doc(module, doctype, docname, force=False):
  280. webnotes.connect()
  281. webnotes.reload_doc(module, doctype, docname, force=force)
  282. webnotes.conn.commit()
  283. webnotes.destroy()
  284. @cmd
  285. def build():
  286. import webnotes.build
  287. import webnotes
  288. webnotes.build.bundle(False)
  289. @cmd
  290. def watch():
  291. import webnotes.build
  292. webnotes.build.watch(True)
  293. @cmd
  294. def backup(with_files=False, verbose=True, backup_path_db=None, backup_path_files=None):
  295. from webnotes.utils.backups import scheduled_backup
  296. webnotes.connect()
  297. odb = scheduled_backup(ignore_files=not with_files, backup_path_db=backup_path_db, backup_path_files=backup_path_files)
  298. if verbose:
  299. from webnotes.utils import now
  300. print "database backup taken -", odb.backup_path_db, "- on", now()
  301. if with_files:
  302. print "files backup taken -", odb.backup_path_files, "- on", now()
  303. webnotes.destroy()
  304. return odb
  305. @cmd
  306. def move(dest_dir=None):
  307. import os
  308. if not dest_dir:
  309. raise Exception, "--dest_dir is required for --move"
  310. if not os.path.isdir(dest_dir):
  311. raise Exception, "destination is not a directory or does not exist"
  312. old_path = webnotes.utils.get_site()
  313. new_path = os.path.join(dest_dir, site)
  314. # check if site dump of same name already exists
  315. site_dump_exists = True
  316. count = 0
  317. while site_dump_exists:
  318. final_new_path = new_path + (count and str(count) or "")
  319. site_dump_exists = os.path.exists(final_new_path)
  320. count = int(count or 0) + 1
  321. os.rename(old_path, final_new_path)
  322. webnotes.destroy()
  323. return os.path.basename(final_new_path)
  324. @cmd
  325. def domain(host_url=None):
  326. webnotes.connect()
  327. if host_url:
  328. webnotes.conn.set_value("Website Settings", None, "subdomain", host_url)
  329. webnotes.conn.commit()
  330. else:
  331. print webnotes.conn.get_value("Website Settings", None, "subdomain")
  332. webnotes.destroy()
  333. @cmd
  334. def make_conf(db_name=None, db_password=None, site_config=None):
  335. from webnotes.install_lib.install import make_conf
  336. make_conf(db_name=db_name, db_password=db_password, site_config=site_config)
  337. @cmd
  338. def make_custom_server_script(doctype):
  339. from webnotes.core.doctype.custom_script.custom_script import make_custom_server_script_file
  340. webnotes.connect()
  341. make_custom_server_script_file(doctype)
  342. webnotes.destroy()
  343. # clear
  344. @cmd
  345. def clear_cache():
  346. import webnotes.sessions
  347. webnotes.connect()
  348. webnotes.clear_cache()
  349. webnotes.destroy()
  350. @cmd
  351. def clear_web():
  352. import webnotes.webutils
  353. webnotes.connect()
  354. webnotes.webutils.clear_cache()
  355. webnotes.destroy()
  356. @cmd
  357. def build_sitemap():
  358. from webnotes.website import rebuild_config
  359. webnotes.connect()
  360. rebuild_config()
  361. webnotes.destroy()
  362. @cmd
  363. def reset_perms():
  364. webnotes.connect()
  365. for d in webnotes.conn.sql_list("""select name from `tabDocType`
  366. where ifnull(istable, 0)=0 and ifnull(custom, 0)=0"""):
  367. webnotes.clear_cache(doctype=d)
  368. webnotes.reset_perms(d)
  369. webnotes.destroy()
  370. # scheduler
  371. @cmd
  372. def run_scheduler():
  373. from webnotes.utils.file_lock import create_lock, delete_lock
  374. import webnotes.utils.scheduler
  375. if create_lock('scheduler'):
  376. webnotes.connect()
  377. print webnotes.utils.scheduler.execute()
  378. delete_lock('scheduler')
  379. webnotes.destroy()
  380. @cmd
  381. def run_scheduler_event(event):
  382. import webnotes.utils.scheduler
  383. webnotes.connect()
  384. print webnotes.utils.scheduler.trigger(event)
  385. webnotes.destroy()
  386. # replace
  387. @cmd
  388. def replace(search_regex, replacement, extn, force=False):
  389. print search_regex, replacement, extn
  390. replace_code('.', search_regex, replacement, extn, force=force)
  391. # import/export
  392. @cmd
  393. def export_doc(doctype, docname):
  394. import webnotes.modules
  395. webnotes.connect()
  396. webnotes.modules.export_doc(doctype, docname)
  397. webnotes.destroy()
  398. @cmd
  399. def export_doclist(doctype, name, path):
  400. from webnotes.core.page.data_import_tool import data_import_tool
  401. webnotes.connect()
  402. data_import_tool.export_json(doctype, name, path)
  403. webnotes.destroy()
  404. @cmd
  405. def export_csv(doctype, path):
  406. from webnotes.core.page.data_import_tool import data_import_tool
  407. webnotes.connect()
  408. data_import_tool.export_csv(doctype, path)
  409. webnotes.destroy()
  410. @cmd
  411. def import_doclist(path, force=False):
  412. from webnotes.core.page.data_import_tool import data_import_tool
  413. webnotes.connect()
  414. data_import_tool.import_doclist(path, overwrite=force)
  415. webnotes.destroy()
  416. # translation
  417. @cmd
  418. def build_message_files():
  419. import webnotes.translate
  420. webnotes.connect()
  421. webnotes.translate.rebuild_all_translation_files()
  422. webnotes.destroy()
  423. @cmd
  424. def get_untranslated(lang, untranslated_file):
  425. import webnotes.translate
  426. webnotes.connect()
  427. webnotes.translate.get_untranslated(lang, untranslated_file)
  428. webnotes.destroy()
  429. @cmd
  430. def update_translations(lang, untranslated_file, translated_file):
  431. import webnotes.translate
  432. webnotes.connect()
  433. webnotes.translate.update_translations(lang, untranslated_file, translated_file)
  434. webnotes.destroy()
  435. # git
  436. @cmd
  437. def git(param):
  438. if isinstance(param, (list, tuple)):
  439. param = " ".join(param)
  440. import os
  441. os.system("""cd lib && git %s""" % param)
  442. os.system("""cd app && git %s""" % param)
  443. def get_remote_and_branch(remote=None, branch=None):
  444. if not (remote and branch):
  445. if not webnotes.conf.branch:
  446. raise Exception("Please specify remote and branch")
  447. remote = remote or "origin"
  448. branch = branch or webnotes.conf.branch
  449. webnotes.destroy()
  450. return remote, branch
  451. @cmd
  452. def pull(remote=None, branch=None):
  453. remote, branch = get_remote_and_branch(remote, branch)
  454. git(("pull", remote, branch))
  455. @cmd
  456. def push(remote=None, branch=None):
  457. remote, branch = get_remote_and_branch(remote, branch)
  458. git(("push", remote, branch))
  459. @cmd
  460. def status():
  461. git("status")
  462. @cmd
  463. def commit(message):
  464. git("""commit -a -m "%s" """ % message.replace('"', '\"'))
  465. @cmd
  466. def checkout(branch):
  467. git(("checkout", branch))
  468. @cmd
  469. def set_admin_password(admin_password):
  470. import webnotes
  471. webnotes.connect()
  472. webnotes.conn.sql("""update __Auth set `password`=password(%s)
  473. where user='Administrator'""", (admin_password,))
  474. webnotes.conn.commit()
  475. webnotes.destroy()
  476. @cmd
  477. def mysql():
  478. import webnotes
  479. import commands, os
  480. msq = commands.getoutput('which mysql')
  481. os.execv(msq, [msq, '-u', webnotes.conf.db_name, '-p'+webnotes.conf.db_password, webnotes.conf.db_name, '-h', webnotes.conf.db_host or "localhost", "-A"])
  482. webnotes.destroy()
  483. @cmd
  484. def python(site):
  485. import webnotes
  486. import commands, os
  487. python = commands.getoutput('which python')
  488. if site:
  489. os.environ["site"] = site
  490. os.environ["PYTHONSTARTUP"] = os.path.join(os.path.dirname(webnotes.__file__), "pythonrc.py")
  491. os.execv(python, [python])
  492. webnotes.destroy()
  493. @cmd
  494. def ipython():
  495. import webnotes
  496. webnotes.connect()
  497. import IPython
  498. IPython.embed()
  499. @cmd
  500. def smtp_debug_server():
  501. import commands, os
  502. python = commands.getoutput('which python')
  503. os.execv(python, [python, '-m', "smtpd", "-n", "-c", "DebuggingServer", "localhost:25"])
  504. @cmd
  505. def run_tests(app=None, module=None, doctype=None, verbose=False):
  506. import webnotes.test_runner
  507. ret = webnotes.test_runner.main(app and app[0], module and module[0], doctype and doctype[0], verbose)
  508. if len(ret.failures) > 0 or len(ret.errors) > 0:
  509. exit(1)
  510. @cmd
  511. def serve(port=8000, profile=False, site=None):
  512. import webnotes.app
  513. webnotes.app.serve(port=port, profile=profile, site=site)
  514. @cmd
  515. def request(args):
  516. import webnotes.handler
  517. webnotes.connect()
  518. webnotes.form_dict = webnotes._dict([a.split("=") for a in args.split("&")])
  519. webnotes.handler.execute_cmd(webnotes.form_dict.cmd)
  520. print webnotes.response
  521. webnotes.destroy()
  522. @cmd
  523. def flush_memcache():
  524. webnotes.cache().flush_all()
  525. def replace_code(start, txt1, txt2, extn, search=None, force=False):
  526. """replace all txt1 by txt2 in files with extension (extn)"""
  527. import webnotes.utils
  528. import os, re
  529. esc = webnotes.utils.make_esc('[]')
  530. if not search: search = esc(txt1)
  531. for wt in os.walk(start, followlinks=1):
  532. for fn in wt[2]:
  533. if fn.split('.')[-1]==extn:
  534. fpath = os.path.join(wt[0], fn)
  535. with open(fpath, 'r') as f:
  536. content = f.read()
  537. if re.search(search, content):
  538. res = search_replace_with_prompt(fpath, txt1, txt2, force)
  539. if res == 'skip':
  540. return 'skip'
  541. def search_replace_with_prompt(fpath, txt1, txt2, force=False):
  542. """ Search and replace all txt1 by txt2 in the file with confirmation"""
  543. from termcolor import colored
  544. with open(fpath, 'r') as f:
  545. content = f.readlines()
  546. tmp = []
  547. for c in content:
  548. if c.find(txt1) != -1:
  549. print fpath
  550. print colored(txt1, 'red').join(c[:-1].split(txt1))
  551. a = ''
  552. if force:
  553. c = c.replace(txt1, txt2)
  554. else:
  555. while a.lower() not in ['y', 'n', 'skip']:
  556. a = raw_input('Do you want to Change [y/n/skip]?')
  557. if a.lower() == 'y':
  558. c = c.replace(txt1, txt2)
  559. elif a.lower() == 'skip':
  560. return 'skip'
  561. tmp.append(c)
  562. with open(fpath, 'w') as f:
  563. f.write(''.join(tmp))
  564. print colored('Updated', 'green')
  565. @cmd
  566. def get_site_status(verbose=False):
  567. import webnotes
  568. import webnotes.utils
  569. from webnotes.profile import get_system_managers
  570. from webnotes.core.doctype.profile.profile import get_total_users, get_active_users, \
  571. get_website_users, get_active_website_users
  572. import json
  573. webnotes.connect()
  574. ret = {
  575. 'last_backup_on': webnotes.local.conf.last_backup_on,
  576. 'active_users': get_active_users(),
  577. 'total_users': get_total_users(),
  578. 'active_website_users': get_active_website_users(),
  579. 'website_users': get_website_users(),
  580. 'system_managers': "\n".join(get_system_managers()),
  581. 'default_company': webnotes.conn.get_default("company"),
  582. 'disk_usage': webnotes.utils.get_disk_usage(),
  583. 'working_directory': webnotes.local.site_path
  584. }
  585. # country, timezone, industry
  586. control_panel_details = webnotes.conn.get_value("Control Panel", "Control Panel",
  587. ["country", "time_zone", "industry"], as_dict=True)
  588. if control_panel_details:
  589. ret.update(control_panel_details)
  590. # basic usage/progress analytics
  591. for doctype in ("Company", "Customer", "Item", "Quotation", "Sales Invoice",
  592. "Journal Voucher", "Stock Ledger Entry"):
  593. key = doctype.lower().replace(" ", "_") + "_exists"
  594. ret[key] = 1 if webnotes.conn.count(doctype) else 0
  595. webnotes.destroy()
  596. if verbose:
  597. print json.dumps(ret, indent=1, sort_keys=True)
  598. return ret
  599. @cmd
  600. def update_site_config(site_config, verbose=False):
  601. import json
  602. if isinstance(site_config, basestring):
  603. site_config = json.loads(site_config)
  604. webnotes.conf.site_config.update(site_config)
  605. site_config_path = webnotes.get_conf_path(webnotes.conf.sites_dir)
  606. with open(site_config_path, "w") as f:
  607. json.dump(webnotes.conf.site_config, f, indent=1, sort_keys=True)
  608. webnotes.destroy()
  609. @cmd
  610. def bump(repo, bump_type):
  611. import json
  612. assert repo in ['lib', 'app']
  613. assert bump_type in ['minor', 'major', 'patch']
  614. def validate(repo_path):
  615. import git
  616. repo = git.Repo(repo_path)
  617. if repo.active_branch != 'master':
  618. raise Exception, "Current branch not master in {}".format(repo_path)
  619. def bump_version(version, version_type):
  620. import semantic_version
  621. v = semantic_version.Version(version)
  622. if version_type == 'minor':
  623. v.minor += 1
  624. elif version_type == 'major':
  625. v.major += 1
  626. elif version_type == 'patch':
  627. v.patch += 1
  628. return unicode(v)
  629. def add_tag(repo_path, version):
  630. import git
  631. repo = git.Repo(repo_path)
  632. repo.index.add(['config.json'])
  633. repo.index.commit('bumped to version {}'.format(version))
  634. repo.create_tag('v' + version, repo.head)
  635. def update_framework_requirement(version):
  636. with open('app/config.json') as f:
  637. config = json.load(f)
  638. config['requires_framework_version'] = '==' + version
  639. with open('app/config.json', 'w') as f:
  640. json.dump(config, f, indent=1, sort_keys=True)
  641. validate('lib/')
  642. validate('app/')
  643. if repo == 'app':
  644. with open('app/config.json') as f:
  645. config = json.load(f)
  646. new_version = bump_version(config['app_version'], bump_type)
  647. config['app_version'] = new_version
  648. with open('app/config.json', 'w') as f:
  649. json.dump(config, f, indent=1, sort_keys=True)
  650. add_tag('app/', new_version)
  651. elif repo == 'lib':
  652. with open('lib/config.json') as f:
  653. config = json.load(f)
  654. new_version = bump_version(config['framework_version'], bump_type)
  655. config['framework_version'] = new_version
  656. with open('lib/config.json', 'w') as f:
  657. json.dump(config, f, indent=1, sort_keys=True)
  658. add_tag('lib/', new_version)
  659. update_framework_requirement(new_version)
  660. bump('app', bump_type)
  661. if __name__=="__main__":
  662. main()