Ejemplo n.º 1
0
class DataFormatConverter(object):
    """A class for converting between various data interchange formats, e.g. XML and JSON."""

#===================================================================================================
#                                                                                       C L A S S

#___________________________________________________________________________________________________ __init__
    def __init__(self):
        """Creates a new instance of ClassTemplate."""
        self._type = None
        self._src  = None
        self._log  = Logger('DataFormatConverter')
        self._path = None

#===================================================================================================
#                                                                                   G E T / S E T

#___________________________________________________________________________________________________ GS: propertyName
    @property
    def source(self):
        return self._src

#===================================================================================================
#                                                                                     P U B L I C

#___________________________________________________________________________________________________ load
    def load(self, path, fileType):
        if not os.path.exists(path):
            self._log.write('ERROR: Path does not exist [%s]. Unable to load.' % path)
            return False

        try:
            fh  = codecs.open(path, 'r', 'utf-8')
            res = fh.read()
            fh.close()
            enc = res.encode('utf-8')
            self.loads(enc, fileType)
        except Exception, err:
            self._log.writeError('Failed to load source file [%s].' % path, err)
            return False

        self._path = path
        return True
Ejemplo n.º 2
0
class IncludeCompressor(object):

#===================================================================================================
#                                                                                       C L A S S

    _REMOVE_COMMENT_RE      = re.compile('/\*.+\*/', re.DOTALL)
    _REMOVE_COMMENT_LINE_RE = re.compile('(^|\n)[\s\t]*//.+(\n|$)')

    JS_TYPE  = 'js'
    CSS_TYPE = 'css'

#___________________________________________________________________________________________________ __init__
    def __init__(self, compileCoffee =False):
        self._log           = Logger('IncludeCompressor')
        self._compileCoffee = compileCoffee

#===================================================================================================
#                                                                                     P U B L I C

#___________________________________________________________________________________________________ compress
    def compress(self, rootPath):
        if not self._fileExists(rootPath):
            return False
        elif os.path.isfile(rootPath):
            return self.compressFile(rootPath)
        else:
            return self.compressPath(rootPath)

#___________________________________________________________________________________________________ compressFile
    def compressFile(self, rootPath, directory =None):
        if not self._fileExists(rootPath):
            return False

        if self._compileCoffee:
            try:
                from pyaid.web.coffeescript.CoffeescriptBuilder import CoffeescriptBuilder
                CoffeescriptBuilder.compileAllOnPath(rootPath, os.path.dirname(rootPath), True)
                self._log.write('Coffeescript compiled.')
            except Exception, err:
                self._log.writeError('Failed to compile coffeescript file.', err)
                return False

        return self._compressFile(rootPath, directory)
Ejemplo n.º 3
0
class SocketHandler(SocketServer.StreamRequestHandler):
    """A class for..."""

#===================================================================================================
#                                                                                       C L A S S

    SERVICE_UID   = 'test'
    VERBOSE       = False
    WORK_PATH     = '/var/lib/'
    RUN_PATH      = '/var/run/'
    LOG_PATH      = '/var/log/'

#___________________________________________________________________________________________________ __init__
    def __init__(self, request, client_address, server):
        self._log = Logger(self)
        self._log.write('Socket handler created')

        SocketServer.StreamRequestHandler.__init__(self, request, client_address, server)

#===================================================================================================
#                                                                                   G E T / S E T

#___________________________________________________________________________________________________ GS: returnResponse
    @property
    def returnResponse(self):
        return getattr(self.__class__, 'RETURN_RESPONSE', False)

#===================================================================================================
#                                                                                     P U B L I C

#___________________________________________________________________________________________________ handle
    def handle(self):
        try:
            data = self.rfile.readline().strip()
            self._log.write('HANDLE: ' + str(data))
            try:
                result = self._respondImpl(JSON.fromString(unquote(data)))
            except Exception as err:
                self._log.writeError('RESPOND FAILURE', err)
                if self.returnResponse:
                    self.wfile.write(JSON.asString({'error':1}))
                return

            if self.returnResponse:
                out = {'error':0}
                if result:
                    out['payload'] = result
                self.wfile.write(out)
        except Exception as err:
            self._log.write('HANDLE FAILURE', err)

        return

#===================================================================================================
#                                                                               P R O T E C T E D

#___________________________________________________________________________________________________ _respondImpl
    def _respondImpl(self, data):
        pass
Ejemplo n.º 4
0
class PyGlassWindow(QtGui.QMainWindow):
    """A class for..."""

#===================================================================================================
#                                                                                       C L A S S

#___________________________________________________________________________________________________ __init__
    def __init__(self, **kwargs):
        """Creates a new instance of PyGlassWindow."""
        parent = ArgsUtils.extract('parent', None, kwargs)
        self._application  = ArgsUtils.extract('pyGlassApp', None, kwargs)
        self._qApplication = ArgsUtils.extract('qApp', None, kwargs)
        self._isMainWindow = ArgsUtils.extract('isMainWindow', bool(parent is None), kwargs)
        self._mainWindow   = ArgsUtils.extract('mainWindow', None, kwargs)
        self._centerWidget = None

        self._keyboardCallback = ArgsUtils.extract('keyboardCallback', None, kwargs)

        if not self._mainWindow:
            if self._isMainWindow:
                self._mainWindow = self
            elif self._application:
                self._mainWindow = self._application.mainWindow

        self._dependentWindows = []
        self._currentWidget    = None

        QtGui.QMainWindow.__init__(self, parent, ArgsUtils.extract('flags', 0, kwargs))

        if self._keyboardCallback is not None:
            self.setFocusPolicy(QtCore.Qt.StrongFocus)

        if self._isMainWindow:
            self._log                 = Logger(self, printOut=True)
            self._config              = ApplicationConfig(self)
            self._commonConfig        = ApplicationConfig(self, common=True)
            self._resourceFolderParts = PyGlassGuiUtils.getResourceFolderParts(self)

            icon = PyGlassGuiUtils.createIcon(
                ArgsUtils.get('iconsPath', self.getAppResourcePath('icons', isDir=True), kwargs) )
            if icon:
                self.setWindowIcon(icon)

        elif self._mainWindow:
            icon = self._mainWindow.windowIcon()
            if icon:
                self.setWindowIcon(icon)

        # Loads the ui file if it exists
        hasWindowFile = ArgsUtils.get('mainWindowFile', False, kwargs)
        if hasWindowFile:
            if not self._centerWidget:
                self._createCentralWidget()
            UiFileLoader.loadWidgetFile(self, target=self._centerWidget)

        self._styleSheet = ArgsUtils.get('styleSheet', None, kwargs)
        if self._styleSheet:
            self.setStyleSheet(self.styleSheetPath)

        # Sets a non-standard central widget
        centralWidgetName = ArgsUtils.get('centralWidgetName', None, kwargs)
        if centralWidgetName and hasattr(self, centralWidgetName):
            self._centerWidget = getattr(self, centralWidgetName)
        elif not hasWindowFile:
            self._centerWidget = None
            if ArgsUtils.get('defaultCenterWidget', False, kwargs):
                self._createCentralWidget()

        self._lastWidgetID  = None
        self._widgetParent  = None
        self._widgets       = None
        self._widgetFlags   = None

        self._widgetClasses = ArgsUtils.get('widgets', None, kwargs)
        if self._widgetClasses:
            self._initializeWidgetChildren()
        else:
            self._widgetClasses = dict()

        self.setWindowTitle(ArgsUtils.get('title', self._createTitleFromClass(), kwargs))
        self.updateStatusBar()

#===================================================================================================
#                                                                                   G E T / S E T

#___________________________________________________________________________________________________ GS: isDeployed
    @ClassGetter
    def isDeployed(cls):
        return PyGlassEnvironment.isDeployed

#___________________________________________________________________________________________________ GS: isOnDisplay
    @property
    def isOnDisplay(self):
        return self.isVisible()

#___________________________________________________________________________________________________ GS: appID
    @property
    def appID(self):
        return self.pyGlassApplication.appID

#___________________________________________________________________________________________________ GS: isMainWindow
    @property
    def isMainWindow(self):
        return self._isMainWindow

#___________________________________________________________________________________________________ GS: allowsOwnership
    @property
    def allowsOwnership(self):
        return True

#___________________________________________________________________________________________________ GS: mainWindow
    @property
    def mainWindow(self):
        if self.isMainWindow:
            return self
        if self._mainWindow:
            return self._mainWindow

        self._mainWindow = PyGlassGuiUtils.getMainWindow(self)
        return self._mainWindow

#___________________________________________________________________________________________________ GS: owner
    @property
    def owner(self):
        if self.isMainWindow:
            return self
        return self.mainWindow

#___________________________________________________________________________________________________ GS: pyGlassApplication
    @property
    def pyGlassApplication(self):
        return self._application if self.isMainWindow else self.mainWindow.pyGlassApplication

#___________________________________________________________________________________________________ GS: qApplication
    @property
    def qApplication(self):
        return self._qApplication if self.isMainWindow else self.mainWindow.qApplication

#___________________________________________________________________________________________________ GS: appConfig
    @property
    def appConfig(self):
        return self._config if self.isMainWindow else self.mainWindow.appConfig

#___________________________________________________________________________________________________ GS: commonAppConfig
    @property
    def commonAppConfig(self):
        return self._commonConfig if self.isMainWindow else self.mainWindow.commonAppConfig

#___________________________________________________________________________________________________ GS: log
    @property
    def log(self):
        return self._log if self.isMainWindow else self.owner.log

#___________________________________________________________________________________________________ GS: styleSheetPath
    @property
    def styleSheetPath(self):
        if not self._styleSheet:
            return None

        if os.path.isabs(self._styleSheet):
            return self._styleSheet

        parts = self._resourceFolderParts + self._stylesheet.split('/')
        return self.getResourcePath(*parts, isFile=True)

#___________________________________________________________________________________________________ GS: rootResourcePath
    @property
    def rootResourcePath(self):
        return PyGlassEnvironment.getRootResourcePath()

#___________________________________________________________________________________________________ GS: rootLocalResourcePath
    @property
    def rootLocalResourcePath(self):
        return PyGlassEnvironment.getRootLocalResourcePath()

#___________________________________________________________________________________________________ GS: appResourcePath
    @property
    def appResourcePath(self):
        if not self.isMainWindow:
            return self.owner.appResourcePath

        out = self.getRootResourcePath('apps', self.appID, isDir=True)
        if not os.path.exists(out):
            os.makedirs(out)
        return out

#___________________________________________________________________________________________________ GS: localAppResourcePath
    @property
    def localAppResourcePath(self):
        if not self.isMainWindow:
            return self.owner.localAppResourcePath

        out = self.getRootLocalResourcePath('apps', self.appID, isDir=True)
        if not os.path.exists(out):
            os.makedirs(out)
        return out

#___________________________________________________________________________________________________ GS: sharedResourcePath
    @property
    def sharedResourcePath(self):
        out = self.getRootResourcePath('shared', isDir=True)
        if not os.path.exists(out):
            os.makedirs(out)
        return out

#___________________________________________________________________________________________________ GS: localSharedResourcePath
    @property
    def localSharedResourcePath(self):
        out = self.getLocalResourcePath('shared', isDir=True)
        if not os.path.exists(out):
            os.makedirs(out)
        return out

#___________________________________________________________________________________________________ GS: widgets
    @property
    def widgets(self):
        return self._widgets

#===================================================================================================
#                                                                                     P U B L I C

#___________________________________________________________________________________________________ keyPressEvent
    def keyPressEvent(self, event):
        if self._keyboardCallback is None or not self._keyboardCallback(event):
            super(PyGlassWindow, self).keyPressEvent(event)

#___________________________________________________________________________________________________ closeEvent
    def closeEvent(self, *args, **kwargs):
        if self.isMainWindow:
            for depWindow in self._dependentWindows:
                depWindow.close()
        return super(PyGlassWindow, self).closeEvent(*args, **kwargs)

#___________________________________________________________________________________________________ addDependentWindow
    def addDependentWindow(self, window):
        if window in self._dependentWindows:
            return True

        self._dependentWindows.append(window)
        return True

#___________________________________________________________________________________________________ removeDependentWindow
    def removeDependentWindow(self, window):
        if window not in self._dependentWindows:
            return True

        self._dependentWindows.remove(window)
        return True

#___________________________________________________________________________________________________ refreshWidgets
    def refreshWidgets(self, **kwargs):
        for name, widget in self._widgets.iteritems():
            widget.refresh(**kwargs)
        self.refreshGui()

#___________________________________________________________________________________________________ updateStatusBar
    def updateStatusBar(self, message =None, timeout =-1):
        if not message:
            self.statusBar().clearMessage()
            self.statusBar().setVisible(False)
        else:
            if timeout < 0:
                timeout = 3000
            self.statusBar().showMessage(message, timeout=timeout)
            self.statusBar().setVisible(True)

#___________________________________________________________________________________________________ getWidgetFromID
    def getWidgetFromID(self, widgetID):
        if widgetID in self._widgets:
            return self._widgets[widgetID]
        return None

#___________________________________________________________________________________________________ getSharedResourcePath
    def getSharedResourcePath(self, *args, **kwargs):
        return FileUtils.createPath(self.sharedResourcePath, *args, **kwargs)

#___________________________________________________________________________________________________ getLocalSharedResourcePath
    def getLocalSharedResourcePath(self, *args, **kwargs):
        return FileUtils.createPath(self.localSharedResourcePath, *args, **kwargs)

#___________________________________________________________________________________________________ getAppResourcePath
    def getAppResourcePath(self, *args, **kwargs):
        """Doc..."""
        return FileUtils.createPath(self.appResourcePath, *args, **kwargs)

#___________________________________________________________________________________________________ getLocalAppResourcePath
    def getLocalAppResourcePath(self, *args, **kwargs):
        """Doc..."""
        return FileUtils.createPath(self.localAppResourcePath, *args, **kwargs)

#___________________________________________________________________________________________________ getRootResourcePath
    def getRootResourcePath(self, *args, **kwargs):
        return PyGlassEnvironment.getRootResourcePath(*args, **kwargs)

#___________________________________________________________________________________________________ getRootLocalResourcePath
    def getRootLocalResourcePath(self, *args, **kwargs):
        return PyGlassEnvironment.getRootLocalResourcePath(*args, **kwargs)

#___________________________________________________________________________________________________ getResourcePath
    def getResourcePath(self, *args, **kwargs):
        """Doc..."""
        return FileUtils.createPath(
            self.rootResourcePath, 'widget', self._resourceFolderParts, *args, **kwargs)

#___________________________________________________________________________________________________ getLocalResourcePath
    def getLocalResourcePath(self, *args, **kwargs):
        """Doc..."""
        return FileUtils.createPath(
            self.rootLocalResourcePath, 'widget', self._resourceFolderParts, *args, **kwargs)

#___________________________________________________________________________________________________ showLoading
    def showLoading(self, **kwargs):
        if self._currentWidget.widgetID != 'loading':
            self._lastWidgetID = self._currentWidget.widgetID
        self._showLoadingImpl(**kwargs)
        self.setActiveWidget('loading', force=True, args=kwargs)

#___________________________________________________________________________________________________ hideLoading
    def hideLoading(self, **kwargs):
        if self._currentWidget.widgetID != 'loading':
            return

        self._hideLoadingImpl(**kwargs)
        if self._lastWidgetID:
            self.setActiveWidget(self._lastWidgetID, args=kwargs)

#___________________________________________________________________________________________________ addWidget
    def addWidget(self, key, widgetClass, setActive =False):
        self._widgetClasses[key] = widgetClass
        if self._widgets is None:
            self._initializeWidgetChildren(key if setActive else None)
        elif setActive:
            return self.setActiveWidget(key)
        return True

#___________________________________________________________________________________________________ setActiveWidget
    def setActiveWidget(self, widgetID, force =False, args =None, doneArgs =None):
        if not self._centerWidget or widgetID is None or widgetID not in self._widgetClasses:
            return False

        if not force and self._currentWidget and self._currentWidget.widgetID == widgetID:
            return True

        if widgetID not in self._widgets:
            self.loadWidgets(widgetID)
        widget = self._widgets[widgetID]

        # Deactivates the current widget if the widgets are being switched. However, ignored if the
        # same widget is being activated for a second time.
        if self._currentWidget and widgetID != self._currentWidget.widgetID:
            if self._currentWidget.widgetID == 'loading':
                self._hideLoadingImpl()

            if doneArgs is None:
                doneArgs = dict()
            self._currentWidget.deactivateWidgetDisplay(**doneArgs)
            self._currentWidget.setParent(self._widgetParent)

        self._currentWidget = widget
        if self._centerWidget:
            layout = self._centerWidget.layout()
            if not layout:
                layout = QtGui.QVBoxLayout()
                layout.setContentsMargins(0, 0, 0, 0)
                self._centerWidget.setLayout(layout)
            layout.addWidget(widget)
        else:
            self.setCentralWidget(widget)
        self.setContentsMargins(0, 0, 0, 0)
        self.refreshGui()

        if args is None:
            args = dict()
        widget.activateWidgetDisplay(**args)
        return True

#___________________________________________________________________________________________________ loadWidgets
    def loadWidgets(self, widgetIdents =None):
        if not widgetIdents:
            widgetIdents = self._widgetClasses.keys()
        elif isinstance(widgetIdents, basestring):
            widgetIdents = [widgetIdents]

        for widgetID in widgetIdents:
            if widgetID in self._widgets:
                continue

            if widgetID not in self._widgetClasses:
                self._log.write(
                    'ERROR: Unrecognized widgetID "%s" in %s' % (str(widgetID), str(self)))

            widget = self._widgetClasses[widgetID](
                self._widgetParent, flags=self._widgetFlags, widgetID=widgetID)
            self._widgets[widgetID] = widget

#___________________________________________________________________________________________________ refreshGui
    def refreshGui(self):
        self.qApplication.processEvents()

#___________________________________________________________________________________________________ exit
    def exit(self):
        self.qApplication.exit()

#___________________________________________________________________________________________________ initialize
    def initialize(self, *args, **kwargs):
        if AlembicUtils.hasAlembic:
            self.pyGlassApplication.updateSplashScreen('Conforming internal data')
            AlembicUtils.upgradeAppDatabases(self.appID)

        self._initializeImpl(*args, **kwargs)

#___________________________________________________________________________________________________ initializeComplete
    def initializeComplete(self, preDisplay =None):
        self.pyGlassApplication.closeSplashScreen()

        result = False
        if preDisplay:
            preDisplay.show()
            result = self.qApplication.exec_()

        if not result:
            self.preShow()
            self.show()
            result = self.qApplication.exec_()
            self.postShow()

        sys.exit(result)

#___________________________________________________________________________________________________ preShow
    def preShow(self, **kwargs):
        self._preShowImpl(**kwargs)

#___________________________________________________________________________________________________ postShow
    def postShow(self, **kwargs):
        self._postShowImpl(**kwargs)

#===================================================================================================
#                                                                               P R O T E C T E D

#___________________________________________________________________________________________________ _createCentralWidget
    def _createCentralWidget(self):
        w1 = self.centralWidget()
        w2 = self._centerWidget
        if w1 and w2:
            return w2
        elif w1 and not w2:
            self._centerWidget = w1
            return w1

        layout = self.layout()
        layout.setContentsMargins(0, 0, 0, 0)
        layout.setSpacing(0)

        w = QtGui.QWidget(self)
        layout.addWidget(w)
        self.setCentralWidget(w)
        self._centerWidget = w
        return w

#___________________________________________________________________________________________________ _initializeWidgetChildren
    def _initializeWidgetChildren(self, activeWidgetID =None):
        if not self._widgetClasses or self._widgets:
            return False

        self._widgetParent = PyGlassBackgroundParent(proxy=self)
        self._widgets      = dict()

        if 'loading' not in self._widgetClasses:
            self._widgetClasses['loading'] = LoadingWidget

        if activeWidgetID:
            self.setActiveWidget(activeWidgetID)

#___________________________________________________________________________________________________ _preShowImpl
    def _preShowImpl(self, **kwargs):
        pass

#___________________________________________________________________________________________________ _postShowImpl
    def _postShowImpl(self, **kwargs):
        pass

#___________________________________________________________________________________________________ _showLoadingImpl
    def _showLoadingImpl(self, **kwargs):
        pass

#___________________________________________________________________________________________________ _hideLoadingImpl
    def _hideLoadingImpl(self, **kwargs):
        pass

#___________________________________________________________________________________________________ _createTitleFromClass
    def _createTitleFromClass(self):
        """Doc..."""
        src = self.__class__.__name__
        out = src[0]
        wasCaps = True
        for c in src[1:]:
            if c.lower() == c:
                out += c
                wasCaps = False
            elif wasCaps:
                out += c
            else:
                out += ' ' + c
                wasCaps = True

        return out

#___________________________________________________________________________________________________ _initializeImpl
    def _initializeImpl(self, *args, **kwargs):
        self.initializeComplete()

#===================================================================================================
#                                                                               I N T R I N S I C

#___________________________________________________________________________________________________ __repr__
    def __repr__(self):
        return self.__str__()

#___________________________________________________________________________________________________ __unicode__
    def __unicode__(self):
        return unicode(self.__str__())

#___________________________________________________________________________________________________ __str__
    def __str__(self):
        return '<%s>' % self.__class__.__name__
Ejemplo n.º 5
0
class TrackLinkConnector(object):
    """A class for..."""

#===============================================================================
#                                                                                       C L A S S

    _TRACK_NUMBER_RE = re.compile('(?P<prefix>[^0-9\-]*)(?P<number>-?[0-9]+)(?P<suffix>[^0-9]*)')

#_______________________________________________________________________________
    def __init__(self, logger =None):
        """Creates a new instance of TrackLinkConnector."""
        self.logger = logger
        if not logger:
            self.logger = Logger(self, printOut=True)

        self.searchNext         = True
        self.searchPrev         = True
        self.overrideExisting   = False
        self.operatedTracks     = []
        self.modifiedTracks     = []
        self.trackLinkages      = []

#===============================================================================
#                                                                                     P U B L I C

#_______________________________________________________________________________
    def echoResult(self):
        out = []
        for item in self.trackLinkages:
            out.append(item[0].name + ' -> ' + item[1].name)
        return u'\n'.join(out)

#_______________________________________________________________________________
    def runAll(self, session):
        model = Tracks_Track.MASTER
        return self.run(session.query(model).all(), session)

#_______________________________________________________________________________
    def run(self, tracks, session):
        """Doc..."""

        for track in tracks:
            if track not in self.operatedTracks:
                self._runTrack(track, session)

#===============================================================================
#                                                                               P R O T E C T E D

#_______________________________________________________________________________
    def _runTrack(self, source, session):
        """Doc..."""

        model = source.__class__
        trackSeries = session.query(model).filter(
            model.site == source.site,
            model.sector == source.sector,
            model.level == source.level,
            model.trackwayType == source.trackwayType,
            model.trackwayNumber == source.trackwayNumber,
            model.pes == source.pes,
            model.left == source.left).order_by(model.number.asc()).all()

        if not trackSeries:
            return False

        #-------------------------------------------------------------------------------------------
        # TRACK ORDERING
        #       Tracks numbers are strings to support naming conventions like 10b or 12c, where the
        #       number is possibly followed by a non-numeric set of characters. To establish track
        #       ordering the sequence should be sorted primarily by the numeric sequence and
        #       secondarily by the suffix first numerically and then alphabetically respectively.

        trackNumbers = dict()
        for track in trackSeries:
            result = self._TRACK_NUMBER_RE.search(track.number)
            if not result or result.group('prefix'):
                self.logger.write([
                    u'ERROR: Unable to parse track number: ' + StringUtils.toUnicode(track.number),
                    u'TRACK: ' + DictUtils.prettyPrint(track.toDict()) ])
                continue

            number = result.group('number')
            suffix = result.group('suffix')

            if number not in trackNumbers:
                trackNumbers[number] = {'track':None, 'extras':{}, 'number':int(number)}
            entry = trackNumbers[number]

            if number == track.number and not suffix:
                entry['track'] = track
            elif not suffix:
                self.logger.write([
                    u'ERROR: Invalid track number: ' + StringUtils.toUnicode(track.number),
                    u'TRACK: ' + DictUtils.prettyPrint(track.toDict()) ])
                continue
            else:
                entry['extras'][suffix] = track

            if track not in self.operatedTracks:
                self.operatedTracks.append(track)

        prev = None
        entries = list(trackNumbers.values())
        entries.sort(key=lambda x: x['number'])
        for entry in entries:
            track = entry['track']
            tracks = [] if track is None else [track]
            for key in sorted(entry['extras']):
                tracks.append(entry['extras'][key])

            for track in tracks:
                if not prev:
                    prev = track
                    continue

                # If the previous entry has an existing next that doesn't match
                if not self.overrideExisting and prev.next and prev.next != track.uid:
                    continue

                prev.next = track.uid
                self.modifiedTracks.append(prev)
                self.trackLinkages.append((prev, track))
                prev = track

#===============================================================================
#                                                                               I N T R I N S I C

#_______________________________________________________________________________
    def __repr__(self):
        return self.__str__()

#_______________________________________________________________________________
    def __str__(self):
        return '<%s>' % self.__class__.__name__
