# Buggy code
def create_app(config_class=Config, load_config=False):
    initialize_sentryio(config_class)
    app = Flask(__name__)
    app.config.from_object(Config)
    app.jinja_env.add_extension("jinja2.ext.do")

    db.init_app(app)
    migrate.init_app(app, db)
    login.init_app(app)
    mail.init_app(app)
    csrf.init_app(app)

    if load_config:
        print("Loading Config from database")
        with app.app_context():
            from app.models import ConfigItem

            # This is gross but we need it because otherwise flask db operations won't work to create the ConfigItem table in the first place.
            try:
                # Look to see if any new config items were added that aren't currently in db
                for item in get_defaults():
                    if not ConfigItem.query.filter_by(name=item[0]).first():
                        newConfItem = ConfigItem(
                            name=item[0], type=item[1], value=item[2]
                        )
                        db.session.add(newConfItem)
                        db.session.commit()

                conf = ConfigItem.query.all()
                if not conf:  # We'll hit this if the table exists but there's no data
                    populate_defaults(verbose=False)
                    conf = (
                        ConfigItem.query.all()
                    )  # populate_defaults should populate data that we can query now
                    if not conf:  # if it didn't, then we don't have config items that we need in order to run, so exit.
                        raise (SystemExit())

                for item in conf:
                    if item.type == "int":
                        app.config[item.name] = int(item.value)
                    elif item.type == "bool":
                        if item.value == "True":
                            app.config[item.name] = True
                        else:
                            app.config[item.name] = False
                    elif item.type == "string":
                        app.config[item.name] = item.value
                    else:
                        print(
                            "Unsupported config type %s:%s:%s"
                            % (item.name, item.type, item.value)
                        )
            except Exception:
                print("ConfigItem table doesn't exist yet. Ignore if flask db upgrade.")

            from app.models import NatlasServices

            try:
                current_services = NatlasServices.query.order_by(
                    NatlasServices.id.desc()
                ).first()
                if current_services:
                    app.current_services = current_services.as_dict()
                else:  # Let's populate server defaults
                    defaultServices = (
                        open(
                            os.path.join(
                                app.config["BASEDIR"], "defaults/natlas-services"
                            )
                        )
                        .read()
                        .rstrip("\r\n")
                    )
                    defaultSha = hashlib.sha256(defaultServices.encode()).hexdigest()
                    current_services = NatlasServices(
                        sha256=defaultSha, services=defaultServices
                    )  # default values until we load something
                    db.session.add(current_services)
                    db.session.commit()
                    print(
                        "NatlasServices populated with defaults from defaults/natlas-services"
                    )
                    app.current_services = current_services.as_dict()
            except Exception:
                print(
                    "NatlasServices table doesn't exist yet. Ignore if flask db upgrade."
                )

            # Load the current agent config, otherwise create it.
            from app.models import AgentConfig

            try:
                agentConfig = AgentConfig.query.get(
                    1
                )  # the agent config is updated in place so only 1 record
                if agentConfig:
                    app.agentConfig = agentConfig.as_dict()
                else:
                    newAgentConfig = (
                        AgentConfig()
                    )  # populate an agent config with database defaults
                    db.session.add(newAgentConfig)
                    db.session.commit()
                    print("AgentConfig populated with defaults")
                    app.agentConfig = newAgentConfig.as_dict()
            except Exception:
                print(
                    "AgentConfig table doesn't exist yet. Ignore if flask db upgrade."
                )

            # Load the current agent config, otherwise create it.
            from app.models import AgentScript

            try:
                agentScripts = AgentScript.query.all()
                if not agentScripts:
                    defaultAgentScript = AgentScript(name="default")
                    db.session.add(defaultAgentScript)
                    db.session.commit()
                    print("AgentScript populated with default")
                    agentScripts = AgentScript.query.all()
                app.agentScripts = agentScripts
                app.agentScriptStr = AgentScript.getScriptsString(
                    scriptList=agentScripts
                )
            except Exception:
                print(
                    "AgentScript table doesn't exist yet. Ignore if flask db upgrade."
                )

        # Grungy thing so we can use flask db and flask shell before the config items are initially populated
        if "ELASTICSEARCH_URL" in app.config:
            app.elastic = Elastic(app.config["ELASTICSEARCH_URL"])

    app.ScopeManager = ScopeManager()

    from app.errors import bp as errors_bp

    app.register_blueprint(errors_bp)

    from app.admin import bp as admin_bp

    app.register_blueprint(admin_bp, url_prefix="/admin")

    from app.api import bp as api_bp

    app.register_blueprint(api_bp, url_prefix="/api")
    csrf.exempt(api_bp)

    from app.auth import bp as auth_bp

    app.register_blueprint(auth_bp, url_prefix="/auth")

    from app.user import bp as user_bp

    app.register_blueprint(user_bp, url_prefix="/user")

    from app.host import bp as host_bp

    app.register_blueprint(host_bp, url_prefix="/host")

    from app.main import bp as main_bp

    app.register_blueprint(main_bp)

    from app.filters import bp as filters_bp

    app.register_blueprint(filters_bp)

    return app


