We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Flask serializer that can use python datetimes/date/timestamp types
We need to introduce a json serializer for flask that can handle python datetime/date/timestamp type.

Note: Figure out commonly used timestamp/datetime types other than the "standard" ones from the `datetime` library that might be yielded from our database clients and make it into the dataframe. 

See example in db.py for how to do this, but maybe there's a easier flask specific way of doing this.

Format should be a string that's `yyyy-MM-dd'T'HH:mm:ss.SSS`, so from years to miliseconds with no timezeone/offset.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `mindsdb/api/http/initialize.py`
Content:
```
1 from distutils.version import LooseVersion
2 import requests
3 import os
4 import shutil
5 import threading
6 import webbrowser
7 from zipfile import ZipFile
8 from pathlib import Path
9 import traceback
10 #import concurrent.futures
11 
12 from flask import Flask, url_for
13 from flask_restx import Api
14 
15 from mindsdb.__about__ import __version__ as mindsdb_version
16 from mindsdb.interfaces.datastore.datastore import DataStore
17 from mindsdb.interfaces.model.model_interface import ModelInterface as NativeInterface
18 from mindsdb.interfaces.custom.custom_models import CustomModels
19 from mindsdb.utilities.ps import is_pid_listen_port, wait_func_is_true
20 from mindsdb.interfaces.database.database import DatabaseWrapper
21 from mindsdb.utilities.telemetry import inject_telemetry_to_static
22 from mindsdb.utilities.config import Config
23 from mindsdb.utilities.log import get_log
24 from mindsdb.interfaces.storage.db import session
25 
26 
27 class Swagger_Api(Api):
28     """
29     This is a modification of the base Flask Restplus Api class due to the issue described here
30     https://github.com/noirbizarre/flask-restplus/issues/223
31     """
32     @property
33     def specs_url(self):
34         return url_for(self.endpoint("specs"), _external=False)
35 
36 
37 def initialize_static(config):
38     ''' Update Scout files basing on compatible-config.json content.
39         Files will be downloaded and updated if new version of GUI > current.
40         Current GUI version stored in static/version.txt.
41     '''
42     log = get_log('http')
43     static_path = Path(config.paths['static'])
44     static_path.mkdir(parents=True, exist_ok=True)
45 
46     try:
47         res = requests.get('https://mindsdb-web-builds.s3.amazonaws.com/compatible-config.json')
48     except (ConnectionError, requests.exceptions.ConnectionError) as e:
49         print(f'Is no connection. {e}')
50         return False
51     except Exception as e:
52         print(f'Is something wrong with getting compatible-config.json: {e}')
53         return False
54 
55     if res.status_code != 200:
56         print(f'Cant get compatible-config.json: returned status code = {res.status_code}')
57         return False
58 
59     try:
60         versions = res.json()
61     except Exception as e:
62         print(f'Cant decode compatible-config.json: {e}')
63         return False
64 
65     current_mindsdb_lv = LooseVersion(mindsdb_version)
66 
67     try:
68         gui_versions = {}
69         max_mindsdb_lv = None
70         max_gui_lv = None
71         for el in versions['mindsdb']:
72             if el['mindsdb_version'] is None:
73                 gui_lv = LooseVersion(el['gui_version'])
74             else:
75                 mindsdb_lv = LooseVersion(el['mindsdb_version'])
76                 gui_lv = LooseVersion(el['gui_version'])
77                 if mindsdb_lv.vstring not in gui_versions or gui_lv > gui_versions[mindsdb_lv.vstring]:
78                     gui_versions[mindsdb_lv.vstring] = gui_lv
79                 if max_mindsdb_lv is None or max_mindsdb_lv < mindsdb_lv:
80                     max_mindsdb_lv = mindsdb_lv
81             if max_gui_lv is None or max_gui_lv < gui_lv:
82                 max_gui_lv = gui_lv
83 
84         all_mindsdb_lv = [LooseVersion(x) for x in gui_versions.keys()]
85         all_mindsdb_lv.sort()
86 
87         if current_mindsdb_lv.vstring in gui_versions:
88             gui_version_lv = gui_versions[current_mindsdb_lv.vstring]
89         elif current_mindsdb_lv > all_mindsdb_lv[-1]:
90             gui_version_lv = max_gui_lv
91         else:
92             lower_versions = {key: value for key, value in gui_versions.items() if LooseVersion(key) < current_mindsdb_lv}
93             if len(lower_versions) == 0:
94                 gui_version_lv = gui_versions[all_mindsdb_lv[0].vstring]
95             else:
96                 all_lower_versions = [LooseVersion(x) for x in lower_versions.keys()]
97                 gui_version_lv = gui_versions[all_lower_versions[-1].vstring]
98     except Exception as e:
99         log.error(f'Error in compatible-config.json structure: {e}')
100         return False
101 
102     current_gui_version = None
103 
104     version_txt_path = static_path.joinpath('version.txt')
105     if version_txt_path.is_file():
106         with open(version_txt_path, 'rt') as f:
107             current_gui_version = f.readline()
108     if current_gui_version is not None:
109         current_gui_lv = LooseVersion(current_gui_version)
110         if current_gui_lv >= gui_version_lv:
111             return True
112 
113     log.info(f'New version of GUI available ({gui_version_lv.vstring}). Downloading...')
114 
115     shutil.rmtree(static_path)
116     static_path.mkdir(parents=True, exist_ok=True)
117 
118     try:
119         css_zip_path = str(static_path.joinpath('css.zip'))
120         js_zip_path = str(static_path.joinpath('js.zip'))
121         media_zip_path = str(static_path.joinpath('media.zip'))
122         bucket = "https://mindsdb-web-builds.s3.amazonaws.com/"
123 
124         gui_version = gui_version_lv.vstring
125 
126         resources = [
127             {
128                 'url': bucket + 'css-V' + gui_version + '.zip',
129                 'path': css_zip_path
130             }, {
131                 'url': bucket + 'js-V' + gui_version + '.zip',
132                 'path': js_zip_path
133             }, {
134                 'url': bucket + 'indexV' + gui_version + '.html',
135                 'path': str(static_path.joinpath('index.html'))
136             }, {
137                 'url': bucket + 'favicon.ico',
138                 'path': str(static_path.joinpath('favicon.ico'))
139             }, {
140                 'url': bucket + 'media.zip',
141                 'path': media_zip_path
142             }
143         ]
144 
145         def get_resources(resource):
146             try:
147                 response = requests.get(resource['url'])
148                 if response.status_code != requests.status_codes.codes.ok:
149                     return Exception(f"Error {response.status_code} GET {resource['url']}")
150                 open(resource['path'], 'wb').write(response.content)
151             except Exception as e:
152                 return e
153             return None
154 
155         for r in resources:
156             get_resources(r)
157 
158         '''
159         # to make downloading faster download each resource in a separate thread
160         with concurrent.futures.ThreadPoolExecutor(max_workers=5) as executor:
161             future_to_url = {executor.submit(get_resources, r): r for r in resources}
162             for future in concurrent.futures.as_completed(future_to_url):
163                 res = future.result()
164                 if res is not None:
165                     raise res
166         '''
167 
168     except Exception as e:
169         log.error(f'Error during downloading files from s3: {e}')
170         session.close()
171         return False
172 
173     static_folder = static_path.joinpath('static')
174     static_folder.mkdir(parents=True, exist_ok=True)
175 
176     # unzip process
177     for zip_path, dir_name in [[js_zip_path, 'js'], [css_zip_path, 'css']]:
178         temp_dir = static_path.joinpath(f'temp_{dir_name}')
179         temp_dir.mkdir(mode=0o777, exist_ok=True, parents=True)
180         ZipFile(zip_path).extractall(temp_dir)
181         files_path = static_path.joinpath('static', dir_name)
182         if temp_dir.joinpath('build', 'static', dir_name).is_dir():
183             shutil.move(temp_dir.joinpath('build', 'static', dir_name), files_path)
184             shutil.rmtree(temp_dir)
185         else:
186             shutil.move(temp_dir, files_path)
187 
188     ZipFile(media_zip_path).extractall(static_folder)
189 
190     os.remove(js_zip_path)
191     os.remove(css_zip_path)
192     os.remove(media_zip_path)
193 
194     with open(version_txt_path, 'wt') as f:
195         f.write(gui_version_lv.vstring)
196 
197     log.info(f'GUI version updated to {gui_version_lv.vstring}')
198     session.close()
199     return True
200 
201 
202 def initialize_flask(config, init_static_thread, no_studio):
203     # Apparently there's a bug that causes the static path not to work if it's '/' -- https://github.com/pallets/flask/issues/3134, I think '' should achieve the same thing (???)
204     if no_studio:
205         app = Flask(
206             __name__
207         )
208     else:
209         app = Flask(
210             __name__,
211             static_url_path='/static',
212             static_folder=os.path.join(config.paths['static'], 'static/')
213         )
214 
215     app.config['SEND_FILE_MAX_AGE_DEFAULT'] = 60
216     app.config['SWAGGER_HOST'] = 'http://localhost:8000/mindsdb'
217     authorizations = {
218         'apikey': {
219             'type': 'apiKey',
220             'in': 'query',
221             'name': 'apikey'
222         }
223     }
224 
225     api = Swagger_Api(
226         app,
227         authorizations=authorizations,
228         security=['apikey'],
229         url_prefix=':8000',
230         prefix='/api',
231         doc='/doc/'
232     )
233 
234     port = config['api']['http']['port']
235     host = config['api']['http']['host']
236 
237     # NOTE rewrite it, that hotfix to see GUI link
238     if not no_studio:
239         log = get_log('http')
240         if host in ('', '0.0.0.0'):
241             url = f'http://127.0.0.1:{port}/'
242         else:
243             url = f'http://{host}:{port}/'
244         log.info(f' - GUI available at {url}')
245 
246         pid = os.getpid()
247         x = threading.Thread(target=_open_webbrowser, args=(url, pid, port, init_static_thread, config.paths['static']), daemon=True)
248         x.start()
249 
250     return app, api
251 
252 
253 def initialize_interfaces(app):
254     app.default_store = DataStore()
255     app.naitve_interface = NativeInterface()
256     app.custom_models = CustomModels()
257     app.dbw = DatabaseWrapper()
258     config = Config()
259     app.config_obj = config
260 
261 
262 def _open_webbrowser(url: str, pid: int, port: int, init_static_thread, static_folder):
263     """Open webbrowser with url when http service is started.
264 
265     If some error then do nothing.
266     """
267     init_static_thread.join()
268     inject_telemetry_to_static(static_folder)
269     logger = get_log('http')
270     try:
271         is_http_active = wait_func_is_true(func=is_pid_listen_port, timeout=10,
272                                            pid=pid, port=port)
273         if is_http_active:
274             webbrowser.open(url)
275     except Exception as e:
276         logger.error(f'Failed to open {url} in webbrowser with exception {e}')
277         logger.error(traceback.format_exc())
278     session.close()
279
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/mindsdb/api/http/initialize.py b/mindsdb/api/http/initialize.py
--- a/mindsdb/api/http/initialize.py
+++ b/mindsdb/api/http/initialize.py
@@ -7,10 +7,13 @@
 from zipfile import ZipFile
 from pathlib import Path
 import traceback
+from datetime import datetime, date, timedelta
 #import concurrent.futures
 
-from flask import Flask, url_for
+from flask import Flask, url_for, make_response
+from flask.json import dumps
 from flask_restx import Api
+from flask.json import JSONEncoder
 
 from mindsdb.__about__ import __version__ as mindsdb_version
 from mindsdb.interfaces.datastore.datastore import DataStore
@@ -34,6 +37,24 @@
         return url_for(self.endpoint("specs"), _external=False)
 
 
+class CustomJSONEncoder(JSONEncoder):
+    def default(self, obj):
+        if isinstance(obj, date):
+            return obj.strftime("%Y-%m-%d")
+        if isinstance(obj, datetime):
+            return obj.strftime("%Y-%m-%dT%H:%M:%S.%f")
+        if isinstance(obj, timedelta):
+            return str(obj)
+
+        return JSONEncoder.default(self, obj)
+
+
+def custom_output_json(data, code, headers=None):
+    resp = make_response(dumps(data), code)
+    resp.headers.extend(headers or {})
+    return resp
+
+
 def initialize_static(config):
     ''' Update Scout files basing on compatible-config.json content.
         Files will be downloaded and updated if new version of GUI > current.
@@ -214,11 +235,13 @@
 
     app.config['SEND_FILE_MAX_AGE_DEFAULT'] = 60
     app.config['SWAGGER_HOST'] = 'http://localhost:8000/mindsdb'
+    app.json_encoder = CustomJSONEncoder
+
     authorizations = {
         'apikey': {
-            'type': 'apiKey',
+            'type': 'session',
             'in': 'query',
-            'name': 'apikey'
+            'name': 'session'
         }
     }
 
@@ -231,6 +254,8 @@
         doc='/doc/'
     )
 
+    api.representations['application/json'] = custom_output_json
+
     port = config['api']['http']['port']
     host = config['api']['http']['host']

{"golden_diff": "diff --git a/mindsdb/api/http/initialize.py b/mindsdb/api/http/initialize.py\n--- a/mindsdb/api/http/initialize.py\n+++ b/mindsdb/api/http/initialize.py\n@@ -7,10 +7,13 @@\n from zipfile import ZipFile\n from pathlib import Path\n import traceback\n+from datetime import datetime, date, timedelta\n #import concurrent.futures\n \n-from flask import Flask, url_for\n+from flask import Flask, url_for, make_response\n+from flask.json import dumps\n from flask_restx import Api\n+from flask.json import JSONEncoder\n \n from mindsdb.__about__ import __version__ as mindsdb_version\n from mindsdb.interfaces.datastore.datastore import DataStore\n@@ -34,6 +37,24 @@\n         return url_for(self.endpoint(\"specs\"), _external=False)\n \n \n+class CustomJSONEncoder(JSONEncoder):\n+    def default(self, obj):\n+        if isinstance(obj, date):\n+            return obj.strftime(\"%Y-%m-%d\")\n+        if isinstance(obj, datetime):\n+            return obj.strftime(\"%Y-%m-%dT%H:%M:%S.%f\")\n+        if isinstance(obj, timedelta):\n+            return str(obj)\n+\n+        return JSONEncoder.default(self, obj)\n+\n+\n+def custom_output_json(data, code, headers=None):\n+    resp = make_response(dumps(data), code)\n+    resp.headers.extend(headers or {})\n+    return resp\n+\n+\n def initialize_static(config):\n     ''' Update Scout files basing on compatible-config.json content.\n         Files will be downloaded and updated if new version of GUI > current.\n@@ -214,11 +235,13 @@\n \n     app.config['SEND_FILE_MAX_AGE_DEFAULT'] = 60\n     app.config['SWAGGER_HOST'] = 'http://localhost:8000/mindsdb'\n+    app.json_encoder = CustomJSONEncoder\n+\n     authorizations = {\n         'apikey': {\n-            'type': 'apiKey',\n+            'type': 'session',\n             'in': 'query',\n-            'name': 'apikey'\n+            'name': 'session'\n         }\n     }\n \n@@ -231,6 +254,8 @@\n         doc='/doc/'\n     )\n \n+    api.representations['application/json'] = custom_output_json\n+\n     port = config['api']['http']['port']\n     host = config['api']['http']['host']\n", "issue": "Flask serializer that can use python datetimes/date/timestamp types\nWe need to introduce a json serializer for flask that can handle python datetime/date/timestamp type.\r\n\r\nNote: Figure out commonly used timestamp/datetime types other than the \"standard\" ones from the `datetime` library that might be yielded from our database clients and make it into the dataframe. \r\n\r\nSee example in db.py for how to do this, but maybe there's a easier flask specific way of doing this.\r\n\r\nFormat should be a string that's `yyyy-MM-dd'T'HH:mm:ss.SSS`, so from years to miliseconds with no timezeone/offset.\n", "before_files": [{"content": "from distutils.version import LooseVersion\nimport requests\nimport os\nimport shutil\nimport threading\nimport webbrowser\nfrom zipfile import ZipFile\nfrom pathlib import Path\nimport traceback\n#import concurrent.futures\n\nfrom flask import Flask, url_for\nfrom flask_restx import Api\n\nfrom mindsdb.__about__ import __version__ as mindsdb_version\nfrom mindsdb.interfaces.datastore.datastore import DataStore\nfrom mindsdb.interfaces.model.model_interface import ModelInterface as NativeInterface\nfrom mindsdb.interfaces.custom.custom_models import CustomModels\nfrom mindsdb.utilities.ps import is_pid_listen_port, wait_func_is_true\nfrom mindsdb.interfaces.database.database import DatabaseWrapper\nfrom mindsdb.utilities.telemetry import inject_telemetry_to_static\nfrom mindsdb.utilities.config import Config\nfrom mindsdb.utilities.log import get_log\nfrom mindsdb.interfaces.storage.db import session\n\n\nclass Swagger_Api(Api):\n    \"\"\"\n    This is a modification of the base Flask Restplus Api class due to the issue described here\n    https://github.com/noirbizarre/flask-restplus/issues/223\n    \"\"\"\n    @property\n    def specs_url(self):\n        return url_for(self.endpoint(\"specs\"), _external=False)\n\n\ndef initialize_static(config):\n    ''' Update Scout files basing on compatible-config.json content.\n        Files will be downloaded and updated if new version of GUI > current.\n        Current GUI version stored in static/version.txt.\n    '''\n    log = get_log('http')\n    static_path = Path(config.paths['static'])\n    static_path.mkdir(parents=True, exist_ok=True)\n\n    try:\n        res = requests.get('https://mindsdb-web-builds.s3.amazonaws.com/compatible-config.json')\n    except (ConnectionError, requests.exceptions.ConnectionError) as e:\n        print(f'Is no connection. {e}')\n        return False\n    except Exception as e:\n        print(f'Is something wrong with getting compatible-config.json: {e}')\n        return False\n\n    if res.status_code != 200:\n        print(f'Cant get compatible-config.json: returned status code = {res.status_code}')\n        return False\n\n    try:\n        versions = res.json()\n    except Exception as e:\n        print(f'Cant decode compatible-config.json: {e}')\n        return False\n\n    current_mindsdb_lv = LooseVersion(mindsdb_version)\n\n    try:\n        gui_versions = {}\n        max_mindsdb_lv = None\n        max_gui_lv = None\n        for el in versions['mindsdb']:\n            if el['mindsdb_version'] is None:\n                gui_lv = LooseVersion(el['gui_version'])\n            else:\n                mindsdb_lv = LooseVersion(el['mindsdb_version'])\n                gui_lv = LooseVersion(el['gui_version'])\n                if mindsdb_lv.vstring not in gui_versions or gui_lv > gui_versions[mindsdb_lv.vstring]:\n                    gui_versions[mindsdb_lv.vstring] = gui_lv\n                if max_mindsdb_lv is None or max_mindsdb_lv < mindsdb_lv:\n                    max_mindsdb_lv = mindsdb_lv\n            if max_gui_lv is None or max_gui_lv < gui_lv:\n                max_gui_lv = gui_lv\n\n        all_mindsdb_lv = [LooseVersion(x) for x in gui_versions.keys()]\n        all_mindsdb_lv.sort()\n\n        if current_mindsdb_lv.vstring in gui_versions:\n            gui_version_lv = gui_versions[current_mindsdb_lv.vstring]\n        elif current_mindsdb_lv > all_mindsdb_lv[-1]:\n            gui_version_lv = max_gui_lv\n        else:\n            lower_versions = {key: value for key, value in gui_versions.items() if LooseVersion(key) < current_mindsdb_lv}\n            if len(lower_versions) == 0:\n                gui_version_lv = gui_versions[all_mindsdb_lv[0].vstring]\n            else:\n                all_lower_versions = [LooseVersion(x) for x in lower_versions.keys()]\n                gui_version_lv = gui_versions[all_lower_versions[-1].vstring]\n    except Exception as e:\n        log.error(f'Error in compatible-config.json structure: {e}')\n        return False\n\n    current_gui_version = None\n\n    version_txt_path = static_path.joinpath('version.txt')\n    if version_txt_path.is_file():\n        with open(version_txt_path, 'rt') as f:\n            current_gui_version = f.readline()\n    if current_gui_version is not None:\n        current_gui_lv = LooseVersion(current_gui_version)\n        if current_gui_lv >= gui_version_lv:\n            return True\n\n    log.info(f'New version of GUI available ({gui_version_lv.vstring}). Downloading...')\n\n    shutil.rmtree(static_path)\n    static_path.mkdir(parents=True, exist_ok=True)\n\n    try:\n        css_zip_path = str(static_path.joinpath('css.zip'))\n        js_zip_path = str(static_path.joinpath('js.zip'))\n        media_zip_path = str(static_path.joinpath('media.zip'))\n        bucket = \"https://mindsdb-web-builds.s3.amazonaws.com/\"\n\n        gui_version = gui_version_lv.vstring\n\n        resources = [\n            {\n                'url': bucket + 'css-V' + gui_version + '.zip',\n                'path': css_zip_path\n            }, {\n                'url': bucket + 'js-V' + gui_version + '.zip',\n                'path': js_zip_path\n            }, {\n                'url': bucket + 'indexV' + gui_version + '.html',\n                'path': str(static_path.joinpath('index.html'))\n            }, {\n                'url': bucket + 'favicon.ico',\n                'path': str(static_path.joinpath('favicon.ico'))\n            }, {\n                'url': bucket + 'media.zip',\n                'path': media_zip_path\n            }\n        ]\n\n        def get_resources(resource):\n            try:\n                response = requests.get(resource['url'])\n                if response.status_code != requests.status_codes.codes.ok:\n                    return Exception(f\"Error {response.status_code} GET {resource['url']}\")\n                open(resource['path'], 'wb').write(response.content)\n            except Exception as e:\n                return e\n            return None\n\n        for r in resources:\n            get_resources(r)\n\n        '''\n        # to make downloading faster download each resource in a separate thread\n        with concurrent.futures.ThreadPoolExecutor(max_workers=5) as executor:\n            future_to_url = {executor.submit(get_resources, r): r for r in resources}\n            for future in concurrent.futures.as_completed(future_to_url):\n                res = future.result()\n                if res is not None:\n                    raise res\n        '''\n\n    except Exception as e:\n        log.error(f'Error during downloading files from s3: {e}')\n        session.close()\n        return False\n\n    static_folder = static_path.joinpath('static')\n    static_folder.mkdir(parents=True, exist_ok=True)\n\n    # unzip process\n    for zip_path, dir_name in [[js_zip_path, 'js'], [css_zip_path, 'css']]:\n        temp_dir = static_path.joinpath(f'temp_{dir_name}')\n        temp_dir.mkdir(mode=0o777, exist_ok=True, parents=True)\n        ZipFile(zip_path).extractall(temp_dir)\n        files_path = static_path.joinpath('static', dir_name)\n        if temp_dir.joinpath('build', 'static', dir_name).is_dir():\n            shutil.move(temp_dir.joinpath('build', 'static', dir_name), files_path)\n            shutil.rmtree(temp_dir)\n        else:\n            shutil.move(temp_dir, files_path)\n\n    ZipFile(media_zip_path).extractall(static_folder)\n\n    os.remove(js_zip_path)\n    os.remove(css_zip_path)\n    os.remove(media_zip_path)\n\n    with open(version_txt_path, 'wt') as f:\n        f.write(gui_version_lv.vstring)\n\n    log.info(f'GUI version updated to {gui_version_lv.vstring}')\n    session.close()\n    return True\n\n\ndef initialize_flask(config, init_static_thread, no_studio):\n    # Apparently there's a bug that causes the static path not to work if it's '/' -- https://github.com/pallets/flask/issues/3134, I think '' should achieve the same thing (???)\n    if no_studio:\n        app = Flask(\n            __name__\n        )\n    else:\n        app = Flask(\n            __name__,\n            static_url_path='/static',\n            static_folder=os.path.join(config.paths['static'], 'static/')\n        )\n\n    app.config['SEND_FILE_MAX_AGE_DEFAULT'] = 60\n    app.config['SWAGGER_HOST'] = 'http://localhost:8000/mindsdb'\n    authorizations = {\n        'apikey': {\n            'type': 'apiKey',\n            'in': 'query',\n            'name': 'apikey'\n        }\n    }\n\n    api = Swagger_Api(\n        app,\n        authorizations=authorizations,\n        security=['apikey'],\n        url_prefix=':8000',\n        prefix='/api',\n        doc='/doc/'\n    )\n\n    port = config['api']['http']['port']\n    host = config['api']['http']['host']\n\n    # NOTE rewrite it, that hotfix to see GUI link\n    if not no_studio:\n        log = get_log('http')\n        if host in ('', '0.0.0.0'):\n            url = f'http://127.0.0.1:{port}/'\n        else:\n            url = f'http://{host}:{port}/'\n        log.info(f' - GUI available at {url}')\n\n        pid = os.getpid()\n        x = threading.Thread(target=_open_webbrowser, args=(url, pid, port, init_static_thread, config.paths['static']), daemon=True)\n        x.start()\n\n    return app, api\n\n\ndef initialize_interfaces(app):\n    app.default_store = DataStore()\n    app.naitve_interface = NativeInterface()\n    app.custom_models = CustomModels()\n    app.dbw = DatabaseWrapper()\n    config = Config()\n    app.config_obj = config\n\n\ndef _open_webbrowser(url: str, pid: int, port: int, init_static_thread, static_folder):\n    \"\"\"Open webbrowser with url when http service is started.\n\n    If some error then do nothing.\n    \"\"\"\n    init_static_thread.join()\n    inject_telemetry_to_static(static_folder)\n    logger = get_log('http')\n    try:\n        is_http_active = wait_func_is_true(func=is_pid_listen_port, timeout=10,\n                                           pid=pid, port=port)\n        if is_http_active:\n            webbrowser.open(url)\n    except Exception as e:\n        logger.error(f'Failed to open {url} in webbrowser with exception {e}')\n        logger.error(traceback.format_exc())\n    session.close()\n", "path": "mindsdb/api/http/initialize.py"}], "after_files": [{"content": "from distutils.version import LooseVersion\nimport requests\nimport os\nimport shutil\nimport threading\nimport webbrowser\nfrom zipfile import ZipFile\nfrom pathlib import Path\nimport traceback\nfrom datetime import datetime, date, timedelta\n#import concurrent.futures\n\nfrom flask import Flask, url_for, make_response\nfrom flask.json import dumps\nfrom flask_restx import Api\nfrom flask.json import JSONEncoder\n\nfrom mindsdb.__about__ import __version__ as mindsdb_version\nfrom mindsdb.interfaces.datastore.datastore import DataStore\nfrom mindsdb.interfaces.model.model_interface import ModelInterface as NativeInterface\nfrom mindsdb.interfaces.custom.custom_models import CustomModels\nfrom mindsdb.utilities.ps import is_pid_listen_port, wait_func_is_true\nfrom mindsdb.interfaces.database.database import DatabaseWrapper\nfrom mindsdb.utilities.telemetry import inject_telemetry_to_static\nfrom mindsdb.utilities.config import Config\nfrom mindsdb.utilities.log import get_log\nfrom mindsdb.interfaces.storage.db import session\n\n\nclass Swagger_Api(Api):\n    \"\"\"\n    This is a modification of the base Flask Restplus Api class due to the issue described here\n    https://github.com/noirbizarre/flask-restplus/issues/223\n    \"\"\"\n    @property\n    def specs_url(self):\n        return url_for(self.endpoint(\"specs\"), _external=False)\n\n\nclass CustomJSONEncoder(JSONEncoder):\n    def default(self, obj):\n        if isinstance(obj, date):\n            return obj.strftime(\"%Y-%m-%d\")\n        if isinstance(obj, datetime):\n            return obj.strftime(\"%Y-%m-%dT%H:%M:%S.%f\")\n        if isinstance(obj, timedelta):\n            return str(obj)\n\n        return JSONEncoder.default(self, obj)\n\n\ndef custom_output_json(data, code, headers=None):\n    resp = make_response(dumps(data), code)\n    resp.headers.extend(headers or {})\n    return resp\n\n\ndef initialize_static(config):\n    ''' Update Scout files basing on compatible-config.json content.\n        Files will be downloaded and updated if new version of GUI > current.\n        Current GUI version stored in static/version.txt.\n    '''\n    log = get_log('http')\n    static_path = Path(config.paths['static'])\n    static_path.mkdir(parents=True, exist_ok=True)\n\n    try:\n        res = requests.get('https://mindsdb-web-builds.s3.amazonaws.com/compatible-config.json')\n    except (ConnectionError, requests.exceptions.ConnectionError) as e:\n        print(f'Is no connection. {e}')\n        return False\n    except Exception as e:\n        print(f'Is something wrong with getting compatible-config.json: {e}')\n        return False\n\n    if res.status_code != 200:\n        print(f'Cant get compatible-config.json: returned status code = {res.status_code}')\n        return False\n\n    try:\n        versions = res.json()\n    except Exception as e:\n        print(f'Cant decode compatible-config.json: {e}')\n        return False\n\n    current_mindsdb_lv = LooseVersion(mindsdb_version)\n\n    try:\n        gui_versions = {}\n        max_mindsdb_lv = None\n        max_gui_lv = None\n        for el in versions['mindsdb']:\n            if el['mindsdb_version'] is None:\n                gui_lv = LooseVersion(el['gui_version'])\n            else:\n                mindsdb_lv = LooseVersion(el['mindsdb_version'])\n                gui_lv = LooseVersion(el['gui_version'])\n                if mindsdb_lv.vstring not in gui_versions or gui_lv > gui_versions[mindsdb_lv.vstring]:\n                    gui_versions[mindsdb_lv.vstring] = gui_lv\n                if max_mindsdb_lv is None or max_mindsdb_lv < mindsdb_lv:\n                    max_mindsdb_lv = mindsdb_lv\n            if max_gui_lv is None or max_gui_lv < gui_lv:\n                max_gui_lv = gui_lv\n\n        all_mindsdb_lv = [LooseVersion(x) for x in gui_versions.keys()]\n        all_mindsdb_lv.sort()\n\n        if current_mindsdb_lv.vstring in gui_versions:\n            gui_version_lv = gui_versions[current_mindsdb_lv.vstring]\n        elif current_mindsdb_lv > all_mindsdb_lv[-1]:\n            gui_version_lv = max_gui_lv\n        else:\n            lower_versions = {key: value for key, value in gui_versions.items() if LooseVersion(key) < current_mindsdb_lv}\n            if len(lower_versions) == 0:\n                gui_version_lv = gui_versions[all_mindsdb_lv[0].vstring]\n            else:\n                all_lower_versions = [LooseVersion(x) for x in lower_versions.keys()]\n                gui_version_lv = gui_versions[all_lower_versions[-1].vstring]\n    except Exception as e:\n        log.error(f'Error in compatible-config.json structure: {e}')\n        return False\n\n    current_gui_version = None\n\n    version_txt_path = static_path.joinpath('version.txt')\n    if version_txt_path.is_file():\n        with open(version_txt_path, 'rt') as f:\n            current_gui_version = f.readline()\n    if current_gui_version is not None:\n        current_gui_lv = LooseVersion(current_gui_version)\n        if current_gui_lv >= gui_version_lv:\n            return True\n\n    log.info(f'New version of GUI available ({gui_version_lv.vstring}). Downloading...')\n\n    shutil.rmtree(static_path)\n    static_path.mkdir(parents=True, exist_ok=True)\n\n    try:\n        css_zip_path = str(static_path.joinpath('css.zip'))\n        js_zip_path = str(static_path.joinpath('js.zip'))\n        media_zip_path = str(static_path.joinpath('media.zip'))\n        bucket = \"https://mindsdb-web-builds.s3.amazonaws.com/\"\n\n        gui_version = gui_version_lv.vstring\n\n        resources = [\n            {\n                'url': bucket + 'css-V' + gui_version + '.zip',\n                'path': css_zip_path\n            }, {\n                'url': bucket + 'js-V' + gui_version + '.zip',\n                'path': js_zip_path\n            }, {\n                'url': bucket + 'indexV' + gui_version + '.html',\n                'path': str(static_path.joinpath('index.html'))\n            }, {\n                'url': bucket + 'favicon.ico',\n                'path': str(static_path.joinpath('favicon.ico'))\n            }, {\n                'url': bucket + 'media.zip',\n                'path': media_zip_path\n            }\n        ]\n\n        def get_resources(resource):\n            try:\n                response = requests.get(resource['url'])\n                if response.status_code != requests.status_codes.codes.ok:\n                    return Exception(f\"Error {response.status_code} GET {resource['url']}\")\n                open(resource['path'], 'wb').write(response.content)\n            except Exception as e:\n                return e\n            return None\n\n        for r in resources:\n            get_resources(r)\n\n        '''\n        # to make downloading faster download each resource in a separate thread\n        with concurrent.futures.ThreadPoolExecutor(max_workers=5) as executor:\n            future_to_url = {executor.submit(get_resources, r): r for r in resources}\n            for future in concurrent.futures.as_completed(future_to_url):\n                res = future.result()\n                if res is not None:\n                    raise res\n        '''\n\n    except Exception as e:\n        log.error(f'Error during downloading files from s3: {e}')\n        session.close()\n        return False\n\n    static_folder = static_path.joinpath('static')\n    static_folder.mkdir(parents=True, exist_ok=True)\n\n    # unzip process\n    for zip_path, dir_name in [[js_zip_path, 'js'], [css_zip_path, 'css']]:\n        temp_dir = static_path.joinpath(f'temp_{dir_name}')\n        temp_dir.mkdir(mode=0o777, exist_ok=True, parents=True)\n        ZipFile(zip_path).extractall(temp_dir)\n        files_path = static_path.joinpath('static', dir_name)\n        if temp_dir.joinpath('build', 'static', dir_name).is_dir():\n            shutil.move(temp_dir.joinpath('build', 'static', dir_name), files_path)\n            shutil.rmtree(temp_dir)\n        else:\n            shutil.move(temp_dir, files_path)\n\n    ZipFile(media_zip_path).extractall(static_folder)\n\n    os.remove(js_zip_path)\n    os.remove(css_zip_path)\n    os.remove(media_zip_path)\n\n    with open(version_txt_path, 'wt') as f:\n        f.write(gui_version_lv.vstring)\n\n    log.info(f'GUI version updated to {gui_version_lv.vstring}')\n    session.close()\n    return True\n\n\ndef initialize_flask(config, init_static_thread, no_studio):\n    # Apparently there's a bug that causes the static path not to work if it's '/' -- https://github.com/pallets/flask/issues/3134, I think '' should achieve the same thing (???)\n    if no_studio:\n        app = Flask(\n            __name__\n        )\n    else:\n        app = Flask(\n            __name__,\n            static_url_path='/static',\n            static_folder=os.path.join(config.paths['static'], 'static/')\n        )\n\n    app.config['SEND_FILE_MAX_AGE_DEFAULT'] = 60\n    app.config['SWAGGER_HOST'] = 'http://localhost:8000/mindsdb'\n    app.json_encoder = CustomJSONEncoder\n\n    authorizations = {\n        'apikey': {\n            'type': 'session',\n            'in': 'query',\n            'name': 'session'\n        }\n    }\n\n    api = Swagger_Api(\n        app,\n        authorizations=authorizations,\n        security=['apikey'],\n        url_prefix=':8000',\n        prefix='/api',\n        doc='/doc/'\n    )\n\n    api.representations['application/json'] = custom_output_json\n\n    port = config['api']['http']['port']\n    host = config['api']['http']['host']\n\n    # NOTE rewrite it, that hotfix to see GUI link\n    if not no_studio:\n        log = get_log('http')\n        if host in ('', '0.0.0.0'):\n            url = f'http://127.0.0.1:{port}/'\n        else:\n            url = f'http://{host}:{port}/'\n        log.info(f' - GUI available at {url}')\n\n        pid = os.getpid()\n        x = threading.Thread(target=_open_webbrowser, args=(url, pid, port, init_static_thread, config.paths['static']), daemon=True)\n        x.start()\n\n    return app, api\n\n\ndef initialize_interfaces(app):\n    app.default_store = DataStore()\n    app.naitve_interface = NativeInterface()\n    app.custom_models = CustomModels()\n    app.dbw = DatabaseWrapper()\n    config = Config()\n    app.config_obj = config\n\n\ndef _open_webbrowser(url: str, pid: int, port: int, init_static_thread, static_folder):\n    \"\"\"Open webbrowser with url when http service is started.\n\n    If some error then do nothing.\n    \"\"\"\n    init_static_thread.join()\n    inject_telemetry_to_static(static_folder)\n    logger = get_log('http')\n    try:\n        is_http_active = wait_func_is_true(func=is_pid_listen_port, timeout=10,\n                                           pid=pid, port=port)\n        if is_http_active:\n            webbrowser.open(url)\n    except Exception as e:\n        logger.error(f'Failed to open {url} in webbrowser with exception {e}')\n        logger.error(traceback.format_exc())\n    session.close()\n", "path": "mindsdb/api/http/initialize.py"}]}