File size: 9,388 Bytes
1212e16
 
 
 
 
 
 
 
 
239e124
1212e16
 
 
 
 
 
 
 
 
 
 
239e124
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1212e16
 
 
 
 
 
239e124
 
8be2bba
239e124
1212e16
239e124
 
 
 
1212e16
 
 
 
 
239e124
8be2bba
1212e16
239e124
 
 
 
 
 
 
 
 
1212e16
239e124
 
1212e16
239e124
 
 
1212e16
239e124
 
 
1212e16
239e124
 
 
 
 
8be2bba
239e124
 
 
 
 
 
8be2bba
1212e16
 
 
 
 
 
239e124
 
1212e16
239e124
 
 
1212e16
8be2bba
 
 
 
 
 
1212e16
 
8be2bba
239e124
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8be2bba
239e124
 
 
8be2bba
239e124
 
8be2bba
 
239e124
8be2bba
 
1212e16
8be2bba
239e124
8be2bba
 
 
 
 
 
 
239e124
 
8be2bba
239e124
1212e16
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
239e124
 
 
1212e16
239e124
 
 
1212e16
 
 
8be2bba
 
 
 
 
 
 
 
 
 
1212e16
 
 
 
 
 
 
 
 
 
 
 
 
 
 
239e124
 
8be2bba
1212e16
 
8be2bba
 
 
 
1212e16
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
#!/usr/bin/env python
# coding: utf-8

# In[1]:


import pandas as pd
import numpy as np
import gradio as gr
# gr.__version__
import os, tempfile
from scipy.sparse import csr_matrix
import gensim.downloader as api
from sentence_transformers import SentenceTransformer, util
from sklearn.feature_extraction.text import CountVectorizer
from gensim.utils import simple_preprocess
from concurrent.futures import ThreadPoolExecutor
import chardet
import torch
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
from UI_input_output import read_clean_model
import time