Ejemplo n.º 6
0
class CadenceDrawing(object):
    """ A class for writing Scalable Vector Graphics (SVG) files, tailored to create overlays for
        site maps. Each site map has a marker that indicates a reference point in Swiss Federal
        coordinates.  At least one such marker appears somewhere within each site map (in cases
        where there were two, the more central was chosen).  The maps are all oriented such that
        the x axis is positve to the right (towards the east) and the y axis is positive down
        (southward).  For visualization in Cadence, a site map is projected onto the y=0 plane of a
        3D scene such that the Federal Coordinate marker is placed at the origin in 3D, and the
        scene's positive x axis increases to the left ('west') and the scene's positive z axis
        increases to the 'north'.  The correspondence between these two coordinate systems is
        handled by public functions to be found in this class, based on information derived from an
        instance of a Tracks_SiteMap (specifically the scale and the location of a federal
        coordinates marker within the siteMap).

        This class wraps (owns) an instance of an svgwrite.Drawing and handles the mapping from 3D
        scene coordinates (x, z) to SVG coordinates (x, y).  Scene coordinates are in real-world cm,
        while the SVG coordinates used in the sitemaps is in (50:1 scaled) mm.  That is, one mm in
        the _drawing equals 5 cm, realworld.  By default, a saved CadenceDrawing can be placed in an
        Adobe Illustrator layer, then adjusted to be in registation with the sitemap.

        CadenceDrawing is built upon the svgwrite.Drawing class, which provides the basic
        functions to draw lines, rectangles, and other SVG objects. For further information on
        svgwrite, consult: https://pythonhosted.org/svgwrite

        A CadenceDrawing instance simplifies the functionality of svgwrite, encapsulating all the
        handling of all SVG fragments through function calls to create lines, polyLines, rects,
        circles, elipses, and text, plus transformable groups, with the resultant SVG file written
        by the function save.  A CadenceDrawing adopts the underlying svgwrite convention of using
        kwargs to provide a Pythonic means to specify SVG attributes such as stroke, stroke_linecap,
        stroke_width, and fill. CadenceDrawing is a wrapper adaptation of svgwrite.  That is, a
        CadenceDrawing encapsulates an instance of an svgwrite Drawing. Coordinate mapping allows
        trackway data (coordinates and dimensions) to be scaled appropriately for inclusion into an
        SVG-format tracksite file to be placed in a layer in Adobe illustrator).  This scaling is
        provided by the kwarg scene=True, wherein scene coordinates (in cm) are converted to scaled
        mm. The following is an example in which all tracks for a given site are loaded and drawn:

            tracks = siteMap.getAllTracks(session)
            for track in tracks:
                x = track.x
                z = track.z
                # Track dimensions are in fractional meters, so multiply by 100 to convert to cm.
                r = 100*0.5*(track.width/2.0 + track.length/2.0)
                drawing.circle((x, z), r, scene=True, fill='none', stroke='blue', stroke_width=1)
                # compute this track's averge uncertainty in cm (also stored in fractional meters)
                u = 100*(track.widthUncertainty + track.lengthUncertainty)/2.0
                drawing.circle((x, z), u, scene=True, fill='red', stroke='red', stroke_width=1)

        A more advanced usage uses groups that can be transformed (rotated, scaled, translated).
        A group is created and given an ID that is then passed to drawing functions (which create
        the shape), then the group is used (instantiated) at somePlace at a rotation of 45 degrees:

            drawing.createGroup('g1')
            drawing.rect((0, 0), width, height, groupId='g1')
            ...etc...

            drawing.use('g1', somePlace, rotation=45)

        The use of groups, while convenient, requires that all map coordinates be converted to px.
        For more detail, see the use function and grid, which produces a 10 m grid. """

    # ===============================================================================
    #                                                                                       C L A S S

    # _______________________________________________________________________________
    def __init__(
        self,
        fileName,
        siteMap,
        labelTracks=True,
        labelColor="black",
        session=None,
        showUncertainty=True,
        showCenters=True,
        **kwargs
    ):
        """ Creates a new instance of CadenceDrawing.  Calls to the public functions line(), rect(),
            and others result in objects being added to the SVG canvas, with the file written by the
            save() method to specified fileName.  The second argument, the siteMap is provided as an
            argument to establish the correspondence between the Maya scene and the site siteMap
            coordinates. """

        self._logger = kwargs.get("logger")
        if not self._logger:
            self._logger = Logger(self, printOut=True)

        self.siteMapReady = siteMap.isReady
        if not self.siteMapReady:
            self._logger.write('[ERROR|CadenceDrawing]: Sitemap "%s-%s" not ready' % (siteMap.name, siteMap.level))
            return

        self.fileName = fileName
        self.siteMap = siteMap
        self.siteName = siteMap.name
        self.siteLevel = siteMap.level

        # Generally units can be specified in millimeters.  In a few cases, however, (e.g.,
        # PolyLine) the coordinates must be unqualified integers (as px).  The site maps, however
        # are in 'scaled mm'.  Hence the need for a cnversion factor pxPerMm. Unfortunately, the
        # conversion between px and mm is OS-dependent. The conversion from px to inches is 72 for
        # Apple but 90 more generally).

        ppi = 72 if OsUtils.isMac() else 90
        self.pxPerMm = ppi / 25.4

        # specify the width and height explicitly in mm, and likewise for the background rect
        left = siteMap.left * mm
        top = siteMap.top * mm
        width = siteMap.width * mm
        height = siteMap.height * mm

        self._drawing = svgwrite.Drawing(fileName, profile="tiny", size=(width, height), stroke=svgwrite.rgb(0, 0, 0))
        self._drawing.add(self._drawing.rect((left, top), (width, height), opacity="0"))

        self.groups = dict()

        if labelTracks:
            self.labelTracks(
                color=labelColor, session=session, showUncertainty=showUncertainty, showCenters=showCenters
            )

    # ===============================================================================
    #                                                                                   G E T / S E T

    # _______________________________________________________________________________
    @property
    def pixelWidth(self):
        return math.ceil(self.siteMap.width * self.pxPerMm)

    # _______________________________________________________________________________
    @property
    def pixelHeight(self):
        return math.ceil(self.siteMap.height * self.pxPerMm)

    # ===============================================================================
    #                                                                                     P U B L I C

    # _______________________________________________________________________________
    def circle(self, center, radius, scene=True, groupId=None, **extra):
        """ Adds a circle object to the SVG file. All coordinates are explicitly labled with 'mm'
            and passed to svgwrite. """

        if not self.siteMapReady:
            return

        # convert from scene coordinates to map coordinates as necessary
        if scene:
            center = self.projectToMap(center)
            radius = self.scaleToMap(radius)

        # convert from (scaled) mm to px
        center = (self.pxPerMm * center[0], self.pxPerMm * center[1])
        radius *= self.pxPerMm

        # create the object
        obj = self._drawing.circle(center, radius, **extra)

        # and add it to either a specific group or to the default _drawing
        if groupId:
            group = self.groups[groupId]
            if group:
                group.add(obj)
            else:
                print("circle:  %s is not a valid group ID" % groupId)
                return
        else:
            self._drawing.add(obj)

    # _______________________________________________________________________________
    def createGroup(self, id, **extra):
        """ Creates an SVG group, so that subsequent SVG fragments can be added to the group.  When
            the group is subsequently used (by the use function) an instance is created, and placed
            at a particular location in the drawing, with a particular scale and rotation. This
            method only creates tghe group; to then add fragments, the group's id is passed to draw
            functions so that those fragments are added to the group rather than to the drawing
            directly. Groups are intended to be placed readily across a drawing, hence the pivot
            for the group should normally be centered on the user space (map) origin."""

        if not self.siteMapReady:
            return

        group = self._drawing.g(id=id, **extra)

        # add it to defs so that it is not directly rendered
        self._drawing.defs.add(group)

        # and keep track of the id so it can be used to refer to the group
        self.groups[id] = group

    # _______________________________________________________________________________
    def ellipse(self, center, radii, scene=True, groupId=None, **extra):
        """ Adds an ellipse object to the SVG file, based on a center point and two radii.  All
            coordinates are explicitly labled with 'mm' and passed to svgwrite. """

        if not self.siteMapReady:
            return

        # convert from scene coordinates to map coordinates as necessary
        if scene:
            center = self.projectToMap(center)
            radii = [self.scaleToMap(radii[0]), self.scaleToMap(radii[1])]

        # convert from (scaled) mm to px
        center = (self.pxPerMm * center[0], self.pxPerMm * center[1])
        radii = (self.pxPerMm * radii[0], self.pxPerMm * radii[1])

        # create the object
        obj = self._drawing.ellipse(center, radii, **extra)

        # and add it to either a specific group or to the default _drawing
        if groupId:
            group = self.groups[groupId]
            if group:
                group.add(obj)
            else:
                print("ellipse:  %s is not a valid group ID" % groupId)
                return
        else:
            self._drawing.add(obj)

    # _______________________________________________________________________________
    def federalCoordinates(self, deltaX=-4, deltaZ=-2.5, diskRadius=2):
        """ Place the coordinates as a text string at the specified offset from the fiducial
            marker. """

        if not self.siteMapReady:
            return

        text = "%s-%s (%s, %s)" % (self.siteName, self.siteLevel, self.siteMap.federalEast, self.siteMap.federalNorth)
        self.text(text, (deltaX, deltaZ), scene=True, stroke_width=0.05, font_size="4")

        # place an unfilled green circle of specified radius atop the federal coordinate marker
        self.circle((0, 0), diskRadius, scene=True, fill="none", stroke="green", stroke_width=1)

    # _______________________________________________________________________________
    def grid(self, size=2, diagonals=True, dx=200, dy=200, **extra):
        """ This is a group-based version of grid.  It creates a rectangular grid of marks.
            The grid marks on a site map are separated by 10 m in the real world, or 200 units
            in the map in their 'scaled mm' convention. Unfortunately, the group construct in
            svgwrite requires px values, and will not allow the mm suffix. The default spacing is
            1 m by 1 m (i.e., dx=20 and dy=20). """

        if not self.siteMapReady:
            return

        x0 = self.siteMap.xFederal % dx
        y0 = self.siteMap.yFederal % dy
        xn = int(self.siteMap.width / dx)
        yn = int(self.siteMap.height / dy)

        self.createGroup("mark")
        self.mark(size, scene=False, groupId="mark")

        for i in range(xn):
            x = x0 + i * dy
            for j in range(yn):
                y = y0 + j * dy
                self.use("mark", [self.pxPerMm * x, self.pxPerMm * y], rotation=45, scene=False)

        p = 0
        delta = 300
        count = [0, 0]
        while p <= self.pixelWidth:
            # Vertical lines
            count[0] += 1

            obj = self._drawing.line(
                (p, 0),
                (p, self.pixelHeight),
                stroke="black",
                stroke_width=1,
                stroke_opacity="0.1",
                stroke_dasharray="5,5",
            )
            self._drawing.add(obj)

            if p == self.pixelWidth:
                break
            p = min(p + delta, self.pixelWidth)

        p = 0
        while p <= self.pixelHeight:
            # Horizontal lines
            count[1] += 1

            obj = self._drawing.line(
                (0, p),
                (self.pixelWidth, p),
                stroke="black",
                stroke_width=1,
                stroke_opacity="0.1",
                stroke_dasharray="5,5",
            )
            self._drawing.add(obj)

            if p == self.pixelHeight:
                break
            p = min(p + delta, self.pixelHeight)

        for ix in range(count[0] - 1):
            # Text Coordinate Labels
            for iy in range(count[1] - 1):
                obj = self._drawing.text(
                    "[%s-%s]" % (iy + 1, ix + 1),
                    (delta * ix + 5, delta * iy + 17),
                    font_size="12",
                    fill="black",
                    fill_opacity="0.15",
                    stroke="none",
                )
                self._drawing.add(obj)

    # _______________________________________________________________________________
    def labelTracks(
        self, color="black", opacity=0.25, strokeWidth=0.5, session=None, showUncertainty=True, showCenters=True
    ):
        """ Finds all tracks for the current tracksite, then marks their centers and adds a text
            label for each track. """

        from cadence.models.tracks.Tracks_Track import Tracks_Track

        model = Tracks_Track.MASTER
        s = session if session else model.createSession()
        query = s.query(model)
        query = query.filter(model.site == self.siteName)
        result = query.filter(model.level == self.siteLevel).all()

        # for each track in this tracksite-level, mark its center and label it (e.g., 'S18 LP3')
        for track in result:

            # Use the position value to draw values rounded to uncertainty
            pos = track.positionValue.toMayaTuple()

            if pos[0] == 0 and pos[1] == 0:
                continue

            if showCenters:
                self.circle(pos, 2, scene=True, fill=color, fill_opacity=opacity, stroke="none")

            if showUncertainty:
                self.circle(
                    pos,
                    100.0 * min(track.width, track.length),
                    scene=True,
                    fill="none",
                    stroke_width=strokeWidth,
                    stroke=color,
                    stroke_opacity=opacity,
                )

            self.text(
                track.sitemapDisplayLabel,
                (pos[0] - 4, pos[1] - 2.5),
                scene=True,
                font_size="4",
                fill=color,
                fill_opacity=opacity,
                stroke="none",
            )

        # all done
        if not session:
            s.close()

    # _______________________________________________________________________________
    def lineSegment(self, line, groupId=None, **extra):
        """ Adds a line object to the svg file based on a LineSegment2D instance. """
        self.line(line.start.toMayaTuple(), line.end.toMayaTuple(), groupId=groupId, **extra)

    # _______________________________________________________________________________
    def line(self, p1, p2, scene=True, groupId=None, **extra):
        """ Adds a line object to the svg file based on two scene points. It first converts from
            scene to siteMap coordinates if necessary, then concatenates the units suffix 'mm' to
            all coordinate values. """

        if not self.siteMapReady:
            return

        # convert from scene coordinates to map coordinates as necessary
        if scene:
            p1 = self.projectToMap(p1)
            p2 = self.projectToMap(p2)

        # convert from (scaled) mm to px
        p1 = (self.pxPerMm * p1[0], self.pxPerMm * p1[1])
        p2 = (self.pxPerMm * p2[0], self.pxPerMm * p2[1])

        # create the object
        obj = self._drawing.line(p1, p2, **extra)

        # and add it to either a specific group or to _drawing (the default)
        if groupId:
            group = self.groups[groupId]
            if group:
                group.add(obj)
            else:
                print("line:  %s is not a valid group ID" % groupId)
                return
        else:
            self._drawing.add(obj)

    # _______________________________________________________________________________
    def mark(self, size, scene=True, groupId=None, **extra):
        """ Adds an axis-aligned '+' mark of given size at the origin. If scene=True, the size is
            transformed to map coordinates, else it is presumed to already be in map coordinates. If
            groupId=True, the mark is added to the specified group, rather than to the drawing.
            This fragment is intended for use as a group (see grid). """

        if not self.siteMapReady:
            return

        self.line([-size, 0], [size, 0], scene=scene, groupId=groupId, **extra)
        self.line([0, -size], [0, size], scene=scene, groupId=groupId, **extra)

    # _______________________________________________________________________________
    def mm(self, p):
        """ Appends the units label 'mm' to each value.  Too many cases where svgwrite will not
            allow this suffix, so not currently used. """

        return (p[0] * mm, p[1] * mm)

    # _______________________________________________________________________________
    def polyLine(self, points, scene=True, groupId=None, **extra):
        """ Adds a polyline object to the SVG file, based on a list of scene points. If canvas is
            specified, this permits adding this object to a """

        if not self.siteMapReady:
            return

        # map from scene coordinates to map coordinates as necessary
        if scene:
            mappedPoints = list()
            for p in points:
                mappedPoints.append(self.projectToMap(p))
            points = mappedPoints

        # svgwrite does not allow coordinates with the suffix 'mm', hence all values must be in px.
        convertedPoints = list()
        for p in points:
            x = self.pxPerMm * p[0]
            y = self.pxPerMm * p[1]
            convertedPoints.append((x, y))

        # create the object
        obj = self._drawing.polyline(convertedPoints, **extra)

        # and add it to either a specific group or symbol, or the default _drawing
        if groupId:
            group = self.groups[groupId]
            if group:
                group.add(obj)
            else:
                print("polyLine:  %s is not a valid group ID" % groupId)
                return
        else:
            self._drawing.add(obj)

    # _______________________________________________________________________________
    def projectToScene(self, p):
        """ The given siteMap location p is projected to the corresponding scene point and returned.
            In the scene, x is positive to the left, and z is positive upwards.  In the siteMap, x
            is positive to the right and y is positive downwards. """

        if not self.siteMapReady:
            return

        xMap = p[0]
        yMap = p[1]
        xScene = -self.scaleToScene(xMap - self.siteMap.xFederal)
        zScene = -self.scaleToScene(yMap - self.siteMap.yFederal)

        return (xScene, zScene)

    # _______________________________________________________________________________
    def projectToMap(self, p):
        """ The given 2D scene point p, comprised of scene cooordinates (xScene, zScene), is
            projected to the corresponding 2D siteMap location (xMap, yMap) and returned. xScene
            is positive to the left, and zScene is positive upwards; xMap is positive to the right
            and yMap is positive downwards. """

        if not self.siteMapReady:
            return

        xScene = p[0]
        yScene = p[1]
        xMap = self.siteMap.xFederal - self.scaleToMap(xScene)
        yMap = self.siteMap.yFederal - self.scaleToMap(yScene)

        return (xMap, yMap)

    # _______________________________________________________________________________
    def rect(self, center, width, height, scene=True, groupId=None, rx=None, ry=None, **extra):
        """ Adds a rect object to the SVG file, based on center and dimensions. If the boolean
            scene is True, the arguments are converted to
            'scaled mm', otherwise they are presumed to be in mm.  All coordinates are explicitly
            labled with 'mm' and passed to svgwrite. """

        if not self.siteMapReady:
            return

        xCenter = center[0]
        yCenter = center[1]

        # convert from scene coordinates to map coordinates as necessary
        if scene:
            xCenter = self.scaleToMap(xCenter)
            yCenter = self.scaleToMap(yCenter)
            width = self.scaleToMap(width)
            height = self.scaleToMap(height)

        # now compute the insert point, i.e., the upper left corner
        insert = (xCenter - width / 2, yCenter - height / 2)

        # convert from (scaled) mm to px
        insert = (self.pxPerMm * insert[0], self.pxPerMm * insert[1])
        size = (self.pxPerMm * width, self.pxPerMm * height)

        # create the object
        obj = self._drawing.rect(insert, size, rx, ry, **extra)

        # and add it to either a specific group or to the default _drawing
        if groupId:
            group = self.groups[groupId]
            if group:
                group.add(obj)
            else:
                print("rect:  %s is not a valid group ID" % groupId)
                return
        else:
            self._drawing.add(obj)

    # _______________________________________________________________________________
    def save(self, toPDF=True):
        """ Writes the current _drawing in SVG format to the file specified at initialization. If
            one wishes to have create a PDF file (same file name as used for the .SVG, but with
            suffix .PDF), then call with toPDF True). """

        if not self.siteMapReady:
            return

        # Make sure the directory where the file will be saved exists before saving
        FileUtils.getDirectoryOf(self._drawing.filename, createIfMissing=True)

        self._drawing.save()

        #  we're done if no PDF version is also required
        if not toPDF:
            return

        # strip any extension off of the file name
        basicName = self.fileName.split(".")[0]

        # load up the command
        cmd = ["/Applications/Inkscape.app/Contents/Resources/bin/inkscape", "-f", None, "-A", None]
        cmd[2] = basicName + ".svg"
        cmd[4] = basicName + ".pdf"

        # and execute it
        response = SystemUtils.executeCommand(cmd)
        if response["error"]:
            print("response[error]=%s" % response["error"])

    # _______________________________________________________________________________
    def scaleToMap(self, v):
        """ Converts from scene coordinates (in cm) to siteMap coordinates ('scaled mm'). The
            siteMap is usually drawn in 50:1 scale. """

        if not self.siteMapReady:
            return

        return v / (0.1 * self.siteMap.scale)

    # _______________________________________________________________________________
    def scaleToScene(self, value):
        """ Site maps (Adobe Illustrator .ai files) are typically in 50:1 scale, and use mm as their
            units.  Consequently a single unit in the site map corresponds to 50 mm in the 'real
            world'. The 3D scene on the other hand, uses cm scale.  This function converts the given
            value from the 'scaled mm' of the map into centimeter units of the 3D scene. For
            example, a value of 20 units corresponds to 100 cm in the scene, which is returned. """

        if not self.siteMapReady:
            return

        return 0.1 * self.siteMap.scale * value

    # _______________________________________________________________________________
    def text(self, textString, insert, scene=True, groupId=None, **extra):
        """ Adds a text of a given fill at the given insertion point. """

        if not self.siteMapReady:
            return

        # convert from scene coordinates to map coordinates as necessary
        if scene:
            insert = self.projectToMap(insert)

        # convert from (scaled) mm to px
        insert = (self.pxPerMm * insert[0], self.pxPerMm * insert[1])

        # create the object
        obj = self._drawing.text(textString, insert, **extra)

        # and add it to either a specific group or to the default _drawing
        if groupId:
            group = self.groups[groupId]
            if group:
                group.add(obj)
            else:
                print("text:  %s is not a valid group ID" % groupId)
                return
        else:

            self._drawing.add(obj)

    # _______________________________________________________________________________
    def use(self, id, center, scene=True, rotation=None, rotationCenter=None, scale=None, scaleY=None, **extra):
        """ Groups are given an id when created.  This id is used to create instances that are
            added to the Cadence drawing (and hence the SVG file) by this function.  The group is
            placed in the drawing using map coordinates.  Rotation defaults to about the origin. A
            preferred usage would be to create a create a group relative to (i.e., centered upon)
            the origin, so that the rotation pivot is naturally at the group's center.  The group
            is then placed at the specfified center location (which either in map coordinates or
            scene coordinates depending upon the kwarg scene).  For example, for a group 'g' to
            be placed at some point (xScene, yScene) and rotated 45 degrees, and with 2x scale:
                use('g', (xScene, yScene), scene=True, rotation=45, scale=2) """

        if not self.siteMapReady:
            return

        if scene:
            center = self.projectToMap(center)
            tx = self.pxPerMm * center[0]
            ty = self.pxPerMm * center[1]
        else:
            tx = center[0]
            ty = center[1]

        element = self.groups[id]
        if not element:
            print("CadenceDrawing.use:  %s is not a valid group id" % id)
            return

        instance = self._drawing.use(element, **extra)
        instance.translate(tx, ty)

        # right-handed coordinates, hence postive counterclockwise about y axis
        if rotation:
            instance.rotate(-rotation, center=rotationCenter)

        # scale anisotropically only if scaleY is specified, else isotropically
        if scale:
            if not scaleY:
                scaleY = scale
            instance.scale(scale, sy=scaleY)

        self._drawing.add(instance)