# Traceback (TypeError)
Nov 05 17:22:50 natlas-server bash[10026]: [2019-11-05 17:22:50,681] ERROR in app: Exception on /api/getwork [GET]
Nov 05 17:22:50 natlas-server bash[10026]: Traceback (most recent call last):
Nov 05 17:22:50 natlas-server bash[10026]:   File "/opt/natlas/natlas-server/venv/lib/python3.6/site-packages/flask/app.py", line 2292, in wsgi_app
Nov 05 17:22:50 natlas-server bash[10026]:     response = self.full_dispatch_request()
Nov 05 17:22:50 natlas-server bash[10026]:   File "/opt/natlas/natlas-server/venv/lib/python3.6/site-packages/flask/app.py", line 1815, in full_dispatch_request
Nov 05 17:22:50 natlas-server bash[10026]:     rv = self.handle_user_exception(e)
Nov 05 17:22:50 natlas-server bash[10026]:   File "/opt/natlas/natlas-server/venv/lib/python3.6/site-packages/flask/app.py", line 1718, in handle_user_exception
Nov 05 17:22:50 natlas-server bash[10026]:     reraise(exc_type, exc_value, tb)
Nov 05 17:22:50 natlas-server bash[10026]:   File "/opt/natlas/natlas-server/venv/lib/python3.6/site-packages/flask/_compat.py", line 35, in reraise
Nov 05 17:22:50 natlas-server bash[10026]:     raise value
Nov 05 17:22:50 natlas-server bash[10026]:   File "/opt/natlas/natlas-server/venv/lib/python3.6/site-packages/flask/app.py", line 1813, in full_dispatch_request
Nov 05 17:22:50 natlas-server bash[10026]:     rv = self.dispatch_request()
Nov 05 17:22:50 natlas-server bash[10026]:   File "/opt/natlas/natlas-server/venv/lib/python3.6/site-packages/flask/app.py", line 1799, in dispatch_request
Nov 05 17:22:50 natlas-server bash[10026]:     return self.view_functions[rule.endpoint](**req.view_args)
Nov 05 17:22:50 natlas-server bash[10026]:   File "/opt/natlas/natlas-server/app/auth/wrappers.py", line 41, in decorated_function
Nov 05 17:22:50 natlas-server bash[10026]:     return f(*args, **kwargs)
Nov 05 17:22:50 natlas-server bash[10026]:   File "/opt/natlas/natlas-server/app/api/routes.py", line 70, in getwork
Nov 05 17:22:50 natlas-server bash[10026]:     count, context = current_app.elastic.gethost_scan_id(rand)
Nov 05 17:22:50 natlas-server bash[10026]:   File "/opt/natlas/natlas-server/app/elastic.py", line 238, in gethost_scan_id
Nov 05 17:22:50 natlas-server bash[10026]:     if not self.checkStatus():
Nov 05 17:22:50 natlas-server bash[10026]:   File "/opt/natlas/natlas-server/app/elastic.py", line 60, in checkStatus
Nov 05 17:22:50 natlas-server bash[10026]:     if not self.attemptReconnect():
Nov 05 17:22:50 natlas-server bash[10026]:   File "/opt/natlas/natlas-server/app/elastic.py", line 43, in attemptReconnect
Nov 05 17:22:50 natlas-server bash[10026]:     delta = now - self.lastReconnectAttempt
Nov 05 17:22:50 natlas-server bash[10026]: TypeError: unsupported operand type(s) for -: 'datetime.datetime' and 'NoneType

