No puede seleccionar más de 25 temas Los temas deben comenzar con una letra o número, pueden incluir guiones ('-') y pueden tener hasta 35 caracteres de largo.
 
 
 
 
 
 

470 líneas
12 KiB

  1. from __future__ import unicode_literals, absolute_import, print_function
  2. import click
  3. import json, os, sys
  4. from distutils.spawn import find_executable
  5. import frappe
  6. from frappe.commands import pass_context, get_site
  7. @click.command('build')
  8. @click.option('--make-copy', is_flag=True, default=False, help='Copy the files instead of symlinking')
  9. @click.option('--verbose', is_flag=True, default=False, help='Verbose')
  10. def build(make_copy=False, verbose=False):
  11. "Minify + concatenate JS and CSS files, build translations"
  12. import frappe.build
  13. import frappe
  14. frappe.init('')
  15. frappe.build.bundle(False, make_copy=make_copy, verbose=verbose)
  16. @click.command('watch')
  17. def watch():
  18. "Watch and concatenate JS and CSS files as and when they change"
  19. import frappe.build
  20. frappe.init('')
  21. frappe.build.watch(True)
  22. @click.command('clear-cache')
  23. @pass_context
  24. def clear_cache(context):
  25. "Clear cache, doctype cache and defaults"
  26. import frappe.sessions
  27. import frappe.website.render
  28. from frappe.desk.notifications import clear_notifications
  29. for site in context.sites:
  30. try:
  31. frappe.connect(site)
  32. frappe.clear_cache()
  33. clear_notifications()
  34. frappe.website.render.clear_cache()
  35. finally:
  36. frappe.destroy()
  37. @click.command('clear-website-cache')
  38. @pass_context
  39. def clear_website_cache(context):
  40. "Clear website cache"
  41. import frappe.website.render
  42. for site in context.sites:
  43. try:
  44. frappe.init(site=site)
  45. frappe.connect()
  46. frappe.website.render.clear_cache()
  47. finally:
  48. frappe.destroy()
  49. @click.command('destroy-all-sessions')
  50. @click.option('--reason')
  51. @pass_context
  52. def destroy_all_sessions(context, reason=None):
  53. "Clear sessions of all users (logs them out)"
  54. import frappe.sessions
  55. for site in context.sites:
  56. try:
  57. frappe.init(site=site)
  58. frappe.connect()
  59. frappe.sessions.clear_all_sessions(reason)
  60. frappe.db.commit()
  61. finally:
  62. frappe.destroy()
  63. @click.command('reset-perms')
  64. @pass_context
  65. def reset_perms(context):
  66. "Reset permissions for all doctypes"
  67. from frappe.permissions import reset_perms
  68. for site in context.sites:
  69. try:
  70. frappe.init(site=site)
  71. frappe.connect()
  72. for d in frappe.db.sql_list("""select name from `tabDocType`
  73. where istable=0 and custom=0"""):
  74. frappe.clear_cache(doctype=d)
  75. reset_perms(d)
  76. finally:
  77. frappe.destroy()
  78. @click.command('execute')
  79. @click.argument('method')
  80. @click.option('--args')
  81. @click.option('--kwargs')
  82. @pass_context
  83. def execute(context, method, args=None, kwargs=None):
  84. "Execute a function"
  85. for site in context.sites:
  86. try:
  87. frappe.init(site=site)
  88. frappe.connect()
  89. if args:
  90. try:
  91. args = eval(args)
  92. except NameError:
  93. args = [args]
  94. else:
  95. args = ()
  96. if kwargs:
  97. kwargs = eval(kwargs)
  98. else:
  99. kwargs = {}
  100. ret = frappe.get_attr(method)(*args, **kwargs)
  101. if frappe.db:
  102. frappe.db.commit()
  103. finally:
  104. frappe.destroy()
  105. if ret:
  106. print(json.dumps(ret))
  107. @click.command('add-to-email-queue')
  108. @click.argument('email-path')
  109. @pass_context
  110. def add_to_email_queue(context, email_path):
  111. "Add an email to the Email Queue"
  112. site = get_site(context)
  113. if os.path.isdir(email_path):
  114. with frappe.init_site(site):
  115. frappe.connect()
  116. for email in os.listdir(email_path):
  117. with open(os.path.join(email_path, email)) as email_data:
  118. kwargs = json.load(email_data)
  119. kwargs['delayed'] = True
  120. frappe.sendmail(**kwargs)
  121. frappe.db.commit()
  122. @click.command('export-doc')
  123. @click.argument('doctype')
  124. @click.argument('docname')
  125. @pass_context
  126. def export_doc(context, doctype, docname):
  127. "Export a single document to csv"
  128. import frappe.modules
  129. for site in context.sites:
  130. try:
  131. frappe.init(site=site)
  132. frappe.connect()
  133. frappe.modules.export_doc(doctype, docname)
  134. finally:
  135. frappe.destroy()
  136. @click.command('export-json')
  137. @click.argument('doctype')
  138. @click.argument('path')
  139. @click.option('--name', help='Export only one document')
  140. @pass_context
  141. def export_json(context, doctype, path, name=None):
  142. "Export doclist as json to the given path, use '-' as name for Singles."
  143. from frappe.core.page.data_import_tool import data_import_tool
  144. for site in context.sites:
  145. try:
  146. frappe.init(site=site)
  147. frappe.connect()
  148. data_import_tool.export_json(doctype, path, name=name)
  149. finally:
  150. frappe.destroy()
  151. @click.command('export-csv')
  152. @click.argument('doctype')
  153. @click.argument('path')
  154. @pass_context
  155. def export_csv(context, doctype, path):
  156. "Export data import template with data for DocType"
  157. from frappe.core.page.data_import_tool import data_import_tool
  158. for site in context.sites:
  159. try:
  160. frappe.init(site=site)
  161. frappe.connect()
  162. data_import_tool.export_csv(doctype, path)
  163. finally:
  164. frappe.destroy()
  165. @click.command('export-fixtures')
  166. @pass_context
  167. def export_fixtures(context):
  168. "Export fixtures"
  169. from frappe.utils.fixtures import export_fixtures
  170. for site in context.sites:
  171. try:
  172. frappe.init(site=site)
  173. frappe.connect()
  174. export_fixtures()
  175. finally:
  176. frappe.destroy()
  177. @click.command('import-doc')
  178. @click.argument('path')
  179. @pass_context
  180. def import_doc(context, path, force=False):
  181. "Import (insert/update) doclist. If the argument is a directory, all files ending with .json are imported"
  182. from frappe.core.page.data_import_tool import data_import_tool
  183. if not os.path.exists(path):
  184. path = os.path.join('..', path)
  185. if not os.path.exists(path):
  186. print('Invalid path {0}'.format(path))
  187. sys.exit(1)
  188. for site in context.sites:
  189. try:
  190. frappe.init(site=site)
  191. frappe.connect()
  192. data_import_tool.import_doc(path, overwrite=context.force)
  193. finally:
  194. frappe.destroy()
  195. @click.command('import-csv')
  196. @click.argument('path')
  197. @click.option('--only-insert', default=False, is_flag=True, help='Do not overwrite existing records')
  198. @click.option('--submit-after-import', default=False, is_flag=True, help='Submit document after importing it')
  199. @click.option('--ignore-encoding-errors', default=False, is_flag=True, help='Ignore encoding errors while coverting to unicode')
  200. @click.option('--no-email', default=True, is_flag=True, help='Send email if applicable')
  201. @pass_context
  202. def import_csv(context, path, only_insert=False, submit_after_import=False, ignore_encoding_errors=False, no_email=True):
  203. "Import CSV using data import tool"
  204. from frappe.core.page.data_import_tool import importer
  205. from frappe.utils.csvutils import read_csv_content
  206. site = get_site(context)
  207. if not os.path.exists(path):
  208. path = os.path.join('..', path)
  209. if not os.path.exists(path):
  210. print('Invalid path {0}'.format(path))
  211. sys.exit(1)
  212. with open(path, 'r') as csvfile:
  213. content = read_csv_content(csvfile.read())
  214. frappe.init(site=site)
  215. frappe.connect()
  216. try:
  217. importer.upload(content, submit_after_import=submit_after_import, no_email=no_email,
  218. ignore_encoding_errors=ignore_encoding_errors, overwrite=not only_insert,
  219. via_console=True)
  220. frappe.db.commit()
  221. except Exception:
  222. print(frappe.get_traceback())
  223. frappe.destroy()
  224. @click.command('bulk-rename')
  225. @click.argument('doctype')
  226. @click.argument('path')
  227. @pass_context
  228. def _bulk_rename(context, doctype, path):
  229. "Rename multiple records via CSV file"
  230. from frappe.model.rename_doc import bulk_rename
  231. from frappe.utils.csvutils import read_csv_content
  232. site = get_site(context)
  233. with open(path, 'r') as csvfile:
  234. rows = read_csv_content(csvfile.read())
  235. frappe.init(site=site)
  236. frappe.connect()
  237. bulk_rename(doctype, rows, via_console = True)
  238. frappe.destroy()
  239. @click.command('mysql')
  240. @pass_context
  241. def mysql(context):
  242. "Start Mariadb console for a site"
  243. site = get_site(context)
  244. frappe.init(site=site)
  245. msq = find_executable('mysql')
  246. os.execv(msq, [msq, '-u', frappe.conf.db_name, '-p'+frappe.conf.db_password, frappe.conf.db_name, '-h', frappe.conf.db_host or "localhost", "-A"])
  247. @click.command('console')
  248. @pass_context
  249. def console(context):
  250. "Start ipython console for a site"
  251. site = get_site(context)
  252. frappe.init(site=site)
  253. frappe.connect()
  254. frappe.local.lang = frappe.db.get_default("lang")
  255. import IPython
  256. IPython.embed()
  257. @click.command('run-tests')
  258. @click.option('--app', help="For App")
  259. @click.option('--doctype', help="For DocType")
  260. @click.option('--test', multiple=True, help="Specific test")
  261. @click.option('--driver', help="For Travis")
  262. @click.option('--module', help="Run tests in a module")
  263. @click.option('--profile', is_flag=True, default=False)
  264. @click.option('--junit-xml-output', help="Destination file path for junit xml report")
  265. @pass_context
  266. def run_tests(context, app=None, module=None, doctype=None, test=(), driver=None, profile=False, junit_xml_output=False):
  267. "Run tests"
  268. import frappe.test_runner
  269. from frappe.utils import sel
  270. tests = test
  271. site = get_site(context)
  272. frappe.init(site=site)
  273. if frappe.conf.run_selenium_tests and False:
  274. sel.start(context.verbose, driver)
  275. try:
  276. ret = frappe.test_runner.main(app, module, doctype, context.verbose, tests=tests,
  277. force=context.force, profile=profile, junit_xml_output=junit_xml_output)
  278. if len(ret.failures) == 0 and len(ret.errors) == 0:
  279. ret = 0
  280. finally:
  281. pass
  282. if frappe.conf.run_selenium_tests:
  283. sel.close()
  284. sys.exit(ret)
  285. @click.command('serve')
  286. @click.option('--port', default=8000)
  287. @click.option('--profile', is_flag=True, default=False)
  288. @pass_context
  289. def serve(context, port=None, profile=False, sites_path='.', site=None):
  290. "Start development web server"
  291. import frappe.app
  292. if not context.sites:
  293. site = None
  294. else:
  295. site = context.sites[0]
  296. frappe.app.serve(port=port, profile=profile, site=site, sites_path='.')
  297. @click.command('request')
  298. @click.argument('args')
  299. @pass_context
  300. def request(context, args):
  301. "Run a request as an admin"
  302. import frappe.handler
  303. import frappe.api
  304. for site in context.sites:
  305. try:
  306. frappe.init(site=site)
  307. frappe.connect()
  308. if "?" in args:
  309. frappe.local.form_dict = frappe._dict([a.split("=") for a in args.split("?")[-1].split("&")])
  310. else:
  311. frappe.local.form_dict = frappe._dict()
  312. if args.startswith("/api/method"):
  313. frappe.local.form_dict.cmd = args.split("?")[0].split("/")[-1]
  314. frappe.handler.execute_cmd(frappe.form_dict.cmd)
  315. print(frappe.response)
  316. finally:
  317. frappe.destroy()
  318. @click.command('make-app')
  319. @click.argument('destination')
  320. @click.argument('app_name')
  321. def make_app(destination, app_name):
  322. "Creates a boilerplate app"
  323. from frappe.utils.boilerplate import make_boilerplate
  324. make_boilerplate(destination, app_name)
  325. @click.command('set-config')
  326. @click.argument('key')
  327. @click.argument('value')
  328. @click.option('--as-dict', is_flag=True, default=False)
  329. @pass_context
  330. def set_config(context, key, value, as_dict=False):
  331. "Insert/Update a value in site_config.json"
  332. from frappe.installer import update_site_config
  333. import ast
  334. if as_dict:
  335. value = ast.literal_eval(value)
  336. for site in context.sites:
  337. frappe.init(site=site)
  338. update_site_config(key, value, validate=False)
  339. frappe.destroy()
  340. @click.command('version')
  341. def get_version():
  342. "Show the versions of all the installed apps"
  343. frappe.init('')
  344. for m in sorted(frappe.get_all_apps()):
  345. module = frappe.get_module(m)
  346. if hasattr(module, "__version__"):
  347. print("{0} {1}".format(m, module.__version__))
  348. @click.command('setup-global-help')
  349. @click.option('--mariadb_root_password')
  350. def setup_global_help(mariadb_root_password=None):
  351. '''setup help table in a separate database that will be
  352. shared by the whole bench and set `global_help_setup` as 1 in
  353. common_site_config.json'''
  354. from frappe.installer import update_site_config
  355. frappe.local.flags = frappe._dict()
  356. frappe.local.flags.in_setup_help = True
  357. frappe.local.flags.in_install = True
  358. frappe.local.lang = 'en'
  359. frappe.local.conf = frappe.get_site_config(sites_path='.')
  360. update_site_config('global_help_setup', 1,
  361. site_config_path=os.path.join('.', 'common_site_config.json'))
  362. if mariadb_root_password:
  363. frappe.local.conf.root_password = mariadb_root_password
  364. from frappe.utils.help import sync
  365. sync()
  366. @click.command('setup-help')
  367. @pass_context
  368. def setup_help(context):
  369. '''Setup help table in the current site (called after migrate)'''
  370. from frappe.utils.help import sync
  371. for site in context.sites:
  372. try:
  373. frappe.init(site)
  374. frappe.connect()
  375. sync()
  376. finally:
  377. frappe.destroy()
  378. commands = [
  379. build,
  380. clear_cache,
  381. clear_website_cache,
  382. console,
  383. destroy_all_sessions,
  384. execute,
  385. export_csv,
  386. export_doc,
  387. export_fixtures,
  388. export_json,
  389. get_version,
  390. import_csv,
  391. import_doc,
  392. make_app,
  393. mysql,
  394. request,
  395. reset_perms,
  396. run_tests,
  397. serve,
  398. set_config,
  399. watch,
  400. _bulk_rename,
  401. add_to_email_queue,
  402. setup_global_help,
  403. setup_help
  404. ]