From fa8c3a4b8b1c2c567d1ec12bcf8c24ae908da254 Mon Sep 17 00:00:00 2001
From: jaylin <chieh.lin@rwth-aachen.de>
Date: Sun, 10 Mar 2024 19:24:33 +0100
Subject: [PATCH] Extract Event and Measurement table

---
 my_flask_app/__pycache__/main.cpython-39.pyc  | Bin 15047 -> 25204 bytes
 my_flask_app/main.py                          | 969 ++++++++----------
 .../models/__pycache__/models.cpython-39.pyc  | Bin 9605 -> 9641 bytes
 my_flask_app/models/models.py                 |   6 +-
 my_flask_app/templates/app.html               | 599 +++++++++--
 5 files changed, 919 insertions(+), 655 deletions(-)

diff --git a/my_flask_app/__pycache__/main.cpython-39.pyc b/my_flask_app/__pycache__/main.cpython-39.pyc
index 197f3a0c6f0b12d1beee27123df9ba4302797d1b..870e9cbe212add277a10cf6075c2b75ab29566f5 100644
GIT binary patch
literal 25204
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
D*D8vZ

delta 6180
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

diff --git a/my_flask_app/main.py b/my_flask_app/main.py
index 5ad81ab..3a3f7ba 100644
--- a/my_flask_app/main.py
+++ b/my_flask_app/main.py
@@ -1,14 +1,16 @@
+from datetime import datetime
 import pandas as pd
 from my_flask_app import app
 from .models.models import CustomTable, CustomColumn, Theme, CompressedDataType, Observation_Spec, RegType, RegRole
 from flask_sqlalchemy import SQLAlchemy
 from flask import jsonify, redirect, render_template, request, session, url_for, json
-from sqlalchemy import ARRAY, BIGINT, BOOLEAN, DOUBLE_PRECISION, FLOAT, INTEGER, JSON, NUMERIC, SMALLINT, TIMESTAMP, UUID, VARCHAR, MetaData, String, create_engine, text, inspect
+from sqlalchemy import ARRAY, BIGINT, BOOLEAN, DOUBLE_PRECISION, FLOAT, INT, INTEGER, JSON, NUMERIC, SMALLINT, TIMESTAMP, UUID, VARCHAR, MetaData, String, create_engine, text, inspect
 import pydot, base64, os, logging
 from sqlalchemy.orm import scoped_session, sessionmaker
 from sqlalchemy.dialects.postgresql.base import ischema_names
 from sqlalchemy.dialects.postgresql import JSONB, TSTZRANGE, INTERVAL, BYTEA, JSON, UUID, DOUBLE_PRECISION, BYTEA, ARRAY, REAL, TSTZRANGE, UUID, BYTEA, JSONB, JSON, ARRAY, FLOAT, INTEGER, TIMESTAMP, TEXT, BOOLEAN, VARCHAR, NUMERIC, REAL
 from sqlalchemy.dialects.sqlite import JSON, FLOAT, INTEGER, TIMESTAMP, TEXT, BOOLEAN, VARCHAR, NUMERIC, REAL
+from bs4 import BeautifulSoup
 
 # Set up database (call db.engine)
 app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
@@ -49,7 +51,9 @@ def index():
                     session['self_defined_labels'] = []
                     session['schema_selected'] = ''
                     session['show_all'] = False
+                    session['object_name'] = {}
                     session['data_header']={'event':[], 'measurement':[], 'segment':[], 'segmentData':[]}
+                    session['current_data_header'] = {'tablename': '', 'type': '', 'label': [], 'features_name': []}
                     print("4")
                 # Initialize inspector here, outside the inner if-else
                 print("4.5")
@@ -98,6 +102,9 @@ def index():
                 image2 = generate_erd(graph_DOT2)
                 print("222")
 
+
+            extract_ME_table(engine, 'event_data', 'E', 'time', ['col', 'machine_id'], [' col ', ' name ', ' Z_ACTUAL_ZERO_POINT '], ['value(fine, coarse)'], datetime(2022, 11, 1, 17, 5), datetime(2022, 11, 3, 0, 0))
+
             return render_template('app.html', database=database, schemas=schemas, show_all=show_all, schema_Selected=schema_Selected, tables=tables1, image1=image1, image2=image2, dropped_items=dropped_items, self_labels=self_labels)
         
         else:
@@ -108,8 +115,6 @@ def index():
         return f"An error occurred: {str(e)}", 400
     
 
-
-
 @app.route('/handle-drop', methods=['POST'])
 def handle_drop():
     data = request.json
@@ -222,32 +227,189 @@ def add_label():
 @app.route('/add-data-header', methods=['POST'])
 def add_data_header():
     data_header = session.get('data_header', {'event':[], 'measurement':[], 'segment':[], 'segmentData':[]})
+    
     data = request.json
     type = data.get('type') if data.get('type') != None else ''
-    label = data.get('label') if data.get('label') != None else ''
+    label_list = eval(data.get('label'))if data.get('label') != None else ['','','']
     object_column = data.get('object_column') if data.get('object_column') != None else ''
     value_list = data.get('value_list') if data.get('value_list') != None else []
 
+    current_table = session.get('target_table_name', '')
+
     if type == 'event':
-        observation = Observation_Spec(type, label, value_list)
-        data_header['event'].append(observation.to_dict())
+        observation = Observation_Spec(current_table, 'E', label_list, value_list)
+        if observation.to_dict() not in data_header['event']:
+            data_header['event'].append(observation.to_dict())
     elif type == 'measurement':
-        observation = Observation_Spec(type, label, value_list)
-        data_header['measurement'].append(observation.to_dict())
+        observation = Observation_Spec(current_table, 'M', label_list, value_list)
+        if observation.to_dict() not in data_header['measurement']:
+            data_header['measurement'].append(observation.to_dict())
     elif type == 'segment':
-        observation = Observation_Spec(type, label, value_list)
-        data_header['segment'].append(observation.to_dict())
+        observation = Observation_Spec(current_table, 'S', label_list, [])
+        if observation.to_dict() not in data_header['segment']:
+            data_header['segment'].append(observation.to_dict())
     elif type == 'segmentData':
-        observation = Observation_Spec(type, label, value_list)
-        data_header['segmentData'].append(observation.to_dict())
-    
+        observation = Observation_Spec(current_table, 'SD', label_list, value_list)
+        if observation.to_dict() not in data_header['segmentData']:
+            data_header['segmentData'].append(observation.to_dict())
+    elif type == 'object':
+        obj = session.get('object_name', {})
+        obj[current_table] = object_column
+        session['object_name'] = obj
+        return jsonify()
+
+    print(observation.to_dict())
     print("88888")
     print(data_header)
     session['data_header'] = data_header
 