# Fixed code
def create_app(config_class=Config, load_config=False):
    initialize_sentryio(config_class)
    app = Flask(__name__)
    app.config.from_object(Config)
    app.jinja_env.add_extension("jinja2.ext.do")

    db.init_app(app)
    migrate.init_app(app, db)
    login.init_app(app)
    mail.init_app(app)
    csrf.init_app(app)

    if load_config:
        print("Loading Config from database")
        with app.app_context():
            from app.models import ConfigItem

            # This is gross but we need it because otherwise flask db operations won't work to create the ConfigItem table in the first place.
            try:
                # Look to see if any new config items were added that aren't currently in db
                for item in get_defaults():
                    if not ConfigItem.query.filter_by(name=item[0]).first():
                        newConfItem = ConfigItem(
                            name=item[0], type=item[1], value=item[2]
                        )
                        db.session.add(newConfItem)
                        db.session.commit()

                conf = ConfigItem.query.all()
                if not conf:  # We'll hit this if the table exists but there's no data
                    populate_defaults(verbose=False)
                    conf = (
                        ConfigItem.query.all()
                    )  # populate_defaults should populate data that we can query now
                    if not conf:  # if it didn't, then we don't have config items that we need in order to run, so exit.
                        raise (SystemExit())

                for item in conf:
                    if item.type == "int":
                        app.config[item.name] = int(item.value)
                    elif item.type == "bool":
                        if item.value == "True":
                            app.config[item.name] = True
                        else:
                            app.config[item.name] = False
                    elif item.type == "string":
                        app.config[item.name] = item.value
                    else:
                        print(
                            "Unsupported config type %s:%s:%s"
                            % (item.name, item.type, item.value)
                        )
            except Exception:
                print("ConfigItem table doesn't exist yet. Ignore if flask db upgrade.")

            from app.models import NatlasServices

            try:
                current_services = NatlasServices.query.order_by(
                    NatlasServices.id.desc()
                ).first()
                if current_services:
                    app.current_services = current_services.as_dict()
                else:  # Let's populate server defaults
                    defaultServices = (
                        open(
                            os.path.join(
                                app.config["BASEDIR"], "defaults/natlas-services"
                            )
                        )
                        .read()
                        .rstrip("\r\n")
                    )
                    defaultSha = hashlib.sha256(defaultServices.encode()).hexdigest()
                    current_services = NatlasServices(
                        sha256=defaultSha, services=defaultServices
                    )  # default values until we load something
                    db.session.add(current_services)
                    db.session.commit()
                    print(
                        "NatlasServices populated with defaults from defaults/natlas-services"
                    )
                    app.current_services = current_services.as_dict()
            except Exception:
                print(
                    "NatlasServices table doesn't exist yet. Ignore if flask db upgrade."
                )

            # Load the current agent config, otherwise create it.
            from app.models import AgentConfig

            try:
                agentConfig = AgentConfig.query.get(
                    1
                )  # the agent config is updated in place so only 1 record
                if agentConfig:
                    app.agentConfig = agentConfig.as_dict()
                else:
                    newAgentConfig = (
                        AgentConfig()
                    )  # populate an agent config with database defaults
                    db.session.add(newAgentConfig)
                    db.session.commit()
                    print("AgentConfig populated with defaults")
                    app.agentConfig = newAgentConfig.as_dict()
            except Exception:
                print(
                    "AgentConfig table doesn't exist yet. Ignore if flask db upgrade."
                )

            # Load the current agent config, otherwise create it.
            from app.models import AgentScript

            try:
                agentScripts = AgentScript.query.all()
                if not agentScripts:
                    defaultAgentScript = AgentScript(name="default")
                    db.session.add(defaultAgentScript)
                    db.session.commit()
                    print("AgentScript populated with default")
                    agentScripts = AgentScript.query.all()
                app.agentScripts = agentScripts
                app.agentScriptStr = AgentScript.getScriptsString(
                    scriptList=agentScripts
                )
            except Exception:
                print(
                    "AgentScript table doesn't exist yet. Ignore if flask db upgrade."
                )

        # Grungy thing so we can use flask db and flask shell before the config items are initially populated
        if "ELASTICSEARCH_URL" in app.config:
            app.elastic = ElasticInterface(app.config["ELASTICSEARCH_URL"])

    app.ScopeManager = ScopeManager()

    from app.errors import bp as errors_bp

    app.register_blueprint(errors_bp)

    from app.admin import bp as admin_bp

    app.register_blueprint(admin_bp, url_prefix="/admin")

    from app.api import bp as api_bp

    app.register_blueprint(api_bp, url_prefix="/api")
    csrf.exempt(api_bp)

    from app.auth import bp as auth_bp

    app.register_blueprint(auth_bp, url_prefix="/auth")

    from app.user import bp as user_bp

    app.register_blueprint(user_bp, url_prefix="/user")

    from app.host import bp as host_bp

    app.register_blueprint(host_bp, url_prefix="/host")

    from app.main import bp as main_bp

    app.register_blueprint(main_bp)

    from app.filters import bp as filters_bp

    app.register_blueprint(filters_bp)

    return app