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.
 
 
 
 
 
 

781 lines
24 KiB

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