Ejemplo n.º 7
0
class CoffeescriptBuilder(object):
    """A class for..."""

    CLASS_PATTERN = "^[\s\t]*class[\s\t]+(?P<class>[^\s\t\r\n]+)[\s\t]*"
    MISSING_CLASS_PATTERN = "[\s\t\(\[\{\!]+(?=[A-Z])(?P<class>[A-Za-z0-9_]+)(?P<next>[^A-Za-z0-9_]+)"

    _WARN_ID_MISSING_IMPORT = "MISSING-IMPORT"

    _GLOBAL_CLASSES = [
        "SFLOW",
        "PAGE",
        "FB",
        "Math",
        "JSON",
        "String",
        "ActiveXObject",
        "Date",
        "DOMParser",
        "RegExp",
        "Object",
        "Number",
        "Array",
        "Function",
        "XMLHttpRequest",
    ]

    _results = None
    _missing = None

    # ===================================================================================================
    #                                                                                       C L A S S

    # ___________________________________________________________________________________________________ __init__
    def __init__(
        self,
        targetPackageOrPath,
        rootPath,
        verbose=True,
        debug=False,
        trace=False,
        force=False,
        compress=False,
        buildOnly=False,
    ):
        """Creates a new instance of CoffeescriptBuilder."""

        self.buildOnly = buildOnly

        self._imports = dict()
        self._requires = dict()
        self._includes = dict()
        self._report = dict()
        self._warnings = []
        self._dependencyReport = dict()
        self._verbose = verbose
        self._log = Logger(self, printOut=True)
        self._trace = trace
        self._debug = debug
        self._targets = []
        self._force = force
        self._compress = compress
        self._rootPath = rootPath

        if not isinstance(targetPackageOrPath, CoffeescriptDependency):
            target = CoffeescriptDependency(targetPackageOrPath, rootPath, None)
        else:
            target = targetPackageOrPath

        if target.exists:
            self._targets.append(target)
        else:
            csFiles = CoffeescriptBuilder.getScriptsInPath(target.packagePath)

            # Look for exec matches first
            for f in csFiles:
                testTarget = CoffeescriptDependency(f, rootPath, None)
                if testTarget.isExec:
                    self._targets.append(testTarget)

            # Look for lib matches second. Lib matches are tested as a second pass because
            # constructing all exec files first potentially optimizes the import process for
            # the libraries.
            for f in csFiles:
                testTarget = CoffeescriptDependency(f, rootPath, None)
                if testTarget.isLib:
                    self._targets.append(testTarget)

        if len(self._targets) == 0:
            print("\n\n")
            self._log.write("No targets exist for: %s. Compilation aborted." % targetPackageOrPath)
            print("\n")

    # ===================================================================================================
    #                                                                                   G E T / S E T

    # ___________________________________________________________________________________________________ GS: report
    @property
    def report(self):
        return self._report

    # ___________________________________________________________________________________________________ GS: warnings
    @property
    def warnings(self):
        return self._warnings

    # ___________________________________________________________________________________________________ GS: imports
    @property
    def imports(self):
        return self._imports

    # ___________________________________________________________________________________________________ GS: requires
    @property
    def requires(self):
        return self._requires

    # ___________________________________________________________________________________________________ GS: includes
    @property
    def includes(self):
        return self._includes

    # ===================================================================================================
    #                                                                                     P U B L I C

    # ___________________________________________________________________________________________________ construct
    def construct(self):
        """Doc..."""
        for t in self._targets:
            self._report[t.package] = -1
            if t.isLib:
                self._constructLibrary(t)
            else:
                self._constructTarget(t)

            if self._compress:
                print("COMPRESSING:", t.package)
                from pyaid.web.coffeescript.IncludeCompressor import IncludeCompressor

                ic = IncludeCompressor()
                if not ic.compressFile(t.compiledPath):
                    print("COMPRESSION FAILURE:", t.compiledPath)

        return self._targets

    # ___________________________________________________________________________________________________ compileAllOnPath
    @staticmethod
    def compileAllOnPath(path, rootPath=None, recursive=False, debug=False, trace=False, force=False, compress=False):

        CoffeescriptBuilder._results = ""
        CoffeescriptBuilder._missing = {}
        if recursive:
            print("RECURSIVE COMPILE AT: " + path)

            def walker(paths, dirName, names):
                out = CoffeescriptBuilder._compileAllInDirectory(
                    os.path.join(paths[0], dirName), paths[1], debug=debug, trace=trace, force=force, compress=compress
                )
                CoffeescriptBuilder._results += out["res"]
                for n, v in DictUtils.iter(out["missing"]):
                    if n in CoffeescriptBuilder._missing:
                        continue
                    CoffeescriptBuilder._missing[n] = v

            FileUtils.walkPath(path, walker, [path, rootPath])
            print("\n\nCOMPILATION RESULTS:" + CoffeescriptBuilder._results)

            if CoffeescriptBuilder._missing:
                print("\n\nMISSING IMPORTS:" + "\n\n")
                for n, v in DictUtils.iter(CoffeescriptBuilder._missing):
                    print(v["class"] + " [LINE: #" + str(v["line"]) + " | " + v["package"] + "]")
        else:
            print("COMPILING DIRECTORY: " + path)
            CoffeescriptBuilder._compileAllInDirectory(
                path, rootPath, debug=debug, trace=trace, force=force, compress=compress
            )

    # ___________________________________________________________________________________________________ getScriptsInPath
    @staticmethod
    def getScriptsInPath(path):
        files = []

        for f in os.listdir(path):
            if f.lower().endswith("." + CoffeescriptDependency.EXTENSION):
                files.append(os.path.join(path, f))

        return files

    # ===================================================================================================
    #                                                                               P R O T E C T E D

    # ___________________________________________________________________________________________________ _constructLibrary
    def _constructLibrary(self, target):
        try:
            if self._verbose:
                print("\n\n" + ("-" * 100) + "\n")
                self._log.add("LIBRARY: %s\n\tsource: %s\n\troot: %s" % (target.package, target.path, target.rootPath))

            # ---------------------------------------------------------------------------------------
            # Compile all includes using library data
            targets, imports, modules, includes = self._getLibraryData(target)

            # Process requires for all of the targets
            for t in targets + imports + modules:
                self._processRequires(t)

            # ---------------------------------------------------------------------------------------
            # IMPORTS

            # Compile all excludes skipping any exec or lib files that are listed in the import
            # statements.
            importExcludes = []
            for t in targets:
                for imp in self._imports[t.package]:
                    if not (imp.isExec or imp.isLib or imp.isInList(importExcludes)):
                        importExcludes.append(imp)

            # Compile all imports needed for the library. Any excludes are added to the shared
            # library to be made accessible via the VIZME registry.
            libImports = []
            sharedImports = []
            for t in imports + modules:
                for imp in self.imports[t.package]:
                    if not imp.isInList(libImports):
                        if imp.isInList(importExcludes):
                            if not imp.isInList(sharedImports):
                                sharedImports.append(imp)
                        else:
                            libImports.append(imp)
            libImports.append(target)

            # ---------------------------------------------------------------------------------------
            # INCLUDES

            # Compile all includes to exclude from the library because they already exist in a
            # target.
            includeExcludes = []
            for t in targets:
                for inc in self._includes[t.package]:
                    if not inc.isInList(includeExcludes):
                        includeExcludes.append(inc)

            # Compile all includes needed for the library.
            libIncludes = []
            sharedIncludes = []

            # Add the top-level includes directly because they are not handled implicitly like
            # the import case
            for inc in includes:
                if inc.isInList(includeExcludes):
                    sharedIncludes.append(inc)
                else:
                    libIncludes.append(inc)

            for t in imports + modules:
                for inc in self.includes[t.package]:
                    if not inc.isInList(libIncludes):
                        if inc.isInList(includeExcludes):
                            if not inc.isInList(sharedIncludes):
                                sharedIncludes.append(inc)
                        else:
                            libIncludes.append(inc)

            if self._verbose:
                print("\n")
                s = "IMPORTING:"
                for imp in libImports:
                    s += "\n\t" + imp.package
                for inc in libIncludes:
                    s += "\n\tEXTERNAL: " + inc.package
                self._log.add(s)

                print("\n")
                s = "EXCLUDING:"
                for imp in sharedImports:
                    s += "\n\t" + imp.package
                for inc in sharedIncludes:
                    s += "\n\tEXTERNAL: " + inc.package
                self._log.add(s)

            # ---------------------------------------------------------------------------------------
            # Construct intermediate compilation file.
            assembledFile = self._assembleFile(target, libImports, sharedImports, {"modules": modules})
            if assembledFile is None:
                self._log.write("ERROR: File assembly failed.")
                return

            # ---------------------------------------------------------------------------------------
            # Compile to Javascript
            if not self.buildOnly:
                self._compileToJavascript(target, assembledFile, libIncludes)

            if self._verbose:
                print("\n" + ("-" * 100) + "\n")

        except Exception as err:
            print("\n\n\n")
            self._log.writeError(
                "ERROR: Compilation failure for: %s\n\tsource: %s\n\troot: %s"
                % (target.package, target.path, target.rootPath),
                err,
            )

    # ___________________________________________________________________________________________________ _constructTarget
    def _constructTarget(self, target):
        try:
            if self._verbose:
                print("\n\n" + ("-" * 100) + "\n")
                self._log.write(
                    "EXECUTABLE: %s\n\tsource: %s\n\troot: %s" % (target.package, target.path, target.rootPath)
                )

            # ---------------------------------------------------------------------------------------
            # Handle imports and requires
            self._parseIncludes(target)
            self._processRequires(target)

            if self._verbose:
                s = "IMPORTING:"
                for imp in self._imports[target.package]:
                    s += "\n\t" + imp.package
                self._log.write(s)

            # ---------------------------------------------------------------------------------------
            # Construct intermediate compilation file.
            assembledFile = self._assembleFile(target)
            if assembledFile is None:
                self._log.write("ERROR: File assembly failed.")
                return

            # ---------------------------------------------------------------------------------------
            # Compile to Javascript
            if not self.buildOnly:
                self._compileToJavascript(target, assembledFile)

            if self._verbose:
                print("\n" + ("-" * 100) + "\n")

        except Exception as err:
            print("\n\n\n")
            self._log.writeError(
                "ERROR: Compilation failure for: %s\n\tsource: %s\n\troot: %s"
                % (target.package, target.path, target.rootPath),
                err,
            )

    # ___________________________________________________________________________________________________ _createOutputFile
    def _createOutputFile(self, target):
        """Creates the output ccs assembly file for writing."""
        outFile = target.assembledPath
        try:
            return open(outFile, "w")
        except Exception as err:
            print("\n\n")
            self._log.write(
                "Unable To Open output file: " + str(outFile) + "\n"
                "Check to make sure you have write permissions to that directory."
            )
            return None

    # ___________________________________________________________________________________________________ _writeRegistryEntry
    def _writeRegistryEntry(self, out, cacheOut, entry):
        # If there is an unconsumed registryEntry write it.
        if not entry:
            return None

        s = "\n" + entry + "\n"
        out.write(s)

        if cacheOut:
            cacheOut.write(s)
        return None

    # ___________________________________________________________________________________________________ _assembleFile
    def _assembleFile(self, target, importOverride=None, replacements=None, assembleData=None):

        # -------------------------------------------------------------------------------------------
        # CREATE FILE
        # Creates the file to write
        out = self._createOutputFile(target)
        if not out:
            self._log("ERROR: Unable to create output file")
            return

        # -------------------------------------------------------------------------------------------
        # DEFINE IMPORTS
        # Specify the files to import. For exec files the default packages are included, for
        # libraries these are overridden based on library target dependencies.
        targetImports = self._imports[target.package] if importOverride is None else importOverride

        replacements = replacements if isinstance(replacements, list) else []
        classList = []

        # -------------------------------------------------------------------------------------------
        # Note the last dependency so that the glue script can be appended prior
        lastDep = targetImports[-1]

        # -------------------------------------------------------------------------------------------
        # DEPENDENCY ASSEMBLY LOOP
        print("\n")
        for dep in targetImports:
            dep.open()

            if self._force or not dep.useCache:
                if not self._compileDependency(dep, out, replacements, targetImports, classList):
                    return None
                continue

            self._log.write("\tFROM CACHE: " + dep.package)
            out.write(dep.cacheSource)
            dep.close()

        out.close()

        if self._verbose:
            print("\n")
            self._log.add("CONSTRUCTED: " + out.name)

        return out.name

    # ___________________________________________________________________________________________________ _compileDependency
    def _compileDependency(self, dep, out, replacements, targetImports, classList):
        classPattern = re.compile(CoffeescriptBuilder.CLASS_PATTERN)
        missingPattern = re.compile(CoffeescriptBuilder.MISSING_CLASS_PATTERN)

        # -------------------------------------------------------------------------------------------
        # MISSING DEPENDENCIES
        # Handle missing dependencies
        if not os.path.exists(dep.path):
            print("\n\n")
            self._log.write("ERROR: " + dep.package + " package does not exist at: " + dep.path)
            return False

        lastWhitespace = ""
        openParens = 0
        openBrackets = 0
        openBraces = 0
        skipNextLine = False
        methodName = ""
        className = ""
        registryEntry = None

        raw = dep.source
        dep.close()

        s = "\n\n\t#" + ("%" * 100) + "\n\t#" + ("%" * 100) + "\n#\t\t" + dep.package + "\n"

        out.write(s)
        if dep.allowCaching:
            cacheOut = open(dep.cachePath, "w")
            cacheOut.write(s)
        else:
            try:
                if os.path.exists(dep.cachePath):
                    os.remove(dep.cachePath)
            except Exception as err:
                pass

            cacheOut = None

        self._log.write("\tCOMPILING: " + dep.package)

        analyzer = CoffeescriptAnalyzer(raw, debug=self._debug)
        analyzer.analyze()

        # ---------------------------------------------------------------------------------------
        # COMPILE
        # Line by line compile to ccs output file
        for l in analyzer:

            # -----------------------------------------------------------------------------------
            # RETARGET CLASS ACCESSORS TO VIZME registry
            # All classes (except internal class references) are made to
            # VIZME registry ClassName to prevent class conflicts.
            for rep in replacements + targetImports:
                if rep != dep:
                    offset = 0
                    res = rep.searchPattern.finditer(l.redacted)
                    for r in res:
                        start = r.start() + offset
                        end = r.end() + offset

                        if self._trace:
                            self._log.write("RETARGET: " + l.source[start:end] + " | " + str(r.groupdict()))

                        # Make the replacement and adjust offsets for additional replacements
                        l.insert(start, end, rep.registryName)
                        offset += len(rep.registryName) - end + start

            # -----------------------------------------------------------------------------------
            # IDENTIFY CLASS DEFINITIONS
            # Find class definitions so they can be added to the VIZME registry.
            res = classPattern.search(l.redacted)
            if res:
                registryEntry = self._writeRegistryEntry(out, cacheOut, registryEntry)
                className = res.group("class").strip()
                registryEntry = "\n%s.%s ?= %s" % (CoffeescriptDependency.REGISTRY, className, className)
                classList.append(className)

            # -----------------------------------------------------------------------------------
            # CHECK FOR MISSING CLASSES
            # Search and find any missing class imports. If a possible missing import is found
            # flag it in the response.
            res = missingPattern.finditer(l.redacted)
            if res:
                for r in res:
                    cn = r.group("class").strip()
                    start = r.start()

                    if cn == className:
                        continue

                    # Ignore anything in all CAPS!
                    if cn.upper() == cn:
                        continue

                    # Ignore globally defined objects and classes
                    if cn in CoffeescriptBuilder._GLOBAL_CLASSES + analyzer.globalObjects:
                        continue

                    self._warnings.append(
                        {
                            "id": CoffeescriptBuilder._WARN_ID_MISSING_IMPORT,
                            "class": cn,
                            "line": l.lineNumber,
                            "package": dep.package,
                        }
                    )

                    print("\n")
                    self._log.write(
                        "WARNING: Possible missing import\n\tmissing: %s\n\tfrom: %s [line #%s]"
                        % (cn, dep.package, str(l.lineNumber))
                    )

            # -----------------------------------------------------------------------------------
            # LINE DEBUGGER ANALYSIS
            c = l.redacted.strip()
            skip = skipNextLine or not l.isSignificant
            skipNextLine = False

            if not skip:
                skips = ["class", "try", "catch", "else", "when", ".", "+", "-", "/", "=", "*", ",", "and", "or"]
                for s in skips:
                    if c.startswith(s):
                        skip = True
                        break

            if not skip:
                skips = ["->", "=>"]
                methodPattern = re.compile("^(?P<method>[^:]+)")

                for s in skips:
                    if c.endswith(s):
                        skip = True
                        res = methodPattern.search(c)
                        if res and res.group("method"):
                            methodName = res.group("method")
                        elif c.startswith("$"):
                            methodName = "$"

                        break

            # Check for line continuations
            if l.isSignificant:
                skips = [".", "+", "-", "/", "=", "*", ",", "and", "or"]
                for s in skips:
                    if c.endswith(s):
                        skipNextLine = True
                        break

            if self._trace:
                self._log.write(
                    c.replace("\n", "")
                    + (
                        "\n\t@@@@ skip: "
                        + str(skip)
                        + "\n\t@@@@ parens: "
                        + str(openParens)
                        + "\n\t@@@@ braces: "
                        + str(openBraces)
                        + "\n\t@@@@ brackets: "
                        + str(openBraces)
                        + "\n\t@@@@ skipNext: "
                        + str(skipNextLine)
                    )
                )

            if self._debug and not skip and openParens == 0 and openBraces == 0 and openBrackets == 0:
                debugLine = "window.___vmiDebug('%s', '%s', '%s', %s)\n" % (
                    dep.package,
                    className,
                    methodName,
                    str(l.lineNumber),
                )

                indent = len(l.indent) > len(lastWhitespace)
                dedent = len(l.indent) < len(lastWhitespace)

                skips = [")", "]", "}"]
                skip = False
                for s in skips:
                    if c.startswith(s):
                        skip = True
                        break

                if dedent and skip:
                    lastWhitespace = lastWhitespace
                else:
                    lastWhitespace = l.indent

                codePattern = re.compile("(?P<code>[^\s\t\n]+)")
                res = codePattern.search(c)
                if not res or len(res.groupdict()["code"]) == 0:
                    if self._trace:
                        self._log.write('EMPTY: "' + c + '"')
                    debugLine = ""

                l.insert(0, 0, l.indent + debugLine)

            if l.isSignificant:
                openParens += l.redacted.count("(") - l.redacted.count(")")
                openBrackets += l.redacted.count("[") - l.redacted.count("]")
                openBraces += l.redacted.count("{") - l.redacted.count("}")

            # ---------------------------------------------------------------------------------------
            # WRITE MODIFIED OUTPUT
            out.write(l.source)

            if cacheOut:
                cacheOut.write(l.source)

        self._writeRegistryEntry(out, cacheOut, registryEntry)

        if cacheOut:
            cacheOut.close()

        return True

    # ___________________________________________________________________________________________________ _compileToJavascript
    def _compileToJavascript(self, target, assembledFile, jsIncludeOverrides=None):

        # Use the Coffeescript compiler to create a JS compilation of the assembled CS file
        result = SystemUtils.executeCommand(["coffee", "-c", "--bare", assembledFile])
        status = result["code"]
        output = result["out"]
        errors = 0
        forceVerbose = False

        # -------------------------------------------------------------------------------------------
        # ERROR HANDLING
        #    Check the error status of the compilation process and if a failure occurred parse the
        #    error results for display and logging.
        if status:
            outputLines = str(output).replace("\r", "").split("\n")
            for line in outputLines:
                if line.startswith("Error:") or line.startswith("SyntaxError:"):
                    errors += 1
                    result = CoffeescriptBuilder._parseError(line)
                    if result:
                        self._log.add(result)
                    else:
                        forceVerbose = True

        if forceVerbose:
            self._log.add(output)

        self._report[target.package] = errors
        if self._verbose:
            print("\n\n")
            if errors == 0 and status == 0:
                self._log.write("Compilation complete: " + target.compiledPath)
            else:
                self._log.write("Compilation FAILED: " + target.package)

        f = open(target.compiledPath, "r")
        res = f.read()
        f.close()

    # ___________________________________________________________________________________________________ _parseIncludes
    def _parseIncludes(self, target, rootTarget=None):
        """Doc..."""
        if rootTarget is None:
            rootTarget = target

        if not rootTarget.package in self._imports:
            self._imports[rootTarget.package] = []

        if not rootTarget.package in self._requires:
            self._requires[rootTarget.package] = []

        if not rootTarget.package in self._includes:
            self._includes[rootTarget.package] = []

        if not os.path.exists(target.path):
            print("\n")
            self._log.add("WARNING: Missing import.\n\tPACKAGE: " + target.package + "\n\tFILE: " + target.path)
            print("\n")
            return

        f = open(target.path)
        for line in f:

            # import parse
            dependency = CoffeescriptDependency.createImport(line, self._rootPath)
            if dependency and not dependency.isInList(self._imports[rootTarget.package]):
                self._parseIncludes(dependency, rootTarget)
                self._imports[rootTarget.package].append(dependency)
                continue

            # require parse
            dependency = CoffeescriptDependency.createRequire(line, self._rootPath)
            if dependency and not dependency.isInList(self._imports[rootTarget.package]):
                self._requires[rootTarget.package].append(dependency)
                continue

            # include parse
            dependency = CoffeescriptDependency.createInclude(line, self._rootPath)
            if dependency and not dependency.isInList(self._includes[rootTarget.package]):
                self._includes[rootTarget.package].append(dependency)
                continue

        f.close()
        self._imports[rootTarget.package].append(target)

    # ___________________________________________________________________________________________________ _processRequires
    def _processRequires(self, target):
        currentTarget = self._imports[target.package].pop()
        while len(self._requires[target.package]) > 0:
            self._parseIncludes(self._requires[target.package].pop(0), target)

        outlist = []
        for item in self._imports[target.package]:
            if not item.isInList(outlist) and not item.compare(currentTarget):
                outlist.append(item)
        self._imports[target.package] = outlist
        self._imports[target.package].append(currentTarget)

    # ___________________________________________________________________________________________________ _getLibraryData
    def _getLibraryData(self, target):
        targets = []
        modules = []
        imports = []
        includes = []

        src = open(target.path, "r")
        for line in src:

            # target parse
            d = CoffeescriptDependency.create(line, self._rootPath)
            if not d:
                continue

            if d.dependencyType == CoffeescriptDependency.TARGET_TYPE:
                targets.append(d)
            elif d.dependencyType == CoffeescriptDependency.IMPORT_TYPE:
                imports.append(d)
            elif d.dependencyType == CoffeescriptDependency.REQUIRE_TYPE:
                imports.append(d)
            elif d.dependencyType == CoffeescriptDependency.INCLUDE_TYPE:
                includes.append(d)
            elif d.dependencyType == CoffeescriptDependency.MODULE_TYPE:
                modules.append(d)
            else:
                continue

            self._parseIncludes(d)

        src.close()

        return targets, imports, modules, includes

    # ___________________________________________________________________________________________________ _compileAllInDirectory
    @staticmethod
    def _compileAllInDirectory(path, rootPath=None, debug=False, trace=False, force=False, compress=False):
        results = ""
        missing = {}
        count = 0
        for f in CoffeescriptBuilder.getScriptsInPath(path):
            target = CoffeescriptDependency(f, rootPath)
            if not (target.exists and (target.isExec or target.isLib)):
                continue

            c = CoffeescriptBuilder(target, rootPath, debug=debug, trace=trace, force=force, compress=compress)
            c.construct()
            count += 1
            for n, v in DictUtils.iter(c.report):
                num = max(0, 60 - len(n))
                results += "\n" + n + ":" + ("." * num)
                if v == 0:
                    results += "SUCCESS"
                elif v > 0:
                    results += "COMPILATION FAILED"
                else:
                    results += "ASSEMBLY FAILED"

            if len(c.warnings) > 0:
                results += "[" + str(len(c.warnings)) + " WARNINGS]"
                for v in c.warnings:
                    if not v["id"] == CoffeescriptBuilder._WARN_ID_MISSING_IMPORT:
                        continue

                    key = v["package"] + "-" + v["class"] + "-" + str(v["line"])
                    if key in missing:
                        continue

                    missing[key] = v

        if len(results) > 0:
            print("\nDIRECTORY " + path + " COMPILE RESULTS [" + str(count) + "]:" + results)
        return {"res": results, "missing": missing}

    # ___________________________________________________________________________________________________ _parseError
    @staticmethod
    def _parseError(error):
        """ Parses errors of the format:
        "Error: In /vizme2/website/js/vmi/blog/author/exec.ccs, Parse error on line 181: Unexpected 'INDENT'"
        """

        ccsFile = None

        prefixReplacements = ["SyntaxError: In ", "Error: In "]
        for p in prefixReplacements:
            error = error.replace(p, "")

        out = "\n-----------------------------------------------\nERROR: "
        try:
            sep = error.index(",")
            ccsFile = error[:sep]
        except Exception:
            pass

        try:
            sep2 = error.index(":")
            out += error[sep2 + 1 :].strip() + "\n"
        except Exception:
            if error and sep:
                out += error[sep + 1 :].strip() + "\n"

        pattern = re.compile("line[\s\t]+(?P<linenumber>[0-9]+)")
        res = pattern.search(error)
        if res and len(res.groups()) > 0:
            lineNumber = int(res.groups()[0]) - 1
        else:
            out += "    Unspecified location"
            return

        if ccsFile:
            padSize = len(str(lineNumber + 3))
            jQueryName = "Exec Function (JQUERY Document ready)"
            functionName = None
            className = None
            trace = ""
            f = open(ccsFile, "r")
            for i, line in enumerate(f):
                if i > lineNumber + 4:
                    break

                if i <= lineNumber:
                    pattern = re.compile("^class[\s\t]+(?P<classname>[a-zA-Z0-9_]+)")
                    res = pattern.search(line)
                    if res and len(res.groups()) > 0:
                        className = res.groups()[0]
                        functionName = None

                    pattern = re.compile("^\$[\s\t]*[-=]+>")
                    res = pattern.search(line)
                    if res:
                        className = jQueryName
                        functionName = None

                    pattern = re.compile("[\s\t]*(?P<name>[a-zA-Z0-9_]+)[\s\t]*:[^-=>]*[-=]+>")
                    res = pattern.search(line)
                    if res and len(res.groups()) > 0:
                        functionName = res.groups()[0]

                if i > lineNumber - 4:
                    marker = ">>" if i == lineNumber else "  "
                    trace += marker + str(i).rjust(padSize) + "| " + line

            f.close()

            if functionName:
                out += "  " + ("METHOD" if className else "FUNCTION") + ": " + functionName + "\n"

            if className:
                out += "  " + ("CLASS" if className != jQueryName else "EXEC") + ": " + className + "\n"

            out += "  TRACE:\n" + trace

        return out + "\n"
Ejemplo n.º 8
0
class CoffeescriptBuilder(object):
    """A class for..."""

    CLASS_PATTERN         = '^[\s\t]*class[\s\t]+(?P<class>[^\s\t\r\n]+)[\s\t]*'
    MISSING_CLASS_PATTERN = '[\s\t\(\[\{\!]+(?=[A-Z])(?P<class>[A-Za-z0-9_]+)(?P<next>[^A-Za-z0-9_]+)'

    _WARN_ID_MISSING_IMPORT = 'MISSING-IMPORT'

    _GLOBAL_CLASSES = [
        'SFLOW', 'PAGE', 'FB', 'Math', 'JSON', 'String', 'ActiveXObject', 'Date', 'DOMParser',
        'RegExp', 'Object', 'Number', 'Array', 'Function', 'XMLHttpRequest']

    _results = None
    _missing = None

#===================================================================================================
#                                                                                       C L A S S

