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.
 
 
 
 
 
 

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