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# !/usr/local/bin/python 

2# encoding: utf-8 

3""" 

4*The code for the frankenstein package* 

5 

6:Author: 

7 David Young 

8""" 

9from __future__ import print_function 

10from builtins import zip 

11from builtins import input 

12from builtins import str 

13from builtins import object 

14import sys 

15import os 

16os.environ['TERM'] = 'vt100' 

17import readline 

18import glob 

19import pickle 

20import shutil 

21import codecs 

22import re 

23from subprocess import Popen, PIPE, STDOUT 

24from docopt import docopt 

25from fundamentals import tools, times 

26from fundamentals.files import recursive_directory_listing 

27 

28class electric(object): 

29 """ 

30 *The worker class for the electric module* 

31 

32 **Key Arguments** 

33 

34 - ``log`` -- logger 

35 - ``settings`` -- the settings dictionary 

36 - ``pathToTemplate`` -- path to the template folder/file 

37 - ``pathToDestination`` -- path to where template should be cloned 

38 - ``ignoreExisting`` - - ignore existing files in the destination for the template 

39  

40 """ 

41 # Initialisation 

42 

43 def __init__( 

44 self, 

45 log, 

46 pathToTemplate, 

47 pathToDestination, 

48 settings=False, 

49 ignoreExisting=False 

50 ): 

51 self.log = log 

52 log.debug("instansiating a new 'electric' object") 

53 self.settings = settings 

54 self.pathToTemplate = pathToTemplate 

55 self.pathToDestination = pathToDestination 

56 self.ignoreExisting = ignoreExisting 

57 # xt-self-arg-tmpx 

58 if self.pathToTemplate[-1] == "/": 

59 self.pathToTemplate = self.pathToTemplate[:-1] 

60 

61 return None 

62 

63 def get(self): 

64 """ 

65 *do the frankenstein magic!* 

66 """ 

67 self.log.debug('starting the ``get`` method') 

68 

69 self._copy_folder_and_get_directory_listings() 

70 self._join_all_filenames_and_text() 

71 self._collect_placeholders_required() 

72 self._populate_dynamic_placeholders() 

73 self._fill_placeholders_from_settings() 

74 self._request_remaining_placeholders() 

75 self._populate_placeholders_in_files() 

76 self._move_template_to_destination(ignoreExisting=self.ignoreExisting) 

77 

78 self.log.debug('completed the ``get`` method') 

79 return None 

80 

81 def list_placeholders(self): 

82 """ 

83 *list the remaining placeholders required by frankenstein* 

84 """ 

85 self.log.debug('starting the ``list_placeholders`` method') 

86 

87 self._copy_folder_and_get_directory_listings() 

88 self._join_all_filenames_and_text() 

89 self._collect_placeholders_required() 

90 self._populate_dynamic_placeholders() 

91 self._fill_placeholders_from_settings() 

92 placeholders = self._list_remaining_placeholders() 

93 

94 self.log.debug('completed the ``list_placeholders`` method') 

95 return placeholders 

96 

97 def _copy_folder_and_get_directory_listings( 

98 self): 

99 """ 

100 *copy template folder to /tmp and get directory listings* 

101 """ 

102 self.log.debug( 

103 'completed the ````_copy_folder_and_get_directory_listings`` method') 

104 

105 # COPY TEMPLATE STRUCTURE TO /tmp/ 

106 basename = os.path.basename(self.pathToTemplate) 

107 tmpPath = "/tmp/%(basename)s" % locals() 

108 try: 

109 shutil.rmtree(tmpPath) 

110 except: 

111 pass 

112 shutil.copytree(self.pathToTemplate, tmpPath) 

113 

114 directoryContents = recursive_directory_listing( 

115 log=self.log, 

116 baseFolderPath=tmpPath, 

117 whatToList="all" # all | files | dirs 

118 ) 

119 

120 self.directoryContents = directoryContents 

121 self.tmpPath = tmpPath 

122 

123 self.log.debug( 

124 'completed the ``_copy_folder_and_get_directory_listings`` method') 

125 return None 

126 

