# Buggy code
def get_unrendered_timelapses():
    global _job_lock
    global current

    delete_old_unrendered_timelapses()

    basedir = settings().getBaseFolder("timelapse_tmp", check_writable=False)
    jobs = collections.defaultdict(
        lambda: dict(count=0, size=None, bytes=0, date=None, timestamp=None)
    )

    for entry in scandir(basedir):
        if not fnmatch.fnmatch(entry.name, "*.jpg"):
            continue

        prefix = _extract_prefix(entry.name)
        if prefix is None:
            continue

        jobs[prefix]["count"] += 1
        jobs[prefix]["bytes"] += entry.stat().st_size
        if (
            jobs[prefix]["timestamp"] is None
            or entry.stat().st_mtime < jobs[prefix]["timestamp"]
        ):
            jobs[prefix]["timestamp"] = entry.stat().st_mtime

    with _job_lock:
        global current_render_job

        def finalize_fields(prefix, job):
            currently_recording = current is not None and current.prefix == prefix
            currently_rendering = (
                current_render_job is not None
                and current_render_job["prefix"] == prefix
            )

            job["size"] = util.get_formatted_size(job["bytes"])
            job["date"] = util.get_formatted_datetime(
                datetime.datetime.fromtimestamp(job["timestamp"])
            )
            job["recording"] = currently_recording
            job["rendering"] = currently_rendering
            job["processing"] = currently_recording or currently_rendering
            del job["timestamp"]

            return job

        return sorted(
            [
                util.dict_merge(dict(name=key), finalize_fields(key, value))
                for key, value in jobs.items()
            ],
            key=lambda x: x["name"],
        )


# Traceback (TypeError)
2019-10-08 14:14:51,399 - octoprint.server.preemptive_cache - INFO - Preemptively caching / (ui _default) for {'base_url': 'http://192.168.0.113:5000/', 'path': '/', 'query_string': 'l10n=en'}
[2019-10-08 14:14:51,556] ERROR in app: Exception on / [GET]
Traceback (most recent call last):
File "c:\octoprint\venv\lib\site-packages\flask\app.py", line 1982, in wsgi_app
response = self.full_dispatch_request()
File "c:\octoprint\venv\lib\site-packages\flask\app.py", line 1614, in full_dispatch_request
rv = self.handle_user_exception(e)
File "c:\octoprint\venv\lib\site-packages\flask\app.py", line 1517, in handle_user_exception
reraise(exc_type, exc_value, tb)
File "c:\octoprint\venv\lib\site-packages\flask\_compat.py", line 33, in reraise
raise value
File "c:\octoprint\venv\lib\site-packages\flask\app.py", line 1612, in full_dispatch_request
rv = self.dispatch_request()
File "c:\octoprint\venv\lib\site-packages\flask\app.py", line 1598, in dispatch_request
return self.view_functions[rule.endpoint](**req.view_args)
File "c:\octoprint\venv\lib\site-packages\octoprint\server\views.py", line 181, in index
fetch_template_data(refresh=force_refresh)
File "c:\octoprint\venv\lib\site-packages\octoprint\server\views.py", line 755, in fetch_template_data
sorted_missing = sorted(missing_in_order, key=key_func)
TypeError: '<' not supported between instances of 'str' and 'NoneType'
[2019-10-08 14:14:51,743] ERROR in app: Exception on / [GET]
Traceback (most recent call last):
File "c:\octoprint\venv\lib\site-packages\flask\app.py", line 1982, in wsgi_app
response = self.full_dispatch_request()
File "c:\octoprint\venv\lib\site-packages\flask\app.py", line 1614, in full_dispatch_request
rv = self.handle_user_exception(e)
File "c:\octoprint\venv\lib\site-packages\flask\app.py", line 1517, in handle_user_exception
reraise(exc_type, exc_value, tb)
File "c:\octoprint\venv\lib\site-packages\flask\_compat.py", line 33, in reraise
raise value
File "c:\octoprint\venv\lib\site-packages\flask\app.py", line 1612, in full_dispatch_request
rv = self.dispatch_request()
File "c:\octoprint\venv\lib\site-packages\flask\app.py", line 1598, in dispatch_request
return self.view_functions[rule.endpoint](**req.view_args)
File "c:\octoprint\venv\lib\site-packages\octoprint\server\views.py", line 181, in index
fetch_template_data(refresh=force_refresh)
File "c:\octoprint\venv\lib\site-packages\octoprint\server\views.py", line 755, in fetch_template_data
sorted_missing = sorted(missing_in_order, key=key_func)
TypeError: '<' not supported between instances of 'str' and 'NoneType'
[2019-10-08 14:14:51,884] ERROR in app: Exception on / [GET]
Traceback (most recent call last):
File "c:\octoprint\venv\lib\site-packages\flask\app.py", line 1982, in wsgi_app
response = self.full_dispatch_request()
File "c:\octoprint\venv\lib\site-packages\flask\app.py", line 1614, in full_dispatch_request
rv = self.handle_user_exception(e)
File "c:\octoprint\venv\lib\site-packages\flask\app.py", line 1517, in handle_user_exception
reraise(exc_type, exc_value, tb)
File "c:\octoprint\venv\lib\site-packages\flask\_compat.py", line 33, in reraise
raise value
File "c:\octoprint\venv\lib\site-packages\flask\app.py", line 1612, in full_dispatch_request
rv = self.dispatch_request()
File "c:\octoprint\venv\lib\site-packages\flask\app.py", line 1598, in dispatch_request
return self.view_functions[rule.endpoint](**req.view_args)
File "c:\octoprint\venv\lib\site-packages\octoprint\server\views.py", line 181, in index
fetch_template_data(refresh=force_refresh)
File "c:\octoprint\venv\lib\site-packages\octoprint\server\views.py", line 755, in fetch_template_data
sorted_missing = sorted(missing_in_order, key=key_func)
TypeError: '<' not supported between instances of 'str' and 'NoneType'

# Fixed code
def get_unrendered_timelapses():
    global _job_lock
    global current

    delete_old_unrendered_timelapses()

    basedir = settings().getBaseFolder("timelapse_tmp", check_writable=False)
    jobs = collections.defaultdict(
        lambda: dict(count=0, size=None, bytes=0, date=None, timestamp=None)
    )

    for entry in scandir(basedir):
        if not fnmatch.fnmatch(entry.name, "*.jpg"):
            continue

        prefix = _extract_prefix(entry.name)
        if prefix is None:
            continue

        jobs[prefix]["count"] += 1
        jobs[prefix]["bytes"] += entry.stat().st_size
        if (
            jobs[prefix]["timestamp"] is None
            or entry.stat().st_mtime < jobs[prefix]["timestamp"]
        ):
            jobs[prefix]["timestamp"] = entry.stat().st_mtime

    with _job_lock:
        global current_render_job

        def finalize_fields(prefix, job):
            currently_recording = current is not None and current.prefix == prefix
            currently_rendering = (
                current_render_job is not None
                and current_render_job["prefix"] == prefix
            )

            job["size"] = util.get_formatted_size(job["bytes"])
            job["date"] = util.get_formatted_datetime(
                datetime.datetime.fromtimestamp(job["timestamp"])
            )
            job["recording"] = currently_recording
            job["rendering"] = currently_rendering
            job["processing"] = currently_recording or currently_rendering
            del job["timestamp"]

            return job

        return sorted(
            [
                util.dict_merge(dict(name=key), finalize_fields(key, value))
                for key, value in jobs.items()
            ],
            key=lambda x: sv(x["name"]),
        )