# Buggy code
def _updateProgressDataCallback(self):
    if self._comm is None:
        progress = None
        filepos = None
        printTime = None
        cleanedPrintTime = None
    else:
        progress = self._comm.getPrintProgress()
        filepos = self._comm.getPrintFilepos()
        printTime = self._comm.getPrintTime()
        cleanedPrintTime = self._comm.getCleanedPrintTime()

    printTimeLeft = printTimeLeftOrigin = None
    if progress is not None:
        progress_int = int(progress * 100)
        if self._lastProgressReport != progress_int:
            self._lastProgressReport = progress_int
            self._reportPrintProgressToPlugins(progress_int)

        if progress == 0:
            printTimeLeft = None
            printTimeLeftOrigin = None
        elif progress == 1.0:
            printTimeLeft = 0
            printTimeLeftOrigin = None
        else:
            statisticalTotalPrintTime = None
            statisticalTotalPrintTimeType = None
            with self._selectedFileMutex:
                if (
                    self._selectedFile
                    and "estimatedPrintTime" in self._selectedFile
                    and self._selectedFile["estimatedPrintTime"]
                ):
                    statisticalTotalPrintTime = self._selectedFile["estimatedPrintTime"]
                    statisticalTotalPrintTimeType = self._selectedFile.get(
                        "estimatedPrintTimeType", None
                    )

            printTimeLeft, printTimeLeftOrigin = self._estimator.estimate(
                progress,
                printTime,
                cleanedPrintTime,
                statisticalTotalPrintTime,
                statisticalTotalPrintTimeType,
            )

    return self._dict(
        completion=progress * 100 if progress is not None else None,
        filepos=filepos,
        printTime=int(printTime) if printTime is not None else None,
        printTimeLeft=int(printTimeLeft) if printTimeLeft is not None else None,
        printTimeLeftOrigin=printTimeLeftOrigin,
    )


# Traceback (AttributeError)
2018-07-05 05:17:16,231 - octoprint.util.comm - INFO - Externally triggered heatup detected
2018-07-05 05:18:34,517 - octoprint.util.comm - INFO - Printer seems to support the busy protocol, telling it to set the busy interval to our "communicationBusy" timeout - 1s = 2s
2018-07-05 05:19:27,589 - octoprint.util.comm - INFO - Timeout while in an active heatup, considering heatup to be over.
2018-07-05 05:19:30,594 - octoprint.util.comm - INFO - Communication timeout while idle, trying to trigger response from printer.
2018-07-05 05:19:32,426 - octoprint.util.comm - INFO - Changing monitoring state from "Operational" to "Printing from SD"
2018-07-05 05:19:32,512 - octoprint.plugins.Octoslack - ERROR - Error processing event: PrintStarted, Error: 'Printer' object has no attribute '_estimator'
Traceback (most recent call last):
File "/home/pi/oprint/local/lib/python2.7/site-packages/octoprint_Octoslack/__init__.py", line 591, in handle_event
self.process_slack_event(event, event_settings, channel_override, payload)
File "/home/pi/oprint/local/lib/python2.7/site-packages/octoprint_Octoslack/__init__.py", line 648, in process_slack_event
printer_data = self._printer.get_current_data()
File "/home/pi/oprint/local/lib/python2.7/site-packages/octoprint/server/__init__.py", line 384, in wrapper
return f(*args, **kwargs)
File "/home/pi/oprint/local/lib/python2.7/site-packages/octoprint/printer/standard.py", line 565, in get_current_data
return util.thaw_frozendict(self._stateMonitor.get_current_data())
File "/home/pi/oprint/local/lib/python2.7/site-packages/octoprint/printer/standard.py", line 1326, in get_current_data
self._progress = self._get_current_progress()
File "/home/pi/oprint/local/lib/python2.7/site-packages/octoprint/printer/standard.py", line 1255, in _get_current_progress
return self._on_get_progress()
File "/home/pi/oprint/local/lib/python2.7/site-packages/octoprint/printer/standard.py", line 773, in _updateProgressDataCallback
printTimeLeft, printTimeLeftOrigin = self._estimator.estimate(progress,
AttributeError: 'Printer' object has no attribute '_estimator'
2018-07-05 05:20:41,680 - octoprint.util.comm - INFO - Communication timeout while printing, trying to trigger response from printer.
2018-07-05 05:20:41,718 - octoprint.util.comm - INFO - Changing monitoring state from "Printing from SD" to "Cancelling"
2018-07-05 05:20:41,880 - octoprint.util.comm - INFO - Changing monitoring state from "Cancelling" to "Operational"
2018-07-05 05:21:35,678 - octoprint.server.api.system - INFO - Performing command for core:restart: sudo service octoprint restart

# Fixed code
def _updateProgressDataCallback(self):
    if self._comm is None:
        progress = None
        filepos = None
        printTime = None
        cleanedPrintTime = None
    else:
        progress = self._comm.getPrintProgress()
        filepos = self._comm.getPrintFilepos()
        printTime = self._comm.getPrintTime()
        cleanedPrintTime = self._comm.getCleanedPrintTime()

    printTimeLeft = printTimeLeftOrigin = None
    estimator = self._estimator
    if progress is not None:
        progress_int = int(progress * 100)
        if self._lastProgressReport != progress_int:
            self._lastProgressReport = progress_int
            self._reportPrintProgressToPlugins(progress_int)

        if progress == 0:
            printTimeLeft = None
            printTimeLeftOrigin = None
        elif progress == 1.0:
            printTimeLeft = 0
            printTimeLeftOrigin = None
        elif estimator is not None:
            statisticalTotalPrintTime = None
            statisticalTotalPrintTimeType = None
            with self._selectedFileMutex:
                if (
                    self._selectedFile
                    and "estimatedPrintTime" in self._selectedFile
                    and self._selectedFile["estimatedPrintTime"]
                ):
                    statisticalTotalPrintTime = self._selectedFile["estimatedPrintTime"]
                    statisticalTotalPrintTimeType = self._selectedFile.get(
                        "estimatedPrintTimeType", None
                    )

            printTimeLeft, printTimeLeftOrigin = estimator.estimate(
                progress,
                printTime,
                cleanedPrintTime,
                statisticalTotalPrintTime,
                statisticalTotalPrintTimeType,
            )

    return self._dict(
        completion=progress * 100 if progress is not None else None,
        filepos=filepos,
        printTime=int(printTime) if printTime is not None else None,
        printTimeLeft=int(printTimeLeft) if printTimeLeft is not None else None,
        printTimeLeftOrigin=printTimeLeftOrigin,
    )