-    return jsonify({'data_header': data_header})
+    data_header_table = generate_html_header_table()
+
+    return jsonify({'data_header': data_header, 'data_header_table': data_header_table})
+
+
+@app.route('/reset-data-header-table', methods=['POST'])
+def reset_data_header_table():
+    session['data_header'] = {'event':[], 'measurement':[], 'segment':[], 'segmentData':[]}
+    data_header_table = generate_html_header_table()
+
+    return jsonify({'data_header_table': data_header_table})
+
+
+@app.route('/init-data-header-table', methods=['POST'])
+def init_data_header_table():
+    data_header_table = generate_html_header_table()
+    return jsonify({'data_header_table': data_header_table})
+
+
+@app.route('/delete-data-header', methods=['POST'])
+def delete_data_header():
+    data_header = session.get('data_header', {'event':[], 'measurement':[], 'segment':[], 'segmentData':[]})
+    if data_header == {'event':[], 'measurement':[], 'segment':[], 'segmentData':[]}:
+        data_header_table = generate_html_header_table()
+        return jsonify({'data_header_table': data_header_table})
+    
+    data = request.json
+    str = data['value']
+    res = readHeaderHTML(str)
+    type = res['type']
+
+    if type == 'M':
+        print(data_header['measurement'])
+        data_header['measurement'].remove(res) 
+    elif type == 'E':
+        data_header['event'].remove(res)
+    elif type == 'S':
+        data_header['segment'].remove(res)
+    elif type == 'SD':
+        data_header['segmentData'].remove(res)
+    session['data_header'] = data_header
+
+    data_header_table = generate_html_header_table()
+    return jsonify({'data_header_table': data_header_table})
+
+
+@app.route('/get-MD-info', methods=['POST'])
+def get_MD_info():
+    data = request.json
+    str = data['value']
+    soup = BeautifulSoup(str, 'html.parser')
+    tds = soup.find_all('td')
+    res = readHeaderHTML(str)  # res = {'tablename': '', 'type': '', 'label': [], 'features_name': []}
+    type = res['type']
+    label_column = res['label'][0]
+    session['current_data_header'] = res
+    print(res)
+
+    if type == 'E' or type == 'M':
+        time = getTimeColumns(res['tablename'])           # list
+        object = getObjectColumns(res['tablename'])       # list
+        if res['label'][0] == 'col' and res['label'][1] in object:
+            object.remove(res['label'][1])
+
+        return jsonify({'time': time, 'object': object})
+    elif type == 'S':
+        time = getTimeColumns(res['tablename'])          # list
+        object = getObjectColumns(res['tablename'])      # list
+        # index = 
+        return jsonify({'time': time, 'object': object})
+    elif type == 'SD':
+        time = getTimeColumns(res['tablename'])          # list
+        object = getObjectColumns(res['tablename'])
+        # index = 
+        return jsonify({'time': time, 'object': object})
+
+
+@app.route('/get-ME-table', methods=['POST'])
+def get_ME_table():
+    engine = create_engine(session.get('db_uri', ''))
+    data = request.json
+    current_data_header = session.get('current_data_header', {'tablename': '', 'type': '', 'label': [], 'features_name': []})
+    table_name = current_data_header['tablename']
+    type = current_data_header['type']
+    time_column = data['time_column']
+    object_column = data['object_column']
+    optgroupLabel = data['optgroupLabel']
+    object_list = [optgroupLabel, object_column]
+    label_list = current_data_header['label']
+    features = []
+    for feature in current_data_header['features_name']:
+        if '(' in feature and ')' in feature:         # "value('coarse', 'fine')"
+            feature = feature.replace("'", "")
+            features.append(feature)
+        else:
+            features.append(feature)
+    start_time = datetime.strptime(data['minDatetime'], '%Y-%m-%d %H:%M:%S') if 'minDatetime' in data else None
+    end_time = datetime.strptime(data['maxDatetime'], '%Y-%m-%d %H:%M:%S') if 'maxDatetime' in data else None
+
+    print(table_name)
+    print(type)
+    print(time_column)
+    print(object_list)
+    print(label_list)
+    print(features)
+
+    query_result = extract_ME_table(engine, table_name, type, time_column, object_list, label_list, features, start_time, end_time)
+    table_HTML = get_ME_table_HTML(query_result)
+
+    if start_time == None and end_time == None:
+        min_datetime, max_datetime = get_min_max_datetime(engine, table_name, time_column)
+        return jsonify({'table_HTML': table_HTML, 'min_datetime': min_datetime, 'max_datetime': max_datetime})
+
+    return jsonify({'table_HTML': table_HTML})
+
+
+def readHeaderHTML(str):
+    soup = BeautifulSoup(str, 'html.parser')
+    tds = soup.find_all('td')
+
+    res = {'tablename': '', 'type': '', 'label': [], 'features_name': []}
+    res['tablename'] = tds[1].get_text()
+    res['type'] = tds[2].get_text()
+    res['label'] = json.loads(tds[3]['data-value'])
+    features = []
+    for td in tds[4:]:
+        data_value = td['data-value']
+        value_list = json.loads(data_value)
+        if value_list[0] == '':
+            a = value_list[1]
+            features.append(a)
+        else:
+            a = value_list[0] + '(' 
+            for i in range(len(value_list)-1):
+                a += "'" + value_list[i+1] + "', "
+            a = a[:len(a)-2]
+            a += ')'
+            if a not in features:
+                features.append(a)
+    res['features_name'] = features   # ['machine_id', "value('coarse', 'fine')"]
+
+    print(features)
+
+    return res
 
-def check_json_column(engine, table_name):
+
+def check_json_column(engine, table_name) -> list:
     insp = inspect(engine)
     schema = getTableSchema(table_name) if insp.dialect.name == 'postgresql' else insp.default_schema_name
     json_column_names = []
@@ -297,6 +459,81 @@ def database_name_from_uri(engine, database_uri: str):
         return 'Unknown'
 
 
+def count_feature_columns(data_header) -> int:
+    max_count = 0
+    for key in data_header.keys():
+        for dict_item in data_header[key]:
+            count = 0
+            for value in dict_item.get('features_name', []):
+                num = tuple(value.split('(')[1].split(')')[0].replace("'","").split(', ')) if '(' in value and ')' in value else 1
+                count += len(num) if type(num) == tuple else 1
+            if count > max_count:
+                max_count = count
+
+    return max_count
+
+
+def count_data_header(data_header):
+    count = 0
+    for key in data_header.keys():
+        count += len(data_header[key])
+
+    return count
+
+
+def generate_html_header_table():
+    # session['data_header'] = {'event':[], 'measurement':[], 'segment':[], 'segmentData':[]}
+    content = session.get('data_header', {'event':[], 'measurement':[], 'segment':[], 'segmentData':[]})
+    count_f_columns= count_feature_columns(content)
+    count_d_headers = count_data_header(content)
+
+    # Generate column headers
+    table_html = "<table class='uk-table uk-table-small uk-table-hover uk-table-divider uk-table-striped' style='cursor: pointer;'><thead><tr>"
+    table_html += "<th></th>"
+    table_html += "<th class='uk-table-expand'>Table name</th>"
+    table_html += "<th>Type</th>"
+    table_html += "<th class='uk-table-expand'>Label</th>"
+    for i in range(count_f_columns):
+        table_html += f"<th>F_{i+1}</th>"
+    table_html += "</tr></thead><tbody>"
+
+    # Generate table rows
+    index = 0
+    for key in content.keys():
+        for dict_item in content[key]:
+            table_html += f"<tr onclick='getRowData(this)'>"
+            table_html += "<td><button type='button' class='btn-close'  aria-label='Close' data-bs-dismiss='modal' onclick='deleteRow1(event, this)'></button></td>"
+            table_html += f"<td>{dict_item.get('tablename', '')}</td>"
+            table_html += f"<td data-id>{dict_item.get('type', '')}</td>"
+            print("723723")
+            print(dict_item.get('label', ''))
+            label_value = json.dumps(dict_item.get('label', ''))
+            table_html += f"<td data-value='{label_value}'>{dict_item.get('label', '')[2]}</td>"
+            print("823823")
+            for value in dict_item.get('features_name', []):
+                if '(' in value and ')' in value:
+                    feature_cloumn = value.split('(')[0]
+                    print(feature_cloumn)
+                    multiple_columns = tuple(value.split('(')[1].split(')')[0].replace("'","").split(', '))
+                    print(multiple_columns)
+                    for column in multiple_columns:
+                        print("624624")
+                        tmp = [feature_cloumn]
+                        for col in multiple_columns:
+                            tmp.append(col)
+                            print(tmp)
+                        data_value = json.dumps(tmp)
+                        table_html += f"<td data-value='{data_value}'>{column}</td>"
+                else:
+                    data_value = json.dumps(["", value])
+                    table_html += f"<td data-value='{data_value}'>{value}</td>"
+            index += 1
+            table_html += "</tr>"
+    table_html += "</tbody></table>"
+    print(table_html)
+    return table_html
+
+
 def generate_html_table(content):
     if not content:
         return "No data found."
@@ -318,7 +555,29 @@ def generate_html_table(content):
     return table_html
 
 
-def query_database_for_table_content(engine, table_name, number=20):
+def getTimeColumns(table_name:str) -> list:
+    engine = create_engine(session.get('db_uri', ''))
+    insp = inspect(engine)
+    schema = getTableSchema(table_name) if insp.dialect.name == 'postgresql' else insp.default_schema_name
+    columns = insp.get_columns(table_name, schema)
+    timestamp_columns = [column['name'] for column in columns if str(column['type']) == 'TIMESTAMP']
+    print(timestamp_columns)
+
+    return timestamp_columns
+
+
+def getObjectColumns(table_name:str) -> list:
+    engine = create_engine(session.get('db_uri', ''))
+    insp = inspect(engine)
+    schema = getTableSchema(table_name) if insp.dialect.name == 'postgresql' else insp.default_schema_name
+    columns = insp.get_columns(table_name, schema)
+    object_columns = [column['name'] for column in columns if str(column['type']) == 'VARCHAR' or str(column['type']) == 'INTEGER' or  str(column['type']) == 'NUMERIC']
+    print(object_columns)
+
+    return object_columns
+
+
+def query_database_for_table_content(engine, table_name, number=100):
     # Initialize content list
     content_list = []
     # Create a connection from the engine
@@ -371,7 +630,7 @@ def getSchema(insp):
 
 def getTableInstance(engine, table_name):
     insp = inspect(engine)
-    table = importMetadata(engine, None, [table_name], False)[table_name]
+    table = importMetadata(engine, None, [table_name], True)[table_name]
     return table
 
 
@@ -392,6 +651,144 @@ def showDistinctValues(engine, table_name, column_name):
     return names
 
 