#___________________________________________________________________________________________________ __init__
    def __init__(
            self, targetPackageOrPath, rootPath, verbose =True, debug =False, trace = False,
            force =False, compress =False, buildOnly =False
    ):
        """Creates a new instance of CoffeescriptBuilder."""

        self.buildOnly = buildOnly

        self._imports           = dict()
        self._requires          = dict()
        self._includes          = dict()
        self._report            = dict()
        self._warnings          = []
        self._dependencyReport  = dict()
        self._verbose  = verbose
        self._log      = Logger(self, printOut=True)
        self._trace    = trace
        self._debug    = debug
        self._targets  = []
        self._force    = force
        self._compress = compress
        self._rootPath = rootPath

        if not isinstance(targetPackageOrPath, CoffeescriptDependency):
            target = CoffeescriptDependency(targetPackageOrPath, rootPath, None)
        else:
            target = targetPackageOrPath

        if target.exists:
            self._targets.append(target)
        else:
            csFiles = CoffeescriptBuilder.getScriptsInPath(target.packagePath)

            # Look for exec matches first
            for f in csFiles:
                testTarget = CoffeescriptDependency(f, rootPath, None)
                if testTarget.isExec:
                    self._targets.append(testTarget)

            # Look for lib matches second. Lib matches are tested as a second pass because
            # constructing all exec files first potentially optimizes the import process for
            # the libraries.
            for f in csFiles:
                testTarget = CoffeescriptDependency(f, rootPath, None)
                if testTarget.isLib:
                    self._targets.append(testTarget)

        if len(self._targets) == 0:
            print '\n\n'
            self._log.write('No targets exist for: %s. Compilation aborted.' % targetPackageOrPath)
            print '\n'

#===================================================================================================
#                                                                                   G E T / S E T

#___________________________________________________________________________________________________ GS: report
    @property
    def report(self):
        return self._report

#___________________________________________________________________________________________________ GS: warnings
    @property
    def warnings(self):
        return self._warnings

#___________________________________________________________________________________________________ GS: imports
    @property
    def imports(self):
        return self._imports

#___________________________________________________________________________________________________ GS: requires
    @property
    def requires(self):
        return self._requires

#___________________________________________________________________________________________________ GS: includes
    @property
    def includes(self):
        return self._includes

#===================================================================================================
#                                                                                     P U B L I C

#___________________________________________________________________________________________________ construct
    def construct(self):
        """Doc..."""
        for t in self._targets:
            self._report[t.package] = -1
            if t.isLib:
                self._constructLibrary(t)
            else:
                self._constructTarget(t)

            if self._compress:
                print 'COMPRESSING:',t.package
                from pyaid.web.coffeescript.IncludeCompressor import IncludeCompressor
                ic = IncludeCompressor()
                if not ic.compressFile(t.compiledPath):
                    print 'COMPRESSION FAILURE:',t.compiledPath

        return self._targets

#___________________________________________________________________________________________________ compileAllOnPath
    @staticmethod
    def compileAllOnPath(path, rootPath =None, recursive =False, debug =False, trace =False,
                         force =False, compress=False):

        CoffeescriptBuilder._results = ''
        CoffeescriptBuilder._missing = {}
        if recursive:
            print 'RECURSIVE COMPILE AT: ' + path
            def walker(paths, dirName, names):
                out = CoffeescriptBuilder._compileAllInDirectory(
                    os.path.join(paths[0], dirName), paths[1], debug=debug, trace=trace,
                    force=force, compress=compress
                )
                CoffeescriptBuilder._results += out['res']
                for n,v in out['missing'].iteritems():
                    if n in CoffeescriptBuilder._missing:
                        continue
                    CoffeescriptBuilder._missing[n] = v

            os.path.walk(path, walker, [path, rootPath])
            print '\n\nCOMPILATION RESULTS:' + CoffeescriptBuilder._results

            if CoffeescriptBuilder._missing:
                print '\n\nMISSING IMPORTS:' + '\n\n'
                for n,v in CoffeescriptBuilder._missing.iteritems():
                    print v['class'] + ' [LINE: #' + str(v['line']) + ' | ' + v['package'] + ']'
        else:
            print 'COMPILING DIRECTORY: ' + path
            CoffeescriptBuilder._compileAllInDirectory(
                path, rootPath, debug=debug, trace=trace, force=force, compress=compress)

#___________________________________________________________________________________________________ getScriptsInPath
    @staticmethod
    def getScriptsInPath(path):
        files = []

        for f in os.listdir(path):
            if f.lower().endswith('.' + CoffeescriptDependency.EXTENSION):
                files.append(os.path.join(path, f))

        return files

#===================================================================================================
#                                                                               P R O T E C T E D

#___________________________________________________________________________________________________ _constructLibrary
    def _constructLibrary(self, target):
        try:
            if self._verbose:
                print "\n\n" + ('-'*100) + '\n'
                self._log.add(
                    'LIBRARY: %s\n\tsource: %s\n\troot: %s' % (
                        target.package, target.path, target.rootPath))

            #---------------------------------------------------------------------------------------
            # Compile all includes using library data
            targets, imports, modules, includes = self._getLibraryData(target)

            # Process requires for all of the targets
            for t in (targets + imports + modules):
                self._processRequires(t)

            #---------------------------------------------------------------------------------------
            # IMPORTS

            # Compile all excludes skipping any exec or lib files that are listed in the import
            # statements.
            importExcludes = []
            for t in targets:
                for imp in self._imports[t.package]:
                    if not (imp.isExec or imp.isLib or imp.isInList(importExcludes)):
                        importExcludes.append(imp)

            # Compile all imports needed for the library. Any excludes are added to the shared
            # library to be made accessible via the VIZME registry.
            libImports    = []
            sharedImports = []
            for t in (imports + modules):
                for imp in self.imports[t.package]:
                    if not imp.isInList(libImports):
                        if imp.isInList(importExcludes):
                            if not imp.isInList(sharedImports):
                                sharedImports.append(imp)
                        else:
                            libImports.append(imp)
            libImports.append(target)

            #---------------------------------------------------------------------------------------
            # INCLUDES

            # Compile all includes to exclude from the library because they already exist in a
            # target.
            includeExcludes = []
            for t in targets:
                for inc in self._includes[t.package]:
                    if not inc.isInList(includeExcludes):
                        includeExcludes.append(inc)

            # Compile all includes needed for the library.
            libIncludes    = []
            sharedIncludes = []

            # Add the top-level includes directly because they are not handled implicitly like
            # the import case
            for inc in includes:
                if inc.isInList(includeExcludes):
                    sharedIncludes.append(inc)
                else:
                    libIncludes.append(inc)

            for t in (imports + modules):
                for inc in self.includes[t.package]:
                    if not inc.isInList(libIncludes):
                        if inc.isInList(includeExcludes):
                            if not inc.isInList(sharedIncludes):
                                sharedIncludes.append(inc)
                        else:
                            libIncludes.append(inc)

            if self._verbose:
                print '\n'
                s = 'IMPORTING:'
                for imp in libImports:
                    s += '\n\t' + imp.package
                for inc in libIncludes:
                    s += '\n\tEXTERNAL: ' + inc.package
                self._log.add(s)

                print '\n'
                s = 'EXCLUDING:'
                for imp in sharedImports:
                    s += '\n\t' + imp.package
                for inc in sharedIncludes:
                    s += '\n\tEXTERNAL: ' + inc.package
                self._log.add(s)

            #---------------------------------------------------------------------------------------
            # Construct intermediate compilation file.
            assembledFile = self._assembleFile(
                target, libImports, sharedImports, {'modules':modules}
            )
            if assembledFile is None:
                self._log.write('ERROR: File assembly failed.')
                return

            #---------------------------------------------------------------------------------------
            # Compile to Javascript
            if not self.buildOnly:
                self._compileToJavascript(target, assembledFile, libIncludes)

            if self._verbose:
                print "\n" + ('-'*100) + '\n'

        except Exception, err:
            print "\n\n\n"
            self._log.writeError(
                'ERROR: Compilation failure for: %s\n\tsource: %s\n\troot: %s'
                % (target.package, target.path, target.rootPath), err)
Ejemplo n.º 9
0
class TrackCsvImporter(object):
    """ Imports track data from CSV formatted spreadsheets into the local Cadence database. """

#===============================================================================
#                                                                                       C L A S S

    # Used to break trackway specifier into separate type and number entries
    _TRACKWAY_PATTERN = re.compile('(?P<type>[^0-9\s\t]+)[\s\t]*(?P<number>[^\(\s\t]+)')

    _UNDERPRINT_IGNORE_TRACKWAY_STR = ':UTW'
    _OVERPRINT_IGNORE_TRACKWAY_STR = ':OTW'

#_______________________________________________________________________________
    def __init__(self, path =None, logger =None):
        """Creates a new instance of TrackCsvImporter."""
        self._path = path

        self.created  = []
        self.modified = []

        self.fingerprints = dict()
        self.remainingTracks = dict()
        self._logger  = logger
        if not logger:
            self._logger = Logger(self, printOut=True)

#===============================================================================
#                                                                                     P U B L I C

#_______________________________________________________________________________
    def read(self, session, analysisSession, path =None, compressed =False):
        """ Reads from the spreadsheet located at the absolute path argument and adds each row
            to the tracks in the database. """

        if path is not None:
            self._path = path
        if self._path is None:
            return False

        model = Tracks_Track.MASTER
        for existingTrack in session.query(model).all():
            self.remainingTracks[existingTrack.uid] = existingTrack.fingerprint

        try:
            data = pd.read_csv(self._path)
        except Exception as err:
            self._writeError({
                'message':'ERROR: Unable to read CSV file "%s"' % self._path,
                'error':err })
            return

        if data is None:
            self._writeError({
                'message':'ERROR: Failed to create CSV reader for file "%s"' % self._path })
            return

        for index, row in data.iterrows():
            # Skip any rows that don't start with the proper numeric index value, which
            # includes the header row (if it exists) with the column names
            try:
                index = int(row[0])
            except Exception:
                continue

            rowDict = dict()
            for column in Reflection.getReflectionList(TrackCsvColumnEnum):
                value = row[column.index]

                if value and StringUtils.isStringType(value) and not StringUtils.isTextType(value):
                    # Try to decode the value into a unicode string using common codecs
                    for codec in ['utf8', 'MacRoman', 'utf16']:
                        try:
                            decodedValue = value.decode(codec)
                            if decodedValue:
                                value = decodedValue
                                break
                        except Exception:
                            continue

                try:
                    # Check to see if the value is NaN, and if it is replace it with an empty
                    # string to be ignored during import
                    value = '' if np.isnan(value) else value
                except Exception:
                    pass

                if value != '' or value is None:
                    rowDict[column.name] = value

            self.fromSpreadsheetEntry(rowDict, session)

        for uid, fingerprint in DictUtils.iter(self.remainingTracks):
            # Iterate through the list of remaining tracks, which are tracks not found by the
            # importer. If the track is marked as custom (meaning it is not managed by the importer)
            # it is ignored. Otherwise, the track is deleted from the database as a track that no
            # longer exists.

            track = Tracks_Track.MASTER.getByUid(uid, session)
            if track.custom:
                continue

            Tracks_Track.removeTrack(track, analysisSession)
            self._logger.write('[REMOVED]: No longer exists "%s" (%s)' % (
                track.fingerprint, track.uid))

        session.flush()

        for track in self.created:
            self._logger.write('[CREATED]: "%s" (%s)' % (track.fingerprint, track.uid))

        return True

#_______________________________________________________________________________
    def fromSpreadsheetEntry(self, csvRowData, session):
        """ From the spreadsheet data dictionary representing raw track data, this method creates
            a track entry in the database. """

        #-------------------------------------------------------------------------------------------
        # MISSING
        #       Try to determine if the missing value has been set for this row data. If so and it
        #       has been marked missing, skip the track during import to prevent importing tracks
        #       with no data.
        try:
            missingValue = csvRowData[TrackCsvColumnEnum.MISSING.name].strip()
            if missingValue:
                return False
        except Exception:
            pass

        try:
            csvIndex = int(csvRowData[TrackCsvColumnEnum.INDEX.name])
        except Exception:
            self._writeError({
                'message':'Missing spreadsheet index',
                'data':csvRowData })
            return False

        model = Tracks_Track.MASTER
        t = model()
        t.importFlags = 0
        t.index = csvIndex

        #-------------------------------------------------------------------------------------------
        # SITE
        try:
            t.site = csvRowData.get(TrackCsvColumnEnum.TRACKSITE.name).strip().upper()
        except Exception:
            self._writeError({
                'message':'Missing track site',
                'data':csvRowData,
                'index':csvIndex })
            return False

        #-------------------------------------------------------------------------------------------
        # SECTOR
        try:
            t.sector = csvRowData.get(TrackCsvColumnEnum.SECTOR.name).strip().upper()
        except Exception:
            self._writeError({
                'message':'Missing sector',
                'data':csvRowData,
                'index':csvIndex })
            return False

        #-------------------------------------------------------------------------------------------
        # LEVEL
        try:
            t.level = csvRowData.get(TrackCsvColumnEnum.LEVEL.name)
        except Exception:
            self._writeError({
                'message':'Missing level',
                'data':csvRowData,
                'index':csvIndex })
            return False

        #-------------------------------------------------------------------------------------------
        # TRACKWAY
        #       Parse the trackway entry into type and number values. In the process illegal
        #       characters are removed to keep the format something that can be handled correctly
        #       within the database.
        try:
            test = csvRowData.get(TrackCsvColumnEnum.TRACKWAY.name).strip().upper()
        except Exception:
            self._writeError({
                'message':'Missing trackway',
                'data':csvRowData,
                'index':csvIndex })
            return False

        # If the trackway contains an ignore pattern then return without creating the track.
        # This is used for tracks in the record that are actually under-prints from a higher
        # level recorded in the spreadsheet only for catalog reference.
        testIndexes = [
            test.find(self._UNDERPRINT_IGNORE_TRACKWAY_STR),
            test.find(self._OVERPRINT_IGNORE_TRACKWAY_STR) ]

        testParensIndex = test.find('(')
        for testIndex in testIndexes:
            if testIndex != -1 and (testParensIndex == -1 or testParensIndex > testIndex):
                return False

        result = self._TRACKWAY_PATTERN.search(test)
        try:
            t.trackwayType   = result.groupdict()['type'].upper().strip()
            t.trackwayNumber = result.groupdict()['number'].upper().strip()
        except Exception:
            self._writeError({
                'message':'Invalid trackway value: %s' % test,
                'data':csvRowData,
                'result':result,
                'match':result.groupdict() if result else 'N/A',
                'index':csvIndex })
            return False

        #-------------------------------------------------------------------------------------------
        # NAME
        #       Parse the name value into left, pes, and number attributes
        try:
            t.name = csvRowData.get(TrackCsvColumnEnum.TRACK_NAME.name).strip()
        except Exception:
            self._writeError({
                'message':'Missing track name',
                'data':csvRowData,
                'index':csvIndex })
            return False

        #-------------------------------------------------------------------------------------------
        # YEAR
        try:
            year = csvRowData.get(TrackCsvColumnEnum.MEASURED_DATE.name)

            if not year:
                year = '2014'
            else:

                try:
                    y = StringUtils.toText(year).split(';')[-1].strip().replace(
                        '/', '_').replace(
                        ' ', '').replace(
                        '-', '_').split('_')[-1]
                    year = int(re.compile('[^0-9]+').sub('', y))
                except Exception:
                    year = 2014

                if year > 2999:
                    # When multiple year entries combine into a single large number
                    year = int(StringUtils.toUnicode(year)[-4:])
                elif year < 2000:
                    # When two digit years (e.g. 09) are used instead of four digit years
                    year += 2000

                year = StringUtils.toUnicode(year)

            t.year = year
        except Exception:
            self._writeError({
                'message':'Missing cast date',
                'data':csvRowData,
                'index':csvIndex })
            return False

        #-------------------------------------------------------------------------------------------
        # FIND EXISTING
        #       Use data set above to attempt to load the track database entry
        fingerprint = t.fingerprint

        for uid, fp in DictUtils.iter(self.remainingTracks):
            # Remove the fingerprint from the list of fingerprints found in the database, which at
            # the end will leave only those fingerprints that exist in the database but were not
            # touched by the importer. These values can be used to identify tracks that should
            # have been "touched" but were not.
            if fp == fingerprint:
                del self.remainingTracks[uid]
                break

        existing = t.findExistingTracks(session)
        if existing and not isinstance(existing, Tracks_Track):
            existing = existing[0]

        if fingerprint in self.fingerprints:
            if not existing:
                existing = self.fingerprints[fingerprint]

            self._writeError({
                'message':'Ambiguous track entry "%s" [%s -> %s]' % (
                    fingerprint, csvIndex, existing.index),
                'data':csvRowData,
                'existing':existing,
                'index':csvIndex })
            return False

        self.fingerprints[fingerprint] = t

        if existing:
            t = existing
        else:
            session.add(t)
            session.flush()

        TCCE = TrackCsvColumnEnum
        IFE  = ImportFlagsEnum

        #-------------------------------------------------------------------------------------------
        # CSV PROPERTY CLEANUP
        #       Cleanup and format additional CSV values before saving the csv data to the track's
        #       snapshot.
        removeNonColumns = [
            TrackCsvColumnEnum.PRESERVED.name,
            TrackCsvColumnEnum.CAST.name,
            TrackCsvColumnEnum.OUTLINE_DRAWING.name]
        for columnName in removeNonColumns:
            if columnName in csvRowData:
                testValue = StringUtils.toText(csvRowData[columnName]).strip().upper()
                if testValue.startswith('NON'):
                    del csvRowData[columnName]

        # Create a snapshot that only includes a subset of properties that are flagged to be
        # included in the database snapshot entry
        snapshot = dict()
        for column in Reflection.getReflectionList(TrackCsvColumnEnum):
            # Include only values that are marked in the enumeration as to be included
            if not column.snapshot or column.name not in csvRowData:
                continue

            value = csvRowData.get(column.name)
            if value is None:
                continue
            elif not value is StringUtils.isStringType(value):
                value = StringUtils.toText(value)

            value = StringUtils.toText(value).strip()
            if value in ['-', b'\xd0'.decode(b'MacRoman')]:
                continue

            snapshot[column.name] = value

        #-------------------------------------------------------------------------------------------
        # WIDTH
        #       Parse the width into a numerical value and assign appropriate default uncertainty
        try:
            t.widthMeasured = 0.01*float(self._collapseManusPesProperty(
                t, csvRowData,
                TCCE.PES_WIDTH, TCCE.PES_WIDTH_GUESS,
                TCCE.MANUS_WIDTH, TCCE.MANUS_WIDTH_GUESS,
                '0', IFE.HIGH_WIDTH_UNCERTAINTY, IFE.NO_WIDTH ))

            t.widthMeasured = t.widthMeasured

            if not existing or t.widthUncertainty == 0:
                t.widthUncertainty = 0.05 if (t.importFlags & IFE.HIGH_WIDTH_UNCERTAINTY) else 0.03

        except Exception as err:
            print(Logger().echoError('WIDTH PARSE ERROR:', err))
            self._writeError({
                'message':'Width parse error',
                'data':csvRowData,
                'error':err,
                'index':csvIndex })

            t.widthMeasured = 0.0
            if not existing:
                t.widthUncertainty = 0.05

        #-------------------------------------------------------------------------------------------
        # LENGTH
        #       Parse the length into a numerical value and assign appropriate default uncertainty
        try:
            t.lengthMeasured = 0.01*float(self._collapseManusPesProperty(
                t, csvRowData,
                TCCE.PES_LENGTH, TCCE.PES_LENGTH_GUESS,
                TCCE.MANUS_LENGTH, TCCE.MANUS_LENGTH_GUESS,
                '0', IFE.HIGH_LENGTH_UNCERTAINTY, IFE.NO_LENGTH ))

            t.lengthMeasured = t.lengthMeasured

            if not existing or t.lengthUncertainty == 0:
                t.lengthUncertainty = 0.05 if (t.importFlags & IFE.HIGH_LENGTH_UNCERTAINTY) else 0.03

        except Exception as err:
            print(Logger().echoError('LENGTH PARSE ERROR:', err))
            self._writeError({
                'message':'Length parse error',
                'data':csvRowData,
                'error':err,
                'index':csvIndex })

            t.lengthMeasured = 0.0
            if not existing:
                t.lengthUncertainty = 0.05

        #-------------------------------------------------------------------------------------------
        # DEPTH
        #       Parse the depth into a numerical value and assign appropriate default uncertainty
        try:
            t.depthMeasured = 0.01*float(self._collapseManusPesProperty(
                t, csvRowData,
                TCCE.PES_DEPTH, TCCE.PES_DEPTH_GUESS,
                TCCE.MANUS_DEPTH, TCCE.MANUS_DEPTH_GUESS,
                '0', IFE.HIGH_DEPTH_UNCERTAINTY, 0 ))

            if not existing or t.depthUncertainty == 0:
                t.depthUncertainty = 0.05 if (t.importFlags & IFE.HIGH_DEPTH_UNCERTAINTY) else 0.03

        except Exception as err:
            print(Logger().echoError('DEPTH PARSE ERROR:', err))
            t.depthMeasured = 0.0
            if not existing:
                t.depthUncertainty = 0.05

        #-------------------------------------------------------------------------------------------
        # ROTATION
        #       Parse the rotation into a numerical value and assign appropriate default uncertainty
        try:
            t.rotationMeasured = float(self._collapseLimbProperty(
                t, csvRowData,
                TCCE.LEFT_PES_ROTATION, TCCE.LEFT_PES_ROTATION_GUESS,
                TCCE.RIGHT_PES_ROTATION, TCCE.RIGHT_PES_ROTATION_GUESS,
                TCCE.LEFT_MANUS_ROTATION, TCCE.LEFT_MANUS_ROTATION_GUESS,
                TCCE.RIGHT_MANUS_ROTATION, TCCE.RIGHT_MANUS_ROTATION_GUESS,
                '0', IFE.HIGH_ROTATION_UNCERTAINTY, 0 ))

            if not existing or t.rotationUncertainty == 0:
                t.rotationUncertainty = \
                    10.0 if (t.importFlags & IFE.HIGH_ROTATION_UNCERTAINTY) else 45.0

        except Exception as err:
            print(Logger().echoError('ROTATION PARSE ERROR:', err))
            self._writeError({
                'message':'Rotation parse error',
                'error':err,
                'data':csvRowData,
                'index':csvIndex })

            t.rotationMeasured  = 0.0
            if not existing:
                t.rotationUncertainty = 45.0

        #-------------------------------------------------------------------------------------------
        # STRIDE
        try:
            strideLength = self._collapseManusPesProperty(
                t, csvRowData,
                TCCE.PES_STRIDE, TCCE.PES_STRIDE_GUESS,
                TCCE.MANUS_STRIDE, TCCE.MANUS_STRIDE_GUESS,
                None, IFE.HIGH_STRIDE_UNCERTAINTY )

            strideFactor = self._collapseManusPesProperty(
                t, csvRowData,
                TCCE.PES_STRIDE_FACTOR, None,
                TCCE.MANUS_STRIDE_FACTOR, None, 1.0)

            if strideLength:
                snapshot[SnapshotDataEnum.STRIDE_LENGTH] = 0.01*float(strideLength)*float(strideFactor)
        except Exception as err:
            print(Logger().echoError('STRIDE PARSE ERROR:', err))

        #-------------------------------------------------------------------------------------------
        # WIDTH ANGULATION PATTERN
        try:
            widthAngulation = self._collapseManusPesProperty(
                t, csvRowData,
                TCCE.WIDTH_PES_ANGULATION_PATTERN, TCCE.WIDTH_PES_ANGULATION_PATTERN_GUESS,
                TCCE.WIDTH_MANUS_ANGULATION_PATTERN, TCCE.WIDTH_MANUS_ANGULATION_PATTERN_GUESS,
                None, IFE.HIGH_WIDTH_ANGULATION_UNCERTAINTY )

            if widthAngulation:
                snapshot[SnapshotDataEnum.WIDTH_ANGULATION_PATTERN] = 0.01*float(widthAngulation)
        except Exception as err:
            print(Logger().echoError('WIDTH ANGULATION PARSE ERROR:', err))

        #-------------------------------------------------------------------------------------------
        # PACE
        try:
            pace = self._collapseLimbProperty(
                t, csvRowData,
                TCCE.LEFT_PES_PACE, TCCE.LEFT_PES_PACE_GUESS,
                TCCE.RIGHT_PES_PACE, TCCE.RIGHT_PES_PACE_GUESS,
                TCCE.LEFT_MANUS_PACE, TCCE.LEFT_MANUS_PACE_GUESS,
                TCCE.RIGHT_MANUS_PACE, TCCE.RIGHT_MANUS_PACE_GUESS,
                None, IFE.HIGH_PACE_UNCERTAINTY )

            if pace:
                snapshot[SnapshotDataEnum.PACE] = 0.01*float(pace)
        except Exception as err:
            print(Logger().echoError('PACE PARSE ERROR:', err))

        #-------------------------------------------------------------------------------------------
        # PACE ANGULATION PATTERN
        try:
            paceAngulation = self._collapseManusPesProperty(
                t, csvRowData,
                TCCE.PES_PACE_ANGULATION, TCCE.PES_PACE_ANGULATION_GUESS,
                TCCE.MANUS_PACE_ANGULATION, TCCE.MANUS_PACE_ANGULATION_GUESS,
                None, IFE.HIGH_WIDTH_ANGULATION_UNCERTAINTY )

            if paceAngulation:
                snapshot[SnapshotDataEnum.PACE_ANGULATION_PATTERN] = float(paceAngulation)
        except Exception as err:
            print(Logger().echoError('PACE ANGULATION PARSE ERROR:', err))

        #-------------------------------------------------------------------------------------------
        # PROGRESSION
        try:
            progression = self._collapseLimbProperty(
                t, csvRowData,
                TCCE.LEFT_PES_PROGRESSION, TCCE.LEFT_PES_PROGRESSION_GUESS,
                TCCE.RIGHT_PES_PROGRESSION, TCCE.RIGHT_PES_PROGRESSION_GUESS,
                TCCE.LEFT_MANUS_PROGRESSION, TCCE.LEFT_MANUS_PROGRESSION_GUESS,
                TCCE.RIGHT_MANUS_PROGRESSION, TCCE.RIGHT_MANUS_PROGRESSION_GUESS,
                None, IFE.HIGH_PROGRESSION_UNCERTAINTY )

            if progression:
                snapshot[SnapshotDataEnum.PROGRESSION] = 0.01*float(progression)
        except Exception as err:
            print(Logger().echoError('PROGRESSION PARSE ERROR:', err))

        #-------------------------------------------------------------------------------------------
        # GLENO-ACETABULAR DISTANCE
        try:
            gad = self._collapseGuessProperty(
                t, csvRowData,
                TCCE.GLENO_ACETABULAR_DISTANCE, TCCE.GLENO_ACETABULAR_DISTANCE_GUESS,
                None, IFE.HIGH_GLENO_ACETABULAR_UNCERTAINTY )

            if gad:
                snapshot[SnapshotDataEnum.GLENO_ACETABULAR_LENGTH] = 0.01*float(gad)
        except Exception as err:
            print(Logger().echoError('GLENO-ACETABULAR DISTANCE PARSE ERROR:', err))

        # Save the snapshot
        try:
            t.snapshot = JSON.asString(snapshot)
        except Exception:
            raise

        if TrackCsvColumnEnum.MEASURED_BY.name not in snapshot:
            # Mark entries that have no field measurements with a flag for future reference
            t.importFlags |= ImportFlagsEnum.NO_FIELD_MEASUREMENTS

        if existing:
            self.modified.append(t)
        else:
            self.created.append(t)

        return t

