Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.
 
 
 
 
 
 

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