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.

11 年之前
11 年之前
11 年之前
11 年之前
11 年之前
11 年之前
11 年之前
11 年之前
11 年之前
11 年之前
11 年之前
11 年之前
11 年之前
11 年之前
11 年之前
11 年之前
13 年之前
11 年之前
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823
  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. from webnotes.installer import install_db, install_app, make_site_dirs
  207. install_db(root_login=root_login, root_password=root_password, db_name=db_name, source_sql=source_sql,
  208. admin_password = admin_password, verbose=verbose, force=force, site_config=site_config, reinstall=reinstall)
  209. make_site_dirs()
  210. install_app("webnotes", verbose=verbose)
  211. webnotes.destroy()
  212. @cmd
  213. def install_app(app_name, verbose=False):
  214. from webnotes.installer import install_app
  215. webnotes.connect()
  216. install_app(app_name, verbose=verbose)
  217. webnotes.destroy()
  218. @cmd
  219. def reinstall(verbose=True):
  220. install(db_name=webnotes.conf.db_name, verbose=verbose, force=True, reinstall=True)
  221. @cmd
  222. def restore(db_name, source_sql, verbose=True, force=False):
  223. install(db_name, source_sql=source_sql, verbose=verbose, force=force)
  224. @cmd
  225. def install_fixtures():
  226. from webnotes.install_lib.install import install_fixtures
  227. install_fixtures()
  228. webnotes.destroy()
  229. @cmd
  230. def add_system_manager(email, first_name=None, last_name=None):
  231. webnotes.connect()
  232. webnotes.profile.add_system_manager(email, first_name, last_name)
  233. webnotes.conn.commit()
  234. webnotes.destroy()
  235. # utilities
  236. @cmd
  237. def update(remote=None, branch=None, reload_gunicorn=False):
  238. pull(remote=remote, branch=branch)
  239. # maybe there are new framework changes, any consequences?
  240. reload(webnotes)
  241. build()
  242. latest()
  243. if reload_gunicorn:
  244. import subprocess
  245. subprocess.check_output("killall -HUP gunicorn".split())
  246. @cmd
  247. def latest(verbose=True):
  248. import webnotes.modules.patch_handler
  249. import webnotes.model.sync
  250. from webnotes.website import rebuild_config
  251. webnotes.connect()
  252. try:
  253. # run patches
  254. webnotes.local.patch_log_list = []
  255. webnotes.modules.patch_handler.run_all()
  256. if verbose:
  257. print "\n".join(webnotes.local.patch_log_list)
  258. # sync
  259. webnotes.model.sync.sync_all()
  260. # build website config if any changes in templates etc.
  261. rebuild_config()
  262. except webnotes.modules.patch_handler.PatchError, e:
  263. print "\n".join(webnotes.local.patch_log_list)
  264. raise
  265. finally:
  266. webnotes.destroy()
  267. @cmd
  268. def sync_all(force=False):
  269. import webnotes.model.sync
  270. webnotes.connect()
  271. webnotes.model.sync.sync_all(force=force)
  272. webnotes.destroy()
  273. @cmd
  274. def patch(patch_module, force=False):
  275. import webnotes.modules.patch_handler
  276. webnotes.connect()
  277. webnotes.local.patch_log_list = []
  278. webnotes.modules.patch_handler.run_single(patch_module, force=force)
  279. print "\n".join(webnotes.local.patch_log_list)
  280. webnotes.destroy()
  281. @cmd
  282. def update_all_sites(remote=None, branch=None, verbose=True):
  283. pull(remote, branch)
  284. # maybe there are new framework changes, any consequences?
  285. reload(webnotes)
  286. build()
  287. for site in get_sites():
  288. webnotes.init(site)
  289. latest(verbose=verbose)
  290. @cmd
  291. def reload_doc(module, doctype, docname, force=False):
  292. webnotes.connect()
  293. webnotes.reload_doc(module, doctype, docname, force=force)
  294. webnotes.conn.commit()
  295. webnotes.destroy()
  296. @cmd
  297. def build():
  298. import webnotes.build
  299. import webnotes
  300. webnotes.build.bundle(False)
  301. @cmd
  302. def watch():
  303. import webnotes.build
  304. webnotes.build.watch(True)
  305. @cmd
  306. def backup(with_files=False, verbose=True, backup_path_db=None, backup_path_files=None):
  307. from webnotes.utils.backups import scheduled_backup
  308. webnotes.connect()
  309. odb = scheduled_backup(ignore_files=not with_files, backup_path_db=backup_path_db, backup_path_files=backup_path_files)
  310. if verbose:
  311. from webnotes.utils import now
  312. print "database backup taken -", odb.backup_path_db, "- on", now()
  313. if with_files:
  314. print "files backup taken -", odb.backup_path_files, "- on", now()
  315. webnotes.destroy()
  316. return odb
  317. @cmd
  318. def move(dest_dir=None):
  319. import os
  320. if not dest_dir:
  321. raise Exception, "--dest_dir is required for --move"
  322. if not os.path.isdir(dest_dir):
  323. raise Exception, "destination is not a directory or does not exist"
  324. old_path = webnotes.utils.get_site()
  325. new_path = os.path.join(dest_dir, site)
  326. # check if site dump of same name already exists
  327. site_dump_exists = True
  328. count = 0
  329. while site_dump_exists:
  330. final_new_path = new_path + (count and str(count) or "")
  331. site_dump_exists = os.path.exists(final_new_path)
  332. count = int(count or 0) + 1
  333. os.rename(old_path, final_new_path)
  334. webnotes.destroy()
  335. return os.path.basename(final_new_path)
  336. @cmd
  337. def domain(host_url=None):
  338. webnotes.connect()
  339. if host_url:
  340. webnotes.conn.set_value("Website Settings", None, "subdomain", host_url)
  341. webnotes.conn.commit()
  342. else:
  343. print webnotes.conn.get_value("Website Settings", None, "subdomain")
  344. webnotes.destroy()
  345. @cmd
  346. def make_conf(db_name=None, db_password=None, site_config=None):
  347. from webnotes.install_lib.install import make_conf
  348. make_conf(db_name=db_name, db_password=db_password, site_config=site_config)
  349. @cmd
  350. def make_custom_server_script(doctype):
  351. from webnotes.core.doctype.custom_script.custom_script import make_custom_server_script_file
  352. webnotes.connect()
  353. make_custom_server_script_file(doctype)
  354. webnotes.destroy()
  355. # clear
  356. @cmd
  357. def clear_cache():
  358. import webnotes.sessions
  359. webnotes.connect()
  360. webnotes.clear_cache()
  361. webnotes.destroy()
  362. @cmd
  363. def clear_web():
  364. import webnotes.webutils
  365. webnotes.connect()
  366. webnotes.webutils.clear_cache()
  367. webnotes.destroy()
  368. @cmd
  369. def build_sitemap():
  370. from webnotes.website import rebuild_config
  371. webnotes.connect()
  372. rebuild_config()
  373. webnotes.destroy()
  374. @cmd
  375. def sync_statics():
  376. from webnotes.website.doctype.web_page import web_page
  377. webnotes.connect()
  378. web_page.sync_statics()
  379. webnotes.conn.commit()
  380. webnotes.destroy()
  381. @cmd
  382. def reset_perms():
  383. webnotes.connect()
  384. for d in webnotes.conn.sql_list("""select name from `tabDocType`
  385. where ifnull(istable, 0)=0 and ifnull(custom, 0)=0"""):
  386. webnotes.clear_cache(doctype=d)
  387. webnotes.reset_perms(d)
  388. webnotes.destroy()
  389. # scheduler
  390. @cmd
  391. def run_scheduler():
  392. from webnotes.utils.file_lock import create_lock, delete_lock
  393. import webnotes.utils.scheduler
  394. if create_lock('scheduler'):
  395. webnotes.connect()
  396. print webnotes.utils.scheduler.execute()
  397. delete_lock('scheduler')
  398. webnotes.destroy()
  399. @cmd
  400. def run_scheduler_event(event):
  401. import webnotes.utils.scheduler
  402. webnotes.connect()
  403. print webnotes.utils.scheduler.trigger(event)
  404. webnotes.destroy()
  405. # replace
  406. @cmd
  407. def replace(search_regex, replacement, extn, force=False):
  408. print search_regex, replacement, extn
  409. replace_code('.', search_regex, replacement, extn, force=force)
  410. # import/export
  411. @cmd
  412. def export_doc(doctype, docname):
  413. import webnotes.modules
  414. webnotes.connect()
  415. webnotes.modules.export_doc(doctype, docname)
  416. webnotes.destroy()
  417. @cmd
  418. def export_doclist(doctype, name, path):
  419. from webnotes.core.page.data_import_tool import data_import_tool
  420. webnotes.connect()
  421. data_import_tool.export_json(doctype, name, path)
  422. webnotes.destroy()
  423. @cmd
  424. def export_csv(doctype, path):
  425. from webnotes.core.page.data_import_tool import data_import_tool
  426. webnotes.connect()
  427. data_import_tool.export_csv(doctype, path)
  428. webnotes.destroy()
  429. @cmd
  430. def import_doclist(path, force=False):
  431. from webnotes.core.page.data_import_tool import data_import_tool
  432. webnotes.connect()
  433. data_import_tool.import_doclist(path, overwrite=force)
  434. webnotes.destroy()
  435. # translation
  436. @cmd
  437. def build_message_files():
  438. import webnotes.translate
  439. webnotes.connect()
  440. webnotes.translate.rebuild_all_translation_files()
  441. webnotes.destroy()
  442. @cmd
  443. def get_untranslated(lang, untranslated_file):
  444. import webnotes.translate
  445. webnotes.connect()
  446. webnotes.translate.get_untranslated(lang, untranslated_file)
  447. webnotes.destroy()
  448. @cmd
  449. def update_translations(lang, untranslated_file, translated_file):
  450. import webnotes.translate
  451. webnotes.connect()
  452. webnotes.translate.update_translations(lang, untranslated_file, translated_file)
  453. webnotes.destroy()
  454. # git
  455. @cmd
  456. def git(param):
  457. if isinstance(param, (list, tuple)):
  458. param = " ".join(param)
  459. import os
  460. os.system("""cd lib && git %s""" % param)
  461. os.system("""cd app && git %s""" % param)
  462. def get_remote_and_branch(remote=None, branch=None):
  463. if not (remote and branch):
  464. if not webnotes.conf.branch:
  465. raise Exception("Please specify remote and branch")
  466. remote = remote or "origin"
  467. branch = branch or webnotes.conf.branch
  468. webnotes.destroy()
  469. return remote, branch
  470. @cmd
  471. def pull(remote=None, branch=None):
  472. remote, branch = get_remote_and_branch(remote, branch)
  473. git(("pull", remote, branch))
  474. @cmd
  475. def push(remote=None, branch=None):
  476. remote, branch = get_remote_and_branch(remote, branch)
  477. git(("push", remote, branch))
  478. @cmd
  479. def status():
  480. git("status")
  481. @cmd
  482. def commit(message):
  483. git("""commit -a -m "%s" """ % message.replace('"', '\"'))
  484. @cmd
  485. def checkout(branch):
  486. git(("checkout", branch))
  487. @cmd
  488. def set_admin_password(admin_password):
  489. import webnotes
  490. webnotes.connect()
  491. webnotes.conn.sql("""update __Auth set `password`=password(%s)
  492. where user='Administrator'""", (admin_password,))
  493. webnotes.conn.commit()
  494. webnotes.destroy()
  495. @cmd
  496. def mysql():
  497. import webnotes
  498. import commands, os
  499. msq = commands.getoutput('which mysql')
  500. 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"])
  501. webnotes.destroy()
  502. @cmd
  503. def python(site):
  504. import webnotes
  505. import commands, os
  506. python = commands.getoutput('which python')
  507. if site:
  508. os.environ["site"] = site
  509. os.environ["PYTHONSTARTUP"] = os.path.join(os.path.dirname(webnotes.__file__), "pythonrc.py")
  510. os.execv(python, [python])
  511. webnotes.destroy()
  512. @cmd
  513. def ipython():
  514. import webnotes
  515. webnotes.connect()
  516. import IPython
  517. IPython.embed()
  518. @cmd
  519. def smtp_debug_server():
  520. import commands, os
  521. python = commands.getoutput('which python')
  522. os.execv(python, [python, '-m', "smtpd", "-n", "-c", "DebuggingServer", "localhost:25"])
  523. @cmd
  524. def run_tests(app=None, module=None, doctype=None, verbose=False):
  525. import webnotes.test_runner
  526. ret = webnotes.test_runner.main(app and app[0], module and module[0], doctype and doctype[0], verbose)
  527. if len(ret.failures) > 0 or len(ret.errors) > 0:
  528. exit(1)
  529. @cmd
  530. def serve(port=8000, profile=False):
  531. import webnotes.app
  532. webnotes.app.serve(port=port, profile=profile, site=webnotes.local.site)
  533. @cmd
  534. def request(args):
  535. import webnotes.handler
  536. import webnotes.api
  537. webnotes.connect()
  538. if "?" in args:
  539. webnotes.local.form_dict = webnotes._dict([a.split("=") for a in args.split("?")[-1].split("&")])
  540. else:
  541. webnotes.local.form_dict = webnotes._dict()
  542. if args.startswith("/api/method"):
  543. webnotes.local.form_dict.cmd = args.split("?")[0].split("/")[-1]
  544. webnotes.handler.execute_cmd(webnotes.form_dict.cmd)
  545. print webnotes.response
  546. webnotes.destroy()
  547. @cmd
  548. def flush_memcache():
  549. webnotes.cache().flush_all()
  550. def replace_code(start, txt1, txt2, extn, search=None, force=False):
  551. """replace all txt1 by txt2 in files with extension (extn)"""
  552. import webnotes.utils
  553. import os, re
  554. esc = webnotes.utils.make_esc('[]')
  555. if not search: search = esc(txt1)
  556. for wt in os.walk(start, followlinks=1):
  557. for fn in wt[2]:
  558. if fn.split('.')[-1]==extn:
  559. fpath = os.path.join(wt[0], fn)
  560. with open(fpath, 'r') as f:
  561. content = f.read()
  562. if re.search(search, content):
  563. res = search_replace_with_prompt(fpath, txt1, txt2, force)
  564. if res == 'skip':
  565. return 'skip'
  566. def search_replace_with_prompt(fpath, txt1, txt2, force=False):
  567. """ Search and replace all txt1 by txt2 in the file with confirmation"""
  568. from termcolor import colored
  569. with open(fpath, 'r') as f:
  570. content = f.readlines()
  571. tmp = []
  572. for c in content:
  573. if c.find(txt1) != -1:
  574. print fpath
  575. print colored(txt1, 'red').join(c[:-1].split(txt1))
  576. a = ''
  577. if force:
  578. c = c.replace(txt1, txt2)
  579. else:
  580. while a.lower() not in ['y', 'n', 'skip']:
  581. a = raw_input('Do you want to Change [y/n/skip]?')
  582. if a.lower() == 'y':
  583. c = c.replace(txt1, txt2)
  584. elif a.lower() == 'skip':
  585. return 'skip'
  586. tmp.append(c)
  587. with open(fpath, 'w') as f:
  588. f.write(''.join(tmp))
  589. print colored('Updated', 'green')
  590. @cmd
  591. def get_site_status(verbose=False):
  592. import webnotes
  593. import webnotes.utils
  594. from webnotes.profile import get_system_managers
  595. from webnotes.core.doctype.profile.profile import get_total_users, get_active_users, \
  596. get_website_users, get_active_website_users
  597. import json
  598. webnotes.connect()
  599. ret = {
  600. 'last_backup_on': webnotes.local.conf.last_backup_on,
  601. 'active_users': get_active_users(),
  602. 'total_users': get_total_users(),
  603. 'active_website_users': get_active_website_users(),
  604. 'website_users': get_website_users(),
  605. 'system_managers': "\n".join(get_system_managers()),
  606. 'default_company': webnotes.conn.get_default("company"),
  607. 'disk_usage': webnotes.utils.get_disk_usage(),
  608. 'working_directory': webnotes.local.site_path
  609. }
  610. # country, timezone, industry
  611. control_panel_details = webnotes.conn.get_value("Control Panel", "Control Panel",
  612. ["country", "time_zone", "industry"], as_dict=True)
  613. if control_panel_details:
  614. ret.update(control_panel_details)
  615. # basic usage/progress analytics
  616. for doctype in ("Company", "Customer", "Item", "Quotation", "Sales Invoice",
  617. "Journal Voucher", "Stock Ledger Entry"):
  618. key = doctype.lower().replace(" ", "_") + "_exists"
  619. ret[key] = 1 if webnotes.conn.count(doctype) else 0
  620. webnotes.destroy()
  621. if verbose:
  622. print json.dumps(ret, indent=1, sort_keys=True)
  623. return ret
  624. @cmd
  625. def update_site_config(site_config, verbose=False):
  626. import json
  627. if isinstance(site_config, basestring):
  628. site_config = json.loads(site_config)
  629. config = webnotes.get_site_config()
  630. config.update(site_config)
  631. site_config_path = os.path.join(webnotes.local.site_path, "site_config.json")
  632. with open(site_config_path, "w") as f:
  633. json.dump(config, f, indent=1, sort_keys=True)
  634. webnotes.destroy()
  635. @cmd
  636. def bump(repo, bump_type):
  637. import json
  638. assert repo in ['lib', 'app']
  639. assert bump_type in ['minor', 'major', 'patch']
  640. def validate(repo_path):
  641. import git
  642. repo = git.Repo(repo_path)
  643. if repo.active_branch != 'master':
  644. raise Exception, "Current branch not master in {}".format(repo_path)
  645. def bump_version(version, version_type):
  646. import semantic_version
  647. v = semantic_version.Version(version)
  648. if version_type == 'minor':
  649. v.minor += 1
  650. elif version_type == 'major':
  651. v.major += 1
  652. elif version_type == 'patch':
  653. v.patch += 1
  654. return unicode(v)
  655. def add_tag(repo_path, version):
  656. import git
  657. repo = git.Repo(repo_path)
  658. repo.index.add(['config.json'])
  659. repo.index.commit('bumped to version {}'.format(version))
  660. repo.create_tag('v' + version, repo.head)
  661. def update_framework_requirement(version):
  662. with open('app/config.json') as f:
  663. config = json.load(f)
  664. config['requires_framework_version'] = '==' + version
  665. with open('app/config.json', 'w') as f:
  666. json.dump(config, f, indent=1, sort_keys=True)
  667. validate('lib/')
  668. validate('app/')
  669. if repo == 'app':
  670. with open('app/config.json') as f:
  671. config = json.load(f)
  672. new_version = bump_version(config['app_version'], bump_type)
  673. config['app_version'] = new_version
  674. with open('app/config.json', 'w') as f:
  675. json.dump(config, f, indent=1, sort_keys=True)
  676. add_tag('app/', new_version)
  677. elif repo == 'lib':
  678. with open('lib/config.json') as f:
  679. config = json.load(f)
  680. new_version = bump_version(config['framework_version'], bump_type)
  681. config['framework_version'] = new_version
  682. with open('lib/config.json', 'w') as f:
  683. json.dump(config, f, indent=1, sort_keys=True)
  684. add_tag('lib/', new_version)
  685. update_framework_requirement(new_version)
  686. bump('app', bump_type)
  687. if __name__=="__main__":
  688. main()