#_______________________________________________________________________________
    def _writeError(self, data):
        """ Writes import error data to the logger, formatting it for human readable display. """
        source = {}

        if 'data' in data:
            for n,v in DictUtils.iter(data['data']):
                source[' '.join(n.split('_')).title()] = v

        indexPrefix = ''
        if 'index' in data:
            indexPrefix = ' [INDEX: %s]:' % data.get('index', 'Unknown')

        result  = [
            'IMPORT ERROR%s: %s' % (indexPrefix, data['message']),
            'DATA: ' + DictUtils.prettyPrint(source)]

        if 'existing' in data:
            source = {}
            snapshot = data['existing'].snapshot
            if snapshot:
                snapshot = JSON.fromString(snapshot)
            if snapshot:
                for n,v in DictUtils.iter(snapshot):
                    source[' '.join(n.split('_')).title()] = v
            result.append('CONFLICT: ' + DictUtils.prettyPrint(source))

        if 'error' in data:
            self._logger.writeError(result, data['error'])
        else:
            self._logger.write(result)

#_______________________________________________________________________________
    @classmethod
    def _getStrippedValue(cls, value):
        try:
            return value.strip()
        except Exception:
            return value

#_______________________________________________________________________________
    @classmethod
    def _getStrippedRowData(cls, source, trackCsvEnum):
        out = source.get(trackCsvEnum.name)
        try:
            return out.strip()
        except Exception:
            return out

#_______________________________________________________________________________
    @classmethod
    def _collapseManusPesProperty(
            cls, track, csvRowData, pesEnum, pesGuessEnum, manusEnum, manusGuessEnum,
            defaultValue, guessFlag =0, missingFlag =0
    ):

        if track.pes:
            return cls._collapseGuessProperty(
                track=track,
                csvRowData=csvRowData,
                regularPropertyEnum=pesEnum,
                guessPropertyEnum=pesGuessEnum,
                defaultValue=defaultValue,
                guessFlag=guessFlag,
                missingFlag=missingFlag)
        else:
            return cls._collapseGuessProperty(
                track=track,
                csvRowData=csvRowData,
                regularPropertyEnum=manusEnum,
                guessPropertyEnum=manusGuessEnum,
                defaultValue=defaultValue,
                guessFlag=guessFlag,
                missingFlag=missingFlag)

#_______________________________________________________________________________
    @classmethod
    def _collapseLimbProperty(
            cls, track, csvRowData, lpEnum, lpGuessEnum, rpEnum, rpGuessEnum, lmEnum, lmGuessEnum,
            rmEnum, rmGuessEnum, defaultValue, guessFlag =0, missingFlag =0
    ):

        if track.pes and track.left:
            return cls._collapseGuessProperty(
                track, csvRowData, lpEnum, lpGuessEnum, defaultValue, guessFlag, missingFlag)
        elif track.pes and not track.left:
            return cls._collapseGuessProperty(
                track, csvRowData, rpEnum, rpGuessEnum, defaultValue, guessFlag, missingFlag)
        elif not track.pes and track.left:
            return cls._collapseGuessProperty(
                track, csvRowData, lmEnum, lmGuessEnum, defaultValue, guessFlag, missingFlag)
        elif not track.pes and not track.left:
            return cls._collapseGuessProperty(
                track, csvRowData, rmEnum, rmGuessEnum, defaultValue, guessFlag, missingFlag)
        else:
            return None

#_______________________________________________________________________________
    @classmethod
    def _collapseGuessProperty(
            cls, track, csvRowData, regularPropertyEnum, guessPropertyEnum, defaultValue,
            guessFlag =0, missingFlag =0
    ):
        value = cls._getStrippedRowData(csvRowData, regularPropertyEnum)
        if guessPropertyEnum:
            valueGuess = cls._getStrippedRowData(csvRowData, guessPropertyEnum)
        else:
            valueGuess = None

        if not value:
            if not valueGuess:
                track.importFlags |= (missingFlag & guessFlag)
                return defaultValue

            track.importFlags |= guessFlag
            return valueGuess

        return value

#===============================================================================
#                                                                               I N T R I N S I C

#_______________________________________________________________________________
    def __repr__(self):
        return self.__str__()

#_______________________________________________________________________________
    def __unicode__(self):
        return StringUtils.toUnicode(self.__str__())

#_______________________________________________________________________________
    def __str__(self):
        return '<%s>' % self.__class__.__name__
Ejemplo n.º 10
0
class WidgetUiCompiler(object):
    """A class for..."""

#===================================================================================================
#                                                                                       C L A S S

    _CLASS_NAME_RE = re.compile('(?<=class )(?P<classname>[a-zA-z0-9_]+)(?=\()')

    _SETUP_UI_RE = re.compile('(?<=def setupUi\(self, )(?P<parentName>[a-zA-z0-9_\-]+)(?=\):)')

    _RETRANSLATE_RE = re.compile(
        '(?<=def retranslateUi\(self, )(?P<parentName>[a-zA-z0-9_\-]+)(?=\):)'
    )

    _SELF_RE = re.compile('(?P<self>self\.)(?!retranslateUi\()')

#___________________________________________________________________________________________________ __init__
    def __init__(self, rootPath =None, recursive =True, **kwargs):
        """Creates a new instance of WidgetUiCompiler."""
        self._log        = Logger(self)
        self._verbose    = ArgsUtils.get('verbose', False, kwargs)
        self._recursive  = recursive
        self._pythonPath = os.path.normpath(sys.exec_prefix)

        if rootPath and os.path.isabs(rootPath):
            self._rootPath = FileUtils.cleanupPath(rootPath, isDir=True)
        elif rootPath:
            parts = rootPath.split(os.sep if rootPath.find(os.sep) != -1 else '/')
            self._rootPath = PyGlassEnvironment.getRootResourcePath(*parts, isDir=True)
        else:
            self._rootPath = PyGlassEnvironment.getRootResourcePath()

#===================================================================================================
#                                                                                     P U B L I C

#___________________________________________________________________________________________________ run
    def run(self):
        """Doc..."""

        arg = dict()
        if self._recursive:
            os.path.walk(self._rootPath, self._compileInFolder, arg)
        else:
            self._compileInFolder(arg, self._rootPath, os.listdir(self._rootPath))

#===================================================================================================
#                                                                               P R O T E C T E D

#___________________________________________________________________________________________________ _compileInFolder
    def _compileInFolder(self, arg, dirname, names):
        for name in names:
            if not name.endswith('.ui'):
                continue
            self._compileUiFile(dirname, name)

#___________________________________________________________________________________________________ _compileUiFile
    def _compileUiFile(self, path, filename):
        """Doc..."""

        source = FileUtils.createPath(path, filename, isFile=True)
        if self._verbose:
            self._log.write('COMPILING: ' + source)

        if PyGlassEnvironment.isWindows:
            uicCommand = FileUtils.createPath(self._pythonPath, 'Scripts', 'pyside-uic.exe')
        else:
            uicCommand = 'pyside-uic'

        cmd = '%s %s' % (uicCommand, source)
        pipe = subprocess.Popen(
            cmd,
            shell=True,
            stdin=subprocess.PIPE,
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE)
        out, error = pipe.communicate()

        if pipe.returncode or error:
            self._log.write('ERROR: Failed to compile %s widget: %s' % (str(source), str(error)))
            return False

        res = WidgetUiCompiler._CLASS_NAME_RE.search(out)
        if not res:
            self._log.write('ERROR: Failed to find widget class name for ' + str(source))
            return False
        out = WidgetUiCompiler._CLASS_NAME_RE.sub('PySideUiFileSetup', out, 1)

        res = WidgetUiCompiler._SETUP_UI_RE.search(out)
        if not res:
            self._log.write('ERROR: Failed to find widget setupUi method for ' + str(source))
            return False
        targetName = res.groupdict().get('parentName')
        out = WidgetUiCompiler._SETUP_UI_RE.sub('\g<parentName>', out, 1)

        res = WidgetUiCompiler._RETRANSLATE_RE.search(out)
        if not res:
            self._log.write('ERROR: Failed to find widget retranslateUi method for ' + str(source))
            return False
        out = WidgetUiCompiler._RETRANSLATE_RE.sub('\g<parentName>', out, 1)

        if isinstance(out, unicode):
            out = out.encode('utf8', 'ignore')

        out = WidgetUiCompiler._SELF_RE.sub(targetName + '.', out)

        dest = FileUtils.createPath(path, filename[:-3] + '.py', isFile=True)
        if os.path.exists(dest):
            os.remove(dest)
        f = open(dest, 'w+')
        f.write(out)
        f.close()

        py_compile.compile(dest)
        return True


#===================================================================================================
#                                                                               I N T R I N S I C

#___________________________________________________________________________________________________ __repr__
    def __repr__(self):
        return self.__str__()

#___________________________________________________________________________________________________ __unicode__
    def __unicode__(self):
        return unicode(self.__str__())

#___________________________________________________________________________________________________ __str__
    def __str__(self):
        return '<%s>' % self.__class__.__name__
Ejemplo n.º 11
0
class DataFormatConverter(object):
    """A class for converting between various data interchange formats, e.g. XML and JSON."""

#===================================================================================================
#                                                                                       C L A S S

#___________________________________________________________________________________________________ __init__
    def __init__(self):
        """Creates a new instance of ClassTemplate."""
        self._type = None
        self._src  = None
        self._log  = Logger('DataFormatConverter')
        self._path = None

#===================================================================================================
#                                                                                   G E T / S E T

#___________________________________________________________________________________________________ GS: propertyName
    @property
    def source(self):
        return self._src

#===================================================================================================
#                                                                                     P U B L I C

#___________________________________________________________________________________________________ load
    def load(self, path, fileType):
        if not os.path.exists(path):
            self._log.write('ERROR: Path does not exist [%s]. Unable to load.' % path)
            return False

        try:
            fh  = codecs.open(path, 'r', 'utf-8')
            res = fh.read()
            fh.close()
            enc = res.encode('utf-8')
            self.loads(enc, fileType)
        except Exception as err:
            self._log.writeError('Failed to load source file [%s].' % path, err)
            return False

        self._path = path
        return True

#___________________________________________________________________________________________________ load
    def loads(self, srcString, srcType):
        if srcString is None:
            self._log.write('ERROR: Source string is empty or invalid.')
            return False

        srcString = StringUtils.toStr2(srcString)

        self._path = None
        self._src  = srcString
        self._type = srcType
        return True

#___________________________________________________________________________________________________ convertDirectory
    def convertDirectory(self, path, srcType, targetType, recursive =False):
        if srcType is None or targetType is None:
            self._log.write('ERROR: Source and/or target types are invalid. Operation aborted.')
            return False

        if not os.path.exists(path):
            self._log.write('ERROR: The specified path [%s] does not exist. Operation aborted.' \
                            % str(path))
            return False

        if recursive:
            FileUtils.walkPath(path, self._convertInDirectory, [srcType, targetType])
        else:
            self._convertInDirectory([srcType, targetType], path, os.listdir(path))

        return True

#___________________________________________________________________________________________________ writeToFile
    def writeToFile(self, targetType, path =None):
        if path is None and self._path is None:
            self._log.write('ERROR: Unable to write to file, no path was specified.')
            return False

        # Assign the reader based on source type
        reader = self._getParserFromType()
        if reader is None:
            self._log.write('ERROR: Unrecognized source type [%s]. Unable to convert.' % self._type)
            return False

        # Assign writer based on target type
        writer = self._getParserFromType(targetType)
        if writer is None:
            self._log.write('ERROR: Unrecognized conversion target type [%s]. Unable to convert.' \
                            % targetType)
            return False

        path = path if path else self._path
        d    = os.path.dirname(path)
        f    = os.path.basename(path).split('.')[0]
        f   += '.' + writer.TYPE_ID

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

        try:
            print(len(self._src))
            src = reader.parse(self._src, None, True)
        except Exception as err:
            self._log.writeError('ERROR: Failed to parse source. Conversion aborted.', err)
            return False

        try:
            res = writer.serialize(src)
        except Exception as err:
            self._log.writeError('ERROR: Failed to serialized data. Conversion aborted.', err)
            return False

        out = os.path.join(d, f)
        try:
            fh = codecs.open(out, 'wb', 'utf-8')
            fh.write(res)
            fh.close()
        except Exception as err:
            self._log.writeError('ERROR: Failed to write file [%s]. Conversion aborted.' \
                                 % str(out), err)
            return False

        self._log.write('Converted: [%s] => [%s].' % (self._path, out))
        return True

#___________________________________________________________________________________________________ getAsXML
    def getAsXML(self):
        """Doc..."""
        if self._type == XMLConfigParser.TYPE_ID:
            return self._src
        else:
            return self._convert(XMLConfigParser.TYPE_ID)

#___________________________________________________________________________________________________ getAsJSON
    def getAsJSON(self):
        """Doc..."""
        if self._type == JSONConfigParser.TYPE_ID:
            return self._src
        else:
            return self._convert(JSONConfigParser.TYPE_ID)

#___________________________________________________________________________________________________ getAsDictionary
    def getAsDictionary(self, asInterchangeFormat =False):
        reader = self._getParserFromType()
        reader.parse(self._src, None, asInterchangeFormat)

#___________________________________________________________________________________________________ executeConversion
    @staticmethod
    def executeConversion(source =None, srcType =None, targetType =None, target =None, recursive =False):
        types = ['xml', 'json']

        if source is None:
            source = queryGeneralValue('Enter the source file (or path) to convert:')

        if srcType is None and os.path.isfile(source):
            fileType = source.split('.')[-1].lower()
            if fileType in types:
                srcType = fileType

        if srcType is None:
            srcType = queryFromList('Specify source file(s) type:', types)

        if targetType is None:
            targetType = queryFromList('Specify target file(s) type:', types)

        d = DataFormatConverter()
        if os.path.isfile(source):
            d.load(source, srcType)
            d.writeToFile(targetType, target)
        else:
            d.convertDirectory(source, srcType, targetType, recursive)

#===================================================================================================
#                                                                               P R O T E C T E D

#___________________________________________________________________________________________________ _convert
    def _convert(self, targetType):
        reader = self._getParserFromType()
        data   = reader.parse(self._src, None, True)

        if data is None:
            self._log.write('ERROR: Failed to parse input from. Skipping conversion.')
            return None

        writer = self._getParserFromType(targetType)
        return writer.serialize(data)

#___________________________________________________________________________________________________ _getParserFromType
    def _getParserFromType(self, typeID =None):
        if typeID is None:
            typeID = self._type

        if typeID == XMLConfigParser.TYPE_ID:
            return XMLConfigParser
        elif typeID == JSONConfigParser.TYPE_ID:
            return JSONConfigParser
        else:
            self._log.write('ERROR: _getParserFromType() failed for type: ' + str(typeID))
            return None

#___________________________________________________________________________________________________ _convertInDirectory
    def _convertInDirectory(self, types, dirname, names):
        if dirname.find('.svn') != -1:
            return

        reader = self._getParserFromType(types[0])
        writer = self._getParserFromType(types[1])
        for n in names:
            if not n.endswith(reader.TYPE_ID):
                continue

            src = os.path.join(dirname, n)
            self.load(src, reader.TYPE_ID)
            self.writeToFile(writer.TYPE_ID)
Ejemplo n.º 12
0
    def populateTrackwaysTable(cls, session =None, logger =None):
        """ Populate the trackways table by removing all existing rows and
            attempting to calculate trackways from the implicit track series
            defined by the linkages of tracks. This operation should only be
            carried out for initial population purposes as it will dispose of
            all existing data and changes made by users. """

        from cadence.models.tracks.Tracks_Track import Tracks_Track
        from cadence.models.tracks.Tracks_SiteMap import Tracks_SiteMap

        missingSitemaps = []

        sitemapModel = Tracks_SiteMap.MASTER
        trackModel   = Tracks_Track.MASTER
        model        = cls.MASTER

        if not logger:
            logger = Logger(cls, printOut=True)

        newSession = False
        if not session:
            newSession = True
            session = model.createSession()

        # Get all tracks that have no next (end of a track series)
        endTracks = session.query(trackModel).filter(
            trackModel.next == '').all()

        index     = 0
        trackways = dict()
        tested    = []

        for track in endTracks:
            if track in tested or track.hidden:
                # Skip tracks that have already been tested or are hidden
                continue

            prev = track
            while prev:
                tested.append(prev)
                t = prev.getPreviousTrack()
                if not t:
                    break
                prev = t

            if not prev:
                continue

            name = prev.trackwayFingerprint

            sitemapStamp = '%s-%s' % (prev.site, prev.level)
            if sitemapStamp in missingSitemaps:
                # Ignore the trackway if there's no sitemap to support it
                continue

            if name in trackways:
                tw = trackways[name]
            else:
                # If the trackway name isn't in the list of existing trackways,
                # create a new trackway model instance for that trackway

                tw       = Tracks_Trackway()
                tw.index = index
                tw.name  = name

                sitemap = session.query(sitemapModel).filter(
                    sitemapModel.name == prev.site).filter(
                    sitemapModel.level == prev.level).first()
                if not sitemap:
                    missingSitemaps.append(sitemapStamp)
                    logger.write(
                        '[WARNING]: No site map found for "%s" level "%s"' % (
                        prev.site, prev.level))
                else:
                    tw.siteMapIndex = sitemap.index
                    index += 1
                    trackways[tw.name] = tw

            if prev.left and prev.pes:
                existing = tw.firstLeftPes
                tw.firstLeftPes = prev.uid
            elif prev.left:
                existing = tw.firstLeftManus
                tw.firstLeftManus = prev.uid
            elif prev.pes:
                existing = tw.firstRightPes
                tw.firstRightPes = prev.uid
            else:
                existing = tw.firstRightManus
                tw.firstRightManus = prev.uid

            if existing and existing != prev.uid:
                logger.write([
                    '[WARNING]: Duplicate tracks found for the same series',
                    'TRACKS: "%s" AND "%s"' % (existing, prev.uid),
                    'TRACKWAY: %s' % tw.name])

        # Delete all existing rows if any exist
        rowCount = session.query(model).count()
        if rowCount > 0:
            session.query(model).delete()

        # Add the new trackways entries to the session
        for fingerprint, trackway in trackways.items():
            session.add(trackway)

        if newSession:
            session.commit()
            session.close()
Ejemplo n.º 13
0
class IncludeCompressor(object):

#===================================================================================================
#                                                                                       C L A S S

    _REMOVE_COMMENT_RE      = re.compile('/\*.+\*/', re.DOTALL)
    _REMOVE_COMMENT_LINE_RE = re.compile('(^|\n)[\s\t]*//.+(\n|$)')

    JS_TYPE  = 'js'
    CSS_TYPE = 'css'

#___________________________________________________________________________________________________ __init__
    def __init__(self, compileCoffee =False):
        self._log           = Logger('IncludeCompressor')
        self._compileCoffee = compileCoffee

#===================================================================================================
#                                                                                     P U B L I C

#___________________________________________________________________________________________________ compress
    def compress(self, rootPath):
        if not self._fileExists(rootPath):
            return False
        elif os.path.isfile(rootPath):
            return self.compressFile(rootPath)
        else:
            return self.compressPath(rootPath)

#___________________________________________________________________________________________________ compressFile
    def compressFile(self, rootPath, directory =None):
        if not self._fileExists(rootPath):
            return False

        if self._compileCoffee:
            try:
                from pyaid.web.coffeescript.CoffeescriptBuilder import CoffeescriptBuilder
                CoffeescriptBuilder.compileAllOnPath(rootPath, os.path.dirname(rootPath), True)
                self._log.write('Coffeescript compiled.')
            except Exception as err:
                self._log.writeError('Failed to compile coffeescript file.', err)
                return False

        return self._compressFile(rootPath, directory)

#___________________________________________________________________________________________________ compressPath
    def compressPath(self, rootPath):
        # First compile any coffee scripts to js files
        if self._compileCoffee:
            try:
                from pyaid.web.coffeescript.CoffeescriptBuilder import CoffeescriptBuilder
                CoffeescriptBuilder.compileAllOnPath(rootPath, rootPath, True)
                self._log.write('Coffee scripts compiled.')
            except Exception as err:
                self._log.writeError('Failed to compile coffeescript files.', err)
                return False

        FileUtils.walkPath(rootPath, self._compressInFolder, None)
        self._log.write('Compression operation complete.')
        return True

#===================================================================================================
#                                                                               P R O T E C T E D

#___________________________________________________________________________________________________ _fileExists
    def _fileExists(self, rootPath):
        if not os.path.exists(rootPath):
            self._log.write('ERROR: [%s] does not exist. Operation aborted.' % rootPath)
            return False

        return True

#___________________________________________________________________________________________________ _compressFile
    def _compressFile(self, target, directory):
        # Skip compiled files.
        if target.endswith('comp.js') or target.endswith('comp.css'):
            return False

        if target.endswith('.js'):
            fileType = IncludeCompressor.JS_TYPE
        elif target.endswith('.css'):
            fileType = IncludeCompressor.CSS_TYPE
        else:
            return False

        if not directory:
            directory = ''
        if not directory.endswith(os.sep) and not target.startswith(os.sep):
            directory += os.sep

        inFile     = directory + target
        tempFile   = directory + target + '.temp'

        try:
            fh         = open(inFile, 'r')
            fileString = fh.read()
            fh.close()
        except Exception as err:
            self._log.writeError('FAILED: Unable to read ' + str(inFile), err)
            return False

        if fileType == IncludeCompressor.CSS_TYPE:
            fileString = fileString.replace('@charset "utf-8";', '')
            ofn        = (target[0:-3] + 'comp.css')
        else:
            ofn = (target[0:-2] + 'comp.js')

        try:
            fh = open(tempFile, 'w')
            fh.write(fileString)
            fh.close()
        except Exception as err:
            self._log.writeError('FAILED: Unable to write temp file ' + str(tempFile), err)
            return False

        outFile = directory + '/' + ofn

        cmd    = ['minify', '"%s"' % tempFile, '"%s"' % outFile]
        result = SystemUtils.executeCommand(cmd)
        if result['code']:
            self._log.write('FAILED: Unable to compress ' + str(inFile))

        if os.path.exists(tempFile):
            os.remove(tempFile)

        if not os.path.exists(outFile):
            self._log.write('FAILED: ' + target + ' -> ' + ofn)
            return False
        elif fileType == IncludeCompressor.JS_TYPE:
            f          = open(outFile, 'r')
            compressed = f.read()
            f.close()

            compressed = IncludeCompressor._REMOVE_COMMENT_RE.sub('', compressed)
            compressed = IncludeCompressor._REMOVE_COMMENT_LINE_RE.sub('', compressed)

            f = open(outFile, 'w')
            f.write(compressed.strip())
            f.close()

        inSize  = SizeUnits.SizeConversion.bytesToKilobytes(inFile, 2)
        outSize = SizeUnits.SizeConversion.bytesToKilobytes(outFile, 2)
        saved   = SizeUnits.SizeConversion.convertDelta(
            inSize, outSize, SizeUnits.SIZES.KILOBYTES, 2)

        self._log.write(
            'Compressed[%s]: %s -> %s [%sKB -> %sKB | Saved: %sKB]' % (
                fileType, target, ofn, inSize, outSize, saved))

        return True