127 def _collect_placeholders_required( 

128 self): 

129 """ 

130 *collect placeholders required from filename etc* 

131 """ 

132 self.log.debug( 

133 'starting the ``_collect_placeholders_required`` method') 

134 

135 phs = self.settings["frankenstein"]["placeholder delimiters"] 

136 phsString = "|".join(phs) 

137 

138 matchObject = re.finditer( 

139 r'(%(phsString)s)([^\s]*?)\1' % locals(), 

140 string=self.contentString, 

141 flags=re.S # re.S 

142 ) 

143 

144 phDict = {} 

145 for match in matchObject: 

146 if len(match.group(2).strip()) > 0: 

147 phDict[match.group(2)] = None 

148 

149 self.phDict = phDict 

150 

151 self.log.debug( 

152 'completed the ``_collect_placeholders_required`` method') 

153 return None 

154 

155 def _join_all_filenames_and_text( 

156 self): 

157 """ 

158 *join all file names, driectory names and text content together* 

159 """ 

160 self.log.debug('starting the ``_join_all_filenames_and_text`` method') 

161 

162 contentString = u"" 

163 for i in self.directoryContents: 

164 contentString += u"%(i)s\n" % locals() 

165 if os.path.isfile(os.path.join(i)): 

166 if i[-4:] in [".png", ".jpg", ".gif"]: 

167 continue 

168 readFile = codecs.open(i, encoding='ISO-8859-1', mode='r') 

169 if ".DS_Store" in i: 

170 continue 

171 data = readFile.read() 

172 contentString += u"%(data)s\n" % locals() 

173 readFile.close() 

174 

175 self.contentString = contentString 

176 

177 self.log.debug('completed the ``_join_all_filenames_and_text`` method') 

178 return None 

179 

180 def _populate_dynamic_placeholders( 

181 self): 

182 """ 

183 *populate dynamic placeholders - times etc* 

184 """ 

185 self.log.debug( 

186 'starting the ``_populate_dynamic_placeholders`` method') 

187 

188 from datetime import datetime, date, time 

189 now = datetime.now() 

190 

191 dynamicPhs = { 

192 "now-ymd": now.strftime("%Y%m%d"), 

193 "now-ymdhms": now.strftime("%Y%m%dt%H%M%S"), 

194 "now-date": now.strftime("%B %e, %Y"), 

195 "now-datetime": now.strftime("%B %e, %Y %I:%M ") + now.strftime("%p").lower(), 

196 "now-time": now.strftime("%I:%M ") + now.strftime("%p").lower(), 

197 "now-hms": now.strftime("%H%M%S"), 

198 "now-year": now.strftime("%Y") 

199 } 

200 

201 for k, v in list(self.phDict.items()): 

202 if k in list(dynamicPhs.keys()): 

203 self.phDict[k] = dynamicPhs[k] 

204 

205 self.log.debug( 

206 'completed the ``_populate_dynamic_placeholders`` method') 

207 return None 

208 

209 def _fill_placeholders_from_settings( 

210 self): 

211 """ 

212 *fill placeholders from the placeholders in the settings file* 

213 """ 

214 self.log.debug( 

215 'completed the ````_fill_placeholders_from_settings`` method') 

216 

217 for k, v in list(self.phDict.items()): 

218 if k in list(self.settings["frankenstein"]["fixed placeholders"].keys()): 

219 self.phDict[k] = self.settings[ 

220 "frankenstein"]["fixed placeholders"][k] 

221 

222 self.log.debug( 

223 'completed the ``_fill_placeholders_from_settings`` method') 

224 return None 

225 

226 def _request_remaining_placeholders( 

227 self): 

228 """ 

229 *request remaining placeholders needing populated from the user* 

230 """ 

231 self.log.debug( 

232 'completed the ````_request_remaining_placeholders`` method') 

233 

234 phNeeded = False 

235 for k, v in list(self.phDict.items()): 

236 if not v: 

237 phNeeded = True 

238 

239 if phNeeded == False: 

240 return 

241 

242 print("please add your placeholder values ...") 

243 

