Coverage for frankenstein/electric.py : 82%

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*
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
28class electric(object):
29 """
30 *The worker class for the electric module*
32 **Key Arguments**
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
40 """
41 # Initialisation
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]
61 return None
63 def get(self):
64 """
65 *do the frankenstein magic!*
66 """
67 self.log.debug('starting the ``get`` method')
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)
78 self.log.debug('completed the ``get`` method')
79 return None
81 def list_placeholders(self):
82 """
83 *list the remaining placeholders required by frankenstein*
84 """
85 self.log.debug('starting the ``list_placeholders`` method')
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()
94 self.log.debug('completed the ``list_placeholders`` method')
95 return placeholders
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')
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)
114 directoryContents = recursive_directory_listing(
115 log=self.log,
116 baseFolderPath=tmpPath,
117 whatToList="all" # all | files | dirs
118 )
120 self.directoryContents = directoryContents
121 self.tmpPath = tmpPath
123 self.log.debug(
124 'completed the ``_copy_folder_and_get_directory_listings`` method')
125 return None
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')
135 phs = self.settings["frankenstein"]["placeholder delimiters"]
136 phsString = "|".join(phs)
138 matchObject = re.finditer(
139 r'(%(phsString)s)([^\s]*?)\1' % locals(),
140 string=self.contentString,
141 flags=re.S # re.S
142 )
144 phDict = {}
145 for match in matchObject:
146 if len(match.group(2).strip()) > 0:
147 phDict[match.group(2)] = None
149 self.phDict = phDict
151 self.log.debug(
152 'completed the ``_collect_placeholders_required`` method')
153 return None
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')
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()
175 self.contentString = contentString
177 self.log.debug('completed the ``_join_all_filenames_and_text`` method')
178 return None
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')
188 from datetime import datetime, date, time
189 now = datetime.now()
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 }
201 for k, v in list(self.phDict.items()):
202 if k in list(dynamicPhs.keys()):
203 self.phDict[k] = dynamicPhs[k]
205 self.log.debug(
206 'completed the ``_populate_dynamic_placeholders`` method')
207 return None
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')
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]
222 self.log.debug(
223 'completed the ``_fill_placeholders_from_settings`` method')
224 return None
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')
234 phNeeded = False
235 for k, v in list(self.phDict.items()):
236 if not v:
237 phNeeded = True
239 if phNeeded == False:
240 return
242 print("please add your placeholder values ...")
244 for k, v in list(self.phDict.items()):
245 if not v:
246 v = input("%(k)s? > " % locals())
247 self.phDict[k] = v
249 self.log.debug(
250 'completed the ``_request_remaining_placeholders`` method')
251 return None
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')
261 remainingPlaceholders = []
262 for k, v in list(self.phDict.items()):
263 if not v:
264 remainingPlaceholders.append(k)
266 self.log.debug(
267 'completed the ``_list_remaining_placeholders`` method')
268 return remainingPlaceholders
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')
278 rev = reversed(self.directoryContents)
279 phs = self.settings["frankenstein"]["placeholder delimiters"]
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)
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)
309 if newContent != thisData:
310 writeFile = codecs.open(
311 pathToReadFile, encoding='ISO-8859-1', mode='w')
312 writeFile.write(newContent)
313 writeFile.close()
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)
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)
361 self.log.debug(
362 'completed the ``_populate_placeholders_in_files`` method')
363 return None
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*
372 **Key Arguments**
374 # -
377 **Return**
379 - None
382 .. todo::
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')
389 # CREATE DIRECTORY STRUCTURE
390 sourceDirectories = recursive_directory_listing(
391 log=self.log,
392 baseFolderPath=self.tmpPath,
393 whatToList="dirs" # all | files | dirs
394 )
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)
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]
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`
432```
433%(content)s
434```
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)
450 # CREATE NOTE TO USER TO APPEND TEXT
451 if len(appendText) > 3:
452 appendText = """
453# Text to Append to Pre-Existing Files
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
469 # REMOVE THE TMP FOLDER
470 shutil.rmtree(self.tmpPath)
472 self.log.debug(
473 'completed the ``_move_template_to_destination`` method')
474 return None
476 # use the tab-trigger below for new method
477 # xt-class-method