#___________________________________________________________________________________________________ _compressInFolder
    def _compressInFolder(self, dumb, directory, names):
        if directory.find('.svn') != -1:
                return

        for fn in names:
            self._compressFile(fn, directory)
Ejemplo n.º 14
0
class CoffeescriptBuilder(object):
    """A class for..."""

    CLASS_PATTERN = '^[\s\t]*class[\s\t]+(?P<class>[^\s\t\r\n]+)[\s\t]*'
    MISSING_CLASS_PATTERN = '[\s\t\(\[\{\!]+(?=[A-Z])(?P<class>[A-Za-z0-9_]+)(?P<next>[^A-Za-z0-9_]+)'

    _WARN_ID_MISSING_IMPORT = 'MISSING-IMPORT'

    _GLOBAL_CLASSES = [
        'SFLOW', 'PAGE', 'FB', 'Math', 'JSON', 'String', 'ActiveXObject',
        'Date', 'DOMParser', 'RegExp', 'Object', 'Number', 'Array', 'Function',
        'XMLHttpRequest'
    ]

    _results = None
    _missing = None

    #===================================================================================================
    #                                                                                       C L A S S

    #___________________________________________________________________________________________________ __init__
    def __init__(self,
                 targetPackageOrPath,
                 rootPath,
                 verbose=True,
                 debug=False,
                 trace=False,
                 force=False,
                 compress=False,
                 buildOnly=False):
        """Creates a new instance of CoffeescriptBuilder."""

        self.buildOnly = buildOnly

        self._imports = dict()
        self._requires = dict()
        self._includes = dict()
        self._report = dict()
        self._warnings = []
        self._dependencyReport = dict()
        self._verbose = verbose
        self._log = Logger(self, printOut=True)
        self._trace = trace
        self._debug = debug
        self._targets = []
        self._force = force
        self._compress = compress
        self._rootPath = rootPath

        if not isinstance(targetPackageOrPath, CoffeescriptDependency):
            target = CoffeescriptDependency(targetPackageOrPath, rootPath,
                                            None)
        else:
            target = targetPackageOrPath

        if target.exists:
            self._targets.append(target)
        else:
            csFiles = CoffeescriptBuilder.getScriptsInPath(target.packagePath)

            # Look for exec matches first
            for f in csFiles:
                testTarget = CoffeescriptDependency(f, rootPath, None)
                if testTarget.isExec:
                    self._targets.append(testTarget)

            # Look for lib matches second. Lib matches are tested as a second pass because
            # constructing all exec files first potentially optimizes the import process for
            # the libraries.
            for f in csFiles:
                testTarget = CoffeescriptDependency(f, rootPath, None)
                if testTarget.isLib:
                    self._targets.append(testTarget)

        if len(self._targets) == 0:
            print('\n\n')
            self._log.write('No targets exist for: %s. Compilation aborted.' %
                            targetPackageOrPath)
            print('\n')

#===================================================================================================
#                                                                                   G E T / S E T

#___________________________________________________________________________________________________ GS: report

    @property
    def report(self):
        return self._report

#___________________________________________________________________________________________________ GS: warnings

    @property
    def warnings(self):
        return self._warnings

#___________________________________________________________________________________________________ GS: imports

    @property
    def imports(self):
        return self._imports

#___________________________________________________________________________________________________ GS: requires

    @property
    def requires(self):
        return self._requires

#___________________________________________________________________________________________________ GS: includes

    @property
    def includes(self):
        return self._includes

#===================================================================================================
#                                                                                     P U B L I C

#___________________________________________________________________________________________________ construct

    def construct(self):
        """Doc..."""
        for t in self._targets:
            self._report[t.package] = -1
            if t.isLib:
                self._constructLibrary(t)
            else:
                self._constructTarget(t)

            if self._compress:
                print('COMPRESSING:', t.package)
                from pyaid.web.coffeescript.IncludeCompressor import IncludeCompressor
                ic = IncludeCompressor()
                if not ic.compressFile(t.compiledPath):
                    print('COMPRESSION FAILURE:', t.compiledPath)

        return self._targets

#___________________________________________________________________________________________________ compileAllOnPath

    @staticmethod
    def compileAllOnPath(path,
                         rootPath=None,
                         recursive=False,
                         debug=False,
                         trace=False,
                         force=False,
                         compress=False):

        CoffeescriptBuilder._results = ''
        CoffeescriptBuilder._missing = {}
        if recursive:
            print('RECURSIVE COMPILE AT: ' + path)

            def walker(paths, dirName, names):
                out = CoffeescriptBuilder._compileAllInDirectory(
                    os.path.join(paths[0], dirName),
                    paths[1],
                    debug=debug,
                    trace=trace,
                    force=force,
                    compress=compress)
                CoffeescriptBuilder._results += out['res']
                for n, v in DictUtils.iter(out['missing']):
                    if n in CoffeescriptBuilder._missing:
                        continue
                    CoffeescriptBuilder._missing[n] = v

            FileUtils.walkPath(path, walker, [path, rootPath])
            print('\n\nCOMPILATION RESULTS:' + CoffeescriptBuilder._results)

            if CoffeescriptBuilder._missing:
                print('\n\nMISSING IMPORTS:' + '\n\n')
                for n, v in DictUtils.iter(CoffeescriptBuilder._missing):
                    print(v['class'] + ' [LINE: #' + str(v['line']) + ' | ' +
                          v['package'] + ']')
        else:
            print('COMPILING DIRECTORY: ' + path)
            CoffeescriptBuilder._compileAllInDirectory(path,
                                                       rootPath,
                                                       debug=debug,
                                                       trace=trace,
                                                       force=force,
                                                       compress=compress)

#___________________________________________________________________________________________________ getScriptsInPath

    @staticmethod
    def getScriptsInPath(path):
        files = []

        for f in os.listdir(path):
            if f.lower().endswith('.' + CoffeescriptDependency.EXTENSION):
                files.append(os.path.join(path, f))

        return files

#===================================================================================================
#                                                                               P R O T E C T E D

#___________________________________________________________________________________________________ _constructLibrary

    def _constructLibrary(self, target):
        try:
            if self._verbose:
                print("\n\n" + ('-' * 100) + '\n')
                self._log.add('LIBRARY: %s\n\tsource: %s\n\troot: %s' %
                              (target.package, target.path, target.rootPath))

            #---------------------------------------------------------------------------------------
            # Compile all includes using library data
            targets, imports, modules, includes = self._getLibraryData(target)

            # Process requires for all of the targets
            for t in (targets + imports + modules):
                self._processRequires(t)

            #---------------------------------------------------------------------------------------
            # IMPORTS

            # Compile all excludes skipping any exec or lib files that are listed in the import
            # statements.
            importExcludes = []
            for t in targets:
                for imp in self._imports[t.package]:
                    if not (imp.isExec or imp.isLib
                            or imp.isInList(importExcludes)):
                        importExcludes.append(imp)

            # Compile all imports needed for the library. Any excludes are added to the shared
            # library to be made accessible via the VIZME registry.
            libImports = []
            sharedImports = []
            for t in (imports + modules):
                for imp in self.imports[t.package]:
                    if not imp.isInList(libImports):
                        if imp.isInList(importExcludes):
                            if not imp.isInList(sharedImports):
                                sharedImports.append(imp)
                        else:
                            libImports.append(imp)
            libImports.append(target)

            #---------------------------------------------------------------------------------------
            # INCLUDES

            # Compile all includes to exclude from the library because they already exist in a
            # target.
            includeExcludes = []
            for t in targets:
                for inc in self._includes[t.package]:
                    if not inc.isInList(includeExcludes):
                        includeExcludes.append(inc)

            # Compile all includes needed for the library.
            libIncludes = []
            sharedIncludes = []

            # Add the top-level includes directly because they are not handled implicitly like
            # the import case
            for inc in includes:
                if inc.isInList(includeExcludes):
                    sharedIncludes.append(inc)
                else:
                    libIncludes.append(inc)

            for t in (imports + modules):
                for inc in self.includes[t.package]:
                    if not inc.isInList(libIncludes):
                        if inc.isInList(includeExcludes):
                            if not inc.isInList(sharedIncludes):
                                sharedIncludes.append(inc)
                        else:
                            libIncludes.append(inc)

            if self._verbose:
                print('\n')
                s = 'IMPORTING:'
                for imp in libImports:
                    s += '\n\t' + imp.package
                for inc in libIncludes:
                    s += '\n\tEXTERNAL: ' + inc.package
                self._log.add(s)

                print('\n')
                s = 'EXCLUDING:'
                for imp in sharedImports:
                    s += '\n\t' + imp.package
                for inc in sharedIncludes:
                    s += '\n\tEXTERNAL: ' + inc.package
                self._log.add(s)

            #---------------------------------------------------------------------------------------
            # Construct intermediate compilation file.
            assembledFile = self._assembleFile(target, libImports,
                                               sharedImports,
                                               {'modules': modules})
            if assembledFile is None:
                self._log.write('ERROR: File assembly failed.')
                return

            #---------------------------------------------------------------------------------------
            # Compile to Javascript
            if not self.buildOnly:
                self._compileToJavascript(target, assembledFile, libIncludes)

            if self._verbose:
                print("\n" + ('-' * 100) + '\n')

        except Exception as err:
            print("\n\n\n")
            self._log.writeError(
                'ERROR: Compilation failure for: %s\n\tsource: %s\n\troot: %s'
                % (target.package, target.path, target.rootPath), err)

#___________________________________________________________________________________________________ _constructTarget

    def _constructTarget(self, target):
        try:
            if self._verbose:
                print("\n\n" + ('-' * 100) + '\n')
                self._log.write('EXECUTABLE: %s\n\tsource: %s\n\troot: %s' %
                                (target.package, target.path, target.rootPath))

            #---------------------------------------------------------------------------------------
            # Handle imports and requires
            self._parseIncludes(target)
            self._processRequires(target)

            if self._verbose:
                s = 'IMPORTING:'
                for imp in self._imports[target.package]:
                    s += '\n\t' + imp.package
                self._log.write(s)

            #---------------------------------------------------------------------------------------
            # Construct intermediate compilation file.
            assembledFile = self._assembleFile(target)
            if assembledFile is None:
                self._log.write('ERROR: File assembly failed.')
                return

            #---------------------------------------------------------------------------------------
            # Compile to Javascript
            if not self.buildOnly:
                self._compileToJavascript(target, assembledFile)

            if self._verbose:
                print("\n" + ('-' * 100) + '\n')

        except Exception as err:
            print("\n\n\n")
            self._log.writeError(
                'ERROR: Compilation failure for: %s\n\tsource: %s\n\troot: %s'
                % (target.package, target.path, target.rootPath), err)

#___________________________________________________________________________________________________ _createOutputFile

    def _createOutputFile(self, target):
        """Creates the output ccs assembly file for writing."""
        outFile = target.assembledPath
        try:
            return open(outFile, 'w')
        except Exception as err:
            print("\n\n")
            self._log.write('Unable To Open output file: ' + str(outFile) + '\n' \
                            'Check to make sure you have write permissions to that directory.')
            return None

#___________________________________________________________________________________________________ _writeRegistryEntry

    def _writeRegistryEntry(self, out, cacheOut, entry):
        # If there is an unconsumed registryEntry write it.
        if not entry:
            return None

        s = '\n' + entry + '\n'
        out.write(s)

        if cacheOut:
            cacheOut.write(s)
        return None

#___________________________________________________________________________________________________ _assembleFile

    def _assembleFile(self,
                      target,
                      importOverride=None,
                      replacements=None,
                      assembleData=None):

        #-------------------------------------------------------------------------------------------
        # CREATE FILE
        # Creates the file to write
        out = self._createOutputFile(target)
        if not out:
            self._log('ERROR: Unable to create output file')
            return

        #-------------------------------------------------------------------------------------------
        # DEFINE IMPORTS
        # Specify the files to import. For exec files the default packages are included, for
        # libraries these are overridden based on library target dependencies.
        targetImports = self._imports[
            target.package] if importOverride is None else importOverride

        replacements = replacements if isinstance(replacements, list) else []
        classList = []

        #-------------------------------------------------------------------------------------------
        # Note the last dependency so that the glue script can be appended prior
        lastDep = targetImports[-1]

        #-------------------------------------------------------------------------------------------
        # DEPENDENCY ASSEMBLY LOOP
        print('\n')
        for dep in targetImports:
            dep.open()

            if self._force or not dep.useCache:
                if not self._compileDependency(dep, out, replacements,
                                               targetImports, classList):
                    return None
                continue

            self._log.write('\tFROM CACHE: ' + dep.package)
            out.write(dep.cacheSource)
            dep.close()

        out.close()

        if self._verbose:
            print('\n')
            self._log.add('CONSTRUCTED: ' + out.name)

        return out.name

#___________________________________________________________________________________________________ _compileDependency

    def _compileDependency(self, dep, out, replacements, targetImports,
                           classList):
        classPattern = re.compile(CoffeescriptBuilder.CLASS_PATTERN)
        missingPattern = re.compile(CoffeescriptBuilder.MISSING_CLASS_PATTERN)

        #-------------------------------------------------------------------------------------------
        # MISSING DEPENDENCIES
        # Handle missing dependencies
        if not os.path.exists(dep.path):
            print("\n\n")
            self._log.write('ERROR: ' + dep.package +
                            ' package does not exist at: ' + dep.path)
            return False

        lastWhitespace = ''
        openParens = 0
        openBrackets = 0
        openBraces = 0
        skipNextLine = False
        methodName = ''
        className = ''
        registryEntry = None

        raw = dep.source
        dep.close()

        s = '\n\n\t#' + ('%' * 100) + '\n\t#' + (
            '%' * 100) + '\n#\t\t' + dep.package + '\n'

        out.write(s)
        if dep.allowCaching:
            cacheOut = open(dep.cachePath, 'w')
            cacheOut.write(s)
        else:
            try:
                if os.path.exists(dep.cachePath):
                    os.remove(dep.cachePath)
            except Exception as err:
                pass

            cacheOut = None

        self._log.write('\tCOMPILING: ' + dep.package)

        analyzer = CoffeescriptAnalyzer(raw, debug=self._debug)
        analyzer.analyze()

        #---------------------------------------------------------------------------------------
        # COMPILE
        # Line by line compile to ccs output file
        for l in analyzer:

            #-----------------------------------------------------------------------------------
            # RETARGET CLASS ACCESSORS TO VIZME registry
            # All classes (except internal class references) are made to
            # VIZME registry ClassName to prevent class conflicts.
            for rep in replacements + targetImports:
                if rep != dep:
                    offset = 0
                    res = rep.searchPattern.finditer(l.redacted)
                    for r in res:
                        start = r.start() + offset
                        end = r.end() + offset

                        if self._trace:
                            self._log.write('RETARGET: ' +
                                            l.source[start:end] + ' | ' +
                                            str(r.groupdict()))

                        # Make the replacement and adjust offsets for additional replacements
                        l.insert(start, end, rep.registryName)
                        offset += len(rep.registryName) - end + start

            #-----------------------------------------------------------------------------------
            # IDENTIFY CLASS DEFINITIONS
            # Find class definitions so they can be added to the VIZME registry.
            res = classPattern.search(l.redacted)
            if res:
                registryEntry = self._writeRegistryEntry(
                    out, cacheOut, registryEntry)
                className = res.group('class').strip()
                registryEntry = '\n%s.%s ?= %s' % (
                    CoffeescriptDependency.REGISTRY, className, className)
                classList.append(className)

            #-----------------------------------------------------------------------------------
            # CHECK FOR MISSING CLASSES
            # Search and find any missing class imports. If a possible missing import is found
            # flag it in the response.
            res = missingPattern.finditer(l.redacted)
            if res:
                for r in res:
                    cn = r.group('class').strip()
                    start = r.start()

                    if cn == className:
                        continue

                    # Ignore anything in all CAPS!
                    if cn.upper() == cn:
                        continue

                    # Ignore globally defined objects and classes
                    if cn in CoffeescriptBuilder._GLOBAL_CLASSES + analyzer.globalObjects:
                        continue

                    self._warnings.append({
                        'id': CoffeescriptBuilder._WARN_ID_MISSING_IMPORT,
                        'class': cn,
                        'line': l.lineNumber,
                        'package': dep.package
                    })

                    print('\n')
                    self._log.write(
                        'WARNING: Possible missing import\n\tmissing: %s\n\tfrom: %s [line #%s]'
                        % (cn, dep.package, str(l.lineNumber)))

            #-----------------------------------------------------------------------------------
            # LINE DEBUGGER ANALYSIS
            c = l.redacted.strip()
            skip = skipNextLine or not l.isSignificant
            skipNextLine = False

            if not skip:
                skips = [
                    'class', 'try', 'catch', 'else', 'when', '.', '+', '-',
                    '/', '=', '*', ',', 'and', 'or'
                ]
                for s in skips:
                    if c.startswith(s):
                        skip = True
                        break

            if not skip:
                skips = ['->', '=>']
                methodPattern = re.compile('^(?P<method>[^:]+)')

                for s in skips:
                    if c.endswith(s):
                        skip = True
                        res = methodPattern.search(c)
                        if res and res.group('method'):
                            methodName = res.group('method')
                        elif c.startswith('$'):
                            methodName = '$'

                        break

            # Check for line continuations
            if l.isSignificant:
                skips = ['.', '+', '-', '/', '=', '*', ',', 'and', 'or']
                for s in skips:
                    if c.endswith(s):
                        skipNextLine = True
                        break

            if self._trace:
                self._log.write(
                    c.replace('\n', '') +
                    ('\n\t@@@@ skip: ' + str(skip) + '\n\t@@@@ parens: ' +
                     str(openParens) + '\n\t@@@@ braces: ' + str(openBraces) +
                     '\n\t@@@@ brackets: ' + str(openBraces) +
                     '\n\t@@@@ skipNext: ' + str(skipNextLine)))

            if self._debug and not skip and openParens == 0 and openBraces == 0 and openBrackets == 0:
                debugLine = 'window.___vmiDebug(\'%s\', \'%s\', \'%s\', %s)\n' % \
                            (dep.package, className, methodName, str(l.lineNumber))

                indent = len(l.indent) > len(lastWhitespace)
                dedent = len(l.indent) < len(lastWhitespace)

                skips = [')', ']', '}']
                skip = False
                for s in skips:
                    if c.startswith(s):
                        skip = True
                        break

                if dedent and skip:
                    lastWhitespace = lastWhitespace
                else:
                    lastWhitespace = l.indent

                codePattern = re.compile('(?P<code>[^\s\t\n]+)')
                res = codePattern.search(c)
                if not res or len(res.groupdict()['code']) == 0:
                    if self._trace:
                        self._log.write('EMPTY: "' + c + '"')
                    debugLine = ''

                l.insert(0, 0, l.indent + debugLine)

            if l.isSignificant:
                openParens += l.redacted.count('(') - l.redacted.count(')')
                openBrackets += l.redacted.count('[') - l.redacted.count(']')
                openBraces += l.redacted.count('{') - l.redacted.count('}')

            #---------------------------------------------------------------------------------------
            # WRITE MODIFIED OUTPUT
            out.write(l.source)

            if cacheOut:
                cacheOut.write(l.source)

        self._writeRegistryEntry(out, cacheOut, registryEntry)

        if cacheOut:
            cacheOut.close()

        return True

#___________________________________________________________________________________________________ _compileToJavascript

    def _compileToJavascript(self,
                             target,
                             assembledFile,
                             jsIncludeOverrides=None):

        # Use the Coffeescript compiler to create a JS compilation of the assembled CS file
        result = SystemUtils.executeCommand(
            ['coffee', '-c', '--bare', assembledFile])
        status = result['code']
        output = result['out']
        errors = 0
        forceVerbose = False

        #-------------------------------------------------------------------------------------------
        # ERROR HANDLING
        #    Check the error status of the compilation process and if a failure occurred parse the
        #    error results for display and logging.
        if status:
            outputLines = str(output).replace('\r', '').split('\n')
            for line in outputLines:
                if line.startswith('Error:') or line.startswith(
                        'SyntaxError:'):
                    errors += 1
                    result = CoffeescriptBuilder._parseError(line)
                    if result:
                        self._log.add(result)
                    else:
                        forceVerbose = True

        if forceVerbose:
            self._log.add(output)

        self._report[target.package] = errors
        if self._verbose:
            print("\n\n")
            if errors == 0 and status == 0:
                self._log.write('Compilation complete: ' + target.compiledPath)
            else:
                self._log.write('Compilation FAILED: ' + target.package)

        f = open(target.compiledPath, 'r')
        res = f.read()
        f.close()

#___________________________________________________________________________________________________ _parseIncludes

    def _parseIncludes(self, target, rootTarget=None):
        """Doc..."""
        if rootTarget is None:
            rootTarget = target

        if not rootTarget.package in self._imports:
            self._imports[rootTarget.package] = []

        if not rootTarget.package in self._requires:
            self._requires[rootTarget.package] = []

        if not rootTarget.package in self._includes:
            self._includes[rootTarget.package] = []

        if not os.path.exists(target.path):
            print("\n")
            self._log.add('WARNING: Missing import.\n\tPACKAGE: ' + target.package + '\n\tFILE: ' \
                          + target.path)
            print("\n")
            return

        f = open(target.path)
        for line in f:

            # import parse
            dependency = CoffeescriptDependency.createImport(
                line, self._rootPath)
            if dependency and not dependency.isInList(
                    self._imports[rootTarget.package]):
                self._parseIncludes(dependency, rootTarget)
                self._imports[rootTarget.package].append(dependency)
                continue

            # require parse
            dependency = CoffeescriptDependency.createRequire(
                line, self._rootPath)
            if dependency and not dependency.isInList(
                    self._imports[rootTarget.package]):
                self._requires[rootTarget.package].append(dependency)
                continue

            # include parse
            dependency = CoffeescriptDependency.createInclude(
                line, self._rootPath)
            if dependency and not dependency.isInList(
                    self._includes[rootTarget.package]):
                self._includes[rootTarget.package].append(dependency)
                continue

        f.close()
        self._imports[rootTarget.package].append(target)

#___________________________________________________________________________________________________ _processRequires

    def _processRequires(self, target):
        currentTarget = self._imports[target.package].pop()
        while len(self._requires[target.package]) > 0:
            self._parseIncludes(self._requires[target.package].pop(0), target)

        outlist = []
        for item in self._imports[target.package]:
            if not item.isInList(outlist) and not item.compare(currentTarget):
                outlist.append(item)
        self._imports[target.package] = outlist
        self._imports[target.package].append(currentTarget)

#___________________________________________________________________________________________________ _getLibraryData

    def _getLibraryData(self, target):
        targets = []
        modules = []
        imports = []
        includes = []

        src = open(target.path, 'r')
        for line in src:

            # target parse
            d = CoffeescriptDependency.create(line, self._rootPath)
            if not d:
                continue

            if d.dependencyType == CoffeescriptDependency.TARGET_TYPE:
                targets.append(d)
            elif d.dependencyType == CoffeescriptDependency.IMPORT_TYPE:
                imports.append(d)
            elif d.dependencyType == CoffeescriptDependency.REQUIRE_TYPE:
                imports.append(d)
            elif d.dependencyType == CoffeescriptDependency.INCLUDE_TYPE:
                includes.append(d)
            elif d.dependencyType == CoffeescriptDependency.MODULE_TYPE:
                modules.append(d)
            else:
                continue

            self._parseIncludes(d)

        src.close()

        return targets, imports, modules, includes

#___________________________________________________________________________________________________ _compileAllInDirectory

    @staticmethod
    def _compileAllInDirectory(path,
                               rootPath=None,
                               debug=False,
                               trace=False,
                               force=False,
                               compress=False):
        results = ''
        missing = {}
        count = 0
        for f in CoffeescriptBuilder.getScriptsInPath(path):
            target = CoffeescriptDependency(f, rootPath)
            if not (target.exists and (target.isExec or target.isLib)):
                continue

            c = CoffeescriptBuilder(target,
                                    rootPath,
                                    debug=debug,
                                    trace=trace,
                                    force=force,
                                    compress=compress)
            c.construct()
            count += 1
            for n, v in DictUtils.iter(c.report):
                num = max(0, 60 - len(n))
                results += '\n' + n + ':' + ('.' * num)
                if v == 0:
                    results += 'SUCCESS'
                elif v > 0:
                    results += 'COMPILATION FAILED'
                else:
                    results += 'ASSEMBLY FAILED'

            if len(c.warnings) > 0:
                results += '[' + str(len(c.warnings)) + ' WARNINGS]'
                for v in c.warnings:
                    if not v[
                            'id'] == CoffeescriptBuilder._WARN_ID_MISSING_IMPORT:
                        continue

                    key = v['package'] + '-' + v['class'] + '-' + str(
                        v['line'])
                    if key in missing:
                        continue

                    missing[key] = v

        if len(results) > 0:
            print('\nDIRECTORY ' + path + ' COMPILE RESULTS [' + str(count) +
                  ']:' + results)
        return {'res': results, 'missing': missing}

