"""VVB_dataupd2023 is a script that updates the data from 2023 in the main table VVB_vragenlijst"""

# Import needed libraries
import os
import pandas as pd
import numpy as np
import sqlalchemy
import pymysql
from sqlalchemy import Integer, Float, String
from sqlalchemy.exc import SQLAlchemyError
print(f"Pandas version: {pd.__version__} (pandas version used: 2.1.4)")
print(f"Numpy version: {np.__version__} (numpy version used: 2.0.25)")
print(f"sqlalchemy version: {sqlalchemy.__version__} (sqlalchemy version used: 1.4.6)")
print(f"pymysql version: {pymysql.__version__} (pymysql version used: 1.4.6)")
# Containrization? 

#############################################################################################

t_columns = ['VVB_id', 'year', 'naam', 'telefoonnummer', 'naam_organisatie', 'verantwoordelijke_naam', 'verantwoordelijke_email', 'postadres', 'postcode', 'plaatsnaam', 'organisatie_type', 'aantal_inwoners', 'grootteklasse', 'bedrijfsvoering_in_shared_service_organisatie', 'provincie', 'coropgebied', 'sportvoorzieningen_en_zwembaden', 'werk_en_inkomen', 'beheer_en_onderhoud_openbare_ruimte', 'afvalverwijdering', 'parkeervoorzieningen', 'belastingen', 'vergunningverlening_toezicht_handhaving', 'wmo', 'werkbedrijf_en_reintegratie', 'sociale_werkvoorziening', 'culturele_voorzieningen', 'formatieve_omvang', 'formatieve_omvang_fte_per_1000_inwoners', 'formatieve_omvang_leidinggevenden_hele_organisatie', 'formatieve_omvang_managementondersteuning_hele_organisatie', 'formatieve_omvang_bestuurszaken', 'werkelijke_bezetting', 'percentage_bezetting', 'totaal_medewerkers', 'aantal_leidinggevende_medewerkers_op_31_dec', 'span_of_control', 'formatieve_omvang_leidinggevenden_bedrijfsvoering', 'formatieve_omvang_managementondersteuning_bedrijfsvoering', 'formatieve_omvang_financien_toezicht_controle', 'formatieve_omvang_p_o_hrm', 'formatieve_omvang_inkoopfunctie', 'formatieve_omvang_communicatiefunctie', 'formatieve_omvang_juridische_zaken', 'formatieve_omvang_ict', 'formatieve_omvang_facilitaire_zaken', 'formatieve_omvang_div', 'totaal_fte_bedrijfsvoering', 'totaal_fte_overhead', 'percentage_overheadformatie', 'percentage_bedrijfsvoering', 'formatieve_omvang_datafuncties', 'percentage_datafunctie', 'aantal_fte_bepaalde_tijd', 'percentage_aanstellingen_bepaalde_tijd', 'aantal_externe_inhuur_fte', 'flexibiliteit_organisatie', 'aantal_externe_inhuur_personen', 'te_servicen_medewerkers', 'aantal_fte_salarisschaal_1_6', 'aantal_fte_salarisschaal_7_9', 'aantal_fte_salarisschaal_10_12', 'aantal_fte_salarisschaal_13_hoger', 'aantal_fte_salarisschaal_onbekend', 'optelsom_fte_salarisschalen', 'percentage_ziekteverzuim', 'gemiddelde_meldingsfrequentie', 'uitgaven_opleiding_ontwikkeling', 'percentage_uitgaven_opleiding_ontwikkeling', 'aantal_medewerkers_met_afstand_tot_arbeidsmarkt_op_31_dec', 'percentage_medewerkers_met_afstand_tot_arbeidsmarkt', 'medewerkers_tot_35_jaar', 'medewerkers_35_45_jaar', 'medewerkers_45_55_jaar', 'medewerkers_55_ouder', 'som_medewerkers_leeftijdscategorie', 'medewerkers_in_dienst', 'percentage_instroom', 'medewerkers_vertrokken', 'percentage_uitstroom', 'dienstverband_korter_dan_3_jaar', 'dienstverband_10_jaar_of_langer', 'gemiddelde_score_kwaliteitsmeting_bedrijfsvoering', 'score_medewerkerstevredenheid', 'totaal_exploitatierekening', 'werkelijke_apparaatskosten', 'controleberekening_apparaatskosten', 'apparaatskosten_per_inwoner', 'percentage_apparaatskosten', 'totale_loonsom', 'gemiddelde_loonsom_per_fte', 'huisvestingskosten_kantoorruimte', 'percentage_huisvestingskosten', 'totale_ict_kosten', 'ict_kosten_personeelskosten', 'ict_kosten_inhuur_uitbesteding', 'ict_kosten_software', 'ict_kosten_hardware', 'ict_kosten_telefonie_datacommunicatie', 'ict_kosten_afschrijvingen', 'totale_ict_kosten_categorien', 'overige_ict_kosten', 'ict_kosten_per_medewerker', 'ict_kosten_per_inwoner', 'totale_ict_kosten_inclusief_procesautomatisering', 'percentage_ict_kosten_inclusief_procesautomatisering', 'percentage_ict_kosten_exclusief_procesautomatisering', 'kosten_inhuur_uitbesteding_overhead', 'facilitaire_kosten_deel', 'totale_overheadkosten_excl_ict', 'percentage_overheadkosten_excl_ict', 'totale_kosten_inhuur', 'percentage_externe_inhuur', 'totale_kosten_verbonden_partijen', 'uitgaven_verbonden_partijen_tov_totale_begroting', 'uitgaven_verbonden_partijen_per_inwoner', 'totale_kosten_GGD_verbonden_partij', 'totale_kosten_veiligheidsregio_verbonden_partij', 'totale_kosten_omgevingsdienst_verbonden_partij', 'totale_kosten_overige_verbonden_partijen', 'percentage_tijdige_betalingen', 'netto_schuldquote_correctie', 'belastingcapaciteit', 'grondexploitatie', 'solvabiliteit', 'structurele_exploitatieruimte', 'verhuurbaar_vloeroppervlak', 'ruimtebeslag_kantoorruimte_per_fte', 'beschikbare_werkplekken', 'werkplekindex', 'werkplekken_per_te_servicen_medewerker', 'percentage_social_return', 'percentage_duurzaamheid_gunningscriterium', 'gemiddeld_wegingspercentage_duurzaamheidscriterium', 'knelpunt_1', 'knelpunt_2', 'knelpunt_3', 'verbeteractie_1', 'verbeteractie_2', 'verbeteractie_3', 'percentage_overhead_streefniveau', 'percentage_ziekteverzuim_streefniveau', 'percentage_inkoopfacturen_binnen_betaaltermijn_streefniveau', 'gemiddeld_ikto_cijfer_streefniveau', 'formatie_fte_per_1000_inwoners', 'bezetting_fte_per_1000_inwoners', 'apparaatskosten_per_inwoner_jaarrekening', 'externe_inhuur_percentage_loonsom_en_totale_kosten', 'overhead_percentage_totaal_lasten', 'klachttoegankelijkheid', 'aandacht_voor_leren_van_klachten', 'geformuleerde_doelstellingen_klachten_monitoring_en_sturing', 'decentrale_klachtbehandeling', 'inzicht_aantallen_klachten', 'inzicht_aard_van_klachten', 'inzicht_diensten_waarop_klachten_betrekking_hebben', 'definitie_van_klacht_als_uiting_van_ongenoegen', 'nadruk_op_persoonlijke_behandeling', 'mogelijkheid_tot_indienen_complimenten', 'triage_bij_ontvangst_van_klachten', 'gebruik_van_inzichten_klachtbehandeling', 'bespreken_van_klachten_met', 'meten_van_tevredenheid_over_afhandeling_van_klachten', 'tevredenheidsscore_afhandeling_klachten', 'gebruik_van_klanten_burgerpanel', 'omschrijving_organisatie']
s_columns = ['u_id', 'Not Found', 'Not Found', 'Not Found', 'Organisatienaam', 'Not Found', 'Not Found', 'Not Found', 'Not Found', 'Not Found', '17234', '15734', '23706', '163208', '23707', '23708', '35953', '103095', '35955', '35956', '35957', '35954', '35958', '103096', '103098', '35952', '35951', '15744', '15745', '15750', '15760', '15757', '15772', '35701', '15780', '15805', '35702', '15751', '17196', '15752', '15753', '15754', '15755', '15756', '15758', '15759', '24293', '15767', '15763', '15764', '15768', '163209', '163218', '15775', '15776', '15777', '15778', '103108', '104020', '96450', '96451', '96452', '96453', '96454', '160651', '15795', '15796', '15801', '96448', '15809', '163221', '15813', '15814', '15815', '15816', '17241', '15820', '15829', '15822', '15831', '15825', '15826', '163217', '163225', '163211', '15847', '157524', '15849', '163212', '15859', '15861', '24316', '163229', '15864', '103109', '103110', '103111', '103112', '103114', '103113', '103115', '104861', '103116', '59153', '103850', '163219', '163220', '163213', '163214', '163223', '163224', '15880', '15882', '103105', '163222', '103106', '103101', '103102', '103103', '103104', '15886', '163202', '163204', '163205', '163206', '163207', '15909', '15913', '15869', '15870', '104086', '15938', '103123', '103124', '163248', '163249', '163250', '163215', '163226', '163227', '56083', '56085', '56091', '161497', '96482', '96483', '96484', '96485', '96486', '163230', '163231', '163232', '163233', '163234', '163235', '163236', '163237', '163238', '163239', '163240', '163241', '163242', '163243', '163244', '163245', '163246']
t_dt_columns =['int unsigned', 'year', 'varchar(255)', 'varchar(255)', 'varchar(255)', 'varchar(255)', 'varchar(255)', 'varchar(255)', 'varchar(255)', 'varchar(255)', 'int', 'int', 'varchar(255)', 'varchar(255)', 'varchar(255)', 'varchar(255)', 'decimal(5,2)', 'decimal(5,2)', 'decimal(5,2)', 'decimal(5,2)', 'decimal(5,2)', 'decimal(5,2)', 'decimal(5,2)', 'decimal(5,2)', 'decimal(5,2)', 'decimal(5,2)', 'decimal(5,2)', 'decimal(10,2)', 'decimal(10,2)', 'decimal(10,2)', 'decimal(10,2)', 'decimal(10,2)', 'decimal(10,2)', 'decimal(5,2)', 'decimal(10,2)', 'decimal(10,2)', 'decimal(10,2)', 'decimal(10,2)', 'decimal(10,2)', 'decimal(10,2)', 'decimal(10,2)', 'decimal(10,2)', 'decimal(10,2)', 'decimal(10,2)', 'decimal(10,2)', 'decimal(10,2)', 'decimal(10,2)', 'decimal(10,2)', 'decimal(10,2)', 'decimal(5,2)', 'decimal(5,2)', 'decimal(10,2)', 'decimal(5,2)', 'decimal(10,2)', 'decimal(5,2)', 'decimal(10,2)', 'decimal(5,2)', 'int', 'int', 'decimal(10,2)', 'decimal(10,2)', 'decimal(10,2)', 'decimal(10,2)', 'decimal(10,2)', 'decimal(10,2)', 'decimal(5,2)', 'decimal(5,2)', 'decimal(15,2)', 'decimal(15,2)', 'decimal(5,2)', 'decimal(5,2)', 'int', 'int', 'int', 'int', 'int', 'int', 'decimal(5,2)', 'int', 'decimal(5,2)', 'int', 'int', 'decimal(5,2)', 'decimal(5,2)', 'decimal(15,2)', 'decimal(15,2)', 'decimal(15,2)', 'decimal(10,2)', 'decimal(5,2)', 'decimal(15,2)', 'decimal(15,2)', 'decimal(15,2)', 'decimal(5,2)', 'decimal(15,2)', 'decimal(15,2)', 'decimal(15,2)', 'decimal(15,2)', 'decimal(15,2)', 'decimal(15,2)', 'decimal(15,2)', 'decimal(15,2)', 'decimal(15,2)', 'decimal(15,2)', 'decimal(10,2)', 'decimal(15,2)', 'decimal(5,2)', 'decimal(5,2)', 'decimal(15,2)', 'decimal(10,2)', 'decimal(15,2)', 'decimal(5,2)', 'decimal(15,2)', 'decimal(5,2)', 'decimal(15,2)', 'decimal(5,2)', 'decimal(10,2)', 'decimal(15,2)', 'decimal(15,2)', 'decimal(15,2)', 'decimal(15,2)', 'decimal(5,2)', 'decimal(10,2)', 'decimal(10,2)', 'decimal(10,2)', 'decimal(10,2)', 'decimal(10,2)', 'decimal(10,2)', 'decimal(5,2)', 'int', 'decimal(5,2)', 'decimal(5,2)', 'decimal(5,2)', 'decimal(5,2)', 'decimal(5,2)', 'varchar(500)', 'varchar(500)', 'varchar(500)', 'varchar(500)', 'varchar(500)', 'varchar(500)', 'varchar(1000)', 'varchar(1000)', 'varchar(1000)', 'decimal(5,2)', 'decimal(10,2)', 'decimal(10,2)', 'decimal(10,2)', 'decimal(5,2)', 'decimal(5,2)', 'varchar(255)', 'varchar(255)', 'varchar(255)', 'varchar(255)', 'varchar(255)', 'varchar(255)', 'varchar(255)', 'varchar(255)', 'varchar(255)', 'varchar(255)', 'varchar(255)', 'varchar(255)', 'varchar(255)', 'varchar(255)', 'decimal(5,2)', 'varchar(255)', 'varchar(255)']

