# -*- coding: utf-8 -*-
# ***************************************************************************
# *   Copyright (c) 2016 sliptonic <shopinthewoods@gmail.com>               *
# *                                                                         *
# *   This file is part of the FreeCAD CAx development system.              *
# *                                                                         *
# *   This program is free software; you can redistribute it and/or modify  *
# *   it under the terms of the GNU Lesser General Public License (LGPL)    *
# *   as published by the Free Software Foundation; either version 2 of     *
# *   the License, or (at your option) any later version.                   *
# *   for detail see the LICENCE text file.                                 *
# *                                                                         *
# *   FreeCAD is distributed in the hope that it will be useful,            *
# *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
# *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
# *   GNU Lesser General Public License for more details.                   *
# *                                                                         *
# *   You should have received a copy of the GNU Library General Public     *
# *   License along with FreeCAD; if not, write to the Free Software        *
# *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
# *   USA                                                                   *
# *                                                                         *
# ***************************************************************************

"""
This file has utilities for checking and catching common errors in FreeCAD
Path projects.  Ideally, the user could execute these utilities from an icon
to make sure tools are selected and configured and defaults have been revised
"""

from __future__ import print_function
from PySide import QtCore, QtGui
import FreeCAD
import FreeCADGui
import PathScripts
import PathScripts.PathLog as PathLog
import PathScripts.PathUtil as PathUtil
import PathScripts.PathPreferences as PathPreferences
from collections import Counter
from datetime import datetime
import os
import webbrowser

# Qt translation handling


def translate(context, text, disambig=None):
    return QtCore.QCoreApplication.translate(context, text, disambig)


LOG_MODULE = "PathSanity"
# PathLog.setLevel(PathLog.Level.INFO, LOG_MODULE)
# PathLog.trackModule('PathSanity')