# a wrapper function, to show how long a function takes
def timeit(func):
    def wrapper(*args, **kwargs):
        start = time.time()
        result = func(*args, **kwargs)
        end = time.time()
        duration = end - start
        
        # Convert seconds to minutes and seconds
        minutes = int(duration // 60)
        seconds = int(duration % 60)
        
        print(f"\nFunction '{func.__name__}' took {minutes} minutes and {seconds} seconds.")
        return result
    return wrapper

# Declare global placeholder for dataframes
input_df = pd.DataFrame()
mdr_df = pd.DataFrame()
result_df = pd.DataFrame()

# set the default value for slider
slider_default_v = 0
threshold_slider_input = 'No filter'

# to load the csv files as input and mdr file; return a list of columns for users to select
@timeit
def process_two_csvs(file_path_input, file_path_mdr, model_selected):
    
    ###########  upload files
    file_missing_msg =  """<p>  
        <span style="font-weight:bold; color:red; font-size:20px;">At least one of the files is missing from upload</span>.
        </p>"""
        

    if file_path_input is None or file_path_mdr is None:
        return None, file_missing_msg

    try:
        # Read both files into DataFrames with their own encoding
        with open(file_path_input, 'rb') as f:
            result = chardet.detect(f.read())
        encoding_input = result['encoding']
        
        with open(file_path_mdr, 'rb') as f:
            result = chardet.detect(f.read())
        encoding_mdr = result['encoding']
    
        input_df = pd.read_csv(file_path_input, encoding=encoding_input)
        mdr_df = pd.read_csv(file_path_mdr, encoding=encoding_mdr)
        
        # lower case all column names
        input_df.columns = input_df.columns.str.lower()
        mdr_df.columns = mdr_df.columns.str.lower()
        
        # # the key columns from input and mdr.  check if uploaded files contains these
        # input_key_columns: 'variable','description'
        # mdr_key_columns: 'name','definition'
        
        for col in ['variable','description']:
            if col not in input_df.columns:
                return None, f"""<p>Input file is missing 
                    <span style="font-weight:bold; color:red; font-size:20px;">{col}</span>
                    column
                    </p>"""
            
        for col in ['name','definition']:
            if col not in mdr_df.columns:
                return None, f"""<p>MDR file is missing 
                    <span style="font-weight:bold; color:red; font-size:20px;">{col}</span>
                    column
                    </p>"""
        
        print('\n')
        print(input_df.shape)
        print(mdr_df.shape)
    
        # clean and run through models
        print('\n model selected: ',model_selected)
        result_df = read_clean_model(input_df, mdr_df, model_selected)
    
        # # flag where one input variable matched multiple name from MDR
        result_df['Flag'] = (result_df['input_name'].duplicated(keep=False) | result_df['mdr_name'].duplicated(keep=False)) \
                              .map(lambda x: 'Duplicate' if x else 'Unique')
        
        # because per input_name matches to multiple mdr_name
        ## be able to select top # of mdr matched to input will reduce ananlyst manual labor
        result_df = result_df.sort_values(by=['input_name','similarity_score'], ascending=[True, False])
        result_df['mdr_per_inpnt_name_cnt'] = (result_df.groupby(['input_name']).cumcount() + 1)

        return result_df, None
        
    except Exception as e:
        return None, f"Error during processing: {e}"

# Save a filtered DataFrame to CSV and return path
def save_filtered_csv(df, model_selected):
    # Use the system temp directory to save result dataframe as a CSV temp file
    temp_dir = tempfile.gettempdir()
    custom_filename = f"{model_selected}.csv"
    file_path = os.path.join(temp_dir, custom_filename)
    df.to_csv(file_path, index=False)
    
    return file_path

# Triggered after the initial load after process file 
def on_files_uploaded(file_path_input, file_path_mdr, model_selected):
    process_two_csvs_results = process_two_csvs(file_path_input, file_path_mdr, model_selected)
    df = process_two_csvs_results[0]
    proc_err_msg = process_two_csvs_results[1]
    
    try:
        df # if df exist
        csv_path = save_filtered_csv(df, model_selected)
        return df, proc_err_msg, csv_path, df, slider_default_v, threshold_slider_input
    except:
        print('\n no df from on_files_upload() function')
        return None, 'no output table after processing', None, None, slider_default_v, threshold_slider_input


# Threshold filter on similarity scores, and number filter on the number of mdr matched per input
def apply_filters(score_threshold, num_mdr_per_input, df, model_selected):
    df["similarity_score"] = df["similarity_score"].astype(float)
    filtered_df = df.copy()
    
    # Apply (threshold on similarity score) filter (if not None)
    if score_threshold is not None:
        filtered_df = filtered_df[filtered_df["similarity_score"] >= score_threshold]
    
    # Apply (the number of MDR names matched per Input variable) filter (if not "All")
    if num_mdr_per_input != "No filter":
        filtered_df = filtered_df[filtered_df["mdr_per_inpnt_name_cnt"] <= num_mdr_per_input]
    
    csv_path = save_filtered_csv(filtered_df, model_selected)
    
    return filtered_df, csv_path


######################################## APP ###########################################
with gr.Blocks() as demo:
    
    ################## app layout ####################
    gr.Markdown("# Semantic Matching between Input and MDR")
    
    with gr.Row():
        
        # box to load the input file
        l1 = gr.Markdown(
        """
        Upload Input file
        (file must contains at least two columns (not case sensitive): **variable** and **description**)
        """)
        csv_input = gr.File(label="Upload Input File",
                    file_types=[".csv"])
        
        # box to load the MDR file
        l2 = gr.Markdown(
        """
        Upload MDR file
        (file must contains at least two columns (not case sensitive): **name** and **definition**)
        """)
        csv_mdr = gr.File(label="Upload MDR File", 
                    file_types=[".csv"])

    errmsg_proc = gr.HTML(label="Error message") # to display error message for process csvs
    
    # Store processed DataFrame in State for download button to work after load
    process_df_state = gr.State()

    # drop down to select models
    model_dropdown = gr.Dropdown(label="Select the model", choices=['Cosine','CosJaccard'], value='Cosine', multiselect=False)
    
    # button to click and initiate processing the files, i.e. clean the data sets, run through models
    process_btn = gr.Button("Process Files")
    
    # slider filters row
    with gr.Row():
        with gr.Column(scale=2):
            # slider, for filtering similarity score as a threshold --- position slider above the table due to table size change during sliding
            threshold_slider = gr.Slider(minimum=0, maximum=100, step=1, value=slider_default_v, label=">= Similarity Score") # to download all at initial load
        with gr.Column(scale=1):
            # slider, for filtering the number of mdr names matched per input name
            threshold_slider_input = gr.Dropdown(choices=['No filter', 1, 2, 3, 4, 5], label="<= Number of MDR name Per Input Name (or no filter)", value=threshold_slider_input)
                
            
    # table that displays the final result dataframe
    output_table = gr.Dataframe(
        interactive=True,   # True Enables sorting, and editing;  need False in order to do the filtering
        row_count=10,
        column_widths="auto"
    )

    # the link that enables download of this result table
    download_link = gr.File(label="⬇️ Download Result CSV")
    

    ################## app functions ####################

    # link process button to its function and input output
    process_btn.click(
        fn=on_files_uploaded,
        inputs=[csv_input, csv_mdr, model_dropdown],
        outputs=[output_table, errmsg_proc, download_link, process_df_state, threshold_slider, threshold_slider_input]
    )

    # Trigger filtering on slider or dropdown change
    threshold_slider.change(apply_filters, inputs=[threshold_slider, threshold_slider_input, process_df_state, model_dropdown], outputs=[output_table, download_link])
    threshold_slider_input.change(apply_filters, inputs=[threshold_slider, threshold_slider_input, process_df_state, model_dropdown], outputs=[output_table, download_link])



demo.launch()