# Coverage for api/cmd.py : 0%

## 189 statements

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"""

2 commandline utilities functions

3"""

4 import importlib

5 import os

6 import re

7 import subprocess

8 import sys

9

10 import click

11 from flask.cli import FlaskGroup

12 from flask.cli import pass_script_info

13 from init import db

14 from init import modules_path

15 from init import root_path

16 from init import static_path

17

18 from shopyo.api.cmd_helper import tryrmcache

19 from shopyo.api.cmd_helper import tryrmfile

20 from shopyo.api.cmd_helper import tryrmtree

21 from shopyo.api.file import get_folders

22 from shopyo.api.file import trycopytree

23 from shopyo.api.file import trymkdir

24 from shopyo.api.file import trymkfile

25 from shopyo.api.info import printinfo

26 from shopyo.app import create_app

27

28

29 def _create_shopyo_app(info):

30 config_name = info.data.get("config") or "development"

31 return create_app(config_name=config_name)

32

33

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

37 def cli(info, **parmams):

38 """CLI for shopyo"""

39 printinfo()

40 pass

41

42

43@cli.command("new")

44 def new(help="shopyo new or shopyo new <folder>"):

45 pass

46

47

48 def 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.

53

54 Parameters

55 ----------

56 - app: flask app that that need to be cleaned

57

58 Returns

59 -------

60 None

61 ...

62

63 """

64 SEP_CHAR = "#"

65 SEP_NUM = 23

66

67 print(SEP_CHAR * SEP_NUM, end="\n\n")

68 print("Cleaning...")

69

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")

75

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)

79

80

81 def initialise():

82 """

83 Create db, migrate, adds default users, add settings

84

85 Parameters

86 ----------

87

88

89 Returns

90 -------

91 None

92

93

94 """

95 SEP_CHAR = "#"

96 SEP_NUM = 23

97

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)

101

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 )

110

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 )

116

117 # Uploads

118 print("Uploads")

119 print(SEP_CHAR * SEP_NUM, end="\n\n")

120

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

131

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

148

149 print("Done!")

150

151

152 def create_module(modulename, base_path=None):

153 """

154 creates module with the structure defined in the modules section in docs

155

156 Parameters

157 ----------

158 modulename: str

159 name of module, in alphanumeric-underscore

160

161 Returns

162 -------

163 None

164

165

166 """

167

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 = """

183 Please add your functional tests to this file.

184"""

185 test_model_content = """

186 Please 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 = """

191 from 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

197

198# from shopyo.api.html import notify_success

199# from shopyo.api.forms import flash_errors

200

201 mhelp = ModuleHelp(__file__, __name__)

202 globals()[mhelp.blueprint_str] = mhelp.blueprint

203 module_blueprint = globals()[mhelp.blueprint_str]

204

205@module_blueprint.route("/")

206 def index():

207 return mhelp.info['display_string']

208

209# If "dashboard": "/dashboard" is set in info.json

210#

211# @module_blueprint.route("/dashboard", methods=["GET"])

212# def dashboard():

213

214# context = mhelp.context()

215

216# context.update({

217

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)

239

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>

255

256<div class="card">

257 <div class="card-body">

258

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 = """

268 available_everywhere = {

269

270}

271"""

272 trymkfile(f"{base_path}/global.py", global_file_content)

273

274

275 def create_box(name):

276 """

277 creates box with box_info.json

278

279 Parameters

280 ----------

281 name: str

282 name of box, in alphanumeric-underscore

283

284 Returns

285 -------

286 None

287

288

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)

307

308

309 def create_module_in_box(modulename, boxname):

310 """

311 creates module with the structure defined in the modules section in docs in

312 a box

313

314 Parameters

315 ----------

316 modulename: str

317 name of module, in alphanumeric-underscore

318

319 boxname: str

320 name of box, in alphanumeric-underscore

321

322 Returns

323 -------

324 None

325

326

327 """

328 box_path = os.path.join("modules", boxname)

329 module_path = os.path.join("modules", boxname, modulename)

330

331 if not boxname.startswith("box__"):

332 print(f"Invalid box {boxname}. Boxes should start with box__")

333

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}")

340

341 elif os.path.exists(module_path):

342 print(f"Module {module_path} exists")

343

344 else:

345 print(f"Creating module {module_path}")

346 create_module(modulename, base_path=module_path)

347

348

349 def createmodulebox(string):

350 # todo: add a proper result class

351 exit = True

352 message = ""

353

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]

362

363 create_module_in_box(modulename, boxname)

364

365 elif string.startswith("box__"):

366 create_box(string)

367

368 else:

369 create_module(string)

370

371 message = "Created successfully!"

372 return [exit, message]

373

374

375 def collect_static(target_module="modules", verbose=False):

376

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)

397

398 root_path = os.getcwd()

399 static_path = os.path.join(root_path, "static")

400

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":

404

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)

409

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)

414

415 # get the full path for modules (the src). Defaults to ./modules

416 modules_path = os.path.join(root_path, target_module)

417

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")

421

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)

426

427 # clear ./static/modules before coping to it

428 tryrmtree(modules_path_in_static, verbose=verbose)

429

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 )

442

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 = ""

448

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]

454

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)

461

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)

467

468 click.echo("")

469

470

471 if __name__ == "__main__":

472 cli()