Non puoi selezionare più di 25 argomenti Gli argomenti devono iniziare con una lettera o un numero, possono includere trattini ('-') e possono essere lunghi fino a 35 caratteri.
 
 
 
 
 
 

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