#___________________________________________________________________________________________________ _parseError

    @staticmethod
    def _parseError(error):
        """ Parses errors of the format:
        "Error: In /vizme2/website/js/vmi/blog/author/exec.ccs, Parse error on line 181: Unexpected 'INDENT'"
        """

        ccsFile = None

        prefixReplacements = ['SyntaxError: In ', 'Error: In ']
        for p in prefixReplacements:
            error = error.replace(p, '')

        out = '\n-----------------------------------------------\nERROR: '
        try:
            sep = error.index(',')
            ccsFile = error[:sep]
        except Exception:
            pass

        try:
            sep2 = error.index(':')
            out += error[sep2 + 1:].strip() + '\n'
        except Exception:
            if error and sep:
                out += error[sep + 1:].strip() + '\n'

        pattern = re.compile('line[\s\t]+(?P<linenumber>[0-9]+)')
        res = pattern.search(error)
        if res and len(res.groups()) > 0:
            lineNumber = int(res.groups()[0]) - 1
        else:
            out += '    Unspecified location'
            return

        if ccsFile:
            padSize = len(str(lineNumber + 3))
            jQueryName = 'Exec Function (JQUERY Document ready)'
            functionName = None
            className = None
            trace = ''
            f = open(ccsFile, 'r')
            for i, line in enumerate(f):
                if i > lineNumber + 4:
                    break

                if i <= lineNumber:
                    pattern = re.compile(
                        '^class[\s\t]+(?P<classname>[a-zA-Z0-9_]+)')
                    res = pattern.search(line)
                    if res and len(res.groups()) > 0:
                        className = res.groups()[0]
                        functionName = None

                    pattern = re.compile('^\$[\s\t]*[-=]+>')
                    res = pattern.search(line)
                    if res:
                        className = jQueryName
                        functionName = None

                    pattern = re.compile(
                        '[\s\t]*(?P<name>[a-zA-Z0-9_]+)[\s\t]*:[^-=>]*[-=]+>')
                    res = pattern.search(line)
                    if res and len(res.groups()) > 0:
                        functionName = res.groups()[0]

                if i > lineNumber - 4:
                    marker = ">>" if i == lineNumber else "  "
                    trace += marker + str(i).rjust(padSize) + '| ' + line

            f.close()

            if functionName:
                out += "  " + ("METHOD" if className else
                               "FUNCTION") + ": " + functionName + "\n"

            if className:
                out += "  " + ("CLASS" if className != jQueryName else
                               "EXEC") + ": " + className + "\n"

            out += "  TRACE:\n" + trace

        return out + "\n"
Ejemplo n.º 15
0
class PyGlassWindow(QtGui.QMainWindow):
    """A class for..."""

#===================================================================================================
#                                                                                       C L A S S

#___________________________________________________________________________________________________ __init__
    def __init__(self, **kwargs):
        """Creates a new instance of PyGlassWindow."""
        parent                  = ArgsUtils.extract('parent', None, kwargs)
        self._application       = ArgsUtils.extract('pyGlassApp', None, kwargs)
        self._qApplication      = ArgsUtils.extract('qApp', None, kwargs)
        self._isMainWindow      = ArgsUtils.extract('isMainWindow', bool(parent is None), kwargs)
        self._mainWindow        = ArgsUtils.extract('mainWindow', None, kwargs)
        self._appWrappingWidget = None
        self._centerWidget      = None
        self._hasShown          = False
        self._isHighDpi         = OsUtils.isHighDpiScaledScreen()
        self._settings          = ConfigsDict()

        self._appLevelWidgets              = dict()
        self._appLevelWidgetDisplayHistory = []

        self._keyboardCallback = ArgsUtils.extract('keyboardCallback', None, kwargs)

        if not self._mainWindow:
            if self._isMainWindow:
                self._mainWindow = self
            elif self._application:
                self._mainWindow = self._application.mainWindow

        self._dependentWindows = []
        self._currentWidget    = None

        QtGui.QMainWindow.__init__(self, parent, ArgsUtils.extract('flags', 0, kwargs))

        self._instanceUid = TimeUtils.getUidTimecode(
            prefix=self.__class__.__name__,
            suffix=StringUtils.getRandomString(8))

        self._styleSheet = kwargs.get('styleSheet', None)
        if self._styleSheet:
            self.setStyleSheet(self.styleSheetPath)

        if self._keyboardCallback is not None:
            self.setFocusPolicy(QtCore.Qt.StrongFocus)

        if self._isMainWindow:
            self._log                 = Logger(self, printOut=True)
            self._config              = ApplicationConfig(self)
            self._commonConfig        = ApplicationConfig(self, common=True)
            self._resourceFolderParts = PyGlassGuiUtils.getResourceFolderParts(self)

            icon = PyGlassGuiUtils.createIcon(
                kwargs.get('iconsPath', self.getAppResourcePath('icons', isDir=True)) )
            if icon:
                self.setWindowIcon(icon)

        elif self._mainWindow:
            icon = self._mainWindow.windowIcon()
            if icon:
                self.setWindowIcon(icon)

        self._appWrappingWidget = VisibilityElement(self)
        layout = QtGui.QVBoxLayout()
        layout.setContentsMargins(0, 0, 0, 0)
        layout.setSpacing(0)
        self._appWrappingWidget.setLayout(layout)

        self._contentWrappingWidget = self.addApplicationLevelWidget('main')
        layout = QtGui.QVBoxLayout()
        layout.setContentsMargins(0, 0, 0, 0)
        layout.setSpacing(0)
        self._contentWrappingWidget.setLayout(layout)

        # Loads the ui file if it exists
        externalCentralParent = None
        hasWindowFile = kwargs.get('mainWindowFile', False)
        if hasWindowFile == self:
            UiFileLoader.loadWidgetFile(self)
            externalCentralParent = getattr(self, 'windowContainer')
            if externalCentralParent:
                externalLayout = externalCentralParent.layout()
                if not externalLayout:
                    externalLayout = QtGui.QVBoxLayout()
                    externalLayout.setContentsMargins(0, 0, 0, 0)
                    externalLayout.setSpacing(0)
                    externalCentralParent.setLayout(externalLayout)

        if externalCentralParent:
            self._appWrappingWidget.setParent(externalCentralParent)
            externalLayout.addWidget(self._appWrappingWidget)
        else:
            self.setCentralWidget(self._appWrappingWidget)

        # Sets a non-standard central widget
        centralWidgetName = kwargs.get('centralWidgetName')
        if centralWidgetName and hasattr(self, centralWidgetName):
            self._centerWidget = getattr(self, centralWidgetName)
        elif hasWindowFile and not hasWindowFile == self:
            if not self._centerWidget:
                self._createCentralWidget()
            UiFileLoader.loadWidgetFile(self, target=self._centerWidget)
        elif not hasWindowFile:
            self._centerWidget = None

        if not self._centerWidget and kwargs.get('defaultCenterWidget', True):
            self._createCentralWidget()

        self._lastChildWidgetID  = None
        self._widgetParent  = None
        self._widgets       = None
        self._widgetFlags   = None

        self._widgetClasses = kwargs.get('widgets', dict())
        if self._widgetClasses:
            self._initializeWidgetChildren()

        self.setWindowTitle(kwargs.get('title', self._createTitleFromClass()))
        self.updateStatusBar()

#===================================================================================================
#                                                                                   G E T / S E T

#___________________________________________________________________________________________________ GS: settings
    @property
    def settings(self):
        return self._settings

#___________________________________________________________________________________________________ GS: isHighDpi
    @property
    def isHighDpi(self):
        return self._isHighDpi

#___________________________________________________________________________________________________ GS: instanceUid
    @property
    def instanceUid(self):
        return self._instanceUid

#___________________________________________________________________________________________________ GS: isDeployed
    @ClassGetter
    def isDeployed(cls):
        return PyGlassEnvironment.isDeployed

#___________________________________________________________________________________________________ GS: isOnDisplay
    @property
    def isOnDisplay(self):
        return self.isVisible()

#___________________________________________________________________________________________________ GS: appID
    @property
    def appID(self):
        return self.pyGlassApplication.appID

#___________________________________________________________________________________________________ GS: isMainWindow
    @property
    def isMainWindow(self):
        return self._isMainWindow

#___________________________________________________________________________________________________ GS: allowsOwnership
    @property
    def allowsOwnership(self):
        return True

#___________________________________________________________________________________________________ GS: mainWindow
    @property
    def mainWindow(self):
        if self.isMainWindow:
            return self
        if self._mainWindow:
            return self._mainWindow

        self._mainWindow = PyGlassGuiUtils.getMainWindow(self)

        # Handle case where main window turns out to this window
        if self._mainWindow == self:
            self._isMainWindow = True
            self._mainWindow = None
            return self

        return self._mainWindow

#___________________________________________________________________________________________________ GS: owner
    @property
    def owner(self):
        if self.isMainWindow:
            return self
        return self.mainWindow

#___________________________________________________________________________________________________ GS: pyGlassApplication
    @property
    def pyGlassApplication(self):
        return self._application if self.isMainWindow else self.mainWindow.pyGlassApplication

#___________________________________________________________________________________________________ GS: application
    @property
    def application(self):
        if self._application is not None:
            return self._application
        return self._application if self.isMainWindow else self.mainWindow.application

#___________________________________________________________________________________________________ GS: qApplication
    @property
    def qApplication(self):
        if self._qApplication is not None:
            return self._qApplication

        return self._qApplication if self.isMainWindow else self.mainWindow.qApplication

#___________________________________________________________________________________________________ GS: appConfig
    @property
    def appConfig(self):
        return self._config if self.isMainWindow else self.mainWindow.appConfig

#___________________________________________________________________________________________________ GS: commonAppConfig
    @property
    def commonAppConfig(self):
        return self._commonConfig if self.isMainWindow else self.mainWindow.commonAppConfig

#___________________________________________________________________________________________________ GS: logger
    @property
    def logger(self):
        return self._log if self.isMainWindow else self.owner.log

#___________________________________________________________________________________________________ GS: log
    @property
    def log(self):
        return self.logger

#___________________________________________________________________________________________________ GS: styleSheetPath
    @property
    def styleSheetPath(self):
        if not self._styleSheet:
            return None

        if os.path.isabs(self._styleSheet):
            return self._styleSheet

        parts = self._resourceFolderParts + self._stylesheet.split('/')
        return self.getResourcePath(*parts, isFile=True)

#___________________________________________________________________________________________________ GS: rootResourcePath
    @property
    def rootResourcePath(self):
        return PyGlassEnvironment.getRootResourcePath()

#___________________________________________________________________________________________________ GS: rootLocalResourcePath
    @property
    def rootLocalResourcePath(self):
        return PyGlassEnvironment.getRootLocalResourcePath()

#___________________________________________________________________________________________________ GS: appResourcePath
    @property
    def appResourcePath(self):
        if not self.isMainWindow:
            return self.owner.appResourcePath

        out = self.pyGlassApplication.getAppResourcePath(isDir=True)
        if not os.path.exists(out):
            os.makedirs(out)
        return out

#___________________________________________________________________________________________________ GS: localAppResourcePath
    @property
    def localAppResourcePath(self):
        if not self.isMainWindow:
            return self.owner.localAppResourcePath

        out = self.pyGlassApplication.getLocalAppResourcePath(isDir=True)
        if not os.path.exists(out):
            os.makedirs(out)
        return out

#___________________________________________________________________________________________________ GS: sharedResourcePath
    @property
    def sharedResourcePath(self):
        out = self.getRootResourcePath('shared', isDir=True)
        if not os.path.exists(out):
            os.makedirs(out)
        return out

#___________________________________________________________________________________________________ GS: localSharedResourcePath
    @property
    def localSharedResourcePath(self):
        out = self.getLocalResourcePath('shared', isDir=True)
        if not os.path.exists(out):
            os.makedirs(out)
        return out

#___________________________________________________________________________________________________ GS: widgets
    @property
    def widgets(self):
        return self._widgets

#___________________________________________________________________________________________________ GS: screenWidth
    @property
    def screenWidth(self):
        rect = self._qApplication.desktop().screenGeometry()
        return rect.width()

#___________________________________________________________________________________________________ GS: screenHeight
    @property
    def screenHeight(self):
        rect = self._qApplication.desktop().screenGeometry()
        return rect.height()

#===================================================================================================
#                                                                                     P U B L I C

#___________________________________________________________________________________________________ scaleByDpi
    def scaleByDpi(self, value, rounded =False, asInt =False):
        if self._isHighDpi:
            value *= 2
        if rounded or asInt:
            value = round(value)
        if asInt:
            return int(value)
        return value

#___________________________________________________________________________________________________ keyPressEvent
    def keyPressEvent(self, event):
        if self._keyboardCallback is None or not self._keyboardCallback(event):
            super(PyGlassWindow, self).keyPressEvent(event)

#___________________________________________________________________________________________________ showEvent
    def showEvent(self, *args, **kwargs):
        if not self._hasShown:
            self._firstShowImpl()
            self._hasShown = True

#___________________________________________________________________________________________________ closeEvent
    def closeEvent(self, *args, **kwargs):
        if self.isMainWindow:
            for depWindow in self._dependentWindows:
                depWindow.close()
        return super(PyGlassWindow, self).closeEvent(*args, **kwargs)

#___________________________________________________________________________________________________ addDependentWindow
    def addDependentWindow(self, window):
        if window in self._dependentWindows:
            return True

        self._dependentWindows.append(window)
        return True

#___________________________________________________________________________________________________ removeDependentWindow
    def removeDependentWindow(self, window):
        if window not in self._dependentWindows:
            return True

        self._dependentWindows.remove(window)
        return True

#___________________________________________________________________________________________________ refreshWidgets
    def refreshWidgets(self, **kwargs):
        for name, widget in DictUtils.iter(self._widgets):
            widget.refresh(**kwargs)
        self.refreshGui()

#___________________________________________________________________________________________________ updateStatusBar
    def updateStatusBar(self, message =None, timeout =-1):
        if not message:
            self.statusBar().clearMessage()
            self.statusBar().setVisible(False)
        else:
            if timeout < 0:
                timeout = 3000
            self.statusBar().showMessage(message, timeout=timeout)
            self.statusBar().setVisible(True)

#___________________________________________________________________________________________________ getWidgetFromID
    def getWidgetFromID(self, widgetID, createIfMissing =True):
        if widgetID not in self._widgets and createIfMissing:
            self.loadWidgets(widgetID)
        return self._widgets.get(widgetID, None)

#___________________________________________________________________________________________________ getSharedResourcePath
    def getSharedResourcePath(self, *args, **kwargs):
        return FileUtils.createPath(self.sharedResourcePath, *args, **kwargs)

#___________________________________________________________________________________________________ getLocalSharedResourcePath
    def getLocalSharedResourcePath(self, *args, **kwargs):
        return FileUtils.createPath(self.localSharedResourcePath, *args, **kwargs)

#___________________________________________________________________________________________________ getAppResourcePath
    def getAppResourcePath(self, *args, **kwargs):
        """Doc..."""
        return FileUtils.createPath(self.appResourcePath, *args, **kwargs)

#___________________________________________________________________________________________________ getLocalAppResourcePath
    def getLocalAppResourcePath(self, *args, **kwargs):
        """Doc..."""
        return FileUtils.createPath(self.localAppResourcePath, *args, **kwargs)

#___________________________________________________________________________________________________ getRootResourcePath
    def getRootResourcePath(self, *args, **kwargs):
        return PyGlassEnvironment.getRootResourcePath(*args, **kwargs)

#___________________________________________________________________________________________________ getRootLocalResourcePath
    def getRootLocalResourcePath(self, *args, **kwargs):
        return PyGlassEnvironment.getRootLocalResourcePath(*args, **kwargs)

#___________________________________________________________________________________________________ getResourcePath
    def getResourcePath(self, *args, **kwargs):
        """Doc..."""
        return FileUtils.createPath(
            self.rootResourcePath, 'widget', self._resourceFolderParts, *args, **kwargs)

#___________________________________________________________________________________________________ getLocalResourcePath
    def getLocalResourcePath(self, *args, **kwargs):
        """Doc..."""
        return FileUtils.createPath(
            self.rootLocalResourcePath, 'widget', self._resourceFolderParts, *args, **kwargs)

#___________________________________________________________________________________________________ showApplicationLevelWidget
    def showApplicationLevelWidget(self, widgetID, **kwargs):
        w = self.getApplicationLevelWidget(widgetID)
        if not w:
            return

        for wid, widget in DictUtils.iter(self._appLevelWidgets):
            if wid == widgetID:
                widget.setVisible(True)
                widget.activateWidgetDisplay(**kwargs)
            else:
                widget.visibility.addMuteRequest(w)
        self.refreshGui()

#___________________________________________________________________________________________________ hideApplicationLevelWidget
    def hideApplicationLevelWidget(self, widgetID, **kwargs):
        w = self.getApplicationLevelWidget(widgetID)
        if not w:
            return

        for wid, widget in DictUtils.iter(self._appLevelWidgets):
            if wid == widgetID:
                widget.setVisible(False)
                widget.deactivateWidgetDisplay(**kwargs)
            else:
                widget.visibility.removeMuteRequest(w)
                if widget.visibility.isVisible:
                    widget.refreshWidgetDisplay()
        self.refreshGui()

#___________________________________________________________________________________________________ showLoading
    def showLoading(self, target, **kwargs):
        w = self.getApplicationLevelWidget('loading')
        if not w:
            return
        self.showApplicationLevelWidget('loading', target=target, **kwargs)
        self._showLoadingImpl(target=target, **kwargs)

#___________________________________________________________________________________________________ hideLoading
    def hideLoading(self, target, **kwargs):
        w = self.getApplicationLevelWidget('loading')
        if not w or not w.target == target:
            return
        self.hideApplicationLevelWidget('loading', target=target, **kwargs)
        self._hideLoadingImpl(target=target, **kwargs)

#___________________________________________________________________________________________________ addWidget
    def addWidget(self, key, widgetClass, setActive =False):
        self._widgetClasses[key] = widgetClass
        if self._widgets is None:
            self._initializeWidgetChildren(key if setActive else None)
        elif setActive:
            return self.setActiveWidget(key)
        return True

#___________________________________________________________________________________________________ setActiveWidget
    def setActiveWidget(self, widgetID, force =False, args =None, doneArgs =None):
        if not self._centerWidget or widgetID is None or widgetID not in self._widgetClasses:
            return False

        if not force and self._currentWidget and self._currentWidget.widgetID == widgetID:
            return True

        if widgetID not in self._widgets:
            self.loadWidgets(widgetID)
        widget = self._widgets[widgetID]

        # Deactivates the current widget if the widgets are being switched. However, ignored if the
        # same widget is being activated for a second time.
        if self._currentWidget and widgetID != self._currentWidget.widgetID:
            if doneArgs is None:
                doneArgs = dict()
            self._currentWidget.deactivateWidgetDisplay(**doneArgs)
            self._currentWidget.setParent(self._widgetParent)
            self._lastChildWidgetID = self._currentWidget.widgetID

        self._currentWidget = widget
        if self._centerWidget:
            layout = self._centerWidget.layout()
            if not layout:
                layout = QtGui.QVBoxLayout()
                layout.setContentsMargins(0, 0, 0, 0)
                self._centerWidget.setLayout(layout)
            layout.addWidget(widget)
        else:
            self._contentWrappingWidget.layout().addWidget(widget)
        self.setContentsMargins(0, 0, 0, 0)
        self.refreshGui()

        if args is None:
            args = dict()

        widget.activateWidgetDisplay(lastPeerWidgetID=self._lastChildWidgetID, **args)
        return True

#___________________________________________________________________________________________________ loadWidgets
    def loadWidgets(self, widgetIdents =None):
        if not widgetIdents:
            widgetIdents = self._widgetClasses.keys()
        elif StringUtils.isStringType(widgetIdents):
            widgetIdents = [widgetIdents]

        for widgetID in widgetIdents:
            if widgetID in self._widgets:
                continue

            if widgetID not in self._widgetClasses:
                self._log.write(
                    'ERROR: Unrecognized widgetID "%s" in %s' % (str(widgetID), str(self)))

            try:
                widget = self._widgetClasses[widgetID](
                    parent=self._widgetParent, flags=self._widgetFlags, widgetID=widgetID)
                self._widgets[widgetID] = widget
            except Exception as err:
                self._log.write('ERROR: Failed to load widget with id: "%s" ->' % widgetID)
                raise

#___________________________________________________________________________________________________ refreshGui
    def refreshGui(self):
        self.qApplication.processEvents()

#___________________________________________________________________________________________________ exit
    def exit(self):
        self.qApplication.exit()

#___________________________________________________________________________________________________ initialize
    def initialize(self, *args, **kwargs):
        self._initializeImpl(*args, **kwargs)

#___________________________________________________________________________________________________ initializeComplete
    def initializeComplete(self, preDisplay =None):
        self.pyGlassApplication.closeSplashScreen()

        result = False
        if preDisplay:
            preDisplay.show()
            result = self.qApplication.exec_()

        if result:
            sys.exit(result)

        self._application.runMainLoop()

#___________________________________________________________________________________________________ preShow
    def preShow(self, **kwargs):
        self._preShowImpl(**kwargs)

#___________________________________________________________________________________________________ postShow
    def postShow(self, **kwargs):
        self._postShowImpl(**kwargs)

#___________________________________________________________________________________________________ addApplicationLevelWidget
    def addApplicationLevelWidget(self, widgetID, widgetClass =None, **kwargs):
        if widgetClass is None:
            widgetClass = ApplicationLevelWidget
            ArgsUtils.addIfMissing('widgetFile', False, kwargs)

        widget = widgetClass(parent=self._appWrappingWidget, **kwargs)
        self._appWrappingWidget.layout().addWidget(widget)
        self._appLevelWidgets[widgetID] = widget
        return widget

#___________________________________________________________________________________________________ getApplicationLevelWidget
    def getApplicationLevelWidget(self, widgetID):
        if widgetID in self._appLevelWidgets:
            return self._appLevelWidgets[widgetID]
        return None

#===================================================================================================
#                                                                               P R O T E C T E D

#___________________________________________________________________________________________________ _createCentralWidget
    def _createCentralWidget(self):
        widget = self._centerWidget
        if widget:
            return widget

        w = QtGui.QWidget(self._contentWrappingWidget)
        self._contentWrappingWidget.layout().addWidget(w)
        self._centerWidget = w
        return w

#___________________________________________________________________________________________________ _initializeWidgetChildren
    def _initializeWidgetChildren(self, activeWidgetID =None):
        if not self._widgetClasses or self._widgets:
            return False

        self._widgetParent = PyGlassBackgroundParent(proxy=self)
        self._widgets      = dict()

        if activeWidgetID:
            self.setActiveWidget(activeWidgetID)

#___________________________________________________________________________________________________ _preShowImpl
    def _preShowImpl(self, **kwargs):
        pass

#___________________________________________________________________________________________________ _firstShowImpl
    def _firstShowImpl(self):
        pass

#___________________________________________________________________________________________________ _postShowImpl
    def _postShowImpl(self, **kwargs):
        pass

#___________________________________________________________________________________________________ _showLoadingImpl
    def _showLoadingImpl(self, **kwargs):
        pass

#___________________________________________________________________________________________________ _hideLoadingImpl
    def _hideLoadingImpl(self, **kwargs):
        pass

#___________________________________________________________________________________________________ _createTitleFromClass
    def _createTitleFromClass(self):
        """Doc..."""
        src = self.__class__.__name__
        out = src[0]
        wasCaps = True
        for c in src[1:]:
            if c.lower() == c:
                out += c
                wasCaps = False
            elif wasCaps:
                out += c
            else:
                out += ' ' + c
                wasCaps = True

        return out

#___________________________________________________________________________________________________ _initializeImpl
    def _initializeImpl(self, *args, **kwargs):
        self.initializeComplete()

#===================================================================================================
#                                                                               I N T R I N S I C

#___________________________________________________________________________________________________ __repr__
    def __repr__(self):
        return self.__str__()

#___________________________________________________________________________________________________ __unicode__
    def __unicode__(self):
        return StringUtils.toUnicode(self.__str__())

#___________________________________________________________________________________________________ __str__
    def __str__(self):
        return '<%s>' % self.__class__.__name__
Ejemplo n.º 16
0
class IncludeCompressor(object):

    #===================================================================================================
    #                                                                                       C L A S S

    _REMOVE_COMMENT_RE = re.compile('/\*.+\*/', re.DOTALL)
    _REMOVE_COMMENT_LINE_RE = re.compile('(^|\n)[\s\t]*//.+(\n|$)')

    JS_TYPE = 'js'
    CSS_TYPE = 'css'

    #___________________________________________________________________________________________________ __init__
    def __init__(self, compileCoffee=False):
        self._log = Logger('IncludeCompressor')
        self._compileCoffee = compileCoffee

#===================================================================================================
#                                                                                     P U B L I C

#___________________________________________________________________________________________________ compress

    def compress(self, rootPath):
        if not self._fileExists(rootPath):
            return False
        elif os.path.isfile(rootPath):
            return self.compressFile(rootPath)
        else:
            return self.compressPath(rootPath)

