You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

cli.py 25 KiB

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