# Find the indexes where the value in source (old database) is 'Not Found'
not_found_indexes = [index for index, value in enumerate(s_columns) if value == 'Not Found']

for index in sorted(not_found_indexes, reverse=True):
    del s_columns[index]
    del t_columns[index]
    del t_dt_columns[index]

s_columns = [f"i2023.{item}" if index >= 2 else item for index, item in enumerate(s_columns)]

source_target_dict = dict(zip(s_columns, t_columns))

# Function to convert datatype strings to SQLAlchemy type
def convert_to_sqlalchemy_type(data_type):
    from sqlalchemy import String, Float, Integer
    if "varchar" in data_type:
        return String()
    elif "decimal" in data_type:
        return Float()
    elif "int" in data_type:
        return Integer()
    else:
        raise ValueError(f"Unsupported data type: {data_type}")

# Apply the conversion function to the list
sqlalchemy_data_types = [convert_to_sqlalchemy_type(dt) for dt in t_dt_columns]

export_dt_dict = dict(zip(t_columns, sqlalchemy_data_types))

#############################################################################################

# add these seperately in the VVB_vragenlijst (do not include in script unless the questions are activated)
# inactive columns that will have no values
inactive_columns = ['103829','103830','104786','103831','15898','15899','15901','15902','24297','56010','56011','56014','56023','56024','56044','56048','56053','103119','103121','54310','56079','163210','163216','163228','15903','56007','103833','103835','24298']
decimal_columns = ['sportvoorzieningen_en_zwembaden','werk_en_inkomen','beheer_en_onderhoud_openbare_ruimte','afvalverwijdering','parkeervoorzieningen','belastingen','vergunningverlening_toezicht_handhaving','wmo','werkbedrijf_en_reintegratie','sociale_werkvoorziening','culturele_voorzieningen','formatieve_omvang','formatieve_omvang_fte_per_1000_inwoners','formatieve_omvang_leidinggevenden_hele_organisatie','formatieve_omvang_managementondersteuning_hele_organisatie','formatieve_omvang_bestuurszaken','werkelijke_bezetting','percentage_bezetting','totaal_medewerkers','aantal_leidinggevende_medewerkers_op_31_dec','span_of_control','formatieve_omvang_leidinggevenden_bedrijfsvoering','formatieve_omvang_managementondersteuning_bedrijfsvoering','formatieve_omvang_financien_toezicht_controle','formatieve_omvang_p_o_hrm','formatieve_omvang_inkoopfunctie','formatieve_omvang_communicatiefunctie','formatieve_omvang_juridische_zaken','formatieve_omvang_ict','formatieve_omvang_facilitaire_zaken','formatieve_omvang_div','totaal_fte_bedrijfsvoering','totaal_fte_overhead','percentage_overheadformatie','percentage_bedrijfsvoering','formatieve_omvang_datafuncties','percentage_datafunctie','aantal_fte_bepaalde_tijd','percentage_aanstellingen_bepaalde_tijd','aantal_externe_inhuur_fte','flexibiliteit_organisatie','aantal_fte_salarisschaal_1_6','aantal_fte_salarisschaal_7_9','aantal_fte_salarisschaal_10_12','aantal_fte_salarisschaal_13_hoger','aantal_fte_salarisschaal_onbekend','optelsom_fte_salarisschalen','percentage_ziekteverzuim','gemiddelde_meldingsfrequentie','uitgaven_opleiding_ontwikkeling','percentage_uitgaven_opleiding_ontwikkeling','aantal_medewerkers_met_afstand_tot_arbeidsmarkt_op_31_dec','percentage_medewerkers_met_afstand_tot_arbeidsmarkt','percentage_instroom','percentage_uitstroom','gemiddelde_score_kwaliteitsmeting_bedrijfsvoering','score_medewerkerstevredenheid','totaal_exploitatierekening','werkelijke_apparaatskosten','controleberekening_apparaatskosten','apparaatskosten_per_inwoner','percentage_apparaatskosten','totale_loonsom','gemiddelde_loonsom_per_fte','huisvestingskosten_kantoorruimte','percentage_huisvestingskosten','totale_ict_kosten','ict_kosten_personeelskosten','ict_kosten_inhuur_uitbesteding','ict_kosten_software','ict_kosten_hardware','ict_kosten_telefonie_datacommunicatie','ict_kosten_afschrijvingen','totale_ict_kosten_categorien','overige_ict_kosten','ict_kosten_per_medewerker','ict_kosten_per_inwoner','totale_ict_kosten_inclusief_procesautomatisering','percentage_ict_kosten_inclusief_procesautomatisering','percentage_ict_kosten_exclusief_procesautomatisering','kosten_inhuur_uitbesteding_overhead','facilitaire_kosten_deel','totale_overheadkosten_excl_ict','percentage_overheadkosten_excl_ict','totale_kosten_inhuur','percentage_externe_inhuur','totale_kosten_verbonden_partijen','uitgaven_verbonden_partijen_tov_totale_begroting','uitgaven_verbonden_partijen_per_inwoner','totale_kosten_GGD_verbonden_partij','totale_kosten_veiligheidsregio_verbonden_partij','totale_kosten_omgevingsdienst_verbonden_partij','totale_kosten_overige_verbonden_partijen','percentage_tijdige_betalingen','netto_schuldquote_correctie','belastingcapaciteit','grondexploitatie','solvabiliteit','structurele_exploitatieruimte','verhuurbaar_vloeroppervlak','ruimtebeslag_kantoorruimte_per_fte','werkplekindex','werkplekken_per_te_servicen_medewerker','percentage_social_return','percentage_duurzaamheid_gunningscriterium','gemiddeld_wegingspercentage_duurzaamheidscriterium','gemiddeld_ikto_cijfer_streefniveau','formatie_fte_per_1000_inwoners','bezetting_fte_per_1000_inwoners','apparaatskosten_per_inwoner_jaarrekening','externe_inhuur_percentage_loonsom_en_totale_kosten','overhead_percentage_totaal_lasten','tevredenheidsscore_afhandeling_klachten']
integer_columns = ['VVB_id','organisatie_type','aantal_inwoners','aantal_externe_inhuur_personen','te_servicen_medewerkers','medewerkers_tot_35_jaar','medewerkers_35_45_jaar','medewerkers_45_55_jaar','medewerkers_55_ouder','som_medewerkers_leeftijdscategorie','medewerkers_in_dienst','medewerkers_vertrokken','dienstverband_korter_dan_3_jaar','dienstverband_10_jaar_of_langer','beschikbare_werkplekken']

