Coverage for api/cmd.py : 0%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1"""
2commandline utilities functions
3"""
4import importlib
5import os
6import re
7import subprocess
8import sys
10import click
11from flask.cli import FlaskGroup
12from flask.cli import pass_script_info
13from init import db
14from init import modules_path
15from init import root_path
16from init import static_path
18from shopyo.api.cmd_helper import tryrmcache
19from shopyo.api.cmd_helper import tryrmfile
20from shopyo.api.cmd_helper import tryrmtree
21from shopyo.api.file import get_folders
22from shopyo.api.file import trycopytree
23from shopyo.api.file import trymkdir
24from shopyo.api.file import trymkfile
25from shopyo.api.info import printinfo
26from shopyo.app import create_app
29def _create_shopyo_app(info):
30 config_name = info.data.get("config") or "development"
31 return create_app(config_name=config_name)
34@click.group(cls=FlaskGroup, create_app=_create_shopyo_app)
35@click.option("--config", default="development", help="Flask app configuration type")
36@pass_script_info
37def cli(info, **parmams):
38 """CLI for shopyo"""
39 printinfo()
40 pass
43@cli.command("new")
44def new(help="shopyo new or shopyo new <folder>"):
45 pass
48def clean(app):
49 """
50 Deletes shopyo.db and migrations/ if present in current working directory.
51 Deletes all __pycache__ folders starting from current working directory
52 all the way to leaf directory.
54 Parameters
55 ----------
56 - app: flask app that that need to be cleaned
58 Returns
59 -------
60 None
61 ...
63 """
64 SEP_CHAR = "#"
65 SEP_NUM = 23
67 print(SEP_CHAR * SEP_NUM, end="\n\n")
68 print("Cleaning...")
70 # getting app context creates the shopyo.db file even if it is not present
71 with app.test_request_context():
72 db.drop_all()
73 db.engine.execute("DROP TABLE IF EXISTS alembic_version;")
74 print("[x] all tables dropped")
76 tryrmcache(os.getcwd())
77 tryrmfile(os.path.join(os.getcwd(), "shopyo.db"), verbose=True)
78 tryrmtree(os.path.join(os.getcwd(), "migrations"), verbose=True)
81def initialise():
82 """
83 Create db, migrate, adds default users, add settings
85 Parameters
86 ----------
89 Returns
90 -------
91 None
94 """
95 SEP_CHAR = "#"
96 SEP_NUM = 23
98 print("Creating Db")
99 print(SEP_CHAR * SEP_NUM, end="\n\n")
100 subprocess.run([sys.executable, "manage.py", "db", "init"], stdout=subprocess.PIPE)
102 print("Migrating")
103 print(SEP_CHAR * SEP_NUM, end="\n\n")
104 subprocess.run(
105 [sys.executable, "manage.py", "db", "migrate"], stdout=subprocess.PIPE
106 )
107 subprocess.run(
108 [sys.executable, "manage.py", "db", "upgrade"], stdout=subprocess.PIPE
109 )
111 print("Collecting static")
112 print(SEP_CHAR * SEP_NUM, end="\n\n")
113 subprocess.run(
114 [sys.executable, "manage.py", "collectstatic"], stdout=subprocess.PIPE
115 )
117 # Uploads
118 print("Uploads")
119 print(SEP_CHAR * SEP_NUM, end="\n\n")
121 for folder in os.listdir(os.path.join(root_path, "modules")):
122 if folder.startswith("__"): # ignore __pycache__
123 continue
124 if folder.startswith("box__"):
125 # boxes
126 for sub_folder in os.listdir(os.path.join(root_path, "modules", folder)):
127 if sub_folder.startswith("__"): # ignore __pycache__
128 continue
129 elif sub_folder.endswith(".json"): # box_info.json
130 continue
132 try:
133 upload = importlib.import_module(
134 f"modules.{folder}.{sub_folder}.upload"
135 )
136 upload.upload()
137 except ImportError as e:
138 # print(e)
139 pass
140 else:
141 # apps
142 try:
143 upload = importlib.import_module(f"modules.{folder}.upload")
144 upload.upload()
145 except ImportError as e:
146 # print(e)
147 pass
149 print("Done!")
152def create_module(modulename, base_path=None):
153 """
154 creates module with the structure defined in the modules section in docs
156 Parameters
157 ----------
158 modulename: str
159 name of module, in alphanumeric-underscore
161 Returns
162 -------
163 None
166 """
168 if bool(re.match(r"^[A-Za-z0-9_]+$", modulename)) is False:
169 print(
170 "Error: modulename is not valid, please use alphanumeric and"
171 " underscore only"
172 )
173 sys.exit()
174 print(f"creating module: {modulename}")
175 if not base_path:
176 base_path = f"modules/{modulename}"
177 trymkdir(base_path)
178 trymkdir(f"{base_path}/templates")
179 trymkdir(f"{base_path}/templates/{modulename}")
180 trymkdir(f"{base_path}/tests")
181 trymkdir(f"{base_path}/static")
182 test_func_content = """
183Please add your functional tests to this file.
184"""
185 test_model_content = """
186Please add your models tests to this file.
187"""
188 trymkfile(f"{base_path}/tests/test_{modulename}_functional.py", test_func_content)
189 trymkfile(f"{base_path}/tests/test_{modulename}_models.py", test_model_content)
190 view_content = """
191from shopyo.api.module import ModuleHelp
192# from flask import render_template
193# from flask import url_for
194# from flask import redirect
195# from flask import flash
196# from flask import request
198# from shopyo.api.html import notify_success
199# from shopyo.api.forms import flash_errors
201mhelp = ModuleHelp(__file__, __name__)
202globals()[mhelp.blueprint_str] = mhelp.blueprint
203module_blueprint = globals()[mhelp.blueprint_str]
205@module_blueprint.route("/")
206def index():
207 return mhelp.info['display_string']
209# If "dashboard": "/dashboard" is set in info.json
210#
211# @module_blueprint.route("/dashboard", methods=["GET"])
212# def dashboard():
214# context = mhelp.context()
216# context.update({
218# })
219# return mhelp.render('dashboard.html', **context)
220"""
221 trymkfile(f"{base_path}/view.py", view_content)
222 trymkfile(f"{base_path}/forms.py", "")
223 trymkfile(f"{base_path}/models.py", "")
224 info_json_content = """{{
225 "display_string": "{0}",
226 "module_name":"{1}",
227 "type": "show",
228 "fa-icon": "fa fa-store",
229 "url_prefix": "/{1}",
230 "author": {{
231 "name":"",
232 "website":"",
233 "mail":""
234 }}
235}}""".format(
236 modulename.capitalize(), modulename
237 )
238 trymkfile(f"{base_path}/info.json", info_json_content)
240 trymkdir(f"{base_path}/templates/{modulename}/blocks")
241 trymkfile(f"{base_path}/templates/{modulename}/blocks/sidebar.html", "")
242 dashboard_file_content = """
243{% extends "base/module_base.html" %}
244{% set active_page = info['display_string']+' dashboard' %}
245{% block pagehead %}
246<title></title>
247<style>
248</style>
249{% endblock %}
250{% block sidebar %}
251{% include info['module_name']+'/blocks/sidebar.html' %}
252{% endblock %}
253{% block content %}
254<br>
256<div class="card">
257 <div class="card-body">
259 </div>
260 </div>
261{% endblock %}
262"""
263 trymkfile(
264 f"{base_path}/templates/{modulename}/dashboard.html",
265 dashboard_file_content,
266 )
267 global_file_content = """
268available_everywhere = {
270}
271"""
272 trymkfile(f"{base_path}/global.py", global_file_content)
275def create_box(name):
276 """
277 creates box with box_info.json
279 Parameters
280 ----------
281 name: str
282 name of box, in alphanumeric-underscore
284 Returns
285 -------
286 None
289 """
290 base_path = f"modules/box__{name}"
291 if os.path.exists(base_path):
292 print(f"Box {base_path} exists!")
293 else:
294 trymkdir(base_path)
295 info_json_content = """{{
296 "display_string": "{0}",
297 "box_name":"{1}",
298 "author": {{
299 "name":"",
300 "website":"",
301 "mail":""
302 }}
303 }}""".format(
304 name.capitalize(), name
305 )
306 trymkfile(f"{base_path}/box_info.json", info_json_content)
309def create_module_in_box(modulename, boxname):
310 """
311 creates module with the structure defined in the modules section in docs in
312 a box
314 Parameters
315 ----------
316 modulename: str
317 name of module, in alphanumeric-underscore
319 boxname: str
320 name of box, in alphanumeric-underscore
322 Returns
323 -------
324 None
327 """
328 box_path = os.path.join("modules", boxname)
329 module_path = os.path.join("modules", boxname, modulename)
331 if not boxname.startswith("box__"):
332 print(f"Invalid box {boxname}. Boxes should start with box__")
334 elif not os.path.exists(box_path):
335 print(f"Box {box_path} does not exists!")
336 available_boxes = "\n* ".join(
337 [f for f in os.listdir("modules/") if f.startswith("box__")]
338 )
339 print(f"Available boxes: \n* {available_boxes}")
341 elif os.path.exists(module_path):
342 print(f"Module {module_path} exists")
344 else:
345 print(f"Creating module {module_path}")
346 create_module(modulename, base_path=module_path)
349def createmodulebox(string):
350 # todo: add a proper result class
351 exit = True
352 message = ""
354 if "/" in string:
355 if string.count("/") != 1:
356 exit = False
357 message = "more than one / found in argument"
358 return [exit, message]
359 elif string.count("/") == 1:
360 boxname = string.split("/")[0]
361 modulename = string.split("/")[1]
363 create_module_in_box(modulename, boxname)
365 elif string.startswith("box__"):
366 create_box(string)
368 else:
369 create_module(string)
371 message = "Created successfully!"
372 return [exit, message]
375def collect_static(target_module="modules", verbose=False):
377 """
378 Copies ``module/static`` into ``/static/modules/module``.
379 In static it becomes like
380 ::
381 static/
382 modules/
383 box_something/
384 modulename
385 modulename2
386 Parameters
387 ----------
388 target_module: str
389 name of module, in alphanumeric-underscore,
390 supports ``module`` or ``box__name/module``
391 Returns
392 -------
393 None
394 """
395 click.echo("Collecting static...")
396 click.echo(SEP_CHAR * SEP_NUM)
398 root_path = os.getcwd()
399 static_path = os.path.join(root_path, "static")
401 # if target_module path does not start with 'modules\' add it to as a
402 # prefix to the target_module path
403 if target_module != "modules":
405 # normalize the target_module path to be same as that of OS
406 target_module = re.split(r"[/|\\]+", target_module)
407 target_module_start = target_module[0]
408 target_module = os.path.join(*target_module)
410 # add the modules folder to start of target_module incase it is not
411 # already present in the path
412 if target_module_start != "modules":
413 target_module = os.path.join("modules", target_module)
415 # get the full path for modules (the src). Defaults to ./modules
416 modules_path = os.path.join(root_path, target_module)
418 # get the full path of static folder to copy to (the dest).
419 # always ./static/modules
420 modules_path_in_static = os.path.join(static_path, "modules")
422 # terminate if modules_path (i.e. src to copy static from) does not exist
423 if not os.path.exists(modules_path):
424 click.echo(f"[ ] path: {modules_path} does not exist")
425 sys.exit(1)
427 # clear ./static/modules before coping to it
428 tryrmtree(modules_path_in_static, verbose=verbose)
430 # look for static folders in all project
431 for folder in get_folders(modules_path):
432 if folder.startswith("box__"):
433 box_path = os.path.join(modules_path, folder)
434 for subfolder in get_folders(box_path):
435 module_name = subfolder
436 module_static_folder = os.path.join(box_path, subfolder, "static")
437 if not os.path.exists(module_static_folder):
438 continue
439 module_in_static_dir = os.path.join(
440 modules_path_in_static, folder, module_name
441 )
443 # copy from ./modules/<box__name>/<submodule> to
444 # ./static/modules
445 trycopytree(module_static_folder, module_in_static_dir, verbose=verbose)
446 else:
447 path_split = ""
449 # split the target module if default target_module path name is
450 # not used
451 if target_module != "modules":
452 path_split = re.split(r"[/|\\]", target_module, maxsplit=1)
453 path_split = path_split[1]
455 if folder.lower() == "static":
456 module_static_folder = os.path.join(modules_path, folder)
457 module_name = path_split
458 else:
459 module_static_folder = os.path.join(modules_path, folder, "static")
460 module_name = os.path.join(path_split, folder)
462 if not os.path.exists(module_static_folder):
463 continue
464 module_in_static_dir = os.path.join(modules_path_in_static, module_name)
465 tryrmtree(module_in_static_dir, verbose=verbose)
466 trycopytree(module_static_folder, module_in_static_dir, verbose=verbose)
468 click.echo("")
471if __name__ == "__main__":
472 cli()