244 for k, v in list(self.phDict.items()): 

245 if not v: 

246 v = input("%(k)s? > " % locals()) 

247 self.phDict[k] = v 

248 

249 self.log.debug( 

250 'completed the ``_request_remaining_placeholders`` method') 

251 return None 

252 

253 def _list_remaining_placeholders( 

254 self): 

255 """ 

256 *list the remaining placeholders needing populated from the user* 

257 """ 

258 self.log.debug( 

259 'completed the ````_list_remaining_placeholders`` method') 

260 

261 remainingPlaceholders = [] 

262 for k, v in list(self.phDict.items()): 

263 if not v: 

264 remainingPlaceholders.append(k) 

265 

266 self.log.debug( 

267 'completed the ``_list_remaining_placeholders`` method') 

268 return remainingPlaceholders 

269 

270 def _populate_placeholders_in_files( 

271 self): 

272 """ 

273 *populate placeholders in file names, folder names and content* 

274 """ 

275 self.log.debug( 

276 'completed the ````_populate_placeholders_in_files`` method') 

277 

278 rev = reversed(self.directoryContents) 

279 phs = self.settings["frankenstein"]["placeholder delimiters"] 

280 

281 # FILE CONTENT FIRST 

282 for i in self.directoryContents: 

283 if os.path.isfile(i): 

284 pathToReadFile = i 

285 if ".DS_Store" in i: 

286 os.remove(i) 

287 continue 

288 try: 

289 self.log.debug( 

290 "attempting to open the file %s" % (pathToReadFile,)) 

291 if i[-4:] in [".png", ".jpg", ".gif"]: 

292 continue 

293 readFile = codecs.open( 

294 pathToReadFile, encoding='ISO-8859-1', mode='r') 

295 thisData = readFile.read() 

296 readFile.close() 

297 except IOError as e: 

298 message = 'could not open the file %s' % (pathToReadFile,) 

299 self.log.critical(message) 

300 raise IOError(message) 

301 

302 newContent = thisData 

303 for k, v in list(self.phDict.items()): 

304 for ph in phs: 

305 fullPH = ph + k + ph 

306 if fullPH in thisData: 

307 newContent = newContent.replace(fullPH, v) 

308 

309 if newContent != thisData: 

310 writeFile = codecs.open( 

311 pathToReadFile, encoding='ISO-8859-1', mode='w') 

312 writeFile.write(newContent) 

313 writeFile.close() 

314 

315 # NOW FILE NAMES 

316 for i in self.directoryContents: 

317 if os.path.isfile(i): 

318 newPath = i 

319 newFile = i.split("/")[-1] 

320 for k, v in list(self.phDict.items()): 

321 for ph in phs: 

322 fullPH = ph + k + ph 

323 if fullPH in newFile: 

324 newFile = newFile.replace(fullPH, v) 

325 newPath = "/".join(i.split("/") 

326 [:-1]) + "/" + newFile 

327 if newPath != i: 

328 try: 

329 self.log.debug("attempting to rename file %s to %s" % 

330 (i, newPath)) 

331 shutil.move(i, newPath) 

332 except Exception as e: 

333 self.log.error( 

334 "could not rename file %s to %s - failed with this error: %s " % (i, newPath, str(e),)) 

335 sys.exit(0) 

336 

337 # NOW DRIECTORY NAMES 

338 theseDirs = [] 

339 for i in reversed(self.directoryContents): 

340 if os.path.isdir(i): 

341 theseDirs.append(i) 

342 for i in theseDirs: 

343 newPath = i 

344 newFolder = i.split("/")[-1] 

345 for k, v in list(self.phDict.items()): 

346 for ph in phs: 

347 fullPH = ph + k + ph 

348 if fullPH in newFolder: 

349 newFolder = newFolder.replace(fullPH, v) 

350 newPath = "/".join(i.split("/")[:-1]) + "/" + newFolder 

351 if newPath != i: 

352 try: 

353 self.log.debug("attempting to rename file %s to %s" % 

354 (i, newPath)) 

355 shutil.move(i, newPath) 