+def get_min_max_datetime(engine, table_name, time_column, start_time=None, end_time=None):
+    schema = getTableSchema(table_name) if engine.dialect.name == 'postgresql' else engine.dialect.default_schema_name
+    # Formulate the SQL query using the text function
+    query = text(f"SELECT MIN({time_column}) AS start_datetime, MAX({time_column}) AS end_datetime FROM {schema}.{table_name};")
+
+    # Execute the query
+    with engine.connect() as connection:
+        row = connection.execute(query).mappings().fetchone()
+
+
+    # Extract the min and max datetime values
+    if row:
+        min_datetime, max_datetime = row['start_datetime'], row['end_datetime']
+        print("Minimum datetime:", min_datetime)
+        print("Maximum datetime:", max_datetime)
+        return min_datetime, max_datetime
+    else:
+        print("No datetimes found.")
+        return None, None
+
+
+
+def extract_ME_table(engine, table_name: str, type: str, time_column: str, object: list, label: list, features_name: list, start_time: datetime = None, end_time: datetime = None) -> list:
+    conn = engine.connect()
+    table_instance = getTableInstance(engine, table_name)
+    label_column = label[1].strip()
+    label_value = label[2].strip()
+    object_column_value = object[1].strip()
+    join_clause = ''
+
+    full_table_name = f"{table_instance.schema}.{table_instance.name}" if table_instance.schema else table_instance.name
+    sql_columns = [f"{full_table_name}.{time_column}"]
+
+    # Handling object_column logic
+    if object[0].strip()  != 'self' and object[0].strip()  == 'col':
+        print("1")
+        object_column = table_instance.getColumn(object_column_value)
+        object_column_name = object_column.fkof.table.name if object_column.fkof else ''
+        if object_column and object_column.fkof and object_column_name in session.get('object_name', {}):
+            related_table_instance = getTableInstance(engine,  object_column_name)
+            full_related_table_name = f"{related_table_instance.schema}.{related_table_instance.name}" if related_table_instance.schema else related_table_instance.name
+            join_clause = f"LEFT JOIN {full_related_table_name} ON {full_table_name}.{object_column.name} = {full_related_table_name}.{object_column.fkof.name}"
+            sql_columns.append(f"{full_related_table_name}.{session.get('object_name').get(object_column_name)} AS {object_column_value}")
+        else:
+            sql_columns.append(f"{full_table_name}.{object_column.name}")
+    print("12")
+    # If label[0] is not 'self', add it to SQL columns
+    if label[0].strip() != 'self':
+        sql_columns.append(f"{full_table_name}.{label_column}")
+    print("123")
+    # Handling JSON extractions
+    json_extractions = []
+    for feature in features_name:
+        if '(' in feature and ')' in feature:
+            column_name, keys = feature[:-1].split('(')
+            keys = keys.split(', ')
+            for key in keys:
+                json_extraction = f"{full_table_name}.{column_name}->>'{key}' AS {key}"
+                json_extractions.append(json_extraction)
+        else:
+            sql_columns.append(f"{full_table_name}.{feature}")
+    print("1234")
+    # Adding JSON extractions to the select clause
+    sql_select = ', '.join(sql_columns + json_extractions)
+    
+    # Constructing SQL query
+    sql_joins = join_clause
+    if label[0].strip() == 'col':
+        sql_where = f"WHERE {full_table_name}.{label_column} = :label_value"
+        if start_time:
+            sql_where += f" AND {full_table_name}.{time_column} >= :start_time"
+        if end_time:
+            sql_where += f" AND {full_table_name}.{time_column} <= :end_time"
+    else:
+        sql_where = ''
+        if start_time:
+            sql_where += f"WHERE {full_table_name}.{time_column} >= :start_time"
+        if end_time:
+            sql_where += f" AND {full_table_name}.{time_column} <= :end_time"
+
+    sql_query = f"SELECT {sql_select} FROM {full_table_name} {sql_joins} {sql_where} ORDER BY {time_column} ASC LIMIT 500"
+    print("12345")
+    # Executing the query
+    params = {'label_value': label_value}
+    if start_time:
+        params['start_time'] = start_time
+    if end_time:
+        params['end_time'] = end_time
+
+
+    # res = conn.execute(text(sql_query), params).fetchall()
+        
+    # Print the query for debugging
+    print("SQL Query:", sql_query)
+    print("Parameters:", params)
+    
+    # Executing the query
+    try:
+        res = conn.execute(text(sql_query), params).fetchall()
+    except Exception as e:
+        print(f"Error executing query: {e}")
+        return []
+
+    # Append object and label values if necessary
+    final_res = []
+    for row in res:
+        modified_row = list(row)
+        if object[0].strip()  == 'self':
+            modified_row.insert(1, object_column_value)
+        if label[0].strip()  == 'self':
+            label_index = 2 if object[0].strip()  != 'self' else 1
+            modified_row.insert(label_index, label[2])
+        modified_row.insert(2, type)
+        final_res.append(modified_row)
+
+    for row in final_res:
+        print(row)
+
+    return final_res
+
+
+def get_ME_table_HTML(data: list) -> str:
+    # Start the HTML <body> content with an opening <table> tag
+    html_content = ""
+    
+    # Iterate over the data to populate the table rows
+    for row in data:
+        html_content += "<tr><td><input class='uk-checkbox' type='checkbox' aria-label='Checkbox'></td>"
+        for cell in row:
+            if isinstance(cell, datetime):
+                # cell = cell.isoformat()
+                cell = cell.strftime('%Y-%m-%d %H:%M:%S:%f') 
+            html_content += f"<td>{cell}</td>"
+        html_content += "</tr>\n"
+
+    return html_content
+
+
 def importMetadata(engine, schema=None, tables_selected=None, show_all=False):
     tables = {}
     if engine == None:
@@ -643,540 +1040,4 @@ def getThemes():
 
 
 if __name__ == "__main__":