class CommandPathSanity:
    def resolveOutputPath(self, job):
        if job.PostProcessorOutputFile != "":
            filepath = job.PostProcessorOutputFile
        elif PathPreferences.defaultOutputFile() != "":
            filepath = PathPreferences.defaultOutputFile()
        else:
            filepath = PathPreferences.macroFilePath()

        if "%D" in filepath:
            D = FreeCAD.ActiveDocument.FileName
            if D:
                D = os.path.dirname(D)
                # in case the document is in the current working directory
                if not D:
                    D = "."
            else:
                FreeCAD.Console.PrintError(
                    "Please save document in order to resolve output path!\n"
                )
                return None
            filepath = filepath.replace("%D", D)

        if "%d" in filepath:
            d = FreeCAD.ActiveDocument.Label
            filepath = filepath.replace("%d", d)

        if "%j" in filepath:
            j = job.Label
            filepath = filepath.replace("%j", j)

        if "%M" in filepath:
            pref = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Macro")
            M = pref.GetString("MacroPath", FreeCAD.getUserAppDataDir())
            filepath = filepath.replace("%M", M)

        PathLog.debug("filepath: {}".format(filepath))

        # starting at the derived filename, iterate up until we have a valid
        # directory to write to
        while not os.path.isdir(filepath):
            filepath = os.path.dirname(filepath)

        PathLog.debug("filepath: {}".format(filepath))
        return filepath + os.sep

    def GetResources(self):
        return {
            "Pixmap": "Path_Sanity",
            "MenuText": QtCore.QT_TRANSLATE_NOOP(
                "Path_Sanity", "Check the path job for common errors"
            ),
            "Accel": "P, S",
            "ToolTip": QtCore.QT_TRANSLATE_NOOP(
                "Path_Sanity", "Check the path job for common errors"
            ),
        }

    def IsActive(self):
        obj = FreeCADGui.Selection.getSelectionEx()[0].Object
        return isinstance(obj.Proxy, PathScripts.PathJob.ObjectJob)

    def Activated(self):
        # if everything is ok, execute
        self.squawkData = {"items": []}

        obj = FreeCADGui.Selection.getSelectionEx()[0].Object
        self.outputpath = self.resolveOutputPath(obj)
        data = self.__summarize(obj)
        html = self.__report(data)
        if html is not None:
            FreeCAD.Console.PrintMessage("HTML report written to {}".format(html))
            webbrowser.open(html)

    def __makePicture(self, obj, imageName):
        """
        Makes an image of the target object.  Returns filename
        """

        # remember vis state of document objects. Turn off all but target
        visible = [o for o in obj.Document.Objects if o.Visibility]
        for o in obj.Document.Objects:
            o.Visibility = False
        obj.Visibility = True

        aview = FreeCADGui.activeDocument().activeView()
        aview.setAnimationEnabled(False)

        mw = FreeCADGui.getMainWindow()
        mdi = mw.findChild(QtGui.QMdiArea)
        view = mdi.activeSubWindow()
        view.showNormal()
        view.resize(320, 320)

        imagepath = self.outputpath + "{}".format(imageName)

        aview.viewIsometric()
        FreeCADGui.Selection.clearSelection()
        FreeCADGui.SendMsgToActiveView("PerspectiveCamera")
        FreeCADGui.Selection.addSelection(obj)
        FreeCADGui.SendMsgToActiveView("ViewSelection")
        FreeCADGui.Selection.clearSelection()
        aview.saveImage(imagepath + ".png", 320, 320, "Current")
        aview.saveImage(imagepath + "_t.png", 320, 320, "Transparent")

        view.showMaximized()

        aview.setAnimationEnabled(True)

        # Restore visibility
        obj.Visibility = False
        for o in visible:
            o.Visibility = True

        return "{}_t.png".format(imagepath)

    def __report(self, data):
        """
        generates an asciidoc file with the report information
        """

        reportTemplate = """
= Setup Report for FreeCAD Job: {jobname}
:toc:
:icons: font
:imagesdir: ""
:data-uri:

== Part Information

|===
{infoTable}
|===


== Run Summary

|===
{runTable}
|===

== Rough Stock

|===
{stockTable}
|===

== Tool Data

{toolTables}

== Output (Gcode)

|===
{outTable}
|===

== Fixtures and Workholding

|===
{fixtureTable}
|===

== Squawks

|===
{squawkTable}
|===

"""
        # Generate the markup for the Part Information Section

        infoTable = ""

        PartLabel = translate("Path_Sanity", "Base Object(s)")
        SequenceLabel = translate("Path_Sanity", "Job Sequence")
        DescriptionLabel = translate("Path_Sanity", "Job Description")
        JobTypeLabel = translate("Path_Sanity", "Job Type")
        CADLabel = translate("Path_Sanity", "CAD File Name")
        LastSaveLabel = translate("Path_Sanity", "Last Save Date")
        CustomerLabel = translate("Path_Sanity", "Customer")
        DesignerLabel = translate("Path_Sanity", "Designer")

        d = data["designData"]
        b = data["baseData"]

        jobname = d["JobLabel"]

        basestable = "!===\n"
        for key, val in b["bases"].items():
            basestable += "! " + key + " ! " + val + "\n"

        basestable += "!==="

        infoTable += (
            "|*"
            + PartLabel
            + "* a| "
            + basestable
            + " .7+a|"
            + "image::"
            + b["baseimage"]
            + "["
            + PartLabel
            + "]\n"
        )
        infoTable += "|*" + SequenceLabel + "*|" + d["Sequence"]
        infoTable += "|*" + JobTypeLabel + "*|" + d["JobType"]
        infoTable += "|*" + DescriptionLabel + "*|" + d["JobDescription"]
        infoTable += "|*" + CADLabel + "*|" + d["FileName"]
        infoTable += "|*" + LastSaveLabel + "*|" + d["LastModifiedDate"]
        infoTable += "|*" + CustomerLabel + "*|" + d["Customer"]
        infoTable += "|*" + DesignerLabel + "*|" + d["Designer"]

        # Generate the markup for the Run Summary Section

        runTable = ""
        opLabel = translate("Path_Sanity", "Operation")
        zMinLabel = translate("Path_Sanity", "Minimum Z Height")
        zMaxLabel = translate("Path_Sanity", "Maximum Z Height")
        cycleTimeLabel = translate("Path_Sanity", "Cycle Time")
        coolantLabel = translate("Path_Sanity", "Coolant")
        jobTotalLabel = translate("Path_Sanity", "TOTAL JOB")

        d = data["runData"]

        runTable += (
            "|*"
            + opLabel
            + "*|*"
            + zMinLabel
            + "*|*"
            + zMaxLabel
            + "*|*"
            + coolantLabel
            + "*|*"
            + cycleTimeLabel
            + "*\n"
        )

        for i in d["items"]:
            runTable += "|{}".format(i["opName"])
            runTable += "|{}".format(i["minZ"])
            runTable += "|{}".format(i["maxZ"])
            runTable += "|{}".format(i["coolantMode"])
            runTable += "|{}".format(i["cycleTime"])

        runTable += (
            "|*"
            + jobTotalLabel
            + "* |{} |{} |{}".format(d["jobMinZ"], d["jobMaxZ"], d["cycletotal"])
        )

        # Generate the markup for the Tool Data Section
        toolTables = ""

        toolLabel = translate("Path_Sanity", "Tool Number")
        descriptionLabel = translate("Path_Sanity", "Description")
        manufLabel = translate("Path_Sanity", "Manufacturer")
        partNumberLabel = translate("Path_Sanity", "Part Number")
        urlLabel = translate("Path_Sanity", "URL")
        inspectionNotesLabel = translate("Path_Sanity", "Inspection Notes")
        opLabel = translate("Path_Sanity", "Operation")
        tcLabel = translate("Path_Sanity", "Tool Controller")
        feedLabel = translate("Path_Sanity", "Feed Rate")
        speedLabel = translate("Path_Sanity", "Spindle Speed")
        shapeLabel = translate("Path_Sanity", "Tool Shape")
        diameterLabel = translate("Path_Sanity", "Tool Diameter")

        d = data["toolData"]

        for key, value in d.items():
            toolTables += "=== {}: T{}\n".format(toolLabel, key)

            toolTables += "|===\n"

            toolTables += "|*{}*| {} a| image::{}[{}]\n".format(
                descriptionLabel, value["description"], value["imagepath"], key
            )
            toolTables += "|*{}* 2+| {}\n".format(manufLabel, value["manufacturer"])
            toolTables += "|*{}* 2+| {}\n".format(partNumberLabel, value["partNumber"])
            toolTables += "|*{}* 2+| {}\n".format(urlLabel, value["url"])
            toolTables += "|*{}* 2+| {}\n".format(
                inspectionNotesLabel, value["inspectionNotes"]
            )
            toolTables += "|*{}* 2+| {}\n".format(shapeLabel, value["shape"])
            toolTables += "|*{}* 2+| {}\n".format(diameterLabel, value["diameter"])
            toolTables += "|===\n"

            toolTables += "|===\n"
            toolTables += (
                "|*"
                + opLabel
                + "*|*"
                + tcLabel
                + "*|*"
                + feedLabel
                + "*|*"
                + speedLabel
                + "*\n"
            )
            for o in value["ops"]:
                toolTables += (
                    "|"
                    + o["Operation"]
                    + "|"
                    + o["ToolController"]
                    + "|"
                    + o["Feed"]
                    + "|"
                    + o["Speed"]
                    + "\n"
                )
            toolTables += "|===\n"

            toolTables += "\n"

        # Generate the markup for the Rough Stock Section
        stockTable = ""
        xDimLabel = translate("Path_Sanity", "X Size")
        yDimLabel = translate("Path_Sanity", "Y Size")
        zDimLabel = translate("Path_Sanity", "Z Size")
        materialLabel = translate("Path_Sanity", "Material")

        d = data["stockData"]

        stockTable += "|*{}*|{} .4+a|image::{}[stock]\n".format(
            materialLabel, d["material"], d["stockImage"]
        )
        stockTable += "|*{}*|{}".format(xDimLabel, d["xLen"])
        stockTable += "|*{}*|{}".format(yDimLabel, d["yLen"])
        stockTable += "|*{}*|{}".format(zDimLabel, d["zLen"])

        # Generate the markup for the Fixture Section

        fixtureTable = ""
        offsetsLabel = translate("Path_Sanity", "Work Offsets")
        orderByLabel = translate("Path_Sanity", "Order By")
        datumLabel = translate("Path_Sanity", "Part Datum")

        d = data["fixtureData"]

        fixtureTable += "|*{}*|{}\n".format(offsetsLabel, d["fixtures"])
        fixtureTable += "|*{}*|{}\n".format(orderByLabel, d["orderBy"])
        fixtureTable += "|*{}* a| image::{}[]".format(datumLabel, d["datumImage"])

        # Generate the markup for the Output Section

        outTable = ""
        d = data["outputData"]

        gcodeFileLabel = translate("Path_Sanity", "Gcode File")
        lastpostLabel = translate("Path_Sanity", "Last Post Process Date")
        stopsLabel = translate("Path_Sanity", "Stops")
        programmerLabel = translate("Path_Sanity", "Programmer")
        machineLabel = translate("Path_Sanity", "Machine")
        postLabel = translate("Path_Sanity", "Postprocessor")
        flagsLabel = translate("Path_Sanity", "Post Processor Flags")
        fileSizeLabel = translate("Path_Sanity", "File Size (kbs)")
        lineCountLabel = translate("Path_Sanity", "Line Count")

        outTable += "|*{}*|{}\n".format(gcodeFileLabel, d["lastgcodefile"])
        outTable += "|*{}*|{}\n".format(lastpostLabel, d["lastpostprocess"])
        outTable += "|*{}*|{}\n".format(stopsLabel, d["optionalstops"])
        outTable += "|*{}*|{}\n".format(programmerLabel, d["programmer"])
        outTable += "|*{}*|{}\n".format(machineLabel, d["machine"])
        outTable += "|*{}*|{}\n".format(postLabel, d["postprocessor"])
        outTable += "|*{}*|{}\n".format(flagsLabel, d["postprocessorFlags"])
        outTable += "|*{}*|{}\n".format(fileSizeLabel, d["filesize"])
        outTable += "|*{}*|{}\n".format(lineCountLabel, d["linecount"])

        # Generate the markup for the Squawk Section

        noteLabel = translate("Path_Sanity", "Note")
        operatorLabel = translate("Path_Sanity", "Operator")
        dateLabel = translate("Path_Sanity", "Date")

        squawkTable = "|*{}*|*{}*|*{}*\n".format(noteLabel, operatorLabel, dateLabel)

        d = data["squawkData"]
        for i in d["items"]:
            squawkTable += "a|{}: {}".format(i["squawkType"], i["Note"])
            squawkTable += "|{}".format(i["Operator"])
            squawkTable += "|{}".format(i["Date"])
            squawkTable += "\n"

        # merge template and custom markup

        report = reportTemplate.format(
            jobname=jobname,
            infoTable=infoTable,
            runTable=runTable,
            toolTables=toolTables,
            stockTable=stockTable,
            fixtureTable=fixtureTable,
            outTable=outTable,
            squawkTable=squawkTable,
        )

        # Save the report

        reportraw = self.outputpath + data["outputData"]["outputfilename"] + ".asciidoc"
        reporthtml = self.outputpath + data["outputData"]["outputfilename"] + ".html"
        with open(reportraw, "w") as fd:
            fd.write(report)
            fd.close()
            FreeCAD.Console.PrintMessage(
                "asciidoc file written to {}\n".format(reportraw)
            )

        try:
            result = os.system("asciidoctor {} -o {}".format(reportraw, reporthtml))
            if str(result) == "32512":
                msg = "asciidoctor not found. html cannot be generated."
                QtGui.QMessageBox.information(None, "Path Sanity", msg)
                FreeCAD.Console.PrintMessage(msg)
                reporthtml = None

        except Exception as e:
            FreeCAD.Console.PrintError(e)

        return reporthtml

    def __summarize(self, obj):
        """
        Top level function to summarize information for the report
        Returns a dictionary of sections
        """
        data = {}
        data["baseData"] = self.__baseObjectData(obj)
        data["designData"] = self.__designData(obj)
        data["toolData"] = self.__toolData(obj)
        data["runData"] = self.__runData(obj)
        data["outputData"] = self.__outputData(obj)
        data["fixtureData"] = self.__fixtureData(obj)
        data["stockData"] = self.__stockData(obj)
        data["squawkData"] = self.squawkData
        return data

    def squawk(self, operator, note, date=datetime.now(), squawkType="NOTE"):
        squawkType = (
            squawkType
            if squawkType in ["NOTE", "WARNING", "CAUTION", "TIP"]
            else "NOTE"
        )

        self.squawkData["items"].append(
            {
                "Date": str(date),
                "Operator": operator,
                "Note": note,
                "squawkType": squawkType,
            }
        )

    def __baseObjectData(self, obj):
        data = {"baseimage": "", "bases": ""}
        try:
            bases = {}
            for name, count in PathUtil.keyValueIter(
                Counter([obj.Proxy.baseObject(obj, o).Label for o in obj.Model.Group])
            ):
                bases[name] = str(count)

            data["baseimage"] = self.__makePicture(obj.Model, "baseimage")
            data["bases"] = bases

        except Exception as e:
            data["errors"] = e
            self.squawk("PathSanity(__baseObjectData)", e, squawkType="CAUTION")

        return data

    def __designData(self, obj):
        """
        Returns header information about the design document
        Returns information about issues and concerns (squawks)
        """

        data = {
            "FileName": "",
            "LastModifiedDate": "",
            "Customer": "",
            "Designer": "",
            "JobDescription": "",
            "JobLabel": "",
            "Sequence": "",
            "JobType": "",
        }
        try:
            data["FileName"] = obj.Document.FileName
            data["LastModifiedDate"] = str(obj.Document.LastModifiedDate)
            data["Customer"] = obj.Document.Company
            data["Designer"] = obj.Document.LastModifiedBy
            data["JobDescription"] = obj.Description
            data["JobLabel"] = obj.Label

            n = 0
            m = 0
            for i in obj.Document.Objects:
                if hasattr(i, "Proxy"):
                    if isinstance(i.Proxy, PathScripts.PathJob.ObjectJob):
                        m += 1
                        if i is obj:
                            n = m
            data["Sequence"] = "{} of {}".format(n, m)
            data["JobType"] = "2.5D Milling"  # improve after job types added

        except Exception as e:
            data["errors"] = e
            self.squawk("PathSanity(__designData)", e, squawkType="CAUTION")

        return data

    def __toolData(self, obj):
        """
        Returns information about the tools used in the job, and associated
        toolcontrollers
        Returns information about issues and problems with the tools (squawks)
        """
        data = {}

        try:
            for TC in obj.Tools.Group:
                if not hasattr(TC.Tool, "BitBody"):
                    self.squawk(
                        "PathSanity",
                        "Tool number {} is a legacy tool. Legacy tools not \
                        supported by Path-Sanity".format(
                            TC.ToolNumber
                        ),
                        squawkType="WARNING",
                    )
                    continue  # skip old-style tools
                tooldata = data.setdefault(str(TC.ToolNumber), {})
                bitshape = tooldata.setdefault("BitShape", "")
                if bitshape not in ["", TC.Tool.BitShape]:
                    self.squawk(
                        "PathSanity",
                        "Tool number {} used by multiple tools".format(TC.ToolNumber),
                        squawkType="CAUTION",
                    )
                tooldata["bitShape"] = TC.Tool.BitShape
                tooldata["description"] = TC.Tool.Label
                tooldata["manufacturer"] = ""
                tooldata["url"] = ""
                tooldata["inspectionNotes"] = ""
                tooldata["diameter"] = str(TC.Tool.Diameter)
                tooldata["shape"] = TC.Tool.ShapeName

                tooldata["partNumber"] = ""
                imagedata = TC.Tool.Proxy.getBitThumbnail(TC.Tool)
                imagepath = "{}T{}.png".format(self.outputpath, TC.ToolNumber)
                tooldata["feedrate"] = str(TC.HorizFeed)
                if TC.HorizFeed.Value == 0.0:
                    self.squawk(
                        "PathSanity",
                        "Tool Controller '{}' has no feedrate".format(TC.Label),
                        squawkType="WARNING",
                    )

                tooldata["spindlespeed"] = str(TC.SpindleSpeed)
                if TC.SpindleSpeed == 0.0:
                    self.squawk(
                        "PathSanity",
                        "Tool Controller '{}' has no spindlespeed".format(TC.Label),
                        squawkType="WARNING",
                    )

                with open(imagepath, "wb") as fd:
                    fd.write(imagedata)
                    fd.close()
                tooldata["imagepath"] = imagepath

                used = False
                for op in obj.Operations.Group:
                    if hasattr(op, "ToolController") and op.ToolController is TC:
                        used = True
                        tooldata.setdefault("ops", []).append(
                            {
                                "Operation": op.Label,
                                "ToolController": TC.Label,
                                "Feed": str(TC.HorizFeed),
                                "Speed": str(TC.SpindleSpeed),
                            }
                        )

                if used is False:
                    tooldata.setdefault("ops", [])
                    self.squawk(
                        "PathSanity",
                        "Tool Controller '{}' is not used".format(TC.Label),
                        squawkType="WARNING",
                    )

        except Exception as e:
            data["errors"] = e
            self.squawk("PathSanity(__toolData)", e, squawkType="CAUTION")

        return data

    def __runData(self, obj):
        data = {
            "cycletotal": "",
            "jobMinZ": "",
            "jobMaxZ": "",
            "jobDescription": "",
            "items": [],
        }
        try:
            data["cycletotal"] = str(obj.CycleTime)
            data["jobMinZ"] = FreeCAD.Units.Quantity(
                obj.Path.BoundBox.ZMin, FreeCAD.Units.Length
            ).UserString
            data["jobMaxZ"] = FreeCAD.Units.Quantity(
                obj.Path.BoundBox.ZMax, FreeCAD.Units.Length
            ).UserString
            data["jobDescription"] = obj.Description

            data["items"] = []
            for op in obj.Operations.Group:

                oplabel = op.Label
                ctime = op.CycleTime if hasattr(op, "CycleTime") else 0.0
                cool = op.CoolantMode if hasattr(op, "CoolantMode") else "N/A"

                o = op
                while len(o.ViewObject.claimChildren()) != 0:  # dressup
                    oplabel = "{}:{}".format(oplabel, o.Base.Label)
                    o = o.Base
                    if hasattr(o, "CycleTime"):
                        ctime = o.CycleTime
                    cool = o.CoolantMode if hasattr(o, "CoolantMode") else cool

                if hasattr(op, "Active") and not op.Active:
                    oplabel = "{} (INACTIVE)".format(oplabel)
                    ctime = 0.0

                if op.Path.BoundBox.isValid():
                    zmin = FreeCAD.Units.Quantity(
                        op.Path.BoundBox.ZMin, FreeCAD.Units.Length
                    ).UserString
                    zmax = FreeCAD.Units.Quantity(
                        op.Path.BoundBox.ZMax, FreeCAD.Units.Length
                    ).UserString
                else:
                    zmin = ""
                    zmax = ""

                opdata = {
                    "opName": oplabel,
                    "minZ": zmin,
                    "maxZ": zmax,
                    "cycleTime": ctime,
                    "coolantMode": cool,
                }
                data["items"].append(opdata)

        except Exception as e:
            data["errors"] = e
            self.squawk("PathSanity(__runData)", e, squawkType="CAUTION")

        return data

    def __stockData(self, obj):
        data = {"xLen": "", "yLen": "", "zLen": "", "material": "", "stockImage": ""}

        try:
            bb = obj.Stock.Shape.BoundBox
            data["xLen"] = FreeCAD.Units.Quantity(
                bb.XLength, FreeCAD.Units.Length
            ).UserString
            data["yLen"] = FreeCAD.Units.Quantity(
                bb.YLength, FreeCAD.Units.Length
            ).UserString
            data["zLen"] = FreeCAD.Units.Quantity(
                bb.ZLength, FreeCAD.Units.Length
            ).UserString

            data["material"] = "Not Specified"
            if hasattr(obj.Stock, "Material"):
                if obj.Stock.Material is not None:
                    data["material"] = obj.Stock.Material.Material["Name"]

            if data["material"] == "Not Specified":
                self.squawk(
                    "PathSanity",
                    "Consider Specifying the Stock Material",
                    squawkType="TIP",
                )

            data["stockImage"] = self.__makePicture(obj.Stock, "stockImage")
        except Exception as e:
            data["errors"] = e
            self.squawk("PathSanity(__stockData)", e, squawkType="CAUTION")

        return data

    def __fixtureData(self, obj):
        data = {"fixtures": "", "orderBy": "", "datumImage": ""}
        try:
            data["fixtures"] = str(obj.Fixtures)
            data["orderBy"] = str(obj.OrderOutputBy)

            aview = FreeCADGui.activeDocument().activeView()
            aview.setAnimationEnabled(False)

            obj.Visibility = False
            obj.Operations.Visibility = False

            mw = FreeCADGui.getMainWindow()
            mdi = mw.findChild(QtGui.QMdiArea)
            view = mdi.activeSubWindow()
            view.showNormal()
            view.resize(320, 320)

            imagepath = "{}origin".format(self.outputpath)

            FreeCADGui.Selection.clearSelection()
            FreeCADGui.SendMsgToActiveView("PerspectiveCamera")
            aview.viewIsometric()
            for i in obj.Model.Group:
                FreeCADGui.Selection.addSelection(i)
            FreeCADGui.SendMsgToActiveView("ViewSelection")
            FreeCADGui.Selection.clearSelection()
            obj.ViewObject.Proxy.editObject(obj)
            aview.saveImage("{}.png".format(imagepath), 320, 320, "Current")
            aview.saveImage("{}_t.png".format(imagepath), 320, 320, "Transparent")
            obj.ViewObject.Proxy.uneditObject(obj)
            obj.Visibility = True
            obj.Operations.Visibility = True

            view.showMaximized()

            aview.setAnimationEnabled(True)
            data["datumImage"] = "{}_t.png".format(imagepath)

        except Exception as e:
            data["errors"] = e
            self.squawk("PathSanity(__fixtureData)", e, squawkType="CAUTION")

        return data

    def __outputData(self, obj):
        data = {
            "lastpostprocess": "",
            "lastgcodefile": "",
            "optionalstops": "",
            "programmer": "",
            "machine": "",
            "postprocessor": "",
            "postprocessorFlags": "",
            "filesize": "",
            "linecount": "",
            "outputfilename": "setupreport",
        }
        try:
            data["lastpostprocess"] = str(obj.LastPostProcessDate)
            data["lastgcodefile"] = str(obj.LastPostProcessOutput)
            data["optionalstops"] = "False"
            data["programmer"] = ""
            data["machine"] = ""
            data["postprocessor"] = str(obj.PostProcessor)
            data["postprocessorFlags"] = str(obj.PostProcessorArgs)

            if obj.PostProcessorOutputFile != "":
                fname = obj.PostProcessorOutputFile
                data["outputfilename"] = os.path.splitext(os.path.basename(fname))[0]
            for op in obj.Operations.Group:
                if isinstance(op.Proxy, PathScripts.PathStop.Stop) and op.Stop is True:
                    data["optionalstops"] = "True"

            if obj.LastPostProcessOutput == "":
                data["filesize"] = str(0.0)
                data["linecount"] = str(0)
                self.squawk("PathSanity", "The Job has not been post-processed")
            else:
                data["filesize"] = str(os.path.getsize(obj.LastPostProcessOutput))
                data["linecount"] = str(
                    sum(1 for line in open(obj.LastPostProcessOutput))
                )

        except Exception as e:
            data["errors"] = e
            self.squawk("PathSanity(__outputData)", e, squawkType="CAUTION")

        return data


if FreeCAD.GuiUp:
    # register the FreeCAD command
    FreeCADGui.addCommand("Path_Sanity", CommandPathSanity())