#############################################################################################

# Creating a connection with the database using SQLalchemy instead. !!! username/password solution? 
def create_connection():
    engine = sqlalchemy.create_engine(
        'mysql+pymysql://qvanderlinden:XEMFj9Y8ErKyyY6df2MK@dev.kurtosis.nl/venster_voor_bedrijfsvoering'
        )
    return engine

#############################################################################################

def delete_existing_rows(engine, destination_table):
    with engine.begin() as conn:  # Use `begin()` to ensure the transaction is committed
        # Count rows with year = 2023 before deletion
        count_query = sqlalchemy.text(f"SELECT COUNT(*) FROM {destination_table} WHERE year = 2023")
        initial_count = conn.execute(count_query).scalar()
        
        if initial_count > 0:
            # Delete rows with year = 2023
            delete_query = sqlalchemy.text(f"DELETE FROM {destination_table} WHERE year = 2023")
            result = conn.execute(delete_query)
            
            # Commit is automatically handled with `begin()` context manager
            print(f"Deleted {result.rowcount} rows from {destination_table}.")
            
            # Count rows again after deletion to confirm
            final_count = conn.execute(count_query).scalar()
            print(f"Rows with year 2023 before deletion: {initial_count}")
            print(f"Rows with year 2023 after deletion: {final_count}")
        else:
            print("No rows with year 2023 found.")

