diff --git a/my_flask_app/__pycache__/main.cpython-39.pyc b/my_flask_app/__pycache__/main.cpython-39.pyc index 197f3a0c6f0b12d1beee27123df9ba4302797d1b..870e9cbe212add277a10cf6075c2b75ab29566f5 100644 Binary files a/my_flask_app/__pycache__/main.cpython-39.pyc and b/my_flask_app/__pycache__/main.cpython-39.pyc differ diff --git a/my_flask_app/main.py b/my_flask_app/main.py index 5ad81ab1297e18cdcb0039722521c3ef3107b47e..3a3f7ba6ea91600f1a9958e870465fe1e27705a8 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} </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 Binary files a/my_flask_app/models/__pycache__/models.cpython-39.pyc and b/my_flask_app/models/__pycache__/models.cpython-39.pyc differ diff --git a/my_flask_app/models/models.py b/my_flask_app/models/models.py index f19dfeff392718ba0cb3f7fe50e01a2b2f70d669..f01eeb36ba9657d8d40b20a327537ccb37de939e 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 0d04707fd7ee6f9c22619b10b03f8d00a8363030..d22226f200239a8c725e41f6fc2aad23afbf4e7b 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