Você não pode selecionar mais de 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.
 
 
 
 
 
 

369 linhas
10 KiB

  1. # Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors
  2. # MIT License. See license.txt
  3. import frappe, os, re, git
  4. from frappe.utils import touch_file, cstr
  5. def make_boilerplate(dest, app_name):
  6. if not os.path.exists(dest):
  7. print("Destination directory does not exist")
  8. return
  9. # app_name should be in snake_case
  10. app_name = frappe.scrub(app_name)
  11. hooks = frappe._dict()
  12. hooks.app_name = app_name
  13. app_title = hooks.app_name.replace("_", " ").title()
  14. for key in ("App Title (default: {0})".format(app_title),
  15. "App Description", "App Publisher", "App Email",
  16. "App Icon (default 'octicon octicon-file-directory')",
  17. "App Color (default 'grey')",
  18. "App License (default 'MIT')"):
  19. hook_key = key.split(" (")[0].lower().replace(" ", "_")
  20. hook_val = None
  21. while not hook_val:
  22. hook_val = cstr(input(key + ": "))
  23. if not hook_val:
  24. defaults = {
  25. "app_title": app_title,
  26. "app_icon": "octicon octicon-file-directory",
  27. "app_color": "grey",
  28. "app_license": "MIT"
  29. }
  30. if hook_key in defaults:
  31. hook_val = defaults[hook_key]
  32. if hook_key=="app_name" and hook_val.lower().replace(" ", "_") != hook_val:
  33. print("App Name must be all lowercase and without spaces")
  34. hook_val = ""
  35. elif hook_key=="app_title" and not re.match(r"^(?![\W])[^\d_\s][\w -]+$", hook_val, re.UNICODE):
  36. print("App Title should start with a letter and it can only consist of letters, numbers, spaces and underscores")
  37. hook_val = ""
  38. hooks[hook_key] = hook_val
  39. frappe.create_folder(os.path.join(dest, hooks.app_name, hooks.app_name, frappe.scrub(hooks.app_title)),
  40. with_init=True)
  41. frappe.create_folder(os.path.join(dest, hooks.app_name, hooks.app_name, "templates"), with_init=True)
  42. frappe.create_folder(os.path.join(dest, hooks.app_name, hooks.app_name, "www"))
  43. frappe.create_folder(os.path.join(dest, hooks.app_name, hooks.app_name, "templates",
  44. "pages"), with_init=True)
  45. frappe.create_folder(os.path.join(dest, hooks.app_name, hooks.app_name, "templates",
  46. "includes"))
  47. frappe.create_folder(os.path.join(dest, hooks.app_name, hooks.app_name, "config"), with_init=True)
  48. frappe.create_folder(os.path.join(dest, hooks.app_name, hooks.app_name, "public",
  49. "css"))
  50. frappe.create_folder(os.path.join(dest, hooks.app_name, hooks.app_name, "public",
  51. "js"))
  52. with open(os.path.join(dest, hooks.app_name, hooks.app_name, "__init__.py"), "w") as f:
  53. f.write(frappe.as_unicode(init_template))
  54. with open(os.path.join(dest, hooks.app_name, "MANIFEST.in"), "w") as f:
  55. f.write(frappe.as_unicode(manifest_template.format(**hooks)))
  56. with open(os.path.join(dest, hooks.app_name, ".gitignore"), "w") as f:
  57. f.write(frappe.as_unicode(gitignore_template.format(app_name = hooks.app_name)))
  58. with open(os.path.join(dest, hooks.app_name, "setup.py"), "w") as f:
  59. f.write(frappe.as_unicode(setup_template.format(**hooks)))
  60. with open(os.path.join(dest, hooks.app_name, "requirements.txt"), "w") as f:
  61. f.write("# frappe -- https://github.com/frappe/frappe is installed via 'bench init'")
  62. with open(os.path.join(dest, hooks.app_name, "README.md"), "w") as f:
  63. f.write(frappe.as_unicode("## {0}\n\n{1}\n\n#### License\n\n{2}".format(hooks.app_title,
  64. hooks.app_description, hooks.app_license)))
  65. with open(os.path.join(dest, hooks.app_name, "license.txt"), "w") as f:
  66. f.write(frappe.as_unicode("License: " + hooks.app_license))
  67. with open(os.path.join(dest, hooks.app_name, hooks.app_name, "modules.txt"), "w") as f:
  68. f.write(frappe.as_unicode(hooks.app_title))
  69. with open(os.path.join(dest, hooks.app_name, hooks.app_name, "hooks.py"), "w") as f:
  70. f.write(frappe.as_unicode(hooks_template.format(**hooks)))
  71. touch_file(os.path.join(dest, hooks.app_name, hooks.app_name, "patches.txt"))
  72. with open(os.path.join(dest, hooks.app_name, hooks.app_name, "config", "desktop.py"), "w") as f:
  73. f.write(frappe.as_unicode(desktop_template.format(**hooks)))
  74. with open(os.path.join(dest, hooks.app_name, hooks.app_name, "config", "docs.py"), "w") as f:
  75. f.write(frappe.as_unicode(docs_template.format(**hooks)))
  76. # initialize git repository
  77. app_directory = os.path.join(dest, hooks.app_name)
  78. app_repo = git.Repo.init(app_directory)
  79. app_repo.git.add(A=True)
  80. app_repo.index.commit("feat: Initialize App")
  81. print("'{app}' created at {path}".format(app=app_name, path=app_directory))
  82. manifest_template = """include MANIFEST.in
  83. include requirements.txt
  84. include *.json
  85. include *.md
  86. include *.py
  87. include *.txt
  88. recursive-include {app_name} *.css
  89. recursive-include {app_name} *.csv
  90. recursive-include {app_name} *.html
  91. recursive-include {app_name} *.ico
  92. recursive-include {app_name} *.js
  93. recursive-include {app_name} *.json
  94. recursive-include {app_name} *.md
  95. recursive-include {app_name} *.png
  96. recursive-include {app_name} *.py
  97. recursive-include {app_name} *.svg
  98. recursive-include {app_name} *.txt
  99. recursive-exclude {app_name} *.pyc"""
  100. init_template = """
  101. __version__ = '0.0.1'
  102. """
  103. hooks_template = """from . import __version__ as app_version
  104. app_name = "{app_name}"
  105. app_title = "{app_title}"
  106. app_publisher = "{app_publisher}"
  107. app_description = "{app_description}"
  108. app_icon = "{app_icon}"
  109. app_color = "{app_color}"
  110. app_email = "{app_email}"
  111. app_license = "{app_license}"
  112. # Includes in <head>
  113. # ------------------
  114. # include js, css files in header of desk.html
  115. # app_include_css = "/assets/{app_name}/css/{app_name}.css"
  116. # app_include_js = "/assets/{app_name}/js/{app_name}.js"
  117. # include js, css files in header of web template
  118. # web_include_css = "/assets/{app_name}/css/{app_name}.css"
  119. # web_include_js = "/assets/{app_name}/js/{app_name}.js"
  120. # include custom scss in every website theme (without file extension ".scss")
  121. # website_theme_scss = "{app_name}/public/scss/website"
  122. # include js, css files in header of web form
  123. # webform_include_js = {{"doctype": "public/js/doctype.js"}}
  124. # webform_include_css = {{"doctype": "public/css/doctype.css"}}
  125. # include js in page
  126. # page_js = {{"page" : "public/js/file.js"}}
  127. # include js in doctype views
  128. # doctype_js = {{"doctype" : "public/js/doctype.js"}}
  129. # doctype_list_js = {{"doctype" : "public/js/doctype_list.js"}}
  130. # doctype_tree_js = {{"doctype" : "public/js/doctype_tree.js"}}
  131. # doctype_calendar_js = {{"doctype" : "public/js/doctype_calendar.js"}}
  132. # Home Pages
  133. # ----------
  134. # application home page (will override Website Settings)
  135. # home_page = "login"
  136. # website user home page (by Role)
  137. # role_home_page = {{
  138. # "Role": "home_page"
  139. # }}
  140. # Generators
  141. # ----------
  142. # automatically create page for each record of this doctype
  143. # website_generators = ["Web Page"]
  144. # Jinja
  145. # ----------
  146. # add methods and filters to jinja environment
  147. # jinja = {{
  148. # "methods": "{app_name}.utils.jinja_methods",
  149. # "filters": "{app_name}.utils.jinja_filters"
  150. # }}
  151. # Installation
  152. # ------------
  153. # before_install = "{app_name}.install.before_install"
  154. # after_install = "{app_name}.install.after_install"
  155. # Desk Notifications
  156. # ------------------
  157. # See frappe.core.notifications.get_notification_config
  158. # notification_config = "{app_name}.notifications.get_notification_config"
  159. # Permissions
  160. # -----------
  161. # Permissions evaluated in scripted ways
  162. # permission_query_conditions = {{
  163. # "Event": "frappe.desk.doctype.event.event.get_permission_query_conditions",
  164. # }}
  165. #
  166. # has_permission = {{
  167. # "Event": "frappe.desk.doctype.event.event.has_permission",
  168. # }}
  169. # DocType Class
  170. # ---------------
  171. # Override standard doctype classes
  172. # override_doctype_class = {{
  173. # "ToDo": "custom_app.overrides.CustomToDo"
  174. # }}
  175. # Document Events
  176. # ---------------
  177. # Hook on document methods and events
  178. # doc_events = {{
  179. # "*": {{
  180. # "on_update": "method",
  181. # "on_cancel": "method",
  182. # "on_trash": "method"
  183. # }}
  184. # }}
  185. # Scheduled Tasks
  186. # ---------------
  187. # scheduler_events = {{
  188. # "all": [
  189. # "{app_name}.tasks.all"
  190. # ],
  191. # "daily": [
  192. # "{app_name}.tasks.daily"
  193. # ],
  194. # "hourly": [
  195. # "{app_name}.tasks.hourly"
  196. # ],
  197. # "weekly": [
  198. # "{app_name}.tasks.weekly"
  199. # ],
  200. # "monthly": [
  201. # "{app_name}.tasks.monthly"
  202. # ],
  203. # }}
  204. # Testing
  205. # -------
  206. # before_tests = "{app_name}.install.before_tests"
  207. # Overriding Methods
  208. # ------------------------------
  209. #
  210. # override_whitelisted_methods = {{
  211. # "frappe.desk.doctype.event.event.get_events": "{app_name}.event.get_events"
  212. # }}
  213. #
  214. # each overriding function accepts a `data` argument;
  215. # generated from the base implementation of the doctype dashboard,
  216. # along with any modifications made in other Frappe apps
  217. # override_doctype_dashboards = {{
  218. # "Task": "{app_name}.task.get_dashboard_data"
  219. # }}
  220. # exempt linked doctypes from being automatically cancelled
  221. #
  222. # auto_cancel_exempted_doctypes = ["Auto Repeat"]
  223. # User Data Protection
  224. # --------------------
  225. # user_data_fields = [
  226. # {{
  227. # "doctype": "{{doctype_1}}",
  228. # "filter_by": "{{filter_by}}",
  229. # "redact_fields": ["{{field_1}}", "{{field_2}}"],
  230. # "partial": 1,
  231. # }},
  232. # {{
  233. # "doctype": "{{doctype_2}}",
  234. # "filter_by": "{{filter_by}}",
  235. # "partial": 1,
  236. # }},
  237. # {{
  238. # "doctype": "{{doctype_3}}",
  239. # "strict": False,
  240. # }},
  241. # {{
  242. # "doctype": "{{doctype_4}}"
  243. # }}
  244. # ]
  245. # Authentication and authorization
  246. # --------------------------------
  247. # auth_hooks = [
  248. # "{app_name}.auth.validate"
  249. # ]
  250. """
  251. desktop_template = """from frappe import _
  252. def get_data():
  253. return [
  254. {{
  255. "module_name": "{app_title}",
  256. "color": "{app_color}",
  257. "icon": "{app_icon}",
  258. "type": "module",
  259. "label": _("{app_title}")
  260. }}
  261. ]
  262. """
  263. setup_template = """from setuptools import setup, find_packages
  264. with open('requirements.txt') as f:
  265. install_requires = f.read().strip().split('\\n')
  266. # get version from __version__ variable in {app_name}/__init__.py
  267. from {app_name} import __version__ as version
  268. setup(
  269. name='{app_name}',
  270. version=version,
  271. description='{app_description}',
  272. author='{app_publisher}',
  273. author_email='{app_email}',
  274. packages=find_packages(),
  275. zip_safe=False,
  276. include_package_data=True,
  277. install_requires=install_requires
  278. )
  279. """
  280. gitignore_template = """.DS_Store
  281. *.pyc
  282. *.egg-info
  283. *.swp
  284. tags
  285. {app_name}/docs/current"""
  286. docs_template = '''"""
  287. Configuration for docs
  288. """
  289. # source_link = "https://github.com/[org_name]/{app_name}"
  290. # docs_base_url = "https://[org_name].github.io/{app_name}"
  291. # headline = "App that does everything"
  292. # sub_heading = "Yes, you got that right the first time, everything"
  293. def get_context(context):
  294. context.brand_html = "{app_title}"
  295. '''