Hide keyboard shortcuts

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 

9 

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 

17 

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 

27 

28 

29def _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 

37def cli(info, **parmams): 

38 """CLI for shopyo""" 

39 printinfo() 

40 pass 

41 

42 

43@cli.command("new") 

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

45 pass 

46 

47 

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. 

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 

81def 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 

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

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 

197 

198# from shopyo.api.html import notify_success 

199# from shopyo.api.forms import flash_errors 

200 

201mhelp = ModuleHelp(__file__, __name__) 

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

203module_blueprint = globals()[mhelp.blueprint_str] 

204 

205@module_blueprint.route("/") 

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

268available_everywhere = { 

269 

270} 

271""" 

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

273 

274 

275def 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 

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 

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 

349def 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 

375def 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 

471if __name__ == "__main__": 

472 cli()