-    app.run(debug=True)
-
-
-
-
-
-
-
-
-# from my_flask_app import app
-# from flask_sqlalchemy import SQLAlchemy
-# from flask import jsonify, render_template, request, session, send_file
-# from sqlalchemy import text
-# import re, pydot, base64
-# import pydot
-
-
-# # Set up database
-# app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
-# app.config['SQLALCHEMY_DATABASE_URI'] = "postgresql://postgres:password@localhost:5432/test"
- 
-# db = SQLAlchemy() 
-# db.init_app(app)
-
-# app.secret_key = 'my_secret_key'  # Needed for session management
-# dropped_items = []
-
-# @app.route('/', methods=['POST', 'GET'])
-# def index():
-#     # Initialize variables
-#     database = db.engine.url.database
-#     tables_selected = []
-#     # tables_selected = ['machine_sensor', 'machine_tool', 'machine_trace']
-#     schemas = getSchema()
-#     themes = getThemes()
-
-#     schema_Selected = request.form.get('schema', None)
-#     show_all = request.form.get('show_all') == 'True'
-
-#     tables1 = importMetadata(database, schema_Selected, tables_selected, show_all)
-#     graph_DOT1 = createGraph(tables1, themes["Blue Navy"], True, True, True)
-#     image1 = generate_erd(graph_DOT1)
-
-#     if dropped_items==[]:
-#         image2 = ""
-#     else:
-#         tables2 = importMetadata(database, None, dropped_items, False)
-#         graph_DOT2 = createGraph(tables2, themes["Blue Navy"], True, True, True)
-#         image2 = generate_erd(graph_DOT2)
-
-#     print(getTableSchema('event_data'))
-     
-#     return render_template('app.html', database=database, schemas=schemas, show_all=show_all, schema_Selected=schema_Selected, tables=tables1, image1=image1, image2=image2, dropped_items=dropped_items)
-
-
-
-# @app.route('/handle-drop', methods=['POST'])
-# def handle_drop():
-#     data = request.json
-#     item_name = data.get('item')
-#     action = data.get('action')
-
-#     if action == 'added':
-#         dropped_items.append(item_name)
-#     elif action == 'removed' and item_name in dropped_items:
-#         dropped_items.remove(item_name)
-
-#     # Regenerate ERD based on the updated dropped_items
-#     database = db.engine.url.database
-#     themes = getThemes()
-#     tables2 = importMetadata(database, None, dropped_items, False)
-#     graph_DOT2 = createGraph(tables2, themes["Blue Navy"], True, True, True)
-#     image2 = generate_erd(graph_DOT2)
-
-#     return jsonify(image2=image2)
-
-
-# @app.route('/get-table-data', methods=['POST'])
-# def get_table_data():
-#     data = request.json
-#     table_name = data.get('table_name')
-#     print(table_name)
-
-#     # Query your database to get the data for the table_name
-#     content = query_database_for_table_content(table_name)
-
-#     # Convert content to HTML table format
-#     html_table = generate_html_table(content)
-
-#     return jsonify({'html_table': html_table})
-
-
-# def generate_html_table(content):
-#     if not content:
-#         return "No data found."
-#     # Generate column headers
-#     columns = content[0]
-#     table_html = "<table class='uk-table uk-table-small uk-table-hover uk-table-divider'><thead><tr>"
-#     for col in columns:
-#         table_html += f"<th>{col}</th>"
-
-#     table_html += "</tr></thead><tbody>"
-
-#     # Generate table rows
-#     for i in range(1, len(content)): 
-#         table_html += "<tr>"
-#         for item in content[i]:
-#             table_html += f"<td>{item}</td>"
-#         table_html += "</tr>"
-#     table_html += "</tbody></table>"
-#     return table_html
-
-
-# def query_database_for_table_content(table_name, number=20):
-#     # Initialize content list
-#     content_list = []
-
-#     # Get the schema of the table
-#     schema = getTableSchema(table_name)
-#     # Query the database to get the content of the table
-#     sql_content = text(f"""SELECT * FROM {schema}.{table_name} LIMIT {number};""")
-#     result = db.session.execute(sql_content, {'table_name': table_name, 'number': number}).fetchall()
-    
-#     if not result:
-#         return []
-    
-#     # Get the column names
-#     sql_columns = text("""
-#             SELECT column_name
-#             FROM information_schema.columns
-#             WHERE table_name = :table_name;
-#         """)
-#     column_names = db.session.execute(sql_columns, {'table_name': table_name}).fetchall()
-
-#     # Prepare column names
-#     columns = [column_name[0] for column_name in column_names]
-#     content_list.append(columns)
-
-#     # Append rows to content list
-#     for row in result:
-#         content_list.append(list(row))
-
-#     return content_list
-
-
-# def getTableSchema(table_name):
-#     sql= text(f"""
-#         SELECT table_schema 
-#         FROM information_schema.tables 
-#         WHERE table_name = :table_name;
-#     """)
-#     schema = db.session.execute(sql, {'table_name': table_name}).fetchone()[0]
-#     return schema
-
-
-# def getSchema():
-#     sql = text("""SELECT schema_name FROM information_schema.schemata;""")
-#     result = db.session.execute(sql)
-#     schemas = [row[0] for row in result]
-#     return schemas
-
-
-# def importMetadata(database, schema=None, tables_selected=None, show_all=False):
-#     tables = {}
-#     if database == '':
-#         return tables
-
-#     # Convert tables_selected to a list to ensure compatibility with SQL IN operation.
-#     tables_selected_list = list(tables_selected) if tables_selected else None
-
-#     # Fetch initial tables based on schema and table_names.
-#     tables = fetch_initial_tables(schema, tables_selected_list)
-
-#     # If show_all is True, expand the list to include related tables.
-#     if show_all:
-#         expand_to_include_related_tables(tables)
-
-#     # Fetch columns for each table.
-#     fetch_columns_for_tables(tables)
-
-#     # Fetch constraints (PK, FK, Unique) for each table.
-#     fetch_constraints_for_tables(tables)
-
-#     return tables
-
-
-# def fetch_initial_tables(schema, tables_selected_list):
-#     tables = {}
-
-#     # Construct WHERE clauses based on input parameters.
-#     schema_condition = "AND table_schema = :schema" if schema else ""
-#     tables_selected_condition = "AND table_name = ANY(:tables_selected)" if tables_selected_list else ""
-
-#     # Fetching tables with dynamic schema and table name filtering.
-#     sql_tables = text(f"""
-#         SELECT table_name, table_schema
-#         FROM information_schema.tables
-#         WHERE table_type = 'BASE TABLE'
-#         {schema_condition} {tables_selected_condition};
-#     """)
-    
-#     # Adjust parameters based on the conditions.
-#     params = {}
-#     if schema:
-#         params['schema'] = schema
-#     if tables_selected_list:
-#         params['tables_selected'] = tables_selected_list
-
-#     result = db.session.execute(sql_tables, params)
-
-#     for row in result:
-#         tableName, tableSchema = row
-#         table = Table(tableName, tableSchema)   # adding schema to the table comment
-#         tables[tableName] = table
-#         table.label = f"n{len(tables)}"
-
-#     return tables
-
-
-# def expand_to_include_related_tables(tables):
-#     # This dictionary will temporarily store related tables to fetch later.
-#     related_tables_to_fetch = {}
-
-#     # Iterate over initially fetched tables to find foreign key relationships.
-#     for tableName, table in tables.items():
-#         # Fetch foreign key relationships for the current table.
-#         sql_fk = text("""
-#             SELECT 
-#                 ccu.table_name AS pk_table_name, 
-#                 ccu.table_schema AS pk_table_schema
-#             FROM 
-#                 information_schema.table_constraints AS tc 
-#                 JOIN information_schema.key_column_usage AS kcu 
-#                 ON tc.constraint_name = kcu.constraint_name AND tc.table_schema = kcu.table_schema AND tc.table_name = kcu.table_name
-#                 JOIN information_schema.constraint_column_usage AS ccu 
-#                 ON ccu.constraint_name = tc.constraint_name
-#             WHERE 
-#                 tc.constraint_type = 'FOREIGN KEY' 
-#                 AND tc.table_name = :table_name
-#         """)
-#         fk_result = db.session.execute(sql_fk, {'table_name': tableName})
-
-        
-#         for referenced_table_name, referenced_schema in fk_result:
-#             if referenced_table_name not in tables and referenced_table_name not in related_tables_to_fetch:
-#                 related_tables_to_fetch[referenced_table_name] = referenced_schema
-
-#     # Fetch and add related tables.
-#     for tableName, tableSchema in related_tables_to_fetch.items():
-#         # Assuming a function fetch_table_details(tableName, tableSchema) that fetches and returns
-#         # a Table object with columns and constraints populated.
-#         table = Table(tableName, tableSchema)
-#         tables[tableName] = table
-
-#     return tables
-
-
-# def fetch_columns_for_tables(tables):
-#     for tableName, table in tables.items():
-#         sql_columns = text("""
-#             SELECT column_name, data_type, is_nullable, column_default
-#             FROM information_schema.columns
-#             WHERE table_name = :table_name;
-#         """)
-#         column_result = db.session.execute(sql_columns, {'table_name': tableName})
-
-#         for col in column_result:
-#             name, datatype, nullable, default = col
-#             column = Column(table, name, '')
-#             column.setDataType({
-#                 "type": datatype,
-#                 "nullable": nullable == 'YES',
-#                 "default": default
-#             })
-#             table.columns.append(column)
-#     return tables  
-
-
-# def fetch_constraints_for_tables(tables):
-#     # Fetching Unique Constraints
-#     for tableName, table in tables.items():
-#         sql_unique = text("""
-#             SELECT kcu.column_name, tc.constraint_name
-#             FROM information_schema.table_constraints AS tc
-#             JOIN information_schema.key_column_usage AS kcu ON tc.constraint_name = kcu.constraint_name
-#             WHERE tc.table_name = :table_name AND tc.constraint_type = 'UNIQUE';
-#         """)
-#         unique_result = db.session.execute(sql_unique, {'table_name': tableName})
-#         for col in unique_result:
-#             name, constraintName = col
-#             column = table.getColumn(name)
-#             if column:
-#                 column.isunique = True
-#                 if constraintName not in table.uniques:
-#                     table.uniques[constraintName] = []
-#                 table.uniques[constraintName].append(column)
-
-
-#     # Primary Keys
-#     for tableName, table in tables.items():
-#         sql_pk = text("""
-#             SELECT kcu.column_name, tc.constraint_name, kcu.ordinal_position
-#             FROM information_schema.table_constraints AS tc
-#             JOIN information_schema.key_column_usage AS kcu ON tc.constraint_name = kcu.constraint_name
-#             WHERE tc.table_name = :table_name AND tc.constraint_type = 'PRIMARY KEY';
-#         """)
-#         pk_result = db.session.execute(sql_pk, {'table_name': tableName})
-#         for col in pk_result:
-#             name, constraintName, ordinal_position = col
-#             column = table.getColumn(name)
-#             if column:
-#                 column.ispk = True
-#                 column.pkconstraint = constraintName
-#                 # Assuming you want to order PKs, though not directly used in provided class
-
-
-#     # Fetching Foreign Keys for each table
-#     for tableName, table in tables.items():
-#         sql_fk = text("""
-#             SELECT 
-#                 tc.constraint_name, 
-#                 tc.table_name AS fk_table_name, 
-#                 kcu.column_name AS fk_column_name, 
-#                 ccu.table_name AS pk_table_name, 
-#                 ccu.column_name AS pk_column_name,
-#                 ccu.table_schema AS pk_table_schema
-#             FROM 
-#                 information_schema.table_constraints AS tc 
-#                 JOIN information_schema.key_column_usage AS kcu 
-#                 ON tc.constraint_name = kcu.constraint_name AND tc.table_schema = kcu.table_schema AND tc.table_name = kcu.table_name
-#                 JOIN information_schema.constraint_column_usage AS ccu 
-#                 ON ccu.constraint_name = tc.constraint_name
-#             WHERE 
-#                 tc.constraint_type = 'FOREIGN KEY' 
-#                 AND tc.table_name = :table_name
-#         """)
-
-#         fk_result = db.session.execute(sql_fk, {'table_name': tableName})
-#         for row in fk_result:
-#             constraintName, fkTableName, fkColumnName, pkTableName, pkColumnName, pkTableSchema = row
-
-#             # Ensure the foreign key table is the current table being processed
-#             if fkTableName != tableName:
-#                 continue
-
-
-#             fkTable = tables.get(fkTableName)
-#             pkTable = tables.get(pkTableName)
-
-#             if fkTable and pkTable:
-#                 fkColumn = fkTable.getColumn(fkColumnName)
-#                 pkColumn = pkTable.getColumn(pkColumnName)
-
-#                 if fkColumn and pkColumn:
-#                     # Here, instead of assigning pkColumn directly, store relevant info
-#                     fkColumn.fkof = pkColumn  # Adjust based on your application's needs
-#                     if constraintName not in fkTable.fks:
-#                         fkTable.fks[constraintName] = []
-#                     fkTable.fks[constraintName].append(fkColumn)
-#     return tables
-
-
-# def createGraph(tables, theme, showColumns, showTypes, useUpperCase):
-#     s = ('digraph {\n'
-#         + '  graph [ rankdir="LR" bgcolor="#ffffff" ]\n'
-#         + f'  node [ style="filled" shape="{theme.shape}" gradientangle="180" ]\n'
-#         + '  edge [ arrowhead="none" arrowtail="none" dir="both" ]\n\n')
-
-#     for name in tables:
-#         s += tables[name].getDotShape(theme, showColumns, showTypes, useUpperCase)
-#     s += "\n"
-#     for name in tables:
-#         s += tables[name].getDotLinks(theme)
-#     s += "}\n"
-#     return s
-
-
-# def generate_erd(graph_DOT):
-#     graph_module = pydot.graph_from_dot_data(graph_DOT)
-#     graph = graph_module[0]
-#     png_image_data = graph.create_png()
-#     encoded_image = base64.b64encode(png_image_data).decode('utf-8')
-#     return encoded_image
-
-
-# def getThemes():
-#     return {
-#         "Common Gray": Theme("#6c6c6c", "#e0e0e0", "#f5f5f5",
-#             "#e0e0e0", "#000000", "#000000", "rounded", "Mrecord", "#696969", "1"),
-#         "Blue Navy": Theme("#1a5282", "#1a5282", "#ffffff",
-#             "#1a5282", "#000000", "#ffffff", "rounded", "Mrecord", "#0078d7", "2"),
-#         #"Gradient Green": Theme("#716f64", "#008080:#ffffff", "#008080:#ffffff",
-#         #    "transparent", "#000000", "#000000", "rounded", "Mrecord", "#696969", "1"),
-#         #"Blue Sky": Theme("#716f64", "#d3dcef:#ffffff", "#d3dcef:#ffffff",
-#         #    "transparent", "#000000", "#000000", "rounded", "Mrecord", "#696969", "1"),
-#         "Common Gray Box": Theme("#6c6c6c", "#e0e0e0", "#f5f5f5",
-#             "#e0e0e0", "#000000", "#000000", "rounded", "record", "#696969", "1")
-#     }
-
-
-# if __name__ == "__main__":
-#     app.run(port=5001)
-
-
-# class Theme:
-#     def __init__(self, color, fillcolor, fillcolorC,
-#             bgcolor, icolor, tcolor, style, shape, pencolor, penwidth):
-#         self.color = color
-#         self.fillcolor = fillcolor
-#         self.fillcolorC = fillcolorC
-#         self.bgcolor = bgcolor
-#         self.icolor = icolor
-#         self.tcolor = tcolor
-#         self.style = style
-#         self.shape = shape
-#         self.pencolor = pencolor
-#         self.penwidth = penwidth
-
-
-# class Table:
-#     def __init__(self, name, comment):
-#         self.name = name
-#         self.comment = comment if comment is not None and comment != 'None' else ''
-#         self.label = None
-
-#         self.columns = []           # list of all columns
-#         self.uniques = {}           # dictionary with UNIQUE constraints, by name + list of columns
-#         self.pks = []               # list of PK columns (if any)
-#         self.fks = {}               # dictionary with FK constraints, by name + list of FK columns
-
-
-#     @classmethod
-#     def getClassName(cls, name, useUpperCase, withQuotes=True):
-#         if re.match("^[A-Z_0-9]*$", name) == None:
-#             return f'"{name}"' if withQuotes else name
-#         return name.upper() if useUpperCase else name.lower()
-
-#     def getName(self, useUpperCase, withQuotes=True):
-#         return Table.getClassName(self.name, useUpperCase, withQuotes)
-
-#     def getColumn(self, name):
-#         for column in self.columns:
-#             if column.name == name:
-#                 return column
-#         return None
-
-#     def getDotShape(self, theme, showColumns, showTypes, useUpperCase):
-#         fillcolor = theme.fillcolorC if showColumns else theme.fillcolor
-#         colspan = "2" if showTypes else "1"
-#         tableName = self.getName(useUpperCase, False)
-
-#         s = (f'  {self.label} [\n'
-#             + f'    fillcolor="{fillcolor}" color="{theme.color}" penwidth="1"\n'
-#             + f'    label=<<table style="{theme.style}" border="0" cellborder="0" cellspacing="0" cellpadding="1">\n'
-#             + f'      <tr><td bgcolor="{theme.bgcolor}" align="center"'
-#             + f' colspan="{colspan}"><font color="{theme.tcolor}"><b>{tableName}</b></font></td></tr>\n')
-
-#         if showColumns:
-#             for column in self.columns:
-#                 name = column.getName(useUpperCase, False)
-#                 if column.ispk: name = f"<u>{name}</u>"
-#                 if column.fkof != None: name = f"<i>{name}</i>"
-#                 if column.nullable: name = f"{name}*"
-#                 if column.identity: name = f"{name} I"
-#                 if column.isunique: name = f"{name} U"
-#                 datatype = column.datatype
-#                 if useUpperCase: datatype = datatype.upper()
-
-#                 if showTypes:
-#                     s += (f'      <tr><td align="left"><font color="{theme.icolor}">{name}&nbsp;</font></td>\n'
-#                         + f'        <td align="left"><font color="{theme.icolor}">{datatype}</font></td></tr>\n')
-#                 else:
-#                     s += f'      <tr><td align="left"><font color="{theme.icolor}">{name}</font></td></tr>\n'
-
-#         return s + '    </table>>\n  ]\n'
-
-
-#     def getDotLinks(self, theme):
-#         s = ""
-#         for constraint in self.fks:
-#             fks = self.fks[constraint]
-#             fk1 = fks[0]
-#             dashed = "" if not fk1.nullable else ' style="dashed"'
-#             arrow = "" if fk1.ispk and len(self.pks) == len(fk1.fkof.table.pks) else ' arrowtail="crow"'
-#             s += (f'  {self.label} -> {fk1.fkof.table.label}'
-#                 + f' [ penwidth="{theme.penwidth}" color="{theme.pencolor}"{dashed}{arrow} ]\n')
-#         return s
-    
-
-# class Column:
-#     def __init__(self, table, name, comment):
-#         self.table = table
-#         self.name = name
-#         self.comment = comment if comment is not None and comment != 'None' else ''
-#         self.nullable = True
-#         self.datatype = None        # with (length, or precision/scale)
-#         self.identity = False
-
-#         self.isunique = False
-#         self.ispk = False
-#         self.pkconstraint = None
-#         self.fkof = None            # points to the PK column on the other side
-
-
-#     def getName(self, useUpperCase, withQuotes=True):
-#         return Table.getClassName(self.name, useUpperCase, withQuotes)
-
-
-#     def setDataType(self, datatype):
-#         self.datatype = datatype["type"]
-#         self.nullable = bool(datatype["nullable"])
-
-#         if self.datatype == "FIXED":
-#             self.datatype = "NUMBER"
-#         elif "fixed" in datatype:
-#             fixed = bool(datatype["fixed"])
-#             if self.datatype == "TEXT":
-#                 self.datatype = "CHAR" if fixed else "VARCHAR"
-
-#         if "length" in datatype:
-#             self.datatype += f"({str(datatype['length'])})"
-#         elif "scale" in datatype:
-#             if int(datatype['precision']) == 0:
-#                 self.datatype += f"({str(datatype['scale'])})"
-#                 if self.datatype == "TIMESTAMP_NTZ(9)":
-#                     self.datatype = "TIMESTAMP"
-#             elif "scale" in datatype and int(datatype['scale']) == 0:
-#                 self.datatype += f"({str(datatype['precision'])})"
-#                 if self.datatype == "NUMBER(38)":
-#                     self.datatype = "INT"
-#                 elif self.datatype.startswith("NUMBER("):
-#                     self.datatype = f"INT({str(datatype['precision'])})"
-#             elif "scale" in datatype:
-#                 self.datatype += f"({str(datatype['precision'])},{str(datatype['scale'])})"
-#                 #if column.datatype.startswith("NUMBER("):
-#                 #    column.datatype = f"FLOAT({str(datatype['precision'])},{str(datatype['scale'])})"
-#         self.datatype = self.datatype.lower()
\ No newline at end of file
+    app.run(debug=True)
\ No newline at end of file
diff --git a/my_flask_app/models/__pycache__/models.cpython-39.pyc b/my_flask_app/models/__pycache__/models.cpython-39.pyc
index c934da4ed83aa67dbcb94493d6d76507c6c13579..85c8c24503febc2cee3793115e586cf4f4bf73ef 100644
GIT binary patch
delta 3086
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
T<&_pt;N$0u;qw(>5nurTpF3Al

delta 3064
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

diff --git a/my_flask_app/models/models.py b/my_flask_app/models/models.py
index f19dfef..f01eeb3 100644
--- a/my_flask_app/models/models.py
+++ b/my_flask_app/models/models.py
@@ -4,9 +4,10 @@ import re
 
 
 class Observation_Spec:
-    def __init__(self, type:str, label:str, features_name:list = None):
+    def __init__(self, tablename:str, type:str, label:list[str], features_name:list = None):
+        self.tablename = tablename
         self.type = type    # 'event' or 'measurement'
-        self.label = label
+        self.label = label  # (self-define or from column, column name, column value orself-define label)
         self.features_name = features_name if features_name else []
 
     def add_feature(self, name):
@@ -17,6 +18,7 @@ class Observation_Spec:
 
     def to_dict(self):
         return {
+            'tablename': self.tablename,
             'type': self.type,
             'label': self.label,
             'features_name': self.features_name
diff --git a/my_flask_app/templates/app.html b/my_flask_app/templates/app.html
index 0d04707..d22226f 100644
--- a/my_flask_app/templates/app.html
+++ b/my_flask_app/templates/app.html
@@ -106,6 +106,8 @@
             padding: 0;
             margin-top: 0px;
             border: none;
+            border-top-right-radius: 5px;
+            border-bottom-right-radius: 5px;
         }
         .custom-zoom-button {
             font-size: 14px;
@@ -115,6 +117,7 @@
             background-color: rgba(173, 216, 230, 0.5);
             padding: 0;
             border: none; /* Remove border to make it seamless */
+            border-radius: 5px;
         }
         button { width: 80%; }
         .headerButton {
@@ -279,6 +282,7 @@
             position: sticky;
             top: 0;
         }
+
     </style>
 </head>
 <body>
@@ -487,6 +491,13 @@
                             </div>
                         </div>
 
+
+                        <div class="mb-3" id="self-defined-object-selection" style="display: none; border-bottom: 1px dashed black;">
+                            <label class="form-label">Self-defined Object</label><br>
+                            <input type="text" id="defined-object" class="form-control headerSelect">
+                            <button class="btn btn-primary headerButton" onclick="selfdefinedObject()">add</button>
+                        </div>
+
                         <div class="mb-3" id="object-column-selection" style="display: none;">
                             <label class="form-label">Object name</label><br>
                             <select id="select_column_object" class="form-select headerSelect" name="column" style="margin-right: 5px; display: inline;">
@@ -512,7 +523,7 @@
                             {% endfor %}
                             </select>
 
-                            <select id=label_values class="form-select headerSelect " style="margin-top: 2px; margin-right: 5px; display: inline;">
+                            <select id='label_values' class="form-select headerSelect " style="margin-top: 2px; margin-right: 5px; display: inline;">
                                 <option>Label select...</option>
                                 <optgroup id="defined_label_values" label="self-defined label">
                                 {% for self_label in self_labels %}
@@ -550,87 +561,88 @@
 
             <li>
                 <div style="display: flex; flex-direction: column; height: 87vh;">
-                    <div class="table-container" style="flex: 1; overflow-y: auto;">
-                        <table class="uk-table uk-table-striped" style="overflow-y: scroll;">
+                    <div class="mb-3">
+                        <h4 style="display: inline; margin-left: .2em;">Data Header Table</h4>
+                        <button type="submit" class="btn btn-primary uk-button-small headerButton" style="margin-top: -3px;" onclick="resetDataHeaderTable()">Reset</button>
+                    </div>
+                    <div class="uk-overflow-auto" id="data-header-table" style="flex-grow: 1; overflow-y: auto; max-height: max-content; margin-top: -20px;">
+                        <table class='uk-table uk-table-small uk-table-hover uk-table-divider'>
                             <thead>
                                 <tr>
-                                    <th>Table Heading</th>
-                                    <th>Table Heading</th>
-                                    <th>Table Heading</th>
+                                    <th></th>
+                                    <th>Table name</th>
+                                    <th>Type</th>
+                                    <th>Label</th>
                                 </tr>
                             </thead>
-                            <tbody>
-                                <tr>
-                                    <td>Table Data</td>
-                                    <td>Table Data</td>
-                                    <td>Table Data</td>
-                                </tr>
-                                <tr>
-                                    <td>Table Data</td>
-                                    <td>Table Data</td>
-                                    <td>Table Data</td>
-                                </tr>
-                                <tr>
-                                    <td>Table Data</td>
-                                    <td>Table Data</td>
-                                    <td>Table Data</td>
+                        </table>
+                    </div>
+                    <div class="mb-3">
+                        <h4 style="display: inline; margin-left: .2em;">Machine Data Table (LIMIT 500)</h4>
+                        <button type="submit" class="btn btn-primary uk-button-small headerButton" style="margin-top: -3px;" onclick="resetMachineDataTable()">Reset</button>
+                    </div>
+                    <div class="uk-overflow-auto" id="machine-data-table" style="flex-grow: 1; overflow-y: auto; max-height: max-content; margin-top: -20px;" >
+                        <table  id="MD-table" class="uk-table uk-table-hover uk-table-small uk-table-middle uk-table-divider uk-table-striped" style="cursor: pointer;">
+                            <thead>
+                                <tr class="uk-table-middle">
+                                    <th class="uk-table-shrink">
+                                        <input class="uk-checkbox" onclick="click_all_MD_table(this)" type="checkbox" aria-label="Checkbox" style="margin-top: 7px;">
+                                    </th>
+
+                                    <th data-id="time" class="uk-table-expand">
+                                        <span style="display: flex; flex-direction: row; margin-bottom: -4px;">
+                                            time
+                                            <select id="table_select_time"  class="time-object-select" style="width: 80px; height: 20px; margin-top: -3px; margin-left: 3px;">
+                                                <option>Select a data header first...</option>
+                                            </select>
+                                        </span>
+                                    </th>
+
+                                    <th data-id="object" class="uk-table-expand">
+                                        <span style="display: flex; flex-direction: row; margin-bottom: -4px;">
+                                            object
+                                            <select id="table_object" class="time-object-select" style="width: 80px; height: 20px; margin-top: -3px; margin-left: 3px;">
+                                                <option value="no">Select a data header first...</option>
+                                                
+                                                <optgroup id="defined_object_values" label="self-defined object">
+                                                </optgroup>
+
+                                                <optgroup id="table_select_object" label="object column">             
+                                                </optgroup >
+                                            </select>
+                                            
+                                        </span>
+                                    </th>
+
+                                    <th data-id="type" class="uk-width-small">type</th>
+                                    <th data-id="label" class="uk-table-expand">label</th>
+                                    <th data-id="segment" class="uk-table-expand">segment</th>
+                                    <th data-id="index" class="uk-table-expand">index</th>
+                                    
+                                    <th data-id="starttime" class="uk-table-expand">
+                                        <span style="display: flex; flex-direction: row; margin-bottom: -4px;">
+                                            start time
+                                            <select id="starttime-select" style="width: 80px; height: 20px; margin-top: -3px; margin-left: 3px;">
+                                                <option val="no">Select a column first...</option>
+                                            </select>
+                                        </span>
+                                    </th>
+
+                                    <th data-id="endtime" class="uk-table-expand">
+                                        <span style="display: flex; flex-direction: row; margin-bottom: -4px;">
+                                            end time
+                                            <select id="endtime-select" style="width: 80px; height: 20px; margin-top: -3px; margin-left: 3px;">
+                                                <option val="no">Select a column first...</option>
+                                            </select>
+                                        </span>
+                                    </th>
                                 </tr>
+                            </thead>
+                            <tbody>
+                                
                             </tbody>
                         </table>
                     </div>
-                    <div class="table-container" style="flex: 1; overflow-y: auto;">
-                        <div class="uk-overflow-auto">
-                            <table class="uk-table uk-table-hover uk-table-middle uk-table-divider" style="overflow-y: scroll;">
-                                <thead>
-                                    <tr>
-                                        <th class="uk-table-shrink"></th>
-                                        <th class="uk-table-shrink">Preserve</th>
-                                        <th class="uk-table-expand">Expand + Link</th>
-                                        <th class="uk-width-small">Truncate</th>
-                                        <th class="uk-table-shrink uk-text-nowrap">Shrink + Nowrap</th>
-                                    </tr>
-                                </thead>
-                                <tbody>
-                                    <tr>
-                                        <td><input class="uk-checkbox" type="checkbox" aria-label="Checkbox"></td>
-                                        <td><img class="uk-preserve-width uk-border-circle" src="images/avatar.jpg" width="40" height="40" alt=""></td>
-                                        <td class="uk-table-link">
-                                            <a class="uk-link-reset" href="">Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor.</a>
-                                        </td>
-                                        <td class="uk-text-truncate">Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor.</td>
-                                        <td class="uk-text-nowrap">Lorem ipsum dolor</td>
-                                    </tr>
-                                    <tr>
-                                        <td><input class="uk-checkbox" type="checkbox" aria-label="Checkbox"></td>
-                                        <td><img class="uk-preserve-width uk-border-circle" src="images/avatar.jpg" width="40" height="40" alt=""></td>
-                                        <td class="uk-table-link">
-                                            <a class="uk-link-reset" href="">Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor.</a>
-                                        </td>
-                                        <td class="uk-text-truncate">Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor.</td>
-                                        <td class="uk-text-nowrap">Lorem ipsum dolor</td>
-                                    </tr>
-                                    <tr>
-                                        <td><input class="uk-checkbox" type="checkbox" aria-label="Checkbox"></td>
-                                        <td><img class="uk-preserve-width uk-border-circle" src="images/avatar.jpg" width="40" height="40" alt=""></td>
-                                        <td class="uk-table-link">
-                                            <a class="uk-link-reset" href="">Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor.</a>
-                                        </td>
-                                        <td class="uk-text-truncate">Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor.</td>
-                                        <td class="uk-text-nowrap">Lorem ipsum dolor</td>
-                                    </tr>
-                                    <tr>
-                                        <td><input class="uk-checkbox" type="checkbox" aria-label="Checkbox"></td>
-                                        <td><img class="uk-preserve-width uk-border-circle" src="images/avatar.jpg" width="40" height="40" alt=""></td>
-                                        <td class="uk-table-link">
-                                            <a class="uk-link-reset" href="">Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor.</a>
-                                        </td>
-                                        <td class="uk-text-truncate">Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor.</td>
-                                        <td class="uk-text-nowrap">Lorem ipsum dolor</td>
-                                    </tr>
-                                </tbody>
-                            </table>
-                        </div>
-                    </div>
                 </div>
             </li>
         </ul>
@@ -655,32 +667,109 @@
 
     <div id="sidebar2">
         <ul uk-accordion>
-            <li class="uk-open">
-                <a class="uk-accordion-title" href="#">Item 1</a>
-                <div class="uk-accordion-content">
-                    <p>Content for item 1.</p>
-                </div>
-            </li>
             <li>
-                <a class="uk-accordion-title" href="#">Item 2</a>
+                <a class="uk-accordion-title" href="#">Filter</a>
                 <div class="uk-accordion-content">
-                    <p>Content for item 2.</p>
+
+                    <div class="accordion">
+                        <div class="accordion-item">
+                          <h2 class="accordion-header">
+                            <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapseTwo" aria-expanded="true" aria-controls="collapseTwo">
+                              Time
+                            </button>
+                          </h2>
+                          <div id="collapseTwo" class="accordion-collapse collapse" data-bs-parent="#accordionExample">
+                            <div class="accordion-body">
+                                <link rel="stylesheet" href="//code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css">
+                                <script src="//code.jquery.com/jquery-3.6.0.min.js"></script>
+                                <script src="//code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
+
+                                <p style="margin: -4px -21px 5px -17px; padding: 0px;">
+                                    <label for="amount1">From: </label><br>
+                                    <input type="text" id="amount1" class="form-control" style="display: flex; border: 0; color: #5ea9e2; font-weight:bold;"><br>
+                                    <label for="amount2">Until: </label><br>
+                                    <input type="text" id="amount2" class="form-control" style="border:0; color: #5ea9e2; font-weight:bold;">
+                                </p>
+                                <div id="slider-range"></div>
+                                <button type="submit" class="btn btn-primary headerButton" style="margin: 10px 0px -5px -10px;" onclick="filter_ME_data_table()">Submit</button>
+                            </div>
+                          </div>
+                        </div>
+                    </div>
+        
                 </div>
             </li>
         </ul>
 
-        <!-- set_database.html
-        <form method="post" action="/">
-            <div class="mb-3">
-                <label for="exampleInputPassword1" name="database_uri" class="form-label">Database URI</label>
-                <input type="text" class="form-control" id="exampleInputPassword1" name="database_uri" placeholder="Enter Database URI">
-                <button type="submit" class="btn btn-primary">Submit</button>
-            </div>
-        </form> -->
 
+        
         <div id="resize-handle-left" class="resize-handle-left"></div>
     </div> 
+
     <script>
+        var jq = jQuery.noConflict();
+        function initializeSlider(minDatetime, maxDatetime) {
+            // Default values if min or max datetime is not provided
+            var defaultMinDatetime = '2022-11-01 16:00:00.000000+00:00';
+            var defaultMaxDatetime = '2022-11-02 16:00:00.000000+00:00';
+
+            // Use default values if min or max datetime is empty
+            minDatetime = minDatetime || defaultMinDatetime;
+            maxDatetime = maxDatetime || defaultMaxDatetime;
+            
+            // Function to parse date string with milliseconds
+            function parseDateTime(str) {
+                var date = new Date(str);
+                if (isNaN(date.getTime())) {
+                    console.error('The datetime format is incorrect:', str);
+                    return null;
+                }
+                return date;
+            }
+
+            function toTimestamp(strDate) {
+                var date = parseDateTime(strDate);
+                return date ? date.getTime() : null;
+            }
+
+
+            // Function to format date to string with milliseconds
+            function formatDateTime(date) {
+                var hours = ('0' + date.getHours()).slice(-2);
+                var minutes = ('0' + date.getMinutes()).slice(-2);
+                var seconds = ('0' + date.getSeconds()).slice(-2);
+
+                return jq.datepicker.formatDate('yy-mm-dd', date) +
+                    ' ' + hours + ':' +
+                    minutes + ':' +
+                    seconds;
+            }
+
+            // Initialize the slider with dynamic datetime values
+            console.log("initialize slider with dynamic datetime values");
+            jq("#slider-range").slider({
+                range: true,
+                min: toTimestamp(minDatetime), // Use minDatetime from the server
+                max: toTimestamp(maxDatetime), // Use maxDatetime from the server
+                step: 1, // Step is now 1 millisecond
+                values: [
+                    toTimestamp(minDatetime), // Set the lower handle to minDatetime
+                    toTimestamp(maxDatetime) // Set the upper handle to maxDatetime
+                ],
+                slide: function(event, ui) {
+                    var startDateTime = new Date(ui.values[0]);
+                    var endDateTime = new Date(ui.values[1]);
+                    jq("#amount1").val(formatDateTime(startDateTime));
+                    jq("#amount2").val(formatDateTime(endDateTime));
+                }
+            });
+        }
+
+
+        
+
+
+
         // function select_schema(){
         //     document.getElementById('schemaSelect').addEventListener('changed', function() {
         //         var selectedSchema = this.value;
@@ -747,20 +836,322 @@
         //     });
         // }
 
+        function click_all_MD_table(element) {
+            var checkboxes = document.querySelectorAll('#MD-table tbody input[type="checkbox"]');
+            checkboxes.forEach(function(checkbox) {
+                checkbox.checked = element.checked;
+            });
+        }
+
+        
+        function resetMachineDataTable() {
+            const table = document.getElementById('MD-table');
+            table.querySelector('tbody').innerHTML = ""
+        }
+        
+
+        function getRowData(r) {
+            r.parentNode.querySelectorAll("tr").forEach(function(t) {
+                t.style.backgroundColor = "white";
+            });
+            var value = r.innerHTML;
+            r.style.backgroundColor = "#5ea9e2";
+            const type = r.querySelectorAll("td")[2].innerHTML; // type: M, E, S, SD
+            var numColumns = r.querySelectorAll("td").length;
+
+            const table = document.getElementById('MD-table');
+            table.querySelector('tbody').innerHTML = ""
+
+            var tr = document.getElementById('MD-table').getElementsByTagName('tr')[0];
+            var ths = Array.from(tr.querySelectorAll("th")); 
+            // Start from the end of the NodeList and move backward
+            for (var i = ths.length - 1; i > 0; i--) {
+                if (i > 8) {
+                    ths[i].remove(); // Remove the <th> element
+                } else {
+                    // Safe to apply style because ths[i] exists
+                    ths[i].style.display = "none";
+                }
+            }
+
+            if (type == "M" || type == "E") {
+                [1, 2, 3, 4].forEach(k => {
+                    tr.querySelectorAll("th")[k].style.display = "table-cell";
+                });
+                for (let i = 1; i <= numColumns - 4; i++) {
+                    let th = tr.appendChild(document.createElement('th'));
+                    th.classList.add("uk-table-shrink");
+                    th.innerHTML = "F_" + i;
+                }
+            } else if (type == "S") {
+                [2, 4, 6, 7, 8].forEach(k => {
+                    tr.querySelectorAll("th")[k].style.display = "table-cell";
+                });
+            } else if (type == "SD") {
+                [2, 4, 5, 6].forEach(k => {
+                    tr.querySelectorAll("th")[k].style.display = "table-cell";
+                });
+                for (let i = 1; i <= numColumns - 4; i++) {
+                    let th = tr.appendChild(document.createElement('th'));
+                    th.classList.add("uk-table-shrink");
+                    th.innerHTML = "F_" + i;
+                }
+            }
+
+            fetch('/get-MD-info', {
+                method: 'POST',
+                body: JSON.stringify({ 'value': value }),
+                headers: {
+                    'Content-Type': 'application/json'
+                }
+            })
+            .then(response => response.json())
+            .then(data => {
+                // Set all the select elements to the default value
+                var selectTime = document.getElementById('table_select_time');
+                selectTime.value = "no";
+                var selectObject = document.getElementById('table_object');
+                selectObject.value = "no";
+                
+                //////////////////////////////////////////////////////////////////////////
+                if (type == "M" || type == "E") {
+                    // Update the time select with the new time columns
+                    const selectTime = document.getElementById('table_select_time');
+                    selectTime.innerHTML = '';  // Clear existing options
+                    const init = document.createElement('option');
+                    init.value = "no";
+                    init.textContent = "Select a column...";
+                    selectTime.appendChild(init);
+                    data['time'].forEach(label_value => {
+                        const optionElement = document.createElement('option');
+                        optionElement.value = label_value;
+                        optionElement.textContent = label_value;
+                        selectTime.appendChild(optionElement);  
+                    });
+                    // Update the object select with the new object columns
+                    const selectObject = document.getElementById('table_select_object');
+                    selectObject.innerHTML = '';  // Clear existing options
+                    data['object'].forEach(label_value => {
+                        const optionElement = document.createElement('option');
+                        optionElement.value = label_value;
+                        optionElement.textContent = label_value;
+                        selectObject.appendChild(optionElement);  
+                    });
+                } else if (type == "S") {
+                } else if (type == "SD") {
+                }
+            })
+            .catch(error => {
+                // Handle any error that occurred during the fetch
+                console.error('Error:', error);
+            });
+        }
+
+
+
+        // Select only the specified select elements
+        const time_object_selects = document.querySelectorAll('.time-object-select');
+
+        function checkSelects() {
+        for (let select of time_object_selects) {
+            if (select.value === 'no') {
+            return false; // If any watched select is not chosen, return false
+            }
+        }
+        return true; // If all watched selects have a value, return true
+        }
+
+        time_object_selects.forEach(select => {
+        select.addEventListener('change', () => {
+            if (checkSelects()) {
+                var $ = function(id) { return document.getElementById(id); };
+                // All specified selects have a value, call the fetch function
+                var time_column = document.getElementById('table_select_time').value;
+                var object_column = document.getElementById('table_object').value;
+
+                var selectedOption = $("table_object").options[$("table_object").selectedIndex]; // Get the selected option
+                var optgroupLabel = selectedOption.parentNode.label;   // Get the label of the optgroup
+                if (optgroupLabel == "self-defined object") {
+                    optgroupLabel = "self";
+                } else if (optgroupLabel == "object column") {
+                    optgroupLabel = "col";
+                }
+                console.log(optgroupLabel);
+                console.log(object_column);
+                console.log(time_column);
+
+                fetch('/get-ME-table', {
+                    method: 'POST',
+                    body: JSON.stringify({ 'time_column': time_column, 'object_column': object_column, 'optgroupLabel': optgroupLabel }),
+                    headers: {
+                        'Content-Type': 'application/json'
+                    }
+                })
+                .then(response => response.json())
+                .then(data => {
+                    console.log(data['table_HTML']);
+                    const table = document.getElementById('MD-table');
+                    table.querySelector('tbody').innerHTML = ""
+                    table.querySelector('tbody').innerHTML = data['table_HTML'];
+
+                    if ('min_datetime' in data && 'max_datetime' in data) {
+                        // Initialize the slider with the min and max datetime from the server
+                        console.log("initialize slider with min and max datetime from the server");
+                        initializeSlider(data['min_datetime'], data['max_datetime']);
+                    }
+                })
+                .catch(error => {
+                    // Handle any error that occurred during the fetch
+                    console.error('Error:', error);
+                });
+            }
+        });
+        });
+
+        function filter_ME_data_table() {
+            var $ = function(id) { return document.getElementById(id); };
+            // Get min and max datetime from the slider
+            var minDatetime = $("amount1").value;
+            var maxDatetime = $("amount2").value;
+
+            // All specified selects have a value, call the fetch function
+            var time_column = document.getElementById('table_select_time').value;
+            var object_column = document.getElementById('table_object').value;
+
+            var selectedOption = $("table_object").options[$("table_object").selectedIndex]; // Get the selected option
+            var optgroupLabel = selectedOption.parentNode.label;   // Get the label of the optgroup
+            if (optgroupLabel == "self-defined object") {
+                optgroupLabel = "self";
+            } else if (optgroupLabel == "object column") {
+                optgroupLabel = "col";
+            }
+            console.log(optgroupLabel);
+            console.log(object_column);
+            console.log(time_column);
+
+            fetch('/get-ME-table', {
+                method: 'POST',
+                body: JSON.stringify({ 'time_column': time_column, 'object_column': object_column, 'optgroupLabel': optgroupLabel, 'minDatetime': minDatetime, 'maxDatetime': maxDatetime}),
+                headers: {
+                    'Content-Type': 'application/json'
+                }
+            })
+            .then(response => response.json())
+            .then(data => {
+                console.log(data['table_HTML']);
+                const table = document.getElementById('MD-table');
+                table.querySelector('tbody').innerHTML = ""
+                table.querySelector('tbody').innerHTML = data['table_HTML'];
+            })
+            .catch(error => {
+                // Handle any error that occurred during the fetch
+                console.error('Error:', error);
+            });
+        }
+
+        function selfdefinedObject() {
+            var object = document.getElementById('defined-object').value;
+            var select = document.getElementById('defined_object_values');
+            var opt = document.createElement('option');
+            opt.value = object;
+            opt.innerHTML = object;
+            select.appendChild(opt);
+        }
+
+
+        function deleteRow1(event, r) {
+            event.stopPropagation(); // Stop the event from bubbling up to the <tr>
+            var i = r.parentNode.parentNode.rowIndex;
+            var table = r.closest("table");  // Find the closest table ancestor
+            table.deleteRow(i);
+            // var divv = document.getElementById("data-header-table");
+            // divv.getElementsByTagName("table")[0].deleteRow(i);
+
+            var value = r.parentNode.parentNode.innerHTML
+            
+            console.log(value);
+            console.log("YYYYY");
+            fetch('/delete-data-header', {
+                method: 'POST',
+                body: JSON.stringify({ 'value': value }),
+                headers: {
+                    'Content-Type': 'application/json'
+                }
+            })
+            .then(response => response.json())
+            .then(data => {
+                const table = document.getElementById('data-header-table');
+                table.innerHTML = data['data_header_table'];
+            })
+            .catch(error => {
+                // Handle any error that occurred during the fetch
+                console.error('Error:', error);
+            });
+
+        }
+
+
+        function resetDataHeaderTable() {
+            fetch('/reset-data-header-table', {
+                method: 'POST',
+                headers: {
+                    'Content-Type': 'application/json'
+                }
+            })
+            .then(response => response.json())
+            .then(data => {
+                const table = document.getElementById('data-header-table');
+                table.innerHTML = data['data_header_table'];
+                // "<table class='uk-table uk-table-small uk-table-hover uk-table-divider'><thead><tr><th>Table name</th><th>Type</th><th>Label</th></tr></thead></table>";
+            })
+            .catch(error => {
+                // Handle any error that occurred during the fetch
+                console.error('Error:', error);
+            });
+        }
+
+
+        function initializeDataHeaderTable() {
+            fetch('/init-data-header-table', {
+                method: 'POST',
+                headers: {
+                    'Content-Type': 'application/json'
+                }
+            })
+            .then(response => response.json())
+            .then(data => {
+                const table = document.getElementById('data-header-table');
+                table.innerHTML = data['data_header_table'];
+                // "<table class='uk-table uk-table-small uk-table-hover uk-table-divider'><thead><tr><th>Table name</th><th>Type</th><th>Label</th></tr></thead></table>";
+            })
+            .catch(error => {
+                // Handle any error that occurred during the fetch
+                console.error('Error:', error);
+            });
+        }
+
 
         function submitDataHeader() {
             var $ = function(id) { return document.getElementById(id); };
             var isValid = true;
 
             var radioSelect = document.querySelector('input[name="radio1"]:checked');
-            console.log(radioSelect.value);
-            var label = $("label_values").value;
-            console.log(label);
+
+            var label_column = $("select_column").value;
+            var label = $("label_values").value; 
+            var selectedOption = $("label_values").options[$("label_values").selectedIndex]; // Get the selected option
+            var optgroupLabel = selectedOption.parentNode.label;   // Get the label of the optgroup
+            if (optgroupLabel == "label column data") {
+                optgroupLabel = "col";
+            } else if (optgroupLabel == "self-defined label") {
+                optgroupLabel = "self";
+            }
+            var label_list = ' [" ' + optgroupLabel + ' "," ' + label_column + ' "," ' +  label + ' "] ';
+
             var object_column = $("select_column_object").value;
-            console.log(object_column);
+
             var tmp = $("select_features").selectedOptions;
             var features_list = Array.from(tmp).map(({ value }) => value);
-            console.log(features_list);
 
             if (radioSelect.value == 'measurement') {
                 if (label == 'Label select...' || label == null || label == "") {
@@ -836,20 +1227,22 @@
             $("span").nextElementSibling.textContent = "*";
             $("select_column_object").nextElementSibling.textContent = "*";
 
-            console.log("HERE");
-            console.log(radioSelect, label, object_column, features_list);
+
 
             // Send the value to the server with fetch API
             fetch('/add-data-header', {
                 method: 'POST',
-                body: JSON.stringify({ 'type': radioSelect.value, 'label': label, 'object_column': object_column, 'value_list': features_list}),
+                body: JSON.stringify({ 'type': radioSelect.value, 'label': label_list, 'object_column': object_column, 'value_list': features_list}),
                 headers: {
                     'Content-Type': 'application/json'
                 }
             })
             .then(response => response.json())
             .then(data => {
-                
+                if (radioSelect.value != 'object') {
+                    const table = document.getElementById('data-header-table');
+                    table.innerHTML = data['data_header_table'];
+                }
             })
             .catch(error => {
                 // Handle any error that occurred during the fetch
@@ -1136,6 +1529,7 @@
                 var objectType = document.getElementById('objectType');
 
                 var featuresSelection = document.getElementById('featuresSelection');
+                var selfdefinedObject = document.getElementById('self-defined-object-selection');
                 var objectSelection = document.getElementById('object-column-selection');
                 var selfdefinedlabelSelection = document.getElementById('self-defined-label-selection');
                 var labelSelection = document.getElementById('label-selection');
@@ -1143,22 +1537,26 @@
                 if (segmentType.checked && radio.value === "segment") {
                     featuresSelection.style.display = 'none';
                     objectSelection.style.display = 'none';
+                    selfdefinedObject.style.display = 'none';
                     selfdefinedlabelSelection.style.display = 'block';
                     labelSelection.style.display = 'block';
                 } else if (objectType.checked && objectType.value === "object"){
                     featuresSelection.style.display = 'none';
                     objectSelection.style.display = 'block';
+                    selfdefinedObject.style.display = 'block';
                     selfdefinedlabelSelection.style.display = 'none';
                     labelSelection.style.display = 'none';
                 } else {
                     featuresSelection.style.display = 'block';
                     objectSelection.style.display = 'none';
+                    selfdefinedObject.style.display = 'none';
                     selfdefinedlabelSelection.style.display = 'block';
                     labelSelection.style.display = 'block';
                 }
             });
         });
 
+
         document.addEventListener('DOMContentLoaded', function() {
             // Show the ERD canvas
             showErdCanvas("erdCanvas1", "zoomInBtn1", "zoomOutBtn1", '{{ image1 }}');
@@ -1171,6 +1569,9 @@
             makeTerminalResizable();
             toggleTerminal();
 
+            // Initialize the data header table
+            initializeDataHeaderTable();
+
             // Listen for the 'added' event on the target sortable list
             UIkit.util.on('#dropped_items', 'added removed', function (event) {
                 var item = event.detail[1]; // Adjusted to access the second item
-- 
GitLab