356 except Exception as e: 

357 self.log.error( 

358 "could not rename file %s to %s - failed with this error: %s " % (i, newPath, str(e),)) 

359 sys.exit(0) 

360 

361 self.log.debug( 

362 'completed the ``_populate_placeholders_in_files`` method') 

363 return None 

364 

365 # use the tab-trigger below for new method 

366 def _move_template_to_destination( 

367 self, 

368 ignoreExisting=False): 

369 """ 

370 *move template to destination* 

371 

372 **Key Arguments** 

373 

374 # - 

375  

376 

377 **Return** 

378 

379 - None 

380  

381 

382 .. todo:: 

383 

384 - @review: when complete, clean _move_template_to_destination method 

385 - @review: when complete add logging 

386 """ 

387 self.log.debug('starting the ``_move_template_to_destination`` method') 

388 

389 # CREATE DIRECTORY STRUCTURE 

390 sourceDirectories = recursive_directory_listing( 

391 log=self.log, 

392 baseFolderPath=self.tmpPath, 

393 whatToList="dirs" # all | files | dirs 

394 ) 

395 

396 destinationDirectories = [] 

397 destinationDirectories = [] 

398 destinationDirectories[:] = [self.pathToDestination + 

399 d.replace(self.tmpPath, "") for d in sourceDirectories] 

400 for d in destinationDirectories: 

401 # Recursively create missing directories 

402 if not os.path.exists(d): 

403 os.makedirs(d) 

404 

405 # CREATE NEW FILES 

406 sourceFiles = recursive_directory_listing( 

407 log=self.log, 

408 baseFolderPath=self.tmpPath, 

409 whatToList="files" # all | files | dirs 

410 ) 

411 destinationFiles = [] 

412 destinationFiles = [] 

413 destinationFiles[:] = [self.pathToDestination + 

414 d.replace(self.tmpPath, "") for d in sourceFiles] 

415 

416 appendText = "" 

417 for s, f in zip(sourceFiles, destinationFiles): 

418 try: 

419 readFile = codecs.open(f, encoding='ISO-8859-1', mode='r') 

420 content = readFile.read() 

421 readFile.close() 

422 fileExists = True 

423 except IOError: 

424 fileExists = False 

425 if fileExists == True and len(content) > 1 and ignoreExisting == False: 

426 readFile = codecs.open(s, encoding='ISO-8859-1', mode='r') 

427 content = readFile.read() 

428 readFile.close() 

429 appendText += """ 

430## `%(f)s` 

431 

432``` 

433%(content)s  

434``` 

435 

436""" % locals() 

437 else: 

438 try: 

439 if ignoreExisting == False or (ignoreExisting == True and fileExists == False): 

440 self.log.debug("attempting to rename file %s to %s" % 

441 (s, f)) 

442 shutil.move(s, f) 

443 else: 

444 pass 

445 except Exception as e: 

446 self.log.error("could not rename file %s to %s - failed with this error: %s " % 

447 (s, f, str(e),)) 

448 sys.exit(0) 

449 

450 # CREATE NOTE TO USER TO APPEND TEXT 

451 if len(appendText) > 3: 

452 appendText = """ 

453# Text to Append to Pre-Existing Files 

454 

455%(appendText)s 

456""" % locals() 

457 writeFile = codecs.open( 

458 "/tmp/append.md", encoding='ISO-8859-1', mode='w') 

459 writeFile.write(appendText) 

460 writeFile.close() 

461 try: 

462 cmd = """open -a "Marked 2" /tmp/append.md""" % locals() 

463 p = Popen(cmd, stdout=PIPE, stdin=PIPE, shell=True) 

464 output = p.communicate()[0] 

465 self.log.debug('output: %(output)s' % locals()) 

466 except: 

467 pass 

468 

469 # REMOVE THE TMP FOLDER 

470 shutil.rmtree(self.tmpPath) 

471 

472 self.log.debug( 

473 'completed the ``_move_template_to_destination`` method') 

474 return None 

475 

476 # use the tab-trigger below for new method 

477 # xt-class-method