diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index cbad2b94b4c70d6e6ebfad55cf22d251e3c3ed78..94468b42ac18c66c75c8a64b3bd1dd7ab98fd91b 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -119,7 +119,7 @@ test-analyse-service:
   script:
     - "pip install pipenv"
     - "pipenv install gunicorn && pipenv install --dev --system --deploy"
-    - "cd ./dbrepo-analyse-service/ && coverage run -m pytest test/test_determine_dt.py test/test_determine_pk.py --junitxml=report.xml && coverage html && coverage report > ./coverage.txt"
+    - "cd ./dbrepo-analyse-service/ && coverage run -m pytest test/test_determine_dt.py test/test_determine_pk.py test/test_determine_stats.py --junitxml=report.xml && coverage html && coverage report > ./coverage.txt"
     - "cat ./coverage.txt | grep -o 'TOTAL[^%]*%'"
   artifacts:
     when: always
diff --git a/dbrepo-analyse-service/Dockerfile b/dbrepo-analyse-service/Dockerfile
index 50d56f60e22eb354ac003b821148c2ab0313ef3c..714cfc9e8580741f3dc9e24f3cae048001db95ba 100644
--- a/dbrepo-analyse-service/Dockerfile
+++ b/dbrepo-analyse-service/Dockerfile
@@ -1,7 +1,7 @@
 FROM python:3.9-slim
 MAINTAINER Martin Weise <martin.weise@tuwien.ac.at>
 
-RUN apt update && apt install -y curl
+RUN apt update && apt install -y curl gcc libmariadb-dev
 
 WORKDIR /app
 
diff --git a/dbrepo-analyse-service/Pipfile b/dbrepo-analyse-service/Pipfile
index 29d57acc66b8dab57afb21b1a9922439e753af5a..99ab9195d0612621ea6fa0bd535b880e51807896 100644
--- a/dbrepo-analyse-service/Pipfile
+++ b/dbrepo-analyse-service/Pipfile
@@ -16,11 +16,16 @@ numpy = "*"
 pandas = "*"
 messytables = "*"
 minio = "*"
+flask-sqlalchemy = "*"
+opensearch-py = "*"
+pymysql = "*"
 
 [dev-packages]
 coverage = "*"
 pytest = "*"
 testcontainers-minio = "*"
+testcontainers-mysql = "*"
+testcontainers-opensearch = "*"
 
 [requires]
 python_version = "3.9"
diff --git a/dbrepo-analyse-service/Pipfile.lock b/dbrepo-analyse-service/Pipfile.lock
index 1000eddec96027aff2f5400389a7d6fa52842157..776c27c79292e25b1ef041eaf9e7cc4c136f17ed 100644
--- a/dbrepo-analyse-service/Pipfile.lock
+++ b/dbrepo-analyse-service/Pipfile.lock
@@ -1,7 +1,7 @@
 {
     "_meta": {
         "hash": {
-            "sha256": "fe382056038fea58538a52ab9f8ac7bd80659537f8336a534cb79a71e515bceb"
+            "sha256": "593bb7bb2fab9041c4da489021bb7fe2f857624e18f484d53f1c4f3958b04837"
         },
         "pipfile-spec": 6,
         "requires": {
@@ -69,19 +69,19 @@
         },
         "boto3": {
             "hashes": [
-                "sha256:1f94042f4efb5133b6b9b8b3243afc01143a81d21b3197a3afadf5780f97b05d",
-                "sha256:5c1bb487c68120aae236354d81b8a1a55d0aa3395d30748a01825ef90891921e"
+                "sha256:1efc02be786884034d503d59c018cf7650d0cff9fcb37cd2eb49b802a6fe6111",
+                "sha256:8ca248cc84e7e859e4e276eb9c4309fa01a3e58473bf48d6c33448be870c2bb8"
             ],
             "index": "pypi",
-            "version": "==1.34.14"
+            "version": "==1.34.17"
         },
         "botocore": {
             "hashes": [
-                "sha256:041bed0852649cab7e4dcd4d87f9d1cc084467fb846e5b60015e014761d96414",
-                "sha256:3b592f50f0406e236782a3a0a9ad1c3976060fdb2e04a23d18c3df5b7dfad3e0"
+                "sha256:7272c39032c6f1d62781e4c8445d9a1d9140c2bf52ba7ee66bf6db559c4b2427",
+                "sha256:e48a662f3a6919219276b55085e8f73c3347966675f55e9d448be30cf79678ee"
             ],
             "markers": "python_version >= '3.8'",
-            "version": "==1.34.14"
+            "version": "==1.34.17"
         },
         "certifi": {
             "hashes": [
@@ -250,7 +250,7 @@
                 "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519",
                 "sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561"
             ],
-            "markers": "python_full_version >= '3.7.0'",
+            "markers": "python_version >= '3.7'",
             "version": "==3.3.2"
         },
         "click": {
@@ -284,6 +284,14 @@
             "index": "pypi",
             "version": "==3.0.0"
         },
+        "flask-sqlalchemy": {
+            "hashes": [
+                "sha256:4ba4be7f419dc72f4efd8802d69974803c37259dd42f3913b0dcf75c9447e0a0",
+                "sha256:e4b68bb881802dda1a7d878b2fc84c06d1ee57fb40b874d3dc97dabfa36b8312"
+            ],
+            "index": "pypi",
+            "version": "==3.1.1"
+        },
         "gevent": {
             "hashes": [
                 "sha256:272cffdf535978d59c38ed837916dfd2b5d193be1e9e5dcc60a5f4d5025dd98a",
@@ -391,7 +399,7 @@
                 "sha256:fd096eb7ffef17c456cfa587523c5f92321ae02427ff955bebe9e3c63bc9f0da",
                 "sha256:fe754d231288e1e64323cfad462fcee8f0288654c10bdf4f603a39ed923bef33"
             ],
-            "markers": "python_version >= '3.11' and platform_python_implementation == 'CPython'",
+            "index": "pypi",
             "version": "==3.0.3"
         },
         "gunicorn": {
@@ -418,6 +426,14 @@
             "markers": "python_version >= '3.5'",
             "version": "==3.6"
         },
+        "importlib-metadata": {
+            "hashes": [
+                "sha256:4805911c3a4ec7c3966410053e9ec6a1fecd629117df5adee56dfc9432a1081e",
+                "sha256:f238736bb06590ae52ac1fab06a3a9ef1d8dce2b7a35b5ab329371d6c8f5d2cc"
+            ],
+            "markers": "python_version < '3.10'",
+            "version": "==7.0.1"
+        },
         "itsdangerous": {
             "hashes": [
                 "sha256:2c2349112351b88699d8d4b6b075022c0808887cb7ad10069318a8b0bc88db44",
@@ -428,11 +444,11 @@
         },
         "jinja2": {
             "hashes": [
-                "sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852",
-                "sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61"
+                "sha256:7d6d50dd97d52cbc355597bd845fabfbac3f551e1f99619e39a35ce8c370b5fa",
+                "sha256:ac8bd6544d4bb2c9792bf3a159e80bba8fda7f07e81bc3aed565432d5925ba90"
             ],
             "markers": "python_version >= '3.7'",
-            "version": "==3.1.2"
+            "version": "==3.1.3"
         },
         "jmespath": {
             "hashes": [
@@ -466,97 +482,87 @@
         },
         "lxml": {
             "hashes": [
-                "sha256:00bfccab28f710bb13f7f644c980ad50ce3e5b6a412b5bb9a6c30136b298fb2c",
-                "sha256:03c977ffc9a4bf17b3e0f8db0451dc38e9f4ec92cfdb5df462d38fbe6e6e0825",
-                "sha256:0499310df9afc0ce634ce14cacbb333d62f561038ea4db640494e4a22ac4f2e9",
-                "sha256:070469a23f2ef3a9e72165af7e0b12eca9a6e47c3a8ec1cad60d14fb2f2c3aa8",
-                "sha256:0963de4fe463caff48e6ce4d83d19a3c099126874185d10cf490c29057ca518d",
-                "sha256:0a79eca2ef5e032c8ed9da07f84a07a29105f220b777613dfe7fc31445691ee3",
-                "sha256:0dc36ec06514fe8848c4733d47f96a0636f82d9ca3eaa2132373426bc03f178f",
-                "sha256:0ecc0f1e1d901b66f2f68edff85b8ff421fa9683d02eaea6742a42c145d741b6",
-                "sha256:0f70e5de6b3e24ababeca597f776e5f37973f05d28a4d9f467aa5b45745af762",
-                "sha256:0fb55d77e685def5218701d5d296fca62f595752c88402404da685864b06b67e",
-                "sha256:1215c8e57a25ad68488abb83a36734f6c6b3f0ccd880f0c68da98682d463ef09",
-                "sha256:1546fa25a6dde5698271e19787062446f433c98ca7eab35545f665dde2c1bd34",
-                "sha256:18b456f1bfb338be94a916166ac5507e73b9eb9f6e1f0fbd1c8caff2f3fa5535",
-                "sha256:18caa0d3925902949cb060fe5f2da70c953d60bd9ef78657bd389f6db30533cc",
-                "sha256:19251f782ea51a4841e747158195312ef63e06b47889e159dc5f1b2e5d668465",
-                "sha256:1d78e91cbffe733ff325e0d258bb64702c8d91f8f652db088bd2989c6a8f90cc",
-                "sha256:2ec9fa65e0638551a5ad31cb9fa160b321f19632e5ec517fe68d7b4110133e69",
-                "sha256:3013823b0069cb4bd9b558e87076a18142703c6d2ac3a5d5521dd35734d23a72",
-                "sha256:31362b900b8fd2a026b9d24695ebe5449ea8f0c948af2445d7880b738d9fc368",
-                "sha256:32a135d4ef8f966bc087d450d641df73fc6874f04cf6608111541b50090e6f13",
-                "sha256:331237209fe76951450c1119af0879f04f32d1b07b21e83a34ba439520492feb",
-                "sha256:3714f339449d2738b4fadd078a6022704a2af3cab06bec9627b19eaa4205090d",
-                "sha256:387c5416b8bb4b8ad7306797cb2719a841f5f3836b3c39fcaa56b9af5448dd2a",
-                "sha256:39a3cf581e9040e66aaa719b2f338b2b7eb43ce1db059089c82ae72e0fd48b47",
-                "sha256:3ffc56d68b9782919d69ae9a6fac99efd7547f2666ccb7ecfd12871564d16133",
-                "sha256:42069ce16dab9755b381b90defd846ca407b9ead05dc20732fd5502b5cc49b87",
-                "sha256:430780608d16b3bb96ef99a67a1a0626c8a295193af53ce9c4d5ec3fef2fbc79",
-                "sha256:4432a1d89a9b340bc6bd1201aef3ba03112f151d3f340d9218247dc0c85028ab",
-                "sha256:45e747b4e82390ffd802528b9e7b39333c1ce71423057bf5961b30ec0d52f76f",
-                "sha256:473f2d0511dd84697326ee9362b0c0c2e9f99a433dcb1fbb5aa8df3d1b2185db",
-                "sha256:47f46a2ebade07f3afa47695882e7725440c49bf77cba39c3762a42597e5aad3",
-                "sha256:49dc4dcf14a08f160bb3f5f571f63514d918b8933a25c221536571a5fc010271",
-                "sha256:4ae66b6b0f82e7839b6b8d009182c652d48e7d2ea21a6709f3033ce5fbf199c4",
-                "sha256:4b49a1569ed6d05808f4d163a316e7bf4a230e0c36855b59f56020ae27ae586a",
-                "sha256:4d58af4ebd711dad40f1c024a779950d9918d04d74f49230edf5d271dcd33c28",
-                "sha256:4df6be79be4d7574e9e4002aeb6aa03d3f3809582db07abb166df7fc6e7438af",
-                "sha256:57f5c362cbd9d688fb1fa07c8955cec26d5c066fbcb4163aa341ff751eba7587",
-                "sha256:5892cff0a6817743fe470b7402677310ffc8514a74de14d4e591cecdc457ff61",
-                "sha256:60974986aa80a8bb883da5663f90bc632bd4ce0d0508e66a9132412facec65f6",
-                "sha256:62a3c0fdf70f785cd29824666d1dcea88c207f0b73ddbc28fb7a6a1a5bbb1af7",
-                "sha256:67ddff0272905a0b78a2c3ea01487e0551cc38094cd5994f73af370f355ecb47",
-                "sha256:6af86081c888ce81ca7e361ed7fa2ba1678e2a86eb5a059c96d5a719364d319e",
-                "sha256:6cdd0fb749c80ffcf408f659b209e82333f10b517786083a3dd3c3b5adc60111",
-                "sha256:737a4dba9f7ee842c6597f719dda2eabeeaefe42443f7f24de20c262f88527cd",
-                "sha256:73e71b95c5215310a92e560369ac1f0e2cd018d5a36be182da88958f3d6084f5",
-                "sha256:745383c124f096cc03fb942c8a05ea1e8cb4f44c5b28887adce6224e4540808e",
-                "sha256:7febf50135363e981097eeada84781eeae92bfc3c203495f63d6b542a7132ba7",
-                "sha256:821fb791565536617b058c2cd5b8d28a1285b3375eecc5bd6b5c6d87add0d3dd",
-                "sha256:83ff41e1bc0666f31acda52407d869ea257f232c2d9394806647a0e7454de73e",
-                "sha256:8991837fdf8086486f1c300d936bacd757e2e5398be84cd54a1fba0e6b6d5591",
-                "sha256:8a70c47c14f89b8bfb430f85b608aa460204fe7c005545d79afd31b925cc6669",
-                "sha256:8ba56c3686fa60cc04191d22d1733aad484c9cbc153cdc3e8eb9bdfcad30f704",
-                "sha256:8de180f748a17382dd695b3999be03a647d09af16ae589c4e9c37138ddb6d4c6",
-                "sha256:96c2abcbcb24f850f00f279c4660ce6498cae16ff1659845838d752c26890748",
-                "sha256:99b5ca5775435aa296d32ea711e194aaae871b21dbf0d57cb7d4431e5d3ad699",
-                "sha256:9a4eff4d8ad0bbc9f470a9be19c5e718af4baf47111d7c2d9b036b9986107e7c",
-                "sha256:9a99dae826c80cf0a21dd21eb66db16310d1f58ac4c27c804da980212fbcabe2",
-                "sha256:9e8a4782ecaaacf8e9355f39179b1f00e7f27b774306eccbe80f6215724be4cd",
-                "sha256:a28eab2d9ea79b830be50e3350d827ae8abf5b23e278e14929824d5ab2069008",
-                "sha256:ae3a0ec0f1b6cf1e8bca41bc86cd64ba02e31c71716efbf149a0f7ebc168cf0b",
-                "sha256:b005101a257c494e84d36ecb62b02ba195b02b7f8892f57b1f5aaa352ed44467",
-                "sha256:b4eef43c5dc5c579d0804e55a32dd1bacbd008c8191ed4d65be278bbb11ddc61",
-                "sha256:b6bb5a0a87ab1e01f086cbb418be9e409719cd216954aa38b1cceee36a561ce1",
-                "sha256:b7bb0010c2969c23bf3d2b3892e638a7cb83e7daeb749e3db5f3c002fd191e11",
-                "sha256:b889c0b9be774466308c3590e093ce9a6f9115c78fc8624aa6ba8dfeaf5188ab",
-                "sha256:bcded868b05583d41ab0b024f39f90a04e486a2349a9b493d8d17024f1433aa6",
-                "sha256:c21e60c017cab3a7e7786185cc8853b8614b01ccd69cc8b24608e5356784631b",
-                "sha256:c32a4fbae218b336d547bc626897325202e4e9f1794ac5f4d0bb3caacf41df21",
-                "sha256:c4eaa83b610595ef9f20aa69b96053d5b7f3f70c67c7a3af8f433136a9d315ab",
-                "sha256:c5b23f63fcec652bf1e775eca5e03a713a4994d2a7ce2e70a91e964a26220e0d",
-                "sha256:cfdac95815d3025e4c9edce2ef2ebe4e034edc35a2c44a606dd846554387ae38",
-                "sha256:d0047c90e0ebd0d8f3c1e6636e10f597b8f25e4ef9e6416dd2e5c4c0960270cc",
-                "sha256:d05cf827f160340f67c25ce6f271689a844605aa123849f1a80e21c9bd21f00b",
-                "sha256:d64e543b07964ff73b4eb994bee894803a80e19fd3b29a5ffbe3c637fe43e788",
-                "sha256:d6d3ce5638cd4ed3fa684507f164e7039e1b07475bc8f37ba6e12271c1a2e9e0",
-                "sha256:d80d9f4d986bb6ad65bae86f07391152f7b6c65cfc63d118616b18b0be2e79da",
-                "sha256:dbff288e1869db78f8731ca257553dd699edef07e173b35e71b1122b630d6008",
-                "sha256:dcb25128c9e7f01c00ad116d2c762c3942724981a35c6e5c551ab55d4c2bfcfe",
-                "sha256:dcc7dc4b9b65f185aa8533abc78f0a3b2ac38fe075bb23d3c1590ba0990f6c80",
-                "sha256:e2388a792f9c239510d62a9e615662b8202e4ca275aafcc9c4af154654462a14",
-                "sha256:e2bd8faf6a9682ca385e4bca1a38a057be716dc303f16ddec9e4c9cf01b7d722",
-                "sha256:e97c74725e86d84a477df081eef69b70f048128afee841dbd8c690a9e3d2e8e0",
-                "sha256:ea5e4b3eff9029a02fe7736540675ab8fca44277232f0027397b0d7111d04b1c",
-                "sha256:f36c3103a6f2641777b64f1da860f37cbaa718ce3feea621583627269e68eb03",
-                "sha256:f56e6a38c64a79e228d48344bb3bec265ac035fc1277ce8c049445bb18e4cd41",
-                "sha256:f9464ff2fd1f2ba4d0d246a560d369ee7e7213c556a30db9ae9426850ce1baf9",
-                "sha256:feb1102d9e5de08120d46a1011110c43b2547ecb3ae80030801e0e2dacd1ee18",
-                "sha256:ff29353c12f0abc9cb3395899b7192a970d5a63f80ac1e7f0c3247ed83f5dcd4"
+                "sha256:13521a321a25c641b9ea127ef478b580b5ec82aa2e9fc076c86169d161798b01",
+                "sha256:14deca1460b4b0f6b01f1ddc9557704e8b365f55c63070463f6c18619ebf964f",
+                "sha256:16018f7099245157564d7148165132c70adb272fb5a17c048ba70d9cc542a1a1",
+                "sha256:16dd953fb719f0ffc5bc067428fc9e88f599e15723a85618c45847c96f11f431",
+                "sha256:19a1bc898ae9f06bccb7c3e1dfd73897ecbbd2c96afe9095a6026016e5ca97b8",
+                "sha256:1ad17c20e3666c035db502c78b86e58ff6b5991906e55bdbef94977700c72623",
+                "sha256:22b7ee4c35f374e2c20337a95502057964d7e35b996b1c667b5c65c567d2252a",
+                "sha256:24ef5a4631c0b6cceaf2dbca21687e29725b7c4e171f33a8f8ce23c12558ded1",
+                "sha256:25663d6e99659544ee8fe1b89b1a8c0aaa5e34b103fab124b17fa958c4a324a6",
+                "sha256:262bc5f512a66b527d026518507e78c2f9c2bd9eb5c8aeeb9f0eb43fcb69dc67",
+                "sha256:280f3edf15c2a967d923bcfb1f8f15337ad36f93525828b40a0f9d6c2ad24890",
+                "sha256:2ad3a8ce9e8a767131061a22cd28fdffa3cd2dc193f399ff7b81777f3520e372",
+                "sha256:2befa20a13f1a75c751f47e00929fb3433d67eb9923c2c0b364de449121f447c",
+                "sha256:2f37c6d7106a9d6f0708d4e164b707037b7380fcd0b04c5bd9cae1fb46a856fb",
+                "sha256:304128394c9c22b6569eba2a6d98392b56fbdfbad58f83ea702530be80d0f9df",
+                "sha256:342e95bddec3a698ac24378d61996b3ee5ba9acfeb253986002ac53c9a5f6f84",
+                "sha256:3aeca824b38ca78d9ee2ab82bd9883083d0492d9d17df065ba3b94e88e4d7ee6",
+                "sha256:3d184e0d5c918cff04cdde9dbdf9600e960161d773666958c9d7b565ccc60c45",
+                "sha256:3e3898ae2b58eeafedfe99e542a17859017d72d7f6a63de0f04f99c2cb125936",
+                "sha256:3eea6ed6e6c918e468e693c41ef07f3c3acc310b70ddd9cc72d9ef84bc9564ca",
+                "sha256:3f14a4fb1c1c402a22e6a341a24c1341b4a3def81b41cd354386dcb795f83897",
+                "sha256:436a943c2900bb98123b06437cdd30580a61340fbdb7b28aaf345a459c19046a",
+                "sha256:4946e7f59b7b6a9e27bef34422f645e9a368cb2be11bf1ef3cafc39a1f6ba68d",
+                "sha256:49a9b4af45e8b925e1cd6f3b15bbba2c81e7dba6dce170c677c9cda547411e14",
+                "sha256:4f8b0c78e7aac24979ef09b7f50da871c2de2def043d468c4b41f512d831e912",
+                "sha256:52427a7eadc98f9e62cb1368a5079ae826f94f05755d2d567d93ee1bc3ceb354",
+                "sha256:5e53d7e6a98b64fe54775d23a7c669763451340c3d44ad5e3a3b48a1efbdc96f",
+                "sha256:5fcfbebdb0c5d8d18b84118842f31965d59ee3e66996ac842e21f957eb76138c",
+                "sha256:601f4a75797d7a770daed8b42b97cd1bb1ba18bd51a9382077a6a247a12aa38d",
+                "sha256:61c5a7edbd7c695e54fca029ceb351fc45cd8860119a0f83e48be44e1c464862",
+                "sha256:6a2a2c724d97c1eb8cf966b16ca2915566a4904b9aad2ed9a09c748ffe14f969",
+                "sha256:6d48fc57e7c1e3df57be5ae8614bab6d4e7b60f65c5457915c26892c41afc59e",
+                "sha256:6f11b77ec0979f7e4dc5ae081325a2946f1fe424148d3945f943ceaede98adb8",
+                "sha256:704f5572ff473a5f897745abebc6df40f22d4133c1e0a1f124e4f2bd3330ff7e",
+                "sha256:725e171e0b99a66ec8605ac77fa12239dbe061482ac854d25720e2294652eeaa",
+                "sha256:7cfced4a069003d8913408e10ca8ed092c49a7f6cefee9bb74b6b3e860683b45",
+                "sha256:7ec465e6549ed97e9f1e5ed51c657c9ede767bc1c11552f7f4d022c4df4a977a",
+                "sha256:82bddf0e72cb2af3cbba7cec1d2fd11fda0de6be8f4492223d4a268713ef2147",
+                "sha256:82cd34f1081ae4ea2ede3d52f71b7be313756e99b4b5f829f89b12da552d3aa3",
+                "sha256:843b9c835580d52828d8f69ea4302537337a21e6b4f1ec711a52241ba4a824f3",
+                "sha256:877efb968c3d7eb2dad540b6cabf2f1d3c0fbf4b2d309a3c141f79c7e0061324",
+                "sha256:8b9f19df998761babaa7f09e6bc169294eefafd6149aaa272081cbddc7ba4ca3",
+                "sha256:8cf5877f7ed384dabfdcc37922c3191bf27e55b498fecece9fd5c2c7aaa34c33",
+                "sha256:8d2900b7f5318bc7ad8631d3d40190b95ef2aa8cc59473b73b294e4a55e9f30f",
+                "sha256:8d7b4beebb178e9183138f552238f7e6613162a42164233e2bda00cb3afac58f",
+                "sha256:8f52fe6859b9db71ee609b0c0a70fea5f1e71c3462ecf144ca800d3f434f0764",
+                "sha256:98f3f020a2b736566c707c8e034945c02aa94e124c24f77ca097c446f81b01f1",
+                "sha256:9aa543980ab1fbf1720969af1d99095a548ea42e00361e727c58a40832439114",
+                "sha256:9b99f564659cfa704a2dd82d0684207b1aadf7d02d33e54845f9fc78e06b7581",
+                "sha256:9bcf86dfc8ff3e992fed847c077bd875d9e0ba2fa25d859c3a0f0f76f07f0c8d",
+                "sha256:9bd0ae7cc2b85320abd5e0abad5ccee5564ed5f0cc90245d2f9a8ef330a8deae",
+                "sha256:9d3c0f8567ffe7502d969c2c1b809892dc793b5d0665f602aad19895f8d508da",
+                "sha256:9e5ac3437746189a9b4121db2a7b86056ac8786b12e88838696899328fc44bb2",
+                "sha256:a36c506e5f8aeb40680491d39ed94670487ce6614b9d27cabe45d94cd5d63e1e",
+                "sha256:a5ab722ae5a873d8dcee1f5f45ddd93c34210aed44ff2dc643b5025981908cda",
+                "sha256:a96f02ba1bcd330807fc060ed91d1f7a20853da6dd449e5da4b09bfcc08fdcf5",
+                "sha256:acb6b2f96f60f70e7f34efe0c3ea34ca63f19ca63ce90019c6cbca6b676e81fa",
+                "sha256:ae15347a88cf8af0949a9872b57a320d2605ae069bcdf047677318bc0bba45b1",
+                "sha256:af8920ce4a55ff41167ddbc20077f5698c2e710ad3353d32a07d3264f3a2021e",
+                "sha256:afd825e30f8d1f521713a5669b63657bcfe5980a916c95855060048b88e1adb7",
+                "sha256:b21b4031b53d25b0858d4e124f2f9131ffc1530431c6d1321805c90da78388d1",
+                "sha256:b4b68c961b5cc402cbd99cca5eb2547e46ce77260eb705f4d117fd9c3f932b95",
+                "sha256:b66aa6357b265670bb574f050ffceefb98549c721cf28351b748be1ef9577d93",
+                "sha256:b9e240ae0ba96477682aa87899d94ddec1cc7926f9df29b1dd57b39e797d5ab5",
+                "sha256:bc64d1b1dab08f679fb89c368f4c05693f58a9faf744c4d390d7ed1d8223869b",
+                "sha256:bf8443781533b8d37b295016a4b53c1494fa9a03573c09ca5104550c138d5c05",
+                "sha256:c26aab6ea9c54d3bed716b8851c8bfc40cb249b8e9880e250d1eddde9f709bf5",
+                "sha256:c3cd1fc1dc7c376c54440aeaaa0dcc803d2126732ff5c6b68ccd619f2e64be4f",
+                "sha256:c7257171bb8d4432fe9d6fdde4d55fdbe663a63636a17f7f9aaba9bcb3153ad7",
+                "sha256:d42e3a3fc18acc88b838efded0e6ec3edf3e328a58c68fbd36a7263a874906c8",
+                "sha256:d74fcaf87132ffc0447b3c685a9f862ffb5b43e70ea6beec2fb8057d5d2a1fea",
+                "sha256:d8c1d679df4361408b628f42b26a5d62bd3e9ba7f0c0e7969f925021554755aa",
+                "sha256:e856c1c7255c739434489ec9c8aa9cdf5179785d10ff20add308b5d673bed5cd",
+                "sha256:eac68f96539b32fce2c9b47eb7c25bb2582bdaf1bbb360d25f564ee9e04c542b",
+                "sha256:ed7326563024b6e91fef6b6c7a1a2ff0a71b97793ac33dbbcf38f6005e51ff6e",
+                "sha256:ed8c3d2cd329bf779b7ed38db176738f3f8be637bb395ce9629fc76f78afe3d4",
+                "sha256:f4c9bda132ad108b387c33fabfea47866af87f4ea6ffb79418004f0521e63204",
+                "sha256:f643ffd2669ffd4b5a3e9b41c909b72b2a1d5e4915da90a77e119b8d48ce867a"
             ],
-            "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
-            "version": "==5.0.1"
+            "markers": "python_version >= '3.6'",
+            "version": "==5.1.0"
         },
         "markupsafe": {
             "hashes": [
@@ -689,6 +695,14 @@
             "index": "pypi",
             "version": "==1.26.3"
         },
+        "opensearch-py": {
+            "hashes": [
+                "sha256:564f175af134aa885f4ced6846eb4532e08b414fff0a7976f76b276fe0e69158",
+                "sha256:7867319132133e2974c09f76a54eb1d502b989229be52da583d93ddc743ea111"
+            ],
+            "index": "pypi",
+            "version": "==2.4.2"
+        },
         "packaging": {
             "hashes": [
                 "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5",
@@ -753,41 +767,49 @@
         },
         "pycryptodome": {
             "hashes": [
-                "sha256:08b445799d571041765e7d5c9ca09c5d3866c2f22eeb0dd4394a4169285184f4",
-                "sha256:11ddf6c9b52116b62223b6a9f4741bc4f62bb265392a4463282f7f34bb287180",
-                "sha256:1c6273ca5a03b672e504995529b8bae56da0ebb691d8ef141c4aa68f60765700",
-                "sha256:27e1efcb68993b7ce5d1d047a46a601d41281bba9f1971e6be4aa27c69ab8065",
-                "sha256:2c16426ef49d9cba018be2340ea986837e1dfa25c2ea181787971654dd49aadd",
-                "sha256:37e531bf896b70fe302f003d3be5a0a8697737a8d177967da7e23eff60d6483c",
-                "sha256:420972f9c62978e852c74055d81c354079ce3c3a2213a92c9d7e37bbc63a26e2",
-                "sha256:4464b0e8fd5508bff9baf18e6fd4c6548b1ac2ce9862d6965ff6a84ec9cb302a",
-                "sha256:4805e053571140cb37cf153b5c72cd324bb1e3e837cbe590a19f69b6cf85fd03",
-                "sha256:67939a3adbe637281c611596e44500ff309d547e932c449337649921b17b6297",
-                "sha256:694020d2ff985cd714381b9da949a21028c24b86f562526186f6af7c7547e986",
-                "sha256:6c3df3613592ea6afaec900fd7189d23c8c28b75b550254f4bd33fe94acb84b9",
-                "sha256:6d0d2b97758ebf2f36c39060520447c26455acb3bcff309c28b1c816173a6ff5",
-                "sha256:7c9e222d0976f68d0cf6409cfea896676ddc1d98485d601e9508f90f60e2b0a2",
-                "sha256:81e9d23c0316fc1b45d984a44881b220062336bbdc340aa9218e8d0656587934",
-                "sha256:8ae0dd1bcfada451c35f9e29a3e5db385caabc190f98e4a80ad02a61098fb776",
-                "sha256:8cf5d3d6cf921fa81acd1f632f6cedcc03f5f68fc50c364cd39490ba01d17c49",
-                "sha256:954d156cd50130afd53f8d77f830fe6d5801bd23e97a69d358fed068f433fbfe",
-                "sha256:a470237ee71a1efd63f9becebc0ad84b88ec28e6784a2047684b693f458f41b7",
-                "sha256:a991f8ffe8dfe708f86690948ae46442eebdd0fff07dc1b605987939a34ec979",
-                "sha256:b0bfe61506795877ff974f994397f0c862d037f6f1c0bfc3572195fc00833b96",
-                "sha256:b7efd46b0b4ac869046e814d83244aeab14ef787f4850644119b1c8b0ec2d637",
-                "sha256:b8b80ff92049fd042177282917d994d344365ab7e8ec2bc03e853d93d2401786",
-                "sha256:c1bc0c49d986a1491d66d2a56570f12e960b12508b7e71f2423f532e28857f36",
-                "sha256:c22c80246c3c880c6950d2a8addf156cee74ec0dc5757d01e8e7067a3c7da015",
-                "sha256:cd4e7e8bf0fc1ada854688b9b309ee607e2aa85a8b44180f91021a4dd330a928",
-                "sha256:cd4e95b0eb4b28251c825fe7aa941fe077f993e5ca9b855665935b86fbb1cc08",
-                "sha256:e038ab77fec0956d7aa989a3c647652937fc142ef41c9382c2ebd13c127d5b4a",
-                "sha256:e3e6f89480616781d2a7f981472d0cdb09b9da9e8196f43c1234eff45c915766",
-                "sha256:e70f5c839c7798743a948efa2a65d1fe96bb397fe6d7f2bde93d869fe4f0ad69",
-                "sha256:ed932eb6c2b1c4391e166e1a562c9d2f020bfff44a0e1b108f67af38b390ea89",
-                "sha256:f34976c5c8eb79e14c7d970fb097482835be8d410a4220f86260695ede4c3e17"
+                "sha256:06d6de87c19f967f03b4cf9b34e538ef46e99a337e9a61a77dbe44b2cbcf0690",
+                "sha256:09609209ed7de61c2b560cc5c8c4fbf892f8b15b1faf7e4cbffac97db1fffda7",
+                "sha256:210ba1b647837bfc42dd5a813cdecb5b86193ae11a3f5d972b9a0ae2c7e9e4b4",
+                "sha256:2a1250b7ea809f752b68e3e6f3fd946b5939a52eaeea18c73bdab53e9ba3c2dd",
+                "sha256:2ab6ab0cb755154ad14e507d1df72de9897e99fd2d4922851a276ccc14f4f1a5",
+                "sha256:3427d9e5310af6680678f4cce149f54e0bb4af60101c7f2c16fdf878b39ccccc",
+                "sha256:3cd3ef3aee1079ae44afaeee13393cf68b1058f70576b11439483e34f93cf818",
+                "sha256:405002eafad114a2f9a930f5db65feef7b53c4784495dd8758069b89baf68eab",
+                "sha256:417a276aaa9cb3be91f9014e9d18d10e840a7a9b9a9be64a42f553c5b50b4d1d",
+                "sha256:4401564ebf37dfde45d096974c7a159b52eeabd9969135f0426907db367a652a",
+                "sha256:49a4c4dc60b78ec41d2afa392491d788c2e06edf48580fbfb0dd0f828af49d25",
+                "sha256:5601c934c498cd267640b57569e73793cb9a83506f7c73a8ec57a516f5b0b091",
+                "sha256:6e0e4a987d38cfc2e71b4a1b591bae4891eeabe5fa0f56154f576e26287bfdea",
+                "sha256:76658f0d942051d12a9bd08ca1b6b34fd762a8ee4240984f7c06ddfb55eaf15a",
+                "sha256:76cb39afede7055127e35a444c1c041d2e8d2f1f9c121ecef573757ba4cd2c3c",
+                "sha256:8d6b98d0d83d21fb757a182d52940d028564efe8147baa9ce0f38d057104ae72",
+                "sha256:9b3ae153c89a480a0ec402e23db8d8d84a3833b65fa4b15b81b83be9d637aab9",
+                "sha256:a60fedd2b37b4cb11ccb5d0399efe26db9e0dd149016c1cc6c8161974ceac2d6",
+                "sha256:ac1c7c0624a862f2e53438a15c9259d1655325fc2ec4392e66dc46cdae24d044",
+                "sha256:acae12b9ede49f38eb0ef76fdec2df2e94aad85ae46ec85be3648a57f0a7db04",
+                "sha256:acc2614e2e5346a4a4eab6e199203034924313626f9620b7b4b38e9ad74b7e0c",
+                "sha256:acf6e43fa75aca2d33e93409f2dafe386fe051818ee79ee8a3e21de9caa2ac9e",
+                "sha256:baee115a9ba6c5d2709a1e88ffe62b73ecc044852a925dcb67713a288c4ec70f",
+                "sha256:c18b381553638414b38705f07d1ef0a7cf301bc78a5f9bc17a957eb19446834b",
+                "sha256:d29daa681517f4bc318cd8a23af87e1f2a7bad2fe361e8aa29c77d652a065de4",
+                "sha256:d5954acfe9e00bc83ed9f5cb082ed22c592fbbef86dc48b907238be64ead5c33",
+                "sha256:ec0bb1188c1d13426039af8ffcb4dbe3aad1d7680c35a62d8eaf2a529b5d3d4f",
+                "sha256:ec1f93feb3bb93380ab0ebf8b859e8e5678c0f010d2d78367cf6bc30bfeb148e",
+                "sha256:f0e6d631bae3f231d3634f91ae4da7a960f7ff87f2865b2d2b831af1dfb04e9a",
+                "sha256:f35d6cee81fa145333137009d9c8ba90951d7d77b67c79cbe5f03c7eb74d8fe2",
+                "sha256:f47888542a0633baff535a04726948e876bf1ed880fddb7c10a736fa99146ab3",
+                "sha256:fb3b87461fa35afa19c971b0a2b7456a7b1db7b4eba9a8424666104925b78128"
             ],
             "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
-            "version": "==3.19.1"
+            "version": "==3.20.0"
+        },
+        "pymysql": {
+            "hashes": [
+                "sha256:4f13a7df8bf36a51e81dd9f3605fede45a4878fe02f9236349fd82a3f0612f96",
+                "sha256:8969ec6d763c856f7073c4c64662882675702efcb114b4bcbb955aea3a069fa7"
+            ],
+            "index": "pypi",
+            "version": "==1.1.0"
         },
         "python-dateutil": {
             "hashes": [
@@ -1013,6 +1035,61 @@
             "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
             "version": "==1.16.0"
         },
+        "sqlalchemy": {
+            "hashes": [
+                "sha256:0d3cab3076af2e4aa5693f89622bef7fa770c6fec967143e4da7508b3dceb9b9",
+                "sha256:0dacf67aee53b16f365c589ce72e766efaabd2b145f9de7c917777b575e3659d",
+                "sha256:10331f129982a19df4284ceac6fe87353ca3ca6b4ca77ff7d697209ae0a5915e",
+                "sha256:14a6f68e8fc96e5e8f5647ef6cda6250c780612a573d99e4d881581432ef1669",
+                "sha256:1b1180cda6df7af84fe72e4530f192231b1f29a7496951db4ff38dac1687202d",
+                "sha256:29049e2c299b5ace92cbed0c1610a7a236f3baf4c6b66eb9547c01179f638ec5",
+                "sha256:342d365988ba88ada8af320d43df4e0b13a694dbd75951f537b2d5e4cb5cd002",
+                "sha256:420362338681eec03f53467804541a854617faed7272fe71a1bfdb07336a381e",
+                "sha256:4344d059265cc8b1b1be351bfb88749294b87a8b2bbe21dfbe066c4199541ebd",
+                "sha256:4f7a7d7fcc675d3d85fbf3b3828ecd5990b8d61bd6de3f1b260080b3beccf215",
+                "sha256:555651adbb503ac7f4cb35834c5e4ae0819aab2cd24857a123370764dc7d7e24",
+                "sha256:59a21853f5daeb50412d459cfb13cb82c089ad4c04ec208cd14dddd99fc23b39",
+                "sha256:5fdd402169aa00df3142149940b3bf9ce7dde075928c1886d9a1df63d4b8de62",
+                "sha256:605b6b059f4b57b277f75ace81cc5bc6335efcbcc4ccb9066695e515dbdb3900",
+                "sha256:665f0a3954635b5b777a55111ababf44b4fc12b1f3ba0a435b602b6387ffd7cf",
+                "sha256:6f9e2e59cbcc6ba1488404aad43de005d05ca56e069477b33ff74e91b6319735",
+                "sha256:736ea78cd06de6c21ecba7416499e7236a22374561493b456a1f7ffbe3f6cdb4",
+                "sha256:74b080c897563f81062b74e44f5a72fa44c2b373741a9ade701d5f789a10ba23",
+                "sha256:75432b5b14dc2fff43c50435e248b45c7cdadef73388e5610852b95280ffd0e9",
+                "sha256:75f99202324383d613ddd1f7455ac908dca9c2dd729ec8584c9541dd41822a2c",
+                "sha256:790f533fa5c8901a62b6fef5811d48980adeb2f51f1290ade8b5e7ba990ba3de",
+                "sha256:798f717ae7c806d67145f6ae94dc7c342d3222d3b9a311a784f371a4333212c7",
+                "sha256:7c88f0c7dcc5f99bdb34b4fd9b69b93c89f893f454f40219fe923a3a2fd11625",
+                "sha256:7d505815ac340568fd03f719446a589162d55c52f08abd77ba8964fbb7eb5b5f",
+                "sha256:84daa0a2055df9ca0f148a64fdde12ac635e30edbca80e87df9b3aaf419e144a",
+                "sha256:87d91043ea0dc65ee583026cb18e1b458d8ec5fc0a93637126b5fc0bc3ea68c4",
+                "sha256:87f6e732bccd7dcf1741c00f1ecf33797383128bd1c90144ac8adc02cbb98643",
+                "sha256:884272dcd3ad97f47702965a0e902b540541890f468d24bd1d98bcfe41c3f018",
+                "sha256:8b8cb63d3ea63b29074dcd29da4dc6a97ad1349151f2d2949495418fd6e48db9",
+                "sha256:91f7d9d1c4dd1f4f6e092874c128c11165eafcf7c963128f79e28f8445de82d5",
+                "sha256:a2c69a7664fb2d54b8682dd774c3b54f67f84fa123cf84dda2a5f40dcaa04e08",
+                "sha256:a3be4987e3ee9d9a380b66393b77a4cd6d742480c951a1c56a23c335caca4ce3",
+                "sha256:a86b4240e67d4753dc3092d9511886795b3c2852abe599cffe108952f7af7ac3",
+                "sha256:aa9373708763ef46782d10e950b49d0235bfe58facebd76917d3f5cbf5971aed",
+                "sha256:b64b183d610b424a160b0d4d880995e935208fc043d0302dd29fee32d1ee3f95",
+                "sha256:b801154027107461ee992ff4b5c09aa7cc6ec91ddfe50d02bca344918c3265c6",
+                "sha256:bb209a73b8307f8fe4fe46f6ad5979649be01607f11af1eb94aa9e8a3aaf77f0",
+                "sha256:bc8b7dabe8e67c4832891a5d322cec6d44ef02f432b4588390017f5cec186a84",
+                "sha256:c51db269513917394faec5e5c00d6f83829742ba62e2ac4fa5c98d58be91662f",
+                "sha256:c55731c116806836a5d678a70c84cb13f2cedba920212ba7dcad53260997666d",
+                "sha256:cf18ff7fc9941b8fc23437cc3e68ed4ebeff3599eec6ef5eebf305f3d2e9a7c2",
+                "sha256:d24f571990c05f6b36a396218f251f3e0dda916e0c687ef6fdca5072743208f5",
+                "sha256:db854730a25db7c956423bb9fb4bdd1216c839a689bf9cc15fada0a7fb2f4570",
+                "sha256:dc55990143cbd853a5d038c05e79284baedf3e299661389654551bd02a6a68d7",
+                "sha256:e607cdd99cbf9bb80391f54446b86e16eea6ad309361942bf88318bcd452363c",
+                "sha256:ecf6d4cda1f9f6cb0b45803a01ea7f034e2f1aed9475e883410812d9f9e3cfcf",
+                "sha256:f2a159111a0f58fb034c93eeba211b4141137ec4b0a6e75789ab7a3ef3c7e7e3",
+                "sha256:f37c0caf14b9e9b9e8f6dbc81bc56db06acb4363eba5a633167781a48ef036ed",
+                "sha256:f5693145220517b5f42393e07a6898acdfe820e136c98663b971906120549da5"
+            ],
+            "markers": "python_version >= '3.7'",
+            "version": "==2.0.25"
+        },
         "typing-extensions": {
             "hashes": [
                 "sha256:23478f88c37f27d76ac8aee6c905017a143b0b1b886c3c9f66bc2fd94f9f5783",
@@ -1031,11 +1108,11 @@
         },
         "urllib3": {
             "hashes": [
-                "sha256:c97dfde1f7bd43a71c8d2a58e369e9b2bf692d1334ea9f9cae55add7d0dd0f84",
-                "sha256:fdb6d215c776278489906c2f8916e6e7d4f5a9b602ccbcfdf7f016fc8da0596e"
+                "sha256:34b97092d7e0a3a8cf7cd10e386f401b3737364026c45e622aa02903dffe0f07",
+                "sha256:f8ecc1bba5667413457c529ab955bf8c67b45db799d159066261719e328580a0"
             ],
-            "markers": "python_version >= '3.7'",
-            "version": "==2.0.7"
+            "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'",
+            "version": "==1.26.18"
         },
         "webencodings": {
             "hashes": [
@@ -1060,6 +1137,14 @@
             "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'",
             "version": "==2.0.1"
         },
+        "zipp": {
+            "hashes": [
+                "sha256:0e923e726174922dce09c53c59ad483ff7bbb8e572e00c7f7c46b88556409f31",
+                "sha256:84e64a1c28cf7e91ed2078bb8cc8c259cb19b76942096c8d7b84947690cabaf0"
+            ],
+            "markers": "python_version >= '3.8'",
+            "version": "==3.17.0"
+        },
         "zope.event": {
             "hashes": [
                 "sha256:2832e95014f4db26c47a13fdaef84cef2f4df37e66b59d8f1f4a8f319a632c26",
@@ -1306,7 +1391,7 @@
                 "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519",
                 "sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561"
             ],
-            "markers": "python_full_version >= '3.7.0'",
+            "markers": "python_version >= '3.7'",
             "version": "==3.3.2"
         },
         "coverage": {
@@ -1375,6 +1460,78 @@
             "markers": "python_version >= '3.8'",
             "version": "==7.0.0"
         },
+        "exceptiongroup": {
+            "hashes": [
+                "sha256:4bfd3996ac73b41e9b9628b04e079f193850720ea5945fc96a08633c66912f14",
+                "sha256:91f5c769735f051a4290d52edd0858999b57e5876e9f85937691bd4c9fa3ed68"
+            ],
+            "index": "pypi",
+            "version": "==1.2.0"
+        },
+        "greenlet": {
+            "hashes": [
+                "sha256:01bc7ea167cf943b4c802068e178bbf70ae2e8c080467070d01bfa02f337ee67",
+                "sha256:0448abc479fab28b00cb472d278828b3ccca164531daab4e970a0458786055d6",
+                "sha256:086152f8fbc5955df88382e8a75984e2bb1c892ad2e3c80a2508954e52295257",
+                "sha256:098d86f528c855ead3479afe84b49242e174ed262456c342d70fc7f972bc13c4",
+                "sha256:149e94a2dd82d19838fe4b2259f1b6b9957d5ba1b25640d2380bea9c5df37676",
+                "sha256:1551a8195c0d4a68fac7a4325efac0d541b48def35feb49d803674ac32582f61",
+                "sha256:15d79dd26056573940fcb8c7413d84118086f2ec1a8acdfa854631084393efcc",
+                "sha256:1996cb9306c8595335bb157d133daf5cf9f693ef413e7673cb07e3e5871379ca",
+                "sha256:1a7191e42732df52cb5f39d3527217e7ab73cae2cb3694d241e18f53d84ea9a7",
+                "sha256:1ea188d4f49089fc6fb283845ab18a2518d279c7cd9da1065d7a84e991748728",
+                "sha256:1f672519db1796ca0d8753f9e78ec02355e862d0998193038c7073045899f305",
+                "sha256:2516a9957eed41dd8f1ec0c604f1cdc86758b587d964668b5b196a9db5bfcde6",
+                "sha256:2797aa5aedac23af156bbb5a6aa2cd3427ada2972c828244eb7d1b9255846379",
+                "sha256:2dd6e660effd852586b6a8478a1d244b8dc90ab5b1321751d2ea15deb49ed414",
+                "sha256:3ddc0f794e6ad661e321caa8d2f0a55ce01213c74722587256fb6566049a8b04",
+                "sha256:3ed7fb269f15dc662787f4119ec300ad0702fa1b19d2135a37c2c4de6fadfd4a",
+                "sha256:419b386f84949bf0e7c73e6032e3457b82a787c1ab4a0e43732898a761cc9dbf",
+                "sha256:43374442353259554ce33599da8b692d5aa96f8976d567d4badf263371fbe491",
+                "sha256:52f59dd9c96ad2fc0d5724107444f76eb20aaccb675bf825df6435acb7703559",
+                "sha256:57e8974f23e47dac22b83436bdcf23080ade568ce77df33159e019d161ce1d1e",
+                "sha256:5b51e85cb5ceda94e79d019ed36b35386e8c37d22f07d6a751cb659b180d5274",
+                "sha256:649dde7de1a5eceb258f9cb00bdf50e978c9db1b996964cd80703614c86495eb",
+                "sha256:64d7675ad83578e3fc149b617a444fab8efdafc9385471f868eb5ff83e446b8b",
+                "sha256:68834da854554926fbedd38c76e60c4a2e3198c6fbed520b106a8986445caaf9",
+                "sha256:6b66c9c1e7ccabad3a7d037b2bcb740122a7b17a53734b7d72a344ce39882a1b",
+                "sha256:70fb482fdf2c707765ab5f0b6655e9cfcf3780d8d87355a063547b41177599be",
+                "sha256:7170375bcc99f1a2fbd9c306f5be8764eaf3ac6b5cb968862cad4c7057756506",
+                "sha256:73a411ef564e0e097dbe7e866bb2dda0f027e072b04da387282b02c308807405",
+                "sha256:77457465d89b8263bca14759d7c1684df840b6811b2499838cc5b040a8b5b113",
+                "sha256:7f362975f2d179f9e26928c5b517524e89dd48530a0202570d55ad6ca5d8a56f",
+                "sha256:81bb9c6d52e8321f09c3d165b2a78c680506d9af285bfccbad9fb7ad5a5da3e5",
+                "sha256:881b7db1ebff4ba09aaaeae6aa491daeb226c8150fc20e836ad00041bcb11230",
+                "sha256:894393ce10ceac937e56ec00bb71c4c2f8209ad516e96033e4b3b1de270e200d",
+                "sha256:99bf650dc5d69546e076f413a87481ee1d2d09aaaaaca058c9251b6d8c14783f",
+                "sha256:9da2bd29ed9e4f15955dd1595ad7bc9320308a3b766ef7f837e23ad4b4aac31a",
+                "sha256:afaff6cf5200befd5cec055b07d1c0a5a06c040fe5ad148abcd11ba6ab9b114e",
+                "sha256:b1b5667cced97081bf57b8fa1d6bfca67814b0afd38208d52538316e9422fc61",
+                "sha256:b37eef18ea55f2ffd8f00ff8fe7c8d3818abd3e25fb73fae2ca3b672e333a7a6",
+                "sha256:b542be2440edc2d48547b5923c408cbe0fc94afb9f18741faa6ae970dbcb9b6d",
+                "sha256:b7dcbe92cc99f08c8dd11f930de4d99ef756c3591a5377d1d9cd7dd5e896da71",
+                "sha256:b7f009caad047246ed379e1c4dbcb8b020f0a390667ea74d2387be2998f58a22",
+                "sha256:bba5387a6975598857d86de9eac14210a49d554a77eb8261cc68b7d082f78ce2",
+                "sha256:c5e1536de2aad7bf62e27baf79225d0d64360d4168cf2e6becb91baf1ed074f3",
+                "sha256:c5ee858cfe08f34712f548c3c363e807e7186f03ad7a5039ebadb29e8c6be067",
+                "sha256:c9db1c18f0eaad2f804728c67d6c610778456e3e1cc4ab4bbd5eeb8e6053c6fc",
+                "sha256:d353cadd6083fdb056bb46ed07e4340b0869c305c8ca54ef9da3421acbdf6881",
+                "sha256:d46677c85c5ba00a9cb6f7a00b2bfa6f812192d2c9f7d9c4f6a55b60216712f3",
+                "sha256:d4d1ac74f5c0c0524e4a24335350edad7e5f03b9532da7ea4d3c54d527784f2e",
+                "sha256:d73a9fe764d77f87f8ec26a0c85144d6a951a6c438dfe50487df5595c6373eac",
+                "sha256:da70d4d51c8b306bb7a031d5cff6cc25ad253affe89b70352af5f1cb68e74b53",
+                "sha256:daf3cb43b7cf2ba96d614252ce1684c1bccee6b2183a01328c98d36fcd7d5cb0",
+                "sha256:dca1e2f3ca00b84a396bc1bce13dd21f680f035314d2379c4160c98153b2059b",
+                "sha256:dd4f49ae60e10adbc94b45c0b5e6a179acc1736cf7a90160b404076ee283cf83",
+                "sha256:e1f145462f1fa6e4a4ae3c0f782e580ce44d57c8f2c7aae1b6fa88c0b2efdb41",
+                "sha256:e3391d1e16e2a5a1507d83e4a8b100f4ee626e8eca43cf2cadb543de69827c4c",
+                "sha256:fcd2469d6a2cf298f198f0487e0a5b1a47a42ca0fa4dfd1b6862c999f018ebbf",
+                "sha256:fd096eb7ffef17c456cfa587523c5f92321ae02427ff955bebe9e3c63bc9f0da",
+                "sha256:fe754d231288e1e64323cfad462fcee8f0288654c10bdf4f603a39ed923bef33"
+            ],
+            "index": "pypi",
+            "version": "==3.0.3"
+        },
         "idna": {
             "hashes": [
                 "sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca",
@@ -1399,6 +1556,14 @@
             "index": "pypi",
             "version": "==7.2.3"
         },
+        "opensearch-py": {
+            "hashes": [
+                "sha256:564f175af134aa885f4ced6846eb4532e08b414fff0a7976f76b276fe0e69158",
+                "sha256:7867319132133e2974c09f76a54eb1d502b989229be52da583d93ddc743ea111"
+            ],
+            "index": "pypi",
+            "version": "==2.4.2"
+        },
         "packaging": {
             "hashes": [
                 "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5",
@@ -1424,41 +1589,49 @@
         },
         "pycryptodome": {
             "hashes": [
-                "sha256:08b445799d571041765e7d5c9ca09c5d3866c2f22eeb0dd4394a4169285184f4",
-                "sha256:11ddf6c9b52116b62223b6a9f4741bc4f62bb265392a4463282f7f34bb287180",
-                "sha256:1c6273ca5a03b672e504995529b8bae56da0ebb691d8ef141c4aa68f60765700",
-                "sha256:27e1efcb68993b7ce5d1d047a46a601d41281bba9f1971e6be4aa27c69ab8065",
-                "sha256:2c16426ef49d9cba018be2340ea986837e1dfa25c2ea181787971654dd49aadd",
-                "sha256:37e531bf896b70fe302f003d3be5a0a8697737a8d177967da7e23eff60d6483c",
-                "sha256:420972f9c62978e852c74055d81c354079ce3c3a2213a92c9d7e37bbc63a26e2",
-                "sha256:4464b0e8fd5508bff9baf18e6fd4c6548b1ac2ce9862d6965ff6a84ec9cb302a",
-                "sha256:4805e053571140cb37cf153b5c72cd324bb1e3e837cbe590a19f69b6cf85fd03",
-                "sha256:67939a3adbe637281c611596e44500ff309d547e932c449337649921b17b6297",
-                "sha256:694020d2ff985cd714381b9da949a21028c24b86f562526186f6af7c7547e986",
-                "sha256:6c3df3613592ea6afaec900fd7189d23c8c28b75b550254f4bd33fe94acb84b9",
-                "sha256:6d0d2b97758ebf2f36c39060520447c26455acb3bcff309c28b1c816173a6ff5",
-                "sha256:7c9e222d0976f68d0cf6409cfea896676ddc1d98485d601e9508f90f60e2b0a2",
-                "sha256:81e9d23c0316fc1b45d984a44881b220062336bbdc340aa9218e8d0656587934",
-                "sha256:8ae0dd1bcfada451c35f9e29a3e5db385caabc190f98e4a80ad02a61098fb776",
-                "sha256:8cf5d3d6cf921fa81acd1f632f6cedcc03f5f68fc50c364cd39490ba01d17c49",
-                "sha256:954d156cd50130afd53f8d77f830fe6d5801bd23e97a69d358fed068f433fbfe",
-                "sha256:a470237ee71a1efd63f9becebc0ad84b88ec28e6784a2047684b693f458f41b7",
-                "sha256:a991f8ffe8dfe708f86690948ae46442eebdd0fff07dc1b605987939a34ec979",
-                "sha256:b0bfe61506795877ff974f994397f0c862d037f6f1c0bfc3572195fc00833b96",
-                "sha256:b7efd46b0b4ac869046e814d83244aeab14ef787f4850644119b1c8b0ec2d637",
-                "sha256:b8b80ff92049fd042177282917d994d344365ab7e8ec2bc03e853d93d2401786",
-                "sha256:c1bc0c49d986a1491d66d2a56570f12e960b12508b7e71f2423f532e28857f36",
-                "sha256:c22c80246c3c880c6950d2a8addf156cee74ec0dc5757d01e8e7067a3c7da015",
-                "sha256:cd4e7e8bf0fc1ada854688b9b309ee607e2aa85a8b44180f91021a4dd330a928",
-                "sha256:cd4e95b0eb4b28251c825fe7aa941fe077f993e5ca9b855665935b86fbb1cc08",
-                "sha256:e038ab77fec0956d7aa989a3c647652937fc142ef41c9382c2ebd13c127d5b4a",
-                "sha256:e3e6f89480616781d2a7f981472d0cdb09b9da9e8196f43c1234eff45c915766",
-                "sha256:e70f5c839c7798743a948efa2a65d1fe96bb397fe6d7f2bde93d869fe4f0ad69",
-                "sha256:ed932eb6c2b1c4391e166e1a562c9d2f020bfff44a0e1b108f67af38b390ea89",
-                "sha256:f34976c5c8eb79e14c7d970fb097482835be8d410a4220f86260695ede4c3e17"
+                "sha256:06d6de87c19f967f03b4cf9b34e538ef46e99a337e9a61a77dbe44b2cbcf0690",
+                "sha256:09609209ed7de61c2b560cc5c8c4fbf892f8b15b1faf7e4cbffac97db1fffda7",
+                "sha256:210ba1b647837bfc42dd5a813cdecb5b86193ae11a3f5d972b9a0ae2c7e9e4b4",
+                "sha256:2a1250b7ea809f752b68e3e6f3fd946b5939a52eaeea18c73bdab53e9ba3c2dd",
+                "sha256:2ab6ab0cb755154ad14e507d1df72de9897e99fd2d4922851a276ccc14f4f1a5",
+                "sha256:3427d9e5310af6680678f4cce149f54e0bb4af60101c7f2c16fdf878b39ccccc",
+                "sha256:3cd3ef3aee1079ae44afaeee13393cf68b1058f70576b11439483e34f93cf818",
+                "sha256:405002eafad114a2f9a930f5db65feef7b53c4784495dd8758069b89baf68eab",
+                "sha256:417a276aaa9cb3be91f9014e9d18d10e840a7a9b9a9be64a42f553c5b50b4d1d",
+                "sha256:4401564ebf37dfde45d096974c7a159b52eeabd9969135f0426907db367a652a",
+                "sha256:49a4c4dc60b78ec41d2afa392491d788c2e06edf48580fbfb0dd0f828af49d25",
+                "sha256:5601c934c498cd267640b57569e73793cb9a83506f7c73a8ec57a516f5b0b091",
+                "sha256:6e0e4a987d38cfc2e71b4a1b591bae4891eeabe5fa0f56154f576e26287bfdea",
+                "sha256:76658f0d942051d12a9bd08ca1b6b34fd762a8ee4240984f7c06ddfb55eaf15a",
+                "sha256:76cb39afede7055127e35a444c1c041d2e8d2f1f9c121ecef573757ba4cd2c3c",
+                "sha256:8d6b98d0d83d21fb757a182d52940d028564efe8147baa9ce0f38d057104ae72",
+                "sha256:9b3ae153c89a480a0ec402e23db8d8d84a3833b65fa4b15b81b83be9d637aab9",
+                "sha256:a60fedd2b37b4cb11ccb5d0399efe26db9e0dd149016c1cc6c8161974ceac2d6",
+                "sha256:ac1c7c0624a862f2e53438a15c9259d1655325fc2ec4392e66dc46cdae24d044",
+                "sha256:acae12b9ede49f38eb0ef76fdec2df2e94aad85ae46ec85be3648a57f0a7db04",
+                "sha256:acc2614e2e5346a4a4eab6e199203034924313626f9620b7b4b38e9ad74b7e0c",
+                "sha256:acf6e43fa75aca2d33e93409f2dafe386fe051818ee79ee8a3e21de9caa2ac9e",
+                "sha256:baee115a9ba6c5d2709a1e88ffe62b73ecc044852a925dcb67713a288c4ec70f",
+                "sha256:c18b381553638414b38705f07d1ef0a7cf301bc78a5f9bc17a957eb19446834b",
+                "sha256:d29daa681517f4bc318cd8a23af87e1f2a7bad2fe361e8aa29c77d652a065de4",
+                "sha256:d5954acfe9e00bc83ed9f5cb082ed22c592fbbef86dc48b907238be64ead5c33",
+                "sha256:ec0bb1188c1d13426039af8ffcb4dbe3aad1d7680c35a62d8eaf2a529b5d3d4f",
+                "sha256:ec1f93feb3bb93380ab0ebf8b859e8e5678c0f010d2d78367cf6bc30bfeb148e",
+                "sha256:f0e6d631bae3f231d3634f91ae4da7a960f7ff87f2865b2d2b831af1dfb04e9a",
+                "sha256:f35d6cee81fa145333137009d9c8ba90951d7d77b67c79cbe5f03c7eb74d8fe2",
+                "sha256:f47888542a0633baff535a04726948e876bf1ed880fddb7c10a736fa99146ab3",
+                "sha256:fb3b87461fa35afa19c971b0a2b7456a7b1db7b4eba9a8424666104925b78128"
             ],
             "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
-            "version": "==3.19.1"
+            "version": "==3.20.0"
+        },
+        "pymysql": {
+            "hashes": [
+                "sha256:4f13a7df8bf36a51e81dd9f3605fede45a4878fe02f9236349fd82a3f0612f96",
+                "sha256:8969ec6d763c856f7073c4c64662882675702efcb114b4bcbb955aea3a069fa7"
+            ],
+            "index": "pypi",
+            "version": "==1.1.0"
         },
         "pytest": {
             "hashes": [
@@ -1468,6 +1641,14 @@
             "index": "pypi",
             "version": "==7.4.4"
         },
+        "python-dateutil": {
+            "hashes": [
+                "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86",
+                "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"
+            ],
+            "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
+            "version": "==2.8.2"
+        },
         "requests": {
             "hashes": [
                 "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f",
@@ -1476,6 +1657,69 @@
             "markers": "python_version >= '3.7'",
             "version": "==2.31.0"
         },
+        "six": {
+            "hashes": [
+                "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926",
+                "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"
+            ],
+            "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
+            "version": "==1.16.0"
+        },
+        "sqlalchemy": {
+            "hashes": [
+                "sha256:0d3cab3076af2e4aa5693f89622bef7fa770c6fec967143e4da7508b3dceb9b9",
+                "sha256:0dacf67aee53b16f365c589ce72e766efaabd2b145f9de7c917777b575e3659d",
+                "sha256:10331f129982a19df4284ceac6fe87353ca3ca6b4ca77ff7d697209ae0a5915e",
+                "sha256:14a6f68e8fc96e5e8f5647ef6cda6250c780612a573d99e4d881581432ef1669",
+                "sha256:1b1180cda6df7af84fe72e4530f192231b1f29a7496951db4ff38dac1687202d",
+                "sha256:29049e2c299b5ace92cbed0c1610a7a236f3baf4c6b66eb9547c01179f638ec5",
+                "sha256:342d365988ba88ada8af320d43df4e0b13a694dbd75951f537b2d5e4cb5cd002",
+                "sha256:420362338681eec03f53467804541a854617faed7272fe71a1bfdb07336a381e",
+                "sha256:4344d059265cc8b1b1be351bfb88749294b87a8b2bbe21dfbe066c4199541ebd",
+                "sha256:4f7a7d7fcc675d3d85fbf3b3828ecd5990b8d61bd6de3f1b260080b3beccf215",
+                "sha256:555651adbb503ac7f4cb35834c5e4ae0819aab2cd24857a123370764dc7d7e24",
+                "sha256:59a21853f5daeb50412d459cfb13cb82c089ad4c04ec208cd14dddd99fc23b39",
+                "sha256:5fdd402169aa00df3142149940b3bf9ce7dde075928c1886d9a1df63d4b8de62",
+                "sha256:605b6b059f4b57b277f75ace81cc5bc6335efcbcc4ccb9066695e515dbdb3900",
+                "sha256:665f0a3954635b5b777a55111ababf44b4fc12b1f3ba0a435b602b6387ffd7cf",
+                "sha256:6f9e2e59cbcc6ba1488404aad43de005d05ca56e069477b33ff74e91b6319735",
+                "sha256:736ea78cd06de6c21ecba7416499e7236a22374561493b456a1f7ffbe3f6cdb4",
+                "sha256:74b080c897563f81062b74e44f5a72fa44c2b373741a9ade701d5f789a10ba23",
+                "sha256:75432b5b14dc2fff43c50435e248b45c7cdadef73388e5610852b95280ffd0e9",
+                "sha256:75f99202324383d613ddd1f7455ac908dca9c2dd729ec8584c9541dd41822a2c",
+                "sha256:790f533fa5c8901a62b6fef5811d48980adeb2f51f1290ade8b5e7ba990ba3de",
+                "sha256:798f717ae7c806d67145f6ae94dc7c342d3222d3b9a311a784f371a4333212c7",
+                "sha256:7c88f0c7dcc5f99bdb34b4fd9b69b93c89f893f454f40219fe923a3a2fd11625",
+                "sha256:7d505815ac340568fd03f719446a589162d55c52f08abd77ba8964fbb7eb5b5f",
+                "sha256:84daa0a2055df9ca0f148a64fdde12ac635e30edbca80e87df9b3aaf419e144a",
+                "sha256:87d91043ea0dc65ee583026cb18e1b458d8ec5fc0a93637126b5fc0bc3ea68c4",
+                "sha256:87f6e732bccd7dcf1741c00f1ecf33797383128bd1c90144ac8adc02cbb98643",
+                "sha256:884272dcd3ad97f47702965a0e902b540541890f468d24bd1d98bcfe41c3f018",
+                "sha256:8b8cb63d3ea63b29074dcd29da4dc6a97ad1349151f2d2949495418fd6e48db9",
+                "sha256:91f7d9d1c4dd1f4f6e092874c128c11165eafcf7c963128f79e28f8445de82d5",
+                "sha256:a2c69a7664fb2d54b8682dd774c3b54f67f84fa123cf84dda2a5f40dcaa04e08",
+                "sha256:a3be4987e3ee9d9a380b66393b77a4cd6d742480c951a1c56a23c335caca4ce3",
+                "sha256:a86b4240e67d4753dc3092d9511886795b3c2852abe599cffe108952f7af7ac3",
+                "sha256:aa9373708763ef46782d10e950b49d0235bfe58facebd76917d3f5cbf5971aed",
+                "sha256:b64b183d610b424a160b0d4d880995e935208fc043d0302dd29fee32d1ee3f95",
+                "sha256:b801154027107461ee992ff4b5c09aa7cc6ec91ddfe50d02bca344918c3265c6",
+                "sha256:bb209a73b8307f8fe4fe46f6ad5979649be01607f11af1eb94aa9e8a3aaf77f0",
+                "sha256:bc8b7dabe8e67c4832891a5d322cec6d44ef02f432b4588390017f5cec186a84",
+                "sha256:c51db269513917394faec5e5c00d6f83829742ba62e2ac4fa5c98d58be91662f",
+                "sha256:c55731c116806836a5d678a70c84cb13f2cedba920212ba7dcad53260997666d",
+                "sha256:cf18ff7fc9941b8fc23437cc3e68ed4ebeff3599eec6ef5eebf305f3d2e9a7c2",
+                "sha256:d24f571990c05f6b36a396218f251f3e0dda916e0c687ef6fdca5072743208f5",
+                "sha256:db854730a25db7c956423bb9fb4bdd1216c839a689bf9cc15fada0a7fb2f4570",
+                "sha256:dc55990143cbd853a5d038c05e79284baedf3e299661389654551bd02a6a68d7",
+                "sha256:e607cdd99cbf9bb80391f54446b86e16eea6ad309361942bf88318bcd452363c",
+                "sha256:ecf6d4cda1f9f6cb0b45803a01ea7f034e2f1aed9475e883410812d9f9e3cfcf",
+                "sha256:f2a159111a0f58fb034c93eeba211b4141137ec4b0a6e75789ab7a3ef3c7e7e3",
+                "sha256:f37c0caf14b9e9b9e8f6dbc81bc56db06acb4363eba5a633167781a48ef036ed",
+                "sha256:f5693145220517b5f42393e07a6898acdfe820e136c98663b971906120549da5"
+            ],
+            "markers": "python_version >= '3.7'",
+            "version": "==2.0.25"
+        },
         "testcontainers-core": {
             "hashes": [
                 "sha256:69a8bf2ddb52ac2d03c26401b12c70db0453cced40372ad783d6dce417e52095"
@@ -1490,6 +1734,28 @@
             "index": "pypi",
             "version": "==0.0.1rc1"
         },
+        "testcontainers-mysql": {
+            "hashes": [
+                "sha256:d22894e0d8c7b4f7424afef99f713aa7e7a19ff987b7723aed863b9c478a2c91"
+            ],
+            "index": "pypi",
+            "version": "==0.0.1rc1"
+        },
+        "testcontainers-opensearch": {
+            "hashes": [
+                "sha256:0bdf270b5b7f53915832f7c31dd2bd3ffdc20b534ea6b32231cc7003049bd0e1"
+            ],
+            "index": "pypi",
+            "version": "==0.0.1rc1"
+        },
+        "tomli": {
+            "hashes": [
+                "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc",
+                "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"
+            ],
+            "markers": "python_version < '3.11'",
+            "version": "==2.0.1"
+        },
         "typing-extensions": {
             "hashes": [
                 "sha256:23478f88c37f27d76ac8aee6c905017a143b0b1b886c3c9f66bc2fd94f9f5783",
@@ -1500,11 +1766,11 @@
         },
         "urllib3": {
             "hashes": [
-                "sha256:c97dfde1f7bd43a71c8d2a58e369e9b2bf692d1334ea9f9cae55add7d0dd0f84",
-                "sha256:fdb6d215c776278489906c2f8916e6e7d4f5a9b602ccbcfdf7f016fc8da0596e"
+                "sha256:34b97092d7e0a3a8cf7cd10e386f401b3737364026c45e622aa02903dffe0f07",
+                "sha256:f8ecc1bba5667413457c529ab955bf8c67b45db799d159066261719e328580a0"
             ],
-            "markers": "python_version >= '3.7'",
-            "version": "==2.0.7"
+            "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'",
+            "version": "==1.26.18"
         },
         "wrapt": {
             "hashes": [
diff --git a/dbrepo-analyse-service/app.py b/dbrepo-analyse-service/app.py
index 6cd01304782469a3725db0d613b74b52338b3d5a..d7d206d23d5687e7a8e2856c97f0436f7e74bf33 100644
--- a/dbrepo-analyse-service/app.py
+++ b/dbrepo-analyse-service/app.py
@@ -1,57 +1,78 @@
-import os
+import logging
 from _csv import Error
 
-from flask import Flask, request, Response
 from json import dumps
-from determine_dt import determine_datatypes
-from determine_pk import determine_pk
-import logging
-from flasgger import Swagger
+from logging.config import dictConfig
+
+from flasgger import LazyJSONEncoder, Swagger
 from flasgger.utils import swag_from
-from flasgger import LazyJSONEncoder
+from flask import Flask, Response, abort, request
+from flask_sqlalchemy import SQLAlchemy
 from gevent.pywsgi import WSGIServer
+from opensearchpy import OpenSearch
 from prometheus_flask_exporter import PrometheusMetrics
+from requests import get
 
-log_level = os.getenv('LOG_LEVEL', 'INFO')
-
-logging.basicConfig(level=logging.getLevelName(log_level))
+from determine_dt import determine_datatypes
+from determine_pk import determine_pk
+from determine_stats import determine_stats
 
-from logging.config import dictConfig
+logging.basicConfig(level=logging.DEBUG)
 
-dictConfig({
-    'version': 1,
-    'formatters': {'default': {
-        'format': '[%(asctime)s] %(levelname)s in %(module)s: %(message)s',
-    }},
-    'handlers': {'wsgi': {
-        'class': 'logging.StreamHandler',
-        'stream': 'ext://flask.logging.wsgi_errors_stream',
-        'formatter': 'default'
-    }},
-    'root': {
-        'level': log_level,
-        'handlers': ['wsgi']
+dictConfig(
+    {
+        "version": 1,
+        "formatters": {
+            "default": {
+                "format": "[%(asctime)s] %(levelname)s in %(module)s: %(message)s",
+            }
+        },
+        "handlers": {
+            "wsgi": {
+                "class": "logging.StreamHandler",
+                "stream": "ext://flask.logging.wsgi_errors_stream",
+                "formatter": "default",
+            }
+        },
+        "root": {"level": "INFO", "handlers": ["wsgi"]},
     }
-})
+)
 
 app = Flask(__name__)
+
 metrics = PrometheusMetrics(app)
-metrics.info('app_info', 'Application info', version='1.3.0')
+metrics.info("app_info", "Application info", version="1.3.0")
 app.config["SWAGGER"] = {"openapi": "3.0.1", "title": "Swagger UI", "uiversion": 3}
 
+# ========================= DB Config  ========================= #
+app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False
+app.config[
+    "SQLALCHEMY_DATABASE_URI"
+] = "mysql+pymysql://root:dbrepo@metadata-db:3306/fda"
+db = SQLAlchemy(app)
+
+# ========================= OS Config  ========================= #
+opensearch_client = OpenSearch(
+    hosts=["search-db"],
+    port=9200,
+    http_auth=("admin", "admin"),
+    use_ssl=False,
+)
+
+
 swagger_config = {
     "headers": [],
     "specs": [
         {
             "endpoint": "api-analyse",
             "route": "/api-analyse.json",
-            "rule_filter": lambda rule: rule.endpoint.startswith('analyze'),
+            "rule_filter": lambda rule: rule.endpoint.startswith("analyze"),
             "model_filter": lambda tag: True,  # all in
         },
         {
             "endpoint": "api-mdb",
             "route": "/api-mdb.json",
-            "rule_filter": lambda rule: rule.endpoint.startswith('mdb'),
+            "rule_filter": lambda rule: rule.endpoint.startswith("mdb"),
             "model_filter": lambda tag: True,  # all in
         },
     ],
@@ -68,97 +89,129 @@ template = {
         "version": "$TAG",
         "contact": {
             "name": "Prof. Andreas Rauber",
-            "email": "andreas.rauber@tuwien.ac.at"
+            "email": "andreas.rauber@tuwien.ac.at",
         },
         "license": {
             "name": "Apache 2.0",
-            "url": "https://www.apache.org/licenses/LICENSE-2.0"
+            "url": "https://www.apache.org/licenses/LICENSE-2.0",
         },
     },
     "externalDocs": {
         "description": "Sourcecode Documentation",
-        "url": "https://gitlab.phaidra.org/fair-data-austria-db-repository/fda-services"
+        "url": "https://gitlab.phaidra.org/fair-data-austria-db-repository/fda-services",
     },
     "servers": [
-        {
-            "url": "http://localhost:5000",
-            "description": "Generated server url"
-        },
-        {
-            "url": "https://dbrepo2.tuwien.ac.at",
-            "description": "Sandbox"
-        }
-    ]
+        {"url": "http://localhost:5000", "description": "Generated server url"},
+        {"url": "https://dbrepo2.tuwien.ac.at", "description": "Sandbox"},
+    ],
 }
 
 app.json_encoder = LazyJSONEncoder
 swagger = Swagger(app, config=swagger_config, template=template)
 
-@app.route('/health', methods=["GET"], endpoint='analyze_health')
-@swag_from('as-yml/health.yml')
+
+@app.route("/health", methods=["GET"], endpoint="analyze_health")
+@swag_from("as-yml/health.yml")
 def health():
-    logging.debug('endpoint health, body=%s', request)
+    logging.debug("endpoint health, body=%s", request)
     res = dumps({"status": "UP", "message": "Application is up and running"})
     return Response(res, mimetype="application/json"), 200
 
 
-@app.route('/api/analyse/determinedt', methods=["POST"], endpoint='analyze_determinedt')
-@swag_from('as-yml/determinedt.yml')
+@app.route("/api/analyse/determinedt", methods=["POST"], endpoint="analyze_determinedt")
+@swag_from("as-yml/determinedt.yml")
 def determinedt():
-    logging.debug('endpoint determine datatype, body=%s', request)
+    logging.debug("endpoint determine datatype, body=%s", request)
     input_json = request.get_json()
     try:
-        filename = str(input_json['filename'])
+        filename = str(input_json["filename"])
         enum = False
-        if 'enum' in input_json:
-            enum = bool(input_json['enum'])
+        if "enum" in input_json:
+            enum = bool(input_json["enum"])
             logging.info("Enum is present in payload and set to %s", enum)
         enum_tol = 0.001
-        if 'enum_tol' in input_json:
-            enum_tol = float(input_json['enum_tol'])
-            logging.info("Enum toleration is present in payload and set to %s", enum_tol)
+        if "enum_tol" in input_json:
+            enum_tol = float(input_json["enum_tol"])
+            logging.info(
+                "Enum toleration is present in payload and set to %s", enum_tol
+            )
         separator = None
-        if 'separator' in input_json:
-            separator = str(input_json['separator'])
+        if "separator" in input_json:
+            separator = str(input_json["separator"])
             logging.info("Seperator is present in payload and set to %s", separator)
         res = determine_datatypes(filename, enum, enum_tol, separator)
-        logging.debug('determine datatype resulted in datatypes %s', res)
+        logging.debug("determine datatype resulted in datatypes %s", res)
         return Response(res, mimetype="application/json"), 200
     except OSError as e:
-        logging.error('Failed to determine data types: %s', e)
+        logging.error("Failed to determine data types: %s", e)
         res = dumps({"success": False, "message": str(e)})
         return Response(res, mimetype="application/json"), 409
     except Error as e:
-        logging.error('Failed to determine separator %s', e)
+        logging.error("Failed to determine separator %s", e)
         res = dumps({"success": False, "message": str(e)})
         return Response(res, mimetype="application/json"), 422
     except Exception as e:
-        logging.error('Failed to determine data types: %s', e)
+        logging.error("Failed to determine data types: %s", e)
         res = dumps({"success": False, "message": str(e)})
         return Response(res, mimetype="application/json"), 500
 
 
-@app.route('/api/analyse/determinepk', methods=["POST"], endpoint='analyze_determinepk')
-@swag_from('as-yml/determinepk.yml')
+@app.route("/api/analyse/determinepk", methods=["POST"], endpoint="analyze_determinepk")
+@swag_from("as-yml/determinepk.yml")
 def determinepk():
-    logging.debug('endpoint determine primary key, body=%s', request)
+    logging.debug("endpoint determine primary key, body=%s", request)
     input_json = request.get_json()
     try:
-        filepath = str(input_json['filepath'])
-        seperator = ','
-        if 'seperator' in input_json:
-            seperator = str(input_json['seperator'])
+        filepath = str(input_json["filepath"])
+        seperator = ","
+        if "seperator" in input_json:
+            seperator = str(input_json["seperator"])
         res = determine_pk(filepath, seperator)
-        logging.debug('determined list of primary keys: %s', res)
+        logging.debug("determined list of primary keys: %s", res)
         return Response(res, mimetype="application/json"), 200
     except Exception as e:
-        logging.error('Failed to determine primary key: %s', e)
+        logging.error("Failed to determine primary key: %s", e)
         res = dumps({"success": False, "message": str(e)})
         return Response(res, mimetype="application/json"), 500
 
 
+@app.route("/api/analyse/determineStats", methods=["POST"], endpoint="analyze_stats")
+def determineStats():
+    logging.debug(
+        "endpoint to determine the statistical properties, body = %s", request
+    )
+    input_json = request.get_json()
+    if "filepath" not in input_json:
+        return {"message": "Missing 'filepath'", "status": 400}, 400
+
+    filepath = str(input_json["filepath"])
+    separator = str(input_json.get("separator", ","))
+    return determine_stats(filepath, separator)
+
+
+@app.route("/api/analyse/determineStat", methods=["POST"])
+def determineStat():
+    input_json = request.get_json()
+
+    if "database_id" not in input_json:
+        return {"message": "Missing 'database_id'", "status": 400}, 400
+    if "table_id" not in input_json:
+        return {"message": "Missing 'table_id'", "status": 400}, 400
+
+    res = determine_stats(
+        db,
+        opensearch_client,
+        database_id=input_json["database_id"],
+        table_id=input_json["table_id"],
+    )
+    if res:
+        return {"message": "Analysed statistical properties.", "status": 200}
+    else:
+        return {"message": "Database or table does not exist.", "status": 400}, 400
+
+
 rest_server_port = 5000
 
-if __name__ == '__main__':
-    http_server = WSGIServer(('', 5000), app)
+if __name__ == "__main__":
+    http_server = WSGIServer(("", 5000), app)
     http_server.serve_forever()
diff --git a/dbrepo-analyse-service/build.sh b/dbrepo-analyse-service/build.sh
index 4a0889a662ba1e9d3b704a86c85555c867dfecb9..8460f7b7f5d906a14a2f127ed1c86c3a5637a6de 100755
--- a/dbrepo-analyse-service/build.sh
+++ b/dbrepo-analyse-service/build.sh
@@ -1,4 +1,8 @@
 #!/bin/bash
+
+# needed for MariaDB Connector/C 
+apt update && apt install -y curl gcc libmariadb3 libmariadb-dev
+
 python3 -m venv ./dbrepo-analyse-service/venv
 source ./dbrepo-analyse-service/venv/bin/activate
 PIPENV_PIPFILE=./dbrepo-analyse-service/Pipfile pipenv install --dev
\ No newline at end of file
diff --git a/dbrepo-analyse-service/config.py b/dbrepo-analyse-service/config.py
index 5eb6e77b5dd5046130938348190b1d778e06d2cd..136153e23f736e0b3b21b523de4db274e3d63833 100644
--- a/dbrepo-analyse-service/config.py
+++ b/dbrepo-analyse-service/config.py
@@ -1,2 +1,2 @@
 class Config:
-    EUREKA_SERVER = os.environ.get('EUREKA_SERVER')
+    EUREKA_SERVER = os.environ.get("EUREKA_SERVER")
diff --git a/dbrepo-analyse-service/data/test_stats/test_stats_01.csv b/dbrepo-analyse-service/data/test_stats/test_stats_01.csv
new file mode 100644
index 0000000000000000000000000000000000000000..558d3c7e6d0cfe5f794f0bf76d842ebb948b9b66
--- /dev/null
+++ b/dbrepo-analyse-service/data/test_stats/test_stats_01.csv
@@ -0,0 +1,10 @@
+"col1","col2","col3","col4","col5"
+"2021-02-01 00:00:00","Zch_Stampfenbachstrasse",0.44,"150.25","10"
+"2021-04-13 00:00:00","Zch_Stampfenbachstrasse",4.88,"201.5","11"
+"2021-04-01 00:00:00","Zch_Stampfenbachstrasse",29.46,"180.75","12"
+"2021-01-01 00:00:00","Zch_Stampfenbachstrasse",9.85,"170.15","13"
+"2016-06-01 00:00:00","Zch_Stampfenbachstrasse",41.24,"160.35","14"
+"2015-01-01 00:00:00","Zch_Stampfenbachstrasse",8.51,"140.14","15"
+"2022-07-01 00:00:00","Zch_Stampfenbachstrasse",88.34,"130.45","16"
+"2022-01-01 00:00:00","Zch_Stampfenbachstrasse",75.72,"120.55","17"
+"2023-09-01 00:00:00","Zch_Schimmelstrasse",41.66,"110.295","18"
diff --git a/dbrepo-analyse-service/determine_dt.py b/dbrepo-analyse-service/determine_dt.py
index 76d8c850032a71b5c199081584dca4d440fa15dc..75d8c2290f8095b7ce1cd3368e3e003b16e15f20 100644
--- a/dbrepo-analyse-service/determine_dt.py
+++ b/dbrepo-analyse-service/determine_dt.py
@@ -9,8 +9,13 @@ import io
 from clients.s3_client import S3Client
 
 import messytables, pandas as pd
-from messytables import CSVTableSet, type_guess, \
-    headers_guess, headers_processor, offset_processor
+from messytables import (
+    CSVTableSet,
+    type_guess,
+    headers_guess,
+    headers_processor,
+    offset_processor,
+)
 
 
 def determine_datatypes(filename, enum=False, enum_tol=0.0001, separator=None) -> {}:
@@ -25,16 +30,16 @@ def determine_datatypes(filename, enum=False, enum_tol=0.0001, separator=None) -
         logging.warning(f'Failed to determine data types: file {filename} has empty body')
         return json.dumps({'columns': [], 'separator': ','})
     if separator is None:
-        logging.info('Attempt to guess separator for from first line')
+        logging.info("Attempt to guess separator for from first line")
         with io.BytesIO(stream.read()) as fh:
             line = next(fh)
-            dialect = csv.Sniffer().sniff(line.decode('utf-8'))
+            dialect = csv.Sniffer().sniff(line.decode("utf-8"))
             separator = dialect.delimiter
-            logging.info('determined separator: %s', separator)
+            logging.info("determined separator: %s", separator)
 
     # Load a file object:
     with io.BytesIO(stream.read()) as fh:
-        logging.info('Analysing corpus with separator: %s', separator)
+        logging.info("Analysing corpus with separator: %s", separator)
         table_set = CSVTableSet(fh, delimiter=separator)
 
         # A table set is a collection of tables:
@@ -58,18 +63,27 @@ def determine_datatypes(filename, enum=False, enum_tol=0.0001, separator=None) -
             elif type(types[i]) == messytables.types.IntegerType:
                 r[headers[i]] = "bigint"
             elif type(types[i]) == messytables.types.DateType:
-                if "%H" in types[i].format or "%M" in types[i].format or "%S" in types[i].format or "%Z" in types[
-                    i].format:
-                    r[headers[i]] = "timestamp"  # todo: guesses date format too, return it
+                if (
+                    "%H" in types[i].format
+                    or "%M" in types[i].format
+                    or "%S" in types[i].format
+                    or "%Z" in types[i].format
+                ):
+                    r[
+                        headers[i]
+                    ] = "timestamp"  # todo: guesses date format too, return it
                 else:
                     r[headers[i]] = "date"
-            elif type(types[i]) == messytables.types.DecimalType or type(types[i]) == messytables.types.FloatType:
+            elif (
+                type(types[i]) == messytables.types.DecimalType
+                or type(types[i]) == messytables.types.FloatType
+            ):
                 r[headers[i]] = "decimal"
             elif type(types[i]) == messytables.types.StringType:
                 r[headers[i]] = "varchar"
             else:
                 r[headers[i]] = "text"
         fh.close()
-        s = {'columns': r, 'separator': separator}
-        logging.info('Determined data types %s', s)
+        s = {"columns": r, "separator": separator}
+        logging.info("Determined data types %s", s)
     return json.dumps(s)
diff --git a/dbrepo-analyse-service/determine_pk.py b/dbrepo-analyse-service/determine_pk.py
index cc182052891d8bae34c5e8a57217f0843c5dfa1f..6187e0e913fe3338e22c32a7dfd9670c3c1bb16d 100644
--- a/dbrepo-analyse-service/determine_pk.py
+++ b/dbrepo-analyse-service/determine_pk.py
@@ -8,9 +8,9 @@ from determine_dt import determine_datatypes
 from clients.s3_client import S3Client
 
 
-def determine_pk(filename, separator=','):
+def determine_pk(filename, separator=","):
     dt = json.loads(determine_datatypes(filename=filename, separator=separator))
-    dt = {k.lower(): v for k, v in dt['columns'].items()}
+    dt = {k.lower(): v for k, v in dt["columns"].items()}
     # {k.lower(): v for k, v in dt['columns'].items() if v != 'Numeric'}
     colnames = dt.keys()
     colindex = list(range(0, len(colnames)))
@@ -27,35 +27,49 @@ def determine_pk(filename, separator=','):
         pk = {}
         j = 0
         k = 0
-        logging.info(f'File is {sizeInKb}kb, detection is precise')
+        logging.info(f"File is {sizeInKb}kb, detection is precise")
         for item in colnames:
-            if item == 'id':
+            if item == "id":
                 j = j + 1
                 pk.update({item: j})
                 colindex.remove(k)
             k = k + 1
         csvdata = pd.read_csv(stream, sep=separator)
         for i in colindex:
-            if pd.Series(csvdata.iloc[:, i]).is_unique and pd.Series(csvdata.iloc[:, i]).notnull().values.any():
+            if (
+                pd.Series(csvdata.iloc[:, i]).is_unique
+                and pd.Series(csvdata.iloc[:, i]).notnull().values.any()
+            ):
                 j = j + 1
                 pk.update({list(colnames)[i]: j})
     else:  # stochastic pk determination
         pk = {}
         j = 0
         k = 0
-        logging.info(f'File is {sizeInKb}kB (larger than threshold of 400kB), detection is stochastic')
+        logging.info(
+            f"File is {sizeInKb}kB (larger than threshold of 400kB), detection is stochastic"
+        )
         for item in colnames:
-            if item == 'id':
+            if item == "id":
                 j = j + 1
                 pk.update({item: j})
                 colindex.remove(k)
             k = k + 1
-        p = np.log10(int(response['ContentLength']))  # logarithmic scaled percentage of random inspected rows
-        csvdata = pd.read_csv(filepath_or_buffer=stream, sep=separator, header=0,
-                              skiprows=lambda k: k > 0 and random.random() > p)
+        p = np.log10(
+            int(response["ContentLength"])
+        )  # logarithmic scaled percentage of random inspected rows
+        csvdata = pd.read_csv(
+            filepath_or_buffer=stream,
+            sep=separator,
+            header=0,
+            skiprows=lambda k: k > 0 and random.random() > p,
+        )
         for i in colindex:
-            if pd.Series(csvdata.iloc[:, i]).is_unique and pd.Series(csvdata.iloc[:, i]).notnull().values.any():
+            if (
+                pd.Series(csvdata.iloc[:, i]).is_unique
+                and pd.Series(csvdata.iloc[:, i]).notnull().values.any()
+            ):
                 j = j + 1
                 pk.update({list(colnames)[i]: j})
-        logging.info(f'Determined primary key {pk}')
+        logging.info(f"Determined primary key {pk}")
     return json.dumps(pk)
diff --git a/dbrepo-analyse-service/determine_stats.py b/dbrepo-analyse-service/determine_stats.py
new file mode 100644
index 0000000000000000000000000000000000000000..50642ca52b3f3459df47b9a5f18ad473e4bc25fe
--- /dev/null
+++ b/dbrepo-analyse-service/determine_stats.py
@@ -0,0 +1,96 @@
+from pandas import DataFrame
+from sqlalchemy import create_engine, text
+
+
+def determine_stats(db, os, **kwargs):
+    database_id = kwargs.get("database_id")
+    table_id = kwargs.get("table_id")
+
+    with db.engine.connect() as connection:
+        database_name = connection.execute(
+            text(f"SELECT internal_name FROM mdb_databases WHERE id={database_id}")
+        ).fetchone()[0]
+        table_name = connection.execute(
+            text(f"SELECT internal_name FROM mdb_tables WHERE id={table_id}")
+        ).fetchone()[0]
+
+    if not database_name or not table_name:
+        return False
+
+    data_db_host = kwargs.get("data_db_host", "data-db")
+    data_db_port = kwargs.get("data_db_port", 3306)
+    # Generate data db connection on the fly: database name is varying according to the id given
+    data_db_uri = (
+        f"mysql+pymysql://root:dbrepo@{data_db_host}:{data_db_port}/{database_name}"
+    )
+    data_db_engine = create_engine(data_db_uri)
+
+    with data_db_engine.connect() as connection:
+        result = connection.execute(text(f"SELECT * FROM {table_name}"))
+        rows = result.fetchall()
+
+    df = DataFrame(rows, columns=result.keys())
+    for column, dtype in df.dtypes.items():
+        # Check if the column has a numeric data type
+        if dtype.kind in "fi":
+            # Calculate the statistics for the current column
+            column_stats = {
+                "val_min": df[column].min(),
+                "val_max": df[column].max(),
+                "mean": df[column].mean(),
+                "median": df[column].median(),
+                "std_dev": df[column].std(),
+            }
+
+            # Store statistical properties to the metadata db and index to OS
+            # TODO: use prepared statements to eliminate SQL injection
+            update_query = text(
+                f"""
+                UPDATE mdb_columns
+                SET
+                    val_min = '{column_stats["val_min"]}',
+                    val_max = '{column_stats["val_max"]}',
+                    mean    = '{column_stats["mean"]}',
+                    median  = '{column_stats["median"]}',
+                    std_dev = '{column_stats["std_dev"]}'
+                WHERE
+                    tID = '{table_id}' AND internal_name = '{column}'
+            """
+            )
+            # We need an extra select query to fetch the column ID for OpenSearch
+            select_query = text(
+                f"""
+                SELECT id
+                FROM mdb_columns
+                WHERE tID = '{table_id}' AND internal_name = '{column}'
+            """
+            )
+            with db.engine.begin() as connection:
+                connection.execute(update_query)
+                result = connection.execute(select_query)
+                column_id = result.fetchone()[0]
+                connection.commit()
+
+            # Fetch the existing document
+            existing_document = os.get(index="database", id=database_id)["_source"]
+
+            # Loop over OS response and append the statistics for each column
+            for tidx, table in enumerate(existing_document["tables"]):
+                if table["id"] == table_id:
+                    for cidx, column in enumerate(table["columns"]):
+                        if column["id"] == column_id:
+                            existing_document["tables"][tidx]["columns"][cidx].update(
+                                column_stats
+                            )
+                            # No need to keep searching if column id matches
+                            break
+
+            # Index and force refresh
+            os.index(
+                index="database",
+                id=database_id,
+                body=existing_document,
+                refresh=True,
+            )
+
+    return True
diff --git a/dbrepo-analyse-service/pywsgi.py b/dbrepo-analyse-service/pywsgi.py
index fb981f98d5c1a0a3ecb7fe54e370be61d0cdba87..7ef0e4b63ad98d080ea2169e08359a6bc2647213 100644
--- a/dbrepo-analyse-service/pywsgi.py
+++ b/dbrepo-analyse-service/pywsgi.py
@@ -1,9 +1,10 @@
 from gevent import monkey
+
 monkey.patch_all()
 
 import os
 from gevent.pywsgi import WSGIServer
 from app import app
 
-http_server = WSGIServer(('0.0.0.0', int(os.environ['PORT_APP'])), app)
+http_server = WSGIServer(("0.0.0.0", int(os.environ["PORT_APP"])), app)
 http_server.serve_forever()
diff --git a/dbrepo-analyse-service/test/conftest.py b/dbrepo-analyse-service/test/conftest.py
index c062a1b9d4a57c1eeb9da77faae0a023fe2a419c..50a2e3ab832e01d1cad63629f3b381677a295c71 100644
--- a/dbrepo-analyse-service/test/conftest.py
+++ b/dbrepo-analyse-service/test/conftest.py
@@ -2,9 +2,12 @@ import os
 
 import pytest
 import logging
+import json
 
 from minio.deleteobjects import DeleteObject
 from testcontainers.minio import MinioContainer
+from testcontainers.mysql import MySqlContainer
+from testcontainers.opensearch import OpenSearchContainer
 
 
 @pytest.fixture(scope="session")
@@ -19,13 +22,18 @@ def session(request):
     logging.debug("[fixture] starting container")
     container.start()
     # set the environment for the client
-    endpoint = 'http://' + container.get_container_host_ip() + ':' + container.get_exposed_port(9000)
-    os.environ['S3_STORAGE_ENDPOINT'] = endpoint
+    endpoint = (
+        "http://"
+        + container.get_container_host_ip()
+        + ":"
+        + container.get_exposed_port(9000)
+    )
+    os.environ["S3_STORAGE_ENDPOINT"] = endpoint
     client = container.get_client()
     # create buckets
-    logging.debug('[fixture] make buckets dbrepo-upload, dbrepo-download')
-    client.make_bucket('dbrepo-upload')
-    client.make_bucket('dbrepo-download')
+    logging.debug("[fixture] make buckets dbrepo-upload, dbrepo-download")
+    client.make_bucket("dbrepo-upload")
+    client.make_bucket("dbrepo-download")
 
     # destructor
     def stop_minio():
@@ -48,7 +56,92 @@ def cleanup(request, session):
         objects = []
         for obj in session.get_client().list_objects(bucket):
             objects.append(DeleteObject(obj.object_name))
-        logging.info(f'request to remove objects {objects}')
+        logging.info(f"request to remove objects {objects}")
         errors = session.get_client().remove_objects(bucket, objects)
         for error in errors:
-            raise ConnectionError(f'Failed to delete object with key {error.object_name} of bucket {bucket}')
+            raise ConnectionError(
+                f"Failed to delete object with key {error.object_name} of bucket {bucket}"
+            )
+
+
+@pytest.fixture(scope="function")
+def metadata_db_container():
+    metadata_db = MySqlContainer(
+        "bitnami/mariadb:10.5",
+        MYSQL_USER="root",
+        MYSQL_PASSWORD="dbrepo",
+        MYSQL_DATABASE="fda",
+    )
+    metadata_db._name = "metadata-db-test"
+    metadata_db.ports = {"3306": 33060}
+    metadata_db.env = {
+        "MYSQL_USER": metadata_db.MYSQL_USER,
+        "MARIADB_ROOT_PASSWORD": metadata_db.MYSQL_ROOT_PASSWORD,
+        "MARIADB_DATABASE": metadata_db.MYSQL_DATABASE,
+    }
+    # volume that mounts db schema from metadata-db
+    metadata_db.with_volume_mapping(
+        os.path.abspath("../dbrepo-metadata-db/setup-schema.sql"), "/schema.sql"
+    )
+    # volume for script that initializes schema and inserts test values
+    metadata_db.with_volume_mapping(
+        os.path.abspath("./test/init-db.sh"),
+        "/docker-entrypoint-initdb.d/init-db.sh",
+    )
+
+    # validate creation of schema and data
+    with metadata_db:
+        print(
+            metadata_db.exec(
+                f"mariadb -u{metadata_db.MYSQL_USER} -p{metadata_db.MYSQL_ROOT_PASSWORD} fda -e 'SELECT * FROM mdb_databases;'"
+            )
+        )
+        yield metadata_db
+
+
+@pytest.fixture(scope="function")
+def data_db_container():
+    data_db = MySqlContainer(
+        "bitnami/mariadb:10.5",
+        MYSQL_USER="root",
+        MYSQL_PASSWORD="dbrepo",
+    )
+    data_db._name = "data-db-test"
+    data_db.ports = {"3306": 33061}
+    data_db.env = {
+        "MYSQL_USER": data_db.MYSQL_USER,
+        "MARIADB_ROOT_PASSWORD": data_db.MYSQL_ROOT_PASSWORD,
+    }
+    # volume that mounts csv for data import
+    data_db.with_volume_mapping(
+        os.path.abspath("./data/test_stats/test_stats_01.csv"), "/test_stats_01.csv"
+    )
+    # volume for script to create a test data db and import values from a csv
+    data_db.with_volume_mapping(
+        os.path.abspath("./test/init-data-db.sh"),
+        "/docker-entrypoint-initdb.d/init-data-db.sh",
+    )
+
+    with data_db:
+        yield data_db
+
+
+@pytest.fixture(scope="function")
+def opensearch_container():
+    os_container = OpenSearchContainer("opensearchproject/opensearch:2.10.0")
+
+    with os_container:
+        client = os_container.get_client()
+        index_mapping_path = os.path.join(
+            "..", "dbrepo-search-db", "init", "indices", "database.json"
+        )
+        with open(index_mapping_path, "r") as file:
+            mapping = json.load(file)
+            client.indices.create(index="database", body=mapping)
+
+        yield os_container
+
+
+@pytest.fixture(scope="function")
+def all_containers(opensearch_container, metadata_db_container, data_db_container):
+    yield opensearch_container, metadata_db_container, data_db_container
diff --git a/dbrepo-analyse-service/test/init-data-db.sh b/dbrepo-analyse-service/test/init-data-db.sh
new file mode 100755
index 0000000000000000000000000000000000000000..2ba035d6ed3bf3f07cc01935016d18d142841bb4
--- /dev/null
+++ b/dbrepo-analyse-service/test/init-data-db.sh
@@ -0,0 +1,25 @@
+#!/bin/bash
+#
+# Script to initialize a test data db according to a csv file
+# Intented to be run from pytest inside a mysql/mariadb container
+
+mysql -u"$MYSQL_USER" -p"$MYSQL_ROOT_PASSWORD" <<EOSQL
+CREATE DATABASE internal1;
+
+USE internal1;
+
+CREATE TABLE table1 (
+    col1 DATETIME,
+    col2 VARCHAR(255),
+    col3 FLOAT,
+    col4 FLOAT,
+    col5 INT
+);
+
+LOAD DATA INFILE '/test_stats_01.csv'
+INTO TABLE table1
+FIELDS TERMINATED BY ',' ENCLOSED BY '"'
+LINES TERMINATED BY '\\n'
+IGNORE 1 LINES
+(col1, col2, col3, col4, col5);
+EOSQL
diff --git a/dbrepo-analyse-service/test/init-db.sh b/dbrepo-analyse-service/test/init-db.sh
new file mode 100755
index 0000000000000000000000000000000000000000..c8a8411357f3d6dc83a228c6cd81aa88dad162b7
--- /dev/null
+++ b/dbrepo-analyse-service/test/init-db.sh
@@ -0,0 +1,41 @@
+#!/bin/bash
+#
+# Script to initialize db schema and insert test values
+# Intented to be run from pytest inside a mysql/mariadb docker container
+
+mysql -u"$MYSQL_USER" -p"$MYSQL_ROOT_PASSWORD" "$MYSQL_DATABASE" <<EOSQL
+source /schema.sql
+
+INSERT INTO mdb_users (id, username, firstname, lastname, email, orcid, affiliation, mariadb_password, theme_dark)
+VALUES
+    ('1', 'user1', 'John', 'Doe', 'user1@example.com', '0000-0001-1234-5678', 'University A', 'password1', TRUE);
+
+INSERT INTO mdb_images (name, version, default_port, dialect, driver_class, jdbc_method, created, last_modified)
+VALUES
+    ('Image1', '1.0', 5432, 'postgresql', 'org.postgresql.Driver', 'jdbc:postgresql', NOW(), NOW()),
+    ('Image2', '2.0', 3306, 'mysql', 'com.mysql.jdbc.Driver', 'jdbc:mysql', NOW(), NOW());
+
+INSERT INTO mdb_containers (internal_name, name, host, port, ui_host, ui_port, ui_additional_flags, sidecar_host, sidecar_port, image_id, created, last_modified, privileged_username, privileged_password)
+VALUES
+    ('container1', 'Container1', 'localhost', 3306, 'localhost', 3306, NULL, 'localhost', 3305, 1, NOW(), NOW(), 'admin', 'admin'),
+    ('container2', 'Container2', 'localhost', 3307, 'localhost', 3307, NULL, 'localhost', 3306, 2, NOW(), NOW(), 'admin', 'admin');
+
+INSERT INTO mdb_databases (cid, name, internal_name, exchange_name, description, engine, is_public, created_by, owned_by, contact_person, created, last_modified)
+VALUES
+    (1, 'Database1', 'internal1', 'Exchange1', 'Description1', 'InnoDB', TRUE, NULL, NULL, NULL, NOW(), NOW()),
+    (2, 'Database2', 'internal2', 'Exchange2', 'Description2', 'MyISAM', FALSE, NULL, NULL, NULL, NOW(), NOW());
+
+INSERT INTO mdb_tables (tDBID, internal_name, queue_name, routing_key, tName, tDescription, num_rows, data_length, max_data_length, avg_row_length, \`separator\`, quote, element_null, skip_lines, element_true, element_false, Version, created, versioned, created_by, owned_by, last_modified)
+VALUES
+    (1, 'table1', 'Queue1', 'RoutingKey1', 'TableName1', 'TableDescription1', 5, 9, 15, 3, ',', '"', NULL, 2, TRUE, FALSE, '1.0', NOW(), TRUE, '1', '1', NOW()),
+    (1, 'table2', 'Queue2', 'RoutingKey2', 'TableName2', 'TableDescription2', 0, 0, 0, 0, ',', "'", 'NA', 0, '1', '0', '2.0', NOW(), TRUE, '1', '1', NOW()),
+    (2, 'table3', 'Queue3', 'RoutingKey3', 'TableName3', 'TableDescription3', 0, 0, 0, 0, ',', '"', 'EMPTY', 5, 'yes', 'no', '1.0', NOW(), TRUE, '1', '1', NOW());
+
+INSERT INTO mdb_columns (tID, dfID, cName, internal_name, alias, Datatype, length, ordinal_position, is_primary_key, index_length, size, d, auto_generated, is_null_allowed, val_min, val_max, mean, median, std_dev, created, last_modified)
+VALUES
+    (1, NULL, 'Column1', 'col1', 'Alias1', 'DATETIME', 255, 1, TRUE, NULL, NULL, NULL, FALSE, TRUE, NULL, NULL, NULL, NULL, NULL, NOW(), NOW()),
+    (1, NULL, 'Column2', 'col2', 'Alias2', 'VARCHAR', NULL, 2, FALSE, NULL, NULL, NULL, FALSE, TRUE, NULL, NULL, NULL, NULL, NULL, NOW(), NOW()),
+    (1, NULL, 'Column3', 'col3', 'Alias3', 'FLOAT', 10, 3, FALSE, NULL, NULL, 2, FALSE, TRUE, 0, 100, NULL, NULL, NULL, NOW(), NOW()),
+    (1, NULL, 'Column4', 'col4', 'Alias4', 'FLOAT', 10, 3, FALSE, NULL, NULL, 2, FALSE, TRUE, 0, 100, NULL, NULL, NULL, NOW(), NOW()),
+    (1, NULL, 'Column5', 'col5', 'Alias5', 'INT', 10, 3, FALSE, NULL, NULL, 2, FALSE, TRUE, 0, 100, NULL, NULL, NULL, NOW(), NOW());
+EOSQL
diff --git a/dbrepo-analyse-service/test/test_determine_dt.py b/dbrepo-analyse-service/test/test_determine_dt.py
index 2acf97aacd300b57bf6e9a5a6a214b7ed9b4bd75..199c94fe91186d44b1cd81e0375d1cf8dc84224d 100644
--- a/dbrepo-analyse-service/test/test_determine_dt.py
+++ b/dbrepo-analyse-service/test/test_determine_dt.py
@@ -15,20 +15,19 @@ from determine_dt import determine_datatypes
 
 
 class DetermineDatatypesTest(unittest.TestCase):
-
     # @Test
     def test_determine_datatypesDateTime_succeeds(self):
         exp = {
-            'columns': {
-                'Datum': 'timestamp',
-                'Standort': 'varchar',
-                'Parameter': 'varchar',
-                'Intervall': 'varchar',
-                'Einheit': 'varchar',
-                'Wert': 'decimal',
-                'Status': 'varchar'
+            "columns": {
+                "Datum": "timestamp",
+                "Standort": "varchar",
+                "Parameter": "varchar",
+                "Intervall": "varchar",
+                "Einheit": "varchar",
+                "Wert": "decimal",
+                "Status": "varchar",
             },
-            'separator': ','
+            "separator": ",",
         }
 
         # mock
@@ -41,16 +40,16 @@ class DetermineDatatypesTest(unittest.TestCase):
     # @Test
     def test_determine_datatypesDateTimeWithTimezone_succeeds(self):
         exp = {
-            'columns': {
-                'Datum': 'varchar',
-                'Standort': 'varchar',
-                'Parameter': 'varchar',
-                'Intervall': 'varchar',
-                'Einheit': 'varchar',
-                'Wert': 'decimal',
-                'Status': 'varchar'
+            "columns": {
+                "Datum": "varchar",
+                "Standort": "varchar",
+                "Parameter": "varchar",
+                "Intervall": "varchar",
+                "Einheit": "varchar",
+                "Wert": "decimal",
+                "Status": "varchar",
             },
-            'separator': ','
+            "separator": ",",
         }
 
         # mock
@@ -63,16 +62,16 @@ class DetermineDatatypesTest(unittest.TestCase):
     # @Test
     def test_determine_datatypesDateTimeWithT_succeeds(self):
         exp = {
-            'columns': {
-                'Datum': 'timestamp',
-                'Standort': 'varchar',
-                'Parameter': 'varchar',
-                'Intervall': 'varchar',
-                'Einheit': 'varchar',
-                'Wert': 'decimal',
-                'Status': 'varchar'
+            "columns": {
+                "Datum": "timestamp",
+                "Standort": "varchar",
+                "Parameter": "varchar",
+                "Intervall": "varchar",
+                "Einheit": "varchar",
+                "Wert": "decimal",
+                "Status": "varchar",
             },
-            'separator': ','
+            "separator": ",",
         }
 
         # mock
@@ -85,16 +84,16 @@ class DetermineDatatypesTest(unittest.TestCase):
     # @Test
     def test_determine_datatypes_succeeds(self):
         exp = {
-            'columns': {
-                'int': 'bigint',
-                'float': 'decimal',
-                'string': 'varchar',
-                'boolean': 'bool',
-                'date': 'date',
-                'time': 'timestamp',
-                'enum': 'varchar'  # currently not used
+            "columns": {
+                "int": "bigint",
+                "float": "decimal",
+                "string": "varchar",
+                "boolean": "bool",
+                "date": "date",
+                "time": "timestamp",
+                "enum": "varchar",  # currently not used
             },
-            'separator': ','
+            "separator": ",",
         }
 
         # mock
@@ -106,62 +105,57 @@ class DetermineDatatypesTest(unittest.TestCase):
 
     # @Test
     def test_determine_datatypes_fileDoesNotExist_fails(self):
-
         # test
         try:
             response = determine_datatypes("i_do_not_exist.csv")
         except ClientError as e:
             pass  # s3.head operation
         except Exception:
-            self.fail('unexpected exception raised')
+            self.fail("unexpected exception raised")
         else:
-            self.fail('ExpectedException not raised')
+            self.fail("ExpectedException not raised")
 
     # @Test
     def test_determine_datatypes_fileEmpty_succeeds(self):
-
         # mock
         S3Client().upload_file("empty.csv", './data/test_dt/', 'dbrepo-upload')
 
         # test
         response = determine_datatypes("empty.csv")
         data = json.loads(response)
-        self.assertEqual(data['columns'], [])
-        self.assertEqual(data['separator'], ',')
+        self.assertEqual(data["columns"], [])
+        self.assertEqual(data["separator"], ",")
 
     # @Test
     def test_determine_datatypes_separatorSemicolon_succeeds(self):
-
         # mock
         S3Client().upload_file("separator.csv", './data/test_dt/', 'dbrepo-upload')
 
         # test
         response = determine_datatypes(filename="separator.csv", separator=";")
         data = json.loads(response)
-        self.assertEqual(data['separator'], ';')
+        self.assertEqual(data["separator"], ";")
 
     # @Test
     def test_determine_datatypes_separatorGuess_succeeds(self):
-
         # mock
         S3Client().upload_file("separator.csv", './data/test_dt/', 'dbrepo-upload')
 
         # test
         response = determine_datatypes(filename="separator.csv")
         data = json.loads(response)
-        self.assertEqual(data['separator'], ';')
+        self.assertEqual(data["separator"], ";")
 
     # @Test
     def test_determine_datatypes_separatorGuessLargeDataset_succeeds(self):
-
         # mock
         S3Client().upload_file("large.csv", './data/test_dt/', 'dbrepo-upload')
 
         # test
         response = determine_datatypes(filename="large.csv")
         data = json.loads(response)
-        self.assertEqual(data['separator'], ',')
+        self.assertEqual(data["separator"], ",")
 
 
-if __name__ == '__main__':
+if __name__ == "__main__":
     unittest.main()
diff --git a/dbrepo-analyse-service/test/test_determine_pk.py b/dbrepo-analyse-service/test/test_determine_pk.py
index 4fffda96c524d551efe3d8728713547fa7179a8f..d6f1314a2bb1b56bed76a56aa4d18eff3a514a6e 100644
--- a/dbrepo-analyse-service/test/test_determine_pk.py
+++ b/dbrepo-analyse-service/test/test_determine_pk.py
@@ -14,7 +14,6 @@ from clients.s3_client import S3Client
 from determine_pk import determine_pk
 
 class DeterminePrimaryKeyTest(unittest.TestCase):
-
     # @Test
     def test_determine_pk_largeFileIdFirst_succeeds(self):
 
diff --git a/dbrepo-analyse-service/test/test_determine_stats.py b/dbrepo-analyse-service/test/test_determine_stats.py
new file mode 100644
index 0000000000000000000000000000000000000000..5d82ffedbe65f2f366feac4c317c4b3863f5aa79
--- /dev/null
+++ b/dbrepo-analyse-service/test/test_determine_stats.py
@@ -0,0 +1,157 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+"""
+Created on Mon Jan 17 17:13:25 2024
+
+@author: Sotiris Tsepelakis
+"""
+
+from opensearchpy import OpenSearch
+from sqlalchemy import create_engine
+
+from determine_stats import determine_stats
+
+
+class TestDetermineStatisticalProperties:
+    # @Test
+    def test_determine_statistical_properties_succeeds(self, all_containers):
+        # mock request
+        payload = {
+            "database_id": 1,
+            "table_id": 1,
+            "data_db_host": "localhost",
+            "data_db_port": 33061,
+        }
+
+        os_host = all_containers[0].get_container_host_ip()
+        os_port = all_containers[0].get_exposed_port("9200/tcp")
+        os = OpenSearch([{"host": os_host, "port": os_port}])
+
+        metadata_db_host = all_containers[1].get_container_host_ip()
+        metadata_db_port = all_containers[1].get_exposed_port(3306)
+        db = create_engine(
+            f"mysql+pymysql://root:dbrepo@{metadata_db_host}:{metadata_db_port}/fda"
+        )
+
+        # index a test document
+        all_containers[0].get_client().index(
+            index="database",
+            id="1",
+            body={
+                "id": 1,
+                "name": "testdb",
+                "exchange_name": "dbrepo",
+                "internal_name": "testdb_ygvq",
+                "tables": [
+                    {
+                        "id": 1,
+                        "database_id": 1,
+                        "name": "mytable",
+                        "internal_name": "mytable",
+                        "queue_name": "dbrepo",
+                        "routing_key": "dbrepo.testdb_ygvq.mytable",
+                        "columns": [
+                            {
+                                "id": 1,
+                                "database_id": 1,
+                                "table_id": 1,
+                                "name": "id",
+                                "internal_name": "id",
+                                "auto_generated": True,
+                                "is_primary_key": True,
+                                "column_type": "BIGINT",
+                                "is_public": True,
+                                "is_null_allowed": False,
+                                "enums": [],
+                                "sets": [],
+                            },
+                            {
+                                "id": 2,
+                                "database_id": 1,
+                                "table_id": 1,
+                                "name": "col1",
+                                "internal_name": "col1",
+                                "date_format": {
+                                    "id": 2,
+                                    "database_format": "%Y-%c-%d %H:%i:%S",
+                                    "unix_format": "yyyy-MM-dd HH:mm:ss",
+                                    "has_time": True,
+                                    "created_at": "2024-01-17T13:22:26.000Z",
+                                },
+                                "auto_generated": False,
+                                "is_primary_key": False,
+                                "column_type": "DATETIME",
+                                "is_public": True,
+                                "is_null_allowed": True,
+                                "enums": [],
+                                "sets": [],
+                            },
+                            {
+                                "id": 3,
+                                "database_id": 1,
+                                "table_id": 1,
+                                "name": "col2",
+                                "internal_name": "col2",
+                                "auto_generated": False,
+                                "is_primary_key": False,
+                                "column_type": "VARCHAR",
+                                "size": 255,
+                                "is_public": True,
+                                "is_null_allowed": True,
+                                "enums": [],
+                                "sets": [],
+                            },
+                            {
+                                "id": 4,
+                                "database_id": 1,
+                                "table_id": 1,
+                                "name": "col3",
+                                "internal_name": "col3",
+                                "auto_generated": True,
+                                "is_primary_key": True,
+                                "column_type": "FLOAT",
+                                "size": 24,
+                                "is_public": True,
+                                "is_null_allowed": True,
+                                "enums": [],
+                                "sets": [],
+                            },
+                            {
+                                "id": 5,
+                                "database_id": 1,
+                                "table_id": 1,
+                                "name": "col4",
+                                "internal_name": "col4",
+                                "auto_generated": False,
+                                "is_primary_key": False,
+                                "column_type": "FLOAT",
+                                "size": 24,
+                                "is_public": True,
+                                "is_null_allowed": True,
+                                "enums": [],
+                                "sets": [],
+                            },
+                            {
+                                "id": 6,
+                                "database_id": 1,
+                                "table_id": 1,
+                                "name": "col5",
+                                "internal_name": "col5",
+                                "auto_generated": False,
+                                "is_primary_key": False,
+                                "column_type": "INT",
+                                "size": 255,
+                                "is_public": True,
+                                "is_null_allowed": True,
+                                "enums": [],
+                                "sets": [],
+                            },
+                        ],
+                        "constraints": {"uniques": [], "foreign_keys": []},
+                    }
+                ],
+            },
+        )
+        # test
+        response = determine_stats(db, os, **payload)
+        assert response