Files
hdhs/db/fmp.py

147 lines
4.7 KiB
Python

import json
from datetime import datetime
import numpy as np
import pandas as pd
import pyodbc
from db import DB
from tqdm import tqdm
from utils import edit_distance
"""
File Maker Pro Connection Object
Desc:
This file contains the primary logic for interacting with the file maker pro database
"""
class FMP(DB):
def __init__(self, dsn=None, username=None, password=None, connect=True):
DB.__init__(self)
if dsn is None:
dsn = input("Enter the FMP DSN: ")
if username is None:
username = input("Enter your FMP username: ")
if password is None:
password = input("Enter your FMP password: ")
self.__dsn = dsn
self.__username = username
self.__password = password
self.__connection_string = "DSN={0};UID={1};PWD={2}".format(
self.__dsn, self.__username, self.__password
)
# TODO: consider extracting this list to the DB class
self._table_names = [
"clinics",
"patients",
"visits",
"diagnoses",
"diagnoses_made",
"meds",
"meds_dispensed",
"geonames",
"settings"
# "globals",
# TODO: add the join tables that are still in the FMP model, but weren't used last year
]
self.__python_types_to_sql = {
"str": "VARCHAR",
"date": "TIMESTAMP",
"float64": "FLOAT",
"NoneType": "VARCHAR",
}
self.__pyodbc_connection = None
if connect:
self.__connect()
# METHODS FROM DB SUPER CLASS
# ###########################
def read_table(self, table_name) -> pd.DataFrame:
query = "select * from {table}".format(table=table_name)
df = pd.read_sql(query, self.connection)
return df
def write_table(self, data, table_name):
# TODO: implement write_table in FMP class
print("IMPLEMENT WRITE_TABLE IN FMP")
# ###########################
def __connect(self):
if self.__pyodbc_connection is None:
self.__pyodbc_connection = pyodbc.connect(self.__connection_string)
@property
def python_types_to_sql(self):
return self.__python_types_to_sql
@property
def connection(self):
if self.__pyodbc_connection is None:
self.__connect()
return self.__pyodbc_connection
# ABSTRACT BASE CLASS MEHTODS
def parse_database(self) -> dict():
self._model = dict()
for table_name in tqdm(self.table_names):
df = self.read_table(table_name)
self._model[table_name] = {}
col_types = [
df.iloc[0, index].__class__.__name__ for index in range(len(df.columns))
]
smallest_pk_edit_distance = float("inf")
previous_pk_field = None
for field_name, field_type in zip(df.columns, col_types):
self._model[table_name][field_name] = {}
self._model[table_name][field_name]["type"] = field_type
# extract primary keys
if field_name[:3] == "pk_":
# possible primary key
distance = edit_distance(field_name[3:-3], table_name)
if distance < smallest_pk_edit_distance:
# closest primary key so far
if previous_pk_field is not None:
# remove previously assumed pk
self._model[table_name][previous_pk_field]["pk"] = False
# set current pk
self._model[table_name][field_name]["pk"] = True
previous_pk_field = field_name
smallest_pk_edit_distance = distance
else:
self._model[table_name][field_name]["pk"] = False
else:
self._model[table_name][field_name]["pk"] = False
# extract foreign keys
if field_name[:3] == "fk_":
self._model[table_name][field_name]["fk"] = True
self._model[table_name][field_name][
"fk_table"
] = self.get_tablename_from_fieldname(field_name)
else:
self._model[table_name][field_name]["fk"] = False
return self._model
def write_model(self, model_object: dict = None):
curr_time = datetime.now().strftime(self.datetime_format)
with open("./models/{time}_model.json".format(time=curr_time), "w") as f:
if model_object is not None:
json.dump(model_object, f, indent=4)
else:
json.dump(self._model, f, indent=4)