#############################################################################################
# Importing all specified columns from data_upd into a pandas dataframe. 
#############################################################################################

def import_data(engine, source_table, s_columns):
    columns_str = ', '.join(f"`{col}`" for col in s_columns)
    query = f"SELECT {columns_str} FROM {source_table}"
    df = pd.read_sql(query, engine)
    return df

#############################################################################################
# Transforming the values suitable for export to VVB_vragenlijst
#############################################################################################

def transform_data(df):
    
    # Defining specific functions for transformations
    def map_values1(series):
        series = series.replace({
            'Ongeveer evenveel zelf als uitbesteed': 0.5,
            'Volledig of grotendeels uitbesteed': 1.0,
            'Volledig of grotendeels in eigen beheer': 0.0,
            'nan': np.nan,
            '2': np.nan,
            '1': 1.0,
            '0': 0.0,
            '0.5': 0.5
        })
        return series
    def map_values2(series):
        series = series.replace({
            'nan': pd.NA,
            '2': 2,
            '4': 4,
            '1': 1,
            '0': 0,
            '3': 3,
            ' 25-50%': 2,
            ' &gt;75%': 4,
            ' 50-75%': 3,
            '&lt;25%': 1,
            'Niet van toepassing': 0
        })
        return series
    
    # giving the dataframe columns the same names as the target_table so it will map the data corectly regardless of the column index
    df = df.rename(columns=source_target_dict)

    mask = df['naam_organisatie'].str.startswith(('Gemeente ', 'gemeente ', 'Gemeenten '))

    # Apply the replacements
    df.loc[mask, 'naam_organisatie'] = df.loc[mask, 'naam_organisatie']\
        .str.replace('Gemeente ', '', regex=False)\
        .str.replace('gemeente ', '', regex=False)\
        .str.replace('Gemeenten ', '', regex=False)

    df['organisatie_type'] = pd.to_numeric(df['organisatie_type'].replace({'Gemeente': 0, 'Overig': 3, '0': 0, '3': 3, 'nan': pd.NA}), errors='coerce').astype('Int64')

    df['aantal_inwoners'] = pd.to_numeric(df['aantal_inwoners'], errors='coerce').astype(pd.Int64Dtype())
    
    df['bedrijfsvoering_in_shared_service_organisatie'] = df['bedrijfsvoering_in_shared_service_organisatie'].replace({
        'Ja (geheel)': 'geheel',
        ' Ja (deels)': 'deel',
        ' Nee': 'nee',
        'nan': None
    })

    # Categorizing the values of multiple columns with floats using a function specified in the beginning of this function.
    columnmap1 = ['sportvoorzieningen_en_zwembaden', 'werk_en_inkomen', 'beheer_en_onderhoud_openbare_ruimte', 'afvalverwijdering', 'parkeervoorzieningen', 'belastingen', 'vergunningverlening_toezicht_handhaving', 'wmo', 'werkbedrijf_en_reintegratie', 'sociale_werkvoorziening', 'culturele_voorzieningen']
    for column in columnmap1:
        df[column] = map_values1(df[column])

    # Categorizing the values of one column with integers using a function specified in the beginning of this function.
    df['percentage_duurzaamheid_gunningscriterium'] = pd.to_numeric(map_values2(df['percentage_duurzaamheid_gunningscriterium']), errors='coerce').astype('Int64')

    columnmap2 = ['klachttoegankelijkheid', 'aandacht_voor_leren_van_klachten', 'geformuleerde_doelstellingen_klachten_monitoring_en_sturing', 'decentrale_klachtbehandeling', 'inzicht_aantallen_klachten', 'inzicht_aard_van_klachten', 'inzicht_diensten_waarop_klachten_betrekking_hebben', 'definitie_van_klacht_als_uiting_van_ongenoegen', 'nadruk_op_persoonlijke_behandeling', 'mogelijkheid_tot_indienen_complimenten']
    for column in columnmap2:
        df[column] = df[column].replace({'Geen mening': 'Niet eens/Niet oneens'})

    df['tevredenheidsscore_afhandeling_klachten'] = df['tevredenheidsscore_afhandeling_klachten'].replace('Geen meting', 0)
    df['tevredenheidsscore_afhandeling_klachten'] = pd.to_numeric(df['tevredenheidsscore_afhandeling_klachten'], errors='coerce').fillna(0).astype('Int64')
    
    # Adds a column to the dataframe where all values are '2023' this interger will be suitable for the year column in VVB_vragenlijst.
    df['year'] = 2023

    for column in decimal_columns: # Specify the columns that should have a float data type
        if df[column].dtype != 'float64': # Skipping the columns that are already float, otherwise it will raise an eror with the string replace argument.
            df[column] = df[column].astype(str).str.replace(',', '.') # making sure they are a string and replacing kommas with dots so that they can be converter to float datatypes.
            df[column] = pd.to_numeric(df[column], errors='coerce').astype('float64') #

    # Similar to the float transformations but for integers
    for column in integer_columns:
        if df[column].dtype != 'Int64':
            df[column] = pd.to_numeric(df[column], errors='coerce').astype('Int64')

    # Making sure that the all the values missing in object type columns are 'None'
    object_columns = df.select_dtypes(include=['object']).columns
    for col in object_columns:
        df[col] = df[col].where(pd.notna(df[col]), None)

    # Sanity check 
    column_dtypes_dict = df.dtypes.to_dict()
    #print(column_dtypes_dict)
    print(df.shape[0])

    columns_precalculated = [
    'apparaatskosten_per_inwoner',
    'flexibiliteit_organisatie',
    'formatieve_omvang_fte_per_1000_inwoners',
    'gemiddelde_loonsom_per_fte',
    'ict_kosten_per_inwoner',
    'ict_kosten_per_medewerker',
    'optelsom_fte_salarisschalen',
    'overige_ict_kosten',
    'percentage_aanstellingen_bepaalde_tijd',
    'percentage_apparaatskosten',
    'percentage_bedrijfsvoering',
    'percentage_bezetting',
    'percentage_datafunctie',
    'percentage_externe_inhuur',
    'percentage_huisvestingskosten',
    'percentage_ict_kosten_exclusief_procesautomatisering',
    'percentage_ict_kosten_inclusief_procesautomatisering',
    'percentage_instroom',
    'percentage_medewerkers_met_afstand_tot_arbeidsmarkt',
    'percentage_overheadformatie',
    'percentage_overheadkosten_excl_ict',
    'percentage_uitgaven_opleiding_ontwikkeling',
    'percentage_uitstroom',
    'ruimtebeslag_kantoorruimte_per_fte',
    'som_medewerkers_leeftijdscategorie',
    'span_of_control',
    'te_servicen_medewerkers',
    'totaal_fte_bedrijfsvoering',
    'totaal_fte_overhead',
    'totale_ict_kosten_categorien',
    'totale_overheadkosten_excl_ict',
    'uitgaven_verbonden_partijen_per_inwoner',
    'uitgaven_verbonden_partijen_tov_totale_begroting',
    'werkplekindex',
    'werkplekken_per_te_servicen_medewerker']

    # Drop the specified columns
    df = df.drop(columns=columns_precalculated)

    return df

#############################################################################################

def export_data(engine, df, table_name):
    try:
        df.to_sql(name=table_name, con=engine, 
                  if_exists='append', # If the table already exists, the new data is appended to the existing table. If the table does not exist, it is created.
                  index=False, # We do not include the row index
                  dtype=export_dt_dict # refers to a dictionary that maps the column name to the SQLalchemy datatypes.
                  )
        print("Data inserted successfully.")
    except SQLAlchemyError as err:
        print(f"Error inserting data: {err}")

# Main execution function 
def main():
    source_table = 'data_upd'
    destination_table = 'VVB_vragenlijst'

    engine = create_connection()

    try:
        delete_existing_rows(engine, destination_table)

        df = import_data(engine, source_table, s_columns)

        if not df.empty:
            df = transform_data(df)
            export_data(engine, df, destination_table)
            print("Data transformation and export completed successfully.")
        else:
            print("No new data to process.")
    finally:
        engine.dispose()

# boilerplate code
if __name__ == "__main__":    
    main()