#___________________________________________________________________________________________________ compressFile

    def compressFile(self, rootPath, directory=None):
        if not self._fileExists(rootPath):
            return False

        if self._compileCoffee:
            try:
                from pyaid.web.coffeescript.CoffeescriptBuilder import CoffeescriptBuilder
                CoffeescriptBuilder.compileAllOnPath(rootPath,
                                                     os.path.dirname(rootPath),
                                                     True)
                self._log.write('Coffeescript compiled.')
            except Exception as err:
                self._log.writeError('Failed to compile coffeescript file.',
                                     err)
                return False

        return self._compressFile(rootPath, directory)

#___________________________________________________________________________________________________ compressPath

    def compressPath(self, rootPath):
        # First compile any coffee scripts to js files
        if self._compileCoffee:
            try:
                from pyaid.web.coffeescript.CoffeescriptBuilder import CoffeescriptBuilder
                CoffeescriptBuilder.compileAllOnPath(rootPath, rootPath, True)
                self._log.write('Coffee scripts compiled.')
            except Exception as err:
                self._log.writeError('Failed to compile coffeescript files.',
                                     err)
                return False

        FileUtils.walkPath(rootPath, self._compressInFolder, None)
        self._log.write('Compression operation complete.')
        return True

#===================================================================================================
#                                                                               P R O T E C T E D

#___________________________________________________________________________________________________ _fileExists

    def _fileExists(self, rootPath):
        if not os.path.exists(rootPath):
            self._log.write('ERROR: [%s] does not exist. Operation aborted.' %
                            rootPath)
            return False

        return True

#___________________________________________________________________________________________________ _compressFile

    def _compressFile(self, target, directory):
        # Skip compiled files.
        if target.endswith('comp.js') or target.endswith('comp.css'):
            return False

        if target.endswith('.js'):
            fileType = IncludeCompressor.JS_TYPE
        elif target.endswith('.css'):
            fileType = IncludeCompressor.CSS_TYPE
        else:
            return False

        if not directory:
            directory = ''
        if not directory.endswith(os.sep) and not target.startswith(os.sep):
            directory += os.sep

        inFile = directory + target
        tempFile = directory + target + '.temp'

        try:
            fh = open(inFile, 'r')
            fileString = fh.read()
            fh.close()
        except Exception as err:
            self._log.writeError('FAILED: Unable to read ' + str(inFile), err)
            return False

        if fileType == IncludeCompressor.CSS_TYPE:
            fileString = fileString.replace('@charset "utf-8";', '')
            ofn = (target[0:-3] + 'comp.css')
        else:
            ofn = (target[0:-2] + 'comp.js')

        try:
            fh = open(tempFile, 'w')
            fh.write(fileString)
            fh.close()
        except Exception as err:
            self._log.writeError(
                'FAILED: Unable to write temp file ' + str(tempFile), err)
            return False

        outFile = directory + '/' + ofn

        cmd = ['minify', '"%s"' % tempFile, '"%s"' % outFile]
        result = SystemUtils.executeCommand(cmd)
        if result['code']:
            self._log.write('FAILED: Unable to compress ' + str(inFile))

        if os.path.exists(tempFile):
            os.remove(tempFile)

        if not os.path.exists(outFile):
            self._log.write('FAILED: ' + target + ' -> ' + ofn)
            return False
        elif fileType == IncludeCompressor.JS_TYPE:
            f = open(outFile, 'r')
            compressed = f.read()
            f.close()

            compressed = IncludeCompressor._REMOVE_COMMENT_RE.sub(
                '', compressed)
            compressed = IncludeCompressor._REMOVE_COMMENT_LINE_RE.sub(
                '', compressed)

            f = open(outFile, 'w')
            f.write(compressed.strip())
            f.close()

        inSize = SizeUnits.SizeConversion.bytesToKilobytes(inFile, 2)
        outSize = SizeUnits.SizeConversion.bytesToKilobytes(outFile, 2)
        saved = SizeUnits.SizeConversion.convertDelta(
            inSize, outSize, SizeUnits.SIZES.KILOBYTES, 2)

        self._log.write(
            'Compressed[%s]: %s -> %s [%sKB -> %sKB | Saved: %sKB]' %
            (fileType, target, ofn, inSize, outSize, saved))

        return True

#___________________________________________________________________________________________________ _compressInFolder

    def _compressInFolder(self, dumb, directory, names):
        if directory.find('.svn') != -1:
            return

        for fn in names:
            self._compressFile(fn, directory)
Ejemplo n.º 17
0
class TrackExporter(object):
    """A class for..."""

#===============================================================================
#                                                                                       C L A S S

    DELETED_IDENTIFIER = u'##DEL##'

#_______________________________________________________________________________
    def __init__(self, logger =None):
        """Creates a new instance of TrackExporter."""
        self.results = None
        self.logger = logger
        self.modifications = 0
        if not logger:
            self.logger = Logger(self, printOut=True)

#===============================================================================
#                                                                                     P U B L I C

#_______________________________________________________________________________
    def process(self, session, difference =True):
        """Doc..."""

        if self.results is not None:
            return True

        results = []
        model = Tracks_Track.MASTER

        if session is None:
            session = model.createSession()

        trackStores = session.query(model).all()
        index = 0
        indices = NumericUtils.linearSpace(0, len(trackStores), roundToIntegers=True)[1:]

        for trackStore in trackStores:
            track = trackStore.getMatchingTrack(session)
            if track is None:
                self.modifications += 1
                results.append({'uid':trackStore.uid, 'action':self.DELETED_IDENTIFIER})

                self.logger.write(
                    u'<div>DELETED: %s</div>' %  DictUtils.prettyPrint(
                        trackStore.toDict(uniqueOnly=True)))
            else:
                if difference:
                    diff = trackStore.toDiffDict(track.toDict())
                    if diff is not None:
                        self.modifications += 1
                        results.append(diff)

                        self.logger.write(
                            u'<div>MODIFIED: %s</div>' % trackStore.fingerprint)
                else:
                    results.append(track.toDict())

            index += 1
            if index in indices:
                self.logger.write(
                    u'<div style="color:#33CC33">%s%% Complete</div>' % StringUtils.toUnicode(
                        10*(indices.index(index) + 1)))

        self.logger.write(u'<div style="color:#33CC33">100% Complete</div>')

        self.results = results
        return True

#_______________________________________________________________________________
    def write(self, session, path, pretty =False, gzipped =True, difference =True):
        if self.results is None and not self.process(session, difference):
            return False

        try:
            JSON.toFile(path, self.results, pretty=pretty, gzipped=gzipped, throwError=True)
            return True
        except Exception as err:
            self.logger.writeError([
                u'ERROR: Unable to write export file',
                u'PATH: ' + StringUtils.toUnicode(path)], err)
            return False

#===============================================================================
#                                                                               I N T R I N S I C

#_______________________________________________________________________________
    def __repr__(self):
        return self.__str__()

#_______________________________________________________________________________
    def __unicode__(self):
        return StringUtils.toUnicode(self.__str__())

#_______________________________________________________________________________
    def __str__(self):
        return '<%s>' % self.__class__.__name__
Ejemplo n.º 18
0
class SocketHandler(SocketServer.StreamRequestHandler):
    """A class for..."""

    #===================================================================================================
    #                                                                                       C L A S S

    SERVICE_UID = 'test'
    VERBOSE = False
    WORK_PATH = '/var/lib/'
    RUN_PATH = '/var/run/'
    LOG_PATH = '/var/log/'

    #___________________________________________________________________________________________________ __init__
    def __init__(self, request, client_address, server):
        self._log = Logger(self)
        self._log.write('Socket handler created')

        SocketServer.StreamRequestHandler.__init__(self, request,
                                                   client_address, server)

#===================================================================================================
#                                                                                   G E T / S E T

#___________________________________________________________________________________________________ GS: returnResponse

    @property
    def returnResponse(self):
        return getattr(self.__class__, 'RETURN_RESPONSE', False)

#===================================================================================================
#                                                                                     P U B L I C

#___________________________________________________________________________________________________ handle

    def handle(self):
        try:
            data = self.rfile.readline().strip()
            self._log.write('HANDLE: ' + str(data))
            try:
                result = self._respondImpl(JSON.fromString(unquote(data)))
            except Exception as err:
                self._log.writeError('RESPOND FAILURE', err)
                if self.returnResponse:
                    self.wfile.write(JSON.asString({'error': 1}))
                return

            if self.returnResponse:
                out = {'error': 0}
                if result:
                    out['payload'] = result
                self.wfile.write(out)
        except Exception as err:
            self._log.write('HANDLE FAILURE', err)

        return

#===================================================================================================
#                                                                               P R O T E C T E D

#___________________________________________________________________________________________________ _respondImpl

    def _respondImpl(self, data):
        pass
Ejemplo n.º 19
0
class ResourceCollector(object):
    """A class for..."""

#===================================================================================================
#                                                                                       C L A S S

#___________________________________________________________________________________________________ __init__
    def __init__(self, compiler, **kwargs):
        """Creates a new instance of ResourceCollector."""
        self._log        = Logger(self)
        self._verbose    = ArgsUtils.get('verbose', False, kwargs)
        self._compiler   = compiler

        if OsUtils.isWindows():
            self._targetPath = self._compiler.getBinPath('resources', isDir=True)
        elif OsUtils.isMac():
            self._targetPath = self._compiler.getBinPath('resources', 'resources', isDir=True)

        if os.path.exists(self._targetPath):
            shutil.rmtree(self._targetPath)

        if not os.path.exists(self._targetPath):
            os.makedirs(self._targetPath)

#===================================================================================================
#                                                                                     P U B L I C

#___________________________________________________________________________________________________ run
    def run(self):
        """Doc..."""
        resources = self._compiler.resources

        #-------------------------------------------------------------------------------------------
        # APP RESOURCES
        #       If no resource folders were specified copy the entire contents of the resources
        #       folder. Make sure to skip the local resources path in the process.
        if not resources:
            for item in os.listdir(PyGlassEnvironment.getRootResourcePath(isDir=True)):
                itemPath = PyGlassEnvironment.getRootResourcePath(item)
                if os.path.isdir(itemPath) and not item == 'local':
                    resources.append(item)

        for container in resources:
            parts = container.replace('\\', '/').split('/')
            self._copyResourceFolder(
                PyGlassEnvironment.getRootResourcePath(*parts, isDir=True), parts
            )

        #-------------------------------------------------------------------------------------------
        # PYGLASS RESOURCES
        #       Copy the resources from the PyGlass
        resources = []
        for item in os.listdir(PyGlassEnvironment.getPyGlassResourcePath('..', isDir=True)):
            itemPath = PyGlassEnvironment.getPyGlassResourcePath('..', item)
            if os.path.isdir(itemPath):
                resources.append(item)

        for container in resources:
            self._copyResourceFolder(
                PyGlassEnvironment.getPyGlassResourcePath('..', container), [container]
            )

        #-------------------------------------------------------------------------------------------
        # CLEANUP
        if self._verbose:
            self._log.write('CLEANUP: Removing unwanted destination files.')
        self._cleanupFiles(self._targetPath)

        self._copyPythonStaticResources()

        if self._verbose:
            self._log.write('COMPLETE: Resource Collection')

        return True

#===================================================================================================
#                                                                               P R O T E C T E D

#___________________________________________________________________________________________________ _copyResourceFolder
    def _copyResourceFolder(self, sourcePath, parts):
        targetPath = FileUtils.createPath(self._targetPath, *parts, isDir=True)
        WidgetUiCompiler(sourcePath).run()

        if self._verbose:
            self._log.write('COPYING: %s -> %s' % (sourcePath, targetPath))
        results = FileUtils.mergeCopy(sourcePath, targetPath)



#___________________________________________________________________________________________________ _copyPythonStaticResources
    def _copyPythonStaticResources(self):
        sourcePath = requests.utils.DEFAULT_CA_BUNDLE_PATH
        parts      = sourcePath[len(sys.exec_prefix):].strip(os.sep).split(os.sep)
        destPath   = FileUtils.createPath(self._targetPath, 'pythonRoot', *parts, isFile=True)
        folder     = os.path.dirname(destPath)
        if not os.path.exists(folder):
            os.makedirs(folder)
        shutil.copy(sourcePath, destPath)

#___________________________________________________________________________________________________ _compilePythonFiles
    def _compilePythonFiles(self, rootPath):
        os.path.walk(rootPath, self._compileInFolder, dict())

#___________________________________________________________________________________________________ _compileInFolder
    def _compileInFolder(self, arg, dirname, names):
        for name in names:
            if name.endswith('.py'):
                py_compile.compile(FileUtils.createPath(dirname, name, isFile=True))

#___________________________________________________________________________________________________ _cleanupFiles
    def _cleanupFiles(self, targetPath):
        os.path.walk(targetPath, self._cleanupInFolder, dict())

#___________________________________________________________________________________________________ _cleanupInFolder
    def _cleanupInFolder(self, arg, dirname, names):
        for name in names:
            if StringUtils.ends(name, self._compiler.ignoreExtensions):
                os.remove(FileUtils.createPath(dirname, name, isFile=True))

                # Deletes python (.py) files associated with ui files so only .pyc files remain.
                if name.endswith('.ui'):
                    pyName     = name.rsplit('.', 1)[0] + '.py'
                    pyNamePath = FileUtils.createPath(dirname, pyName, isFile=True)
                    if os.path.exists(pyNamePath):
                        os.remove(pyNamePath)

#===================================================================================================
#                                                                               I N T R I N S I C

#___________________________________________________________________________________________________ __repr__
    def __repr__(self):
        return self.__str__()

#___________________________________________________________________________________________________ __unicode__
    def __unicode__(self):
        return unicode(self.__str__())

#___________________________________________________________________________________________________ __str__
    def __str__(self):
        return '<%s>' % self.__class__.__name__
Ejemplo n.º 20
0
class ResourceCollector(object):
    """A class for..."""

#===================================================================================================
#                                                                                       C L A S S

#___________________________________________________________________________________________________ __init__
    def __init__(self, compiler, **kwargs):
        """Creates a new instance of ResourceCollector."""
        self._log        = Logger(self, printOut=True)
        self._verbose    = ArgsUtils.get('verbose', False, kwargs)
        self._compiler   = compiler

        if OsUtils.isWindows():
            self._targetPath = self._compiler.getBinPath('resources', isDir=True)
        elif OsUtils.isMac():
            # Resource folder resides inside another resource folder so that the copying retains
            # the original directory structure
            self._targetPath = self._compiler.getBinPath('resources', 'resources', isDir=True)
            #self._targetPath = self._compiler.getBinPath('resources', isDir=True)

        if os.path.exists(self._targetPath):
            shutil.rmtree(self._targetPath)

        if not os.path.exists(self._targetPath):
            os.makedirs(self._targetPath)

#===================================================================================================
#                                                                                     P U B L I C

#___________________________________________________________________________________________________ run
    def run(self):
        """Doc..."""
        resources = self._compiler.resources

        #-------------------------------------------------------------------------------------------
        # RESOURCES
        #       If no resource folders were specified copy the entire contents of the resources
        #       folder. Make sure to skip the local resources path in the process.
        if not resources:
            for item in os.listdir(PyGlassEnvironment.getRootResourcePath(isDir=True)):
                itemPath = PyGlassEnvironment.getRootResourcePath(item)
                if os.path.isdir(itemPath) and not item in ['local', 'apps']:
                    resources.append(item)

        for container in resources:
            parts = container.replace('\\', '/').split('/')
            self._copyResourceFolder(
                PyGlassEnvironment.getRootResourcePath(*parts, isDir=True), parts)

        #-------------------------------------------------------------------------------------------
        # APP RESOURCES
        appResources = self._compiler.resourceAppIds
        if not appResources:
            appResources = []
        for appResource in appResources:
            itemPath = PyGlassEnvironment.getRootResourcePath('apps', appResource, isDir=True)
            if not os.path.exists(itemPath):
                self._log.write('[WARNING]: No such app resource path found: %s' % appResource)
                continue
            self._copyResourceFolder(itemPath, ['apps', appResource])

        #-------------------------------------------------------------------------------------------
        # PYGLASS RESOURCES
        #       Copy the resources from the PyGlass
        resources = []
        for item in os.listdir(PyGlassEnvironment.getPyGlassResourcePath('..', isDir=True)):
            itemPath = PyGlassEnvironment.getPyGlassResourcePath('..', item)
            if os.path.isdir(itemPath):
                resources.append(item)

        for container in resources:
            self._copyResourceFolder(
                PyGlassEnvironment.getPyGlassResourcePath('..', container), [container])

        # Create a stamp file in resources for comparing on future installations
        creationStampFile = FileUtils.makeFilePath(self._targetPath, 'install.stamp')
        JSON.toFile(creationStampFile, {'CTS':TimeUtils.toZuluPreciseTimestamp()})

        #-------------------------------------------------------------------------------------------
        # CLEANUP
        if self._verbose:
            self._log.write('CLEANUP: Removing unwanted destination files.')
        self._cleanupFiles(self._targetPath)

        self._copyPythonStaticResources()

        if self._verbose:
            self._log.write('COMPLETE: Resource Collection')

        return True

#===================================================================================================
#                                                                               P R O T E C T E D

#___________________________________________________________________________________________________ _copyResourceFolder
    def _copyResourceFolder(self, sourcePath, parts):
        targetPath = FileUtils.createPath(self._targetPath, *parts, isDir=True)
        WidgetUiCompiler(sourcePath).run()

        if self._verbose:
            self._log.write('COPYING: %s -> %s' % (sourcePath, targetPath))
        return FileUtils.mergeCopy(sourcePath, targetPath)

#___________________________________________________________________________________________________ _copyPythonStaticResources
    def _copyPythonStaticResources(self):
        sourcePath = requests.utils.DEFAULT_CA_BUNDLE_PATH
        parts      = sourcePath.strip(os.sep).split(os.sep)
        index      = parts.index('site-packages')
        parts      = parts[index:]
        destPath   = FileUtils.createPath(self._targetPath, 'pythonRoot', *parts, isFile=True)
        folder     = os.path.dirname(destPath)
        if not os.path.exists(folder):
            os.makedirs(folder)
        shutil.copy(sourcePath, destPath)

#___________________________________________________________________________________________________ _compilePythonFiles
    def _compilePythonFiles(self, rootPath):
        FileUtils.walkPath(rootPath, self._compileInFolder, dict())

#___________________________________________________________________________________________________ _compileInFolder
    def _compileInFolder(self, arg, dirname, names):
        for name in names:
            if name.endswith('.py'):
                py_compile.compile(FileUtils.createPath(dirname, name, isFile=True))

#___________________________________________________________________________________________________ _cleanupFiles
    def _cleanupFiles(self, targetPath):
        FileUtils.walkPath(targetPath, self._cleanupInFolder, dict())

#___________________________________________________________________________________________________ _cleanupInFolder
    def _cleanupInFolder(self, data):
        dirname = data.folder

        for name in data.names:
            if StringUtils.ends(name, self._compiler.ignoreExtensions):
                os.remove(FileUtils.createPath(dirname, name, isFile=True))

                # Deletes python (.py) files associated with ui files so only .pyc files remain.
                if name.endswith('.ui'):
                    pyName     = name.rsplit('.', 1)[0] + '.py'
                    pyNamePath = FileUtils.createPath(dirname, pyName, isFile=True)
                    if os.path.exists(pyNamePath):
                        os.remove(pyNamePath)

#===================================================================================================
#                                                                               I N T R I N S I C

#___________________________________________________________________________________________________ __repr__
    def __repr__(self):
        return self.__str__()

#___________________________________________________________________________________________________ __unicode__
    def __unicode__(self):
        return StringUtils.toUnicode(self.__str__())

#___________________________________________________________________________________________________ __str__
    def __str__(self):
        return '<%s>' % self.__class__.__name__
Ejemplo n.º 21
0
class RemoteExecutionThread(QtCore.QThread):
    """ Threaded external processor execution"""

#===================================================================================================
#                                                                                       C L A S S

    _ACTIVE_THREAD_STORAGE = []

    completeSignal = QtCore.Signal(object)
    eventSignal    = QtCore.Signal(object)
    logSignal      = QtCore.Signal(object)
    progressSignal = QtCore.Signal(object)

#___________________________________________________________________________________________________ __init__
    def __init__(self, parent, **kwargs):
        QtCore.QThread.__init__(self, parent)

        self.userData = ArgsUtils.get('userData', None, kwargs)

        self._events           = dict()
        self._log              = Logger(self)
        self._log.trace        = True
        self._log.addPrintCallback(self._handleLogWritten)
        self._logger           = self._log

        self._maxLogBufferSize = 0
        self._logBuffer        = []
        self._returnCode         = None
        self._output           = None
        self._error            = None
        self._explicitComplete = ArgsUtils.get('explicitComplete', False, kwargs)

        # Add the thread to the static active thread storage so that it won't be garbage collected
        # until the thread completes.
        self.__class__._ACTIVE_THREAD_STORAGE.append(self)

        self._connectSignals(**kwargs)

#===================================================================================================
#                                                                                   G E T / S E T

#___________________________________________________________________________________________________ GS: success
    @property
    def success(self):
        return self.returnCode == 0

#___________________________________________________________________________________________________ GS: log
    @property
    def log(self):
        return self._log

#___________________________________________________________________________________________________ GS: logger
    @property
    def logger(self):
        return self._log

#___________________________________________________________________________________________________ GS: response
    @property
    def response(self):
        warnings.warn(
            'RemoteExceutionThread.response is deprecated in favor of .returnCode',
            DeprecationWarning)
        self._log.write('[DEPRECATION WARNING]: Use returnCode instead of response', traceStack=True)
        return self._returnCode

#___________________________________________________________________________________________________ GS: returnCode
    @property
    def returnCode(self):
        return self._returnCode

#___________________________________________________________________________________________________ GS: output
    @property
    def output(self):
        return self._output

#___________________________________________________________________________________________________ GS: error
    @property
    def error(self):
        return self._error

#===================================================================================================
#                                                                                     P U B L I C

#___________________________________________________________________________________________________ dispatchEvent
    def dispatchEvent(self, signal, identifier =None, data =None):
        signal.emit(RemoteThreadEvent(
            identifier=identifier,
            target=self,
            data=data))

#___________________________________________________________________________________________________ execute
    def execute(
            self, callback =None, logCallback =None, progressCallback =None,
            eventCallback =None, **kwargs
    ):
        self._connectSignals(
            callback=callback,
            logCallback=logCallback,
            progressCallback=progressCallback,
            eventCallback=eventCallback,
            **kwargs)

        self.start()

#___________________________________________________________________________________________________ run
    def run(self):
        """ Thread run method."""
        response = self._runImpl()
        if self._explicitComplete:
            return

        self._runComplete(response)

#___________________________________________________________________________________________________ connectSignals
    def connectSignals(self, onComplete =None, onLog =None, onProgress =None, onEvent =None):
        """ Quick access method to connect callbacks to the various remote thread signals. """

        return self._connectSignals(
            callback=onComplete,
            logCallback=onLog,
            progressCallback=onProgress,
            eventCallback=onEvent)

#___________________________________________________________________________________________________ enableLogBuffer
    def enableLogBuffer(self, maxLength = 0):
        self._maxLogBufferSize = maxLength

#___________________________________________________________________________________________________ disableLogBuffer
    def disableLogBuffer(self):
        self.flushLogBuffer(disable=True)

#___________________________________________________________________________________________________ flushLogBuffer
    def flushLogBuffer(self, disable =False):
        if disable:
            self._maxLogBufferSize = 0

        if self._logBuffer:
            b = self._logBuffer
            self._logBuffer = []
            self.dispatchEvent(self.logSignal, 'log', {'message':'\n'.join(b)})

#===================================================================================================
#                                                                               P R O T E C T E D

#___________________________________________________________________________________________________ _connectSignals
    def _connectSignals(self, **kwargs):
        logCallback = ArgsUtils.get('logCallback', None, kwargs)
        if logCallback:
            self.logSignal.connect(logCallback)

        completeCallback = ArgsUtils.get('callback', None, kwargs)
        if completeCallback:
            self.completeSignal.connect(completeCallback)

        progressCallback = ArgsUtils.get('progressCallback', None, kwargs)
        if progressCallback:
            self.progressSignal.connect(progressCallback)

        eventCallback = ArgsUtils.get('eventCallback', None, kwargs)
        if eventCallback:
            self.eventSignal.connect(eventCallback)

#___________________________________________________________________________________________________ _runComplete
    def _runComplete(self, response):
        self._returnCode = response
        if self._returnCode is None:
            self._returnCode = 0

        self.dispatchEvent(self.completeSignal, 'complete', {
            'response':self._returnCode,
            'error':self._error,
            'output':self._output,
            'thread':self,
            'userData':self.userData })

        # Remove the thread from the active thread storage so that it can be garbage collected.
        self.__class__._ACTIVE_THREAD_STORAGE.remove(self)

#___________________________________________________________________________________________________ _runImpl
    def _runImpl(self):
        return 0

#===================================================================================================
#                                                                                 H A N D L E R S

#___________________________________________________________________________________________________ _handleLogWritten
    def _handleLogWritten(self, logger, value):
        if self._maxLogBufferSize > 0:
            self._logBuffer.append(value)
            if len(self._logBuffer) > self._maxLogBufferSize:
                self.flushLogBuffer()
        else:
            self.dispatchEvent(self.logSignal, 'log', {'message':value})