From a44bdc5fce1429a83da1d545cb72db26171eb07e Mon Sep 17 00:00:00 2001 From: Ricardo Cunha Date: Thu, 12 Jun 2025 14:31:18 +0100 Subject: [PATCH] =?UTF-8?q?ChatBot=20-=20A=20funcionar=20com=20a=20estrutu?= =?UTF-8?q?ra=20mas=20com=20erros=20na=20tradu=C3=A7=C3=A3o(Commit=20corri?= =?UTF-8?q?gido)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ChatBot-Python/.env | 5 - ChatBot-Python/.env.example | 4 + ChatBot-Python/README.md | 70 ++++++++++++ ChatBot-Python/debug.py | 97 ----------------- ChatBot-Python/debugAPI.py | 13 --- ChatBot-Python/package-lock.json | 6 -- ChatBot-Python/requirements.txt | 4 + ChatBot-Python/src/.env | 6 ++ .../__pycache__/main.cpython-313.pyc | Bin 31583 -> 38060 bytes ChatBot-Python/src/chatbot.py | 22 ++++ ChatBot-Python/src/db/__init__.py | 1 + ChatBot-Python/src/db/connector.py | 17 +++ ChatBot-Python/src/db/schema.py | 25 +++++ ChatBot-Python/{ => src}/main.py | 101 +++++++++++++++++- ChatBot-Python/{ => src}/server.py | 100 ++++++++++++++++- ChatBot-Python/src/utils/helpers.py | 13 +++ 16 files changed, 360 insertions(+), 124 deletions(-) delete mode 100644 ChatBot-Python/.env create mode 100644 ChatBot-Python/.env.example create mode 100644 ChatBot-Python/README.md delete mode 100644 ChatBot-Python/debug.py delete mode 100644 ChatBot-Python/debugAPI.py delete mode 100644 ChatBot-Python/package-lock.json create mode 100644 ChatBot-Python/requirements.txt create mode 100644 ChatBot-Python/src/.env rename ChatBot-Python/{ => src}/__pycache__/main.cpython-313.pyc (54%) create mode 100644 ChatBot-Python/src/chatbot.py create mode 100644 ChatBot-Python/src/db/__init__.py create mode 100644 ChatBot-Python/src/db/connector.py create mode 100644 ChatBot-Python/src/db/schema.py rename ChatBot-Python/{ => src}/main.py (83%) rename ChatBot-Python/{ => src}/server.py (58%) create mode 100644 ChatBot-Python/src/utils/helpers.py diff --git a/ChatBot-Python/.env b/ChatBot-Python/.env deleted file mode 100644 index 55744ec..0000000 --- a/ChatBot-Python/.env +++ /dev/null @@ -1,5 +0,0 @@ -OPENAI_API_KEY=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 -DB_SERVER=TECHX-DEV1\SQLENERGYMSDEV # Exemplo: localhost\SQLEXPRESS -DB_NAME=EnergyMS_CMBarcelos -DB_USER=sa # Deixa vazio se usares autenticação Windows -DB_PASSWORD=EnergyMS+DEV # Deixa vazio se usares autenticação Windows \ No newline at end of file diff --git a/ChatBot-Python/.env.example b/ChatBot-Python/.env.example new file mode 100644 index 0000000..c560e34 --- /dev/null +++ b/ChatBot-Python/.env.example @@ -0,0 +1,4 @@ +DATABASE_URL=your_database_url_here +DATABASE_USER=your_database_user_here +DATABASE_PASSWORD=your_database_password_here +OPENAI_API_KEY=your_openai_api_key_here \ No newline at end of file diff --git a/ChatBot-Python/README.md b/ChatBot-Python/README.md new file mode 100644 index 0000000..95e2c83 --- /dev/null +++ b/ChatBot-Python/README.md @@ -0,0 +1,70 @@ +# Chatbot Database Structure + +This project implements a chatbot that can interact with users and provide information about the structure of a database. The chatbot is designed to be easy to use and requires minimal coding knowledge to operate. + +## Project Structure + +``` +chatbot-db-structure +├── src +│ ├── main.py # Entry point of the application +│ ├── chatbot.py # Contains the Chatbot class for managing conversations +│ ├── db +│ │ ├── __init__.py # Initializes the db package +│ │ ├── connector.py # Handles database connection +│ │ └── schema.py # Retrieves and formats the database schema +│ └── utils +│ └── helpers.py # Utility functions for various tasks +├── requirements.txt # Lists project dependencies +├── .env.example # Example environment variables +└── README.md # Documentation for the project +``` + +## Setup Instructions + +1. Clone the repository: + ``` + git clone + cd chatbot-db-structure + ``` + +2. Create a virtual environment: + ``` + python -m venv venv + ``` + +3. Activate the virtual environment: + - On Windows: + ``` + venv\Scripts\activate + ``` + - On macOS/Linux: + ``` + source venv/bin/activate + ``` + +4. Install the required dependencies: + ``` + pip install -r requirements.txt + ``` + +5. Configure your environment variables by copying `.env.example` to `.env` and filling in the necessary values. + +## Usage + +To start the chatbot, run the following command: +``` +python src/main.py +``` + +Once the chatbot is running, you can ask questions about the database structure, and it will respond with relevant information. + +## Capabilities + +- Provides information about the database schema, including tables and their columns. +- Allows users to interact naturally and receive structured responses. +- Designed to be extensible for future enhancements and additional features. + +## Contributing + +Contributions are welcome! Please feel free to submit a pull request or open an issue for any suggestions or improvements. \ No newline at end of file diff --git a/ChatBot-Python/debug.py b/ChatBot-Python/debug.py deleted file mode 100644 index e2ef657..0000000 --- a/ChatBot-Python/debug.py +++ /dev/null @@ -1,97 +0,0 @@ -import fitz -import os -from openai import OpenAI -from dotenv import load_dotenv -from langchain.text_splitter import RecursiveCharacterTextSplitter -from langchain_openai import OpenAIEmbeddings -from langchain_community.vectorstores import FAISS - -load_dotenv() -api_key = os.getenv("OPENAI_API_KEY") -client = OpenAI(api_key=api_key) - -def extract_text_from_pdf(pdf_path): - text = "" - with fitz.open(pdf_path) as doc: - for page in doc: - text += page.get_text("text") + "\n" - return text - -def criarChunk(texto): - text_splitter = RecursiveCharacterTextSplitter( - chunk_size=500, - chunk_overlap=50, - length_function=len - ) - return text_splitter.split_text(texto) - -def create_faiss_index(chunks, embeddings): - vector_store = FAISS.from_texts(chunks, embeddings) # Criar índice FAISS - return vector_store - -def search_faiss(vector_store, query, embeddings, top_k=3): - query_embedding = embeddings.embed_query(query) # Gerar embedding da pergunta - docs = vector_store.similarity_search(query, k=top_k) # Procurar no FAISS - return docs - -def debug_embeddings(chunks, embeddings): - embeddings_list = embeddings.embed_documents(chunks) - - print(f"\n DEBUG: Embeddings Gerados") - print(f"Número total de chunks: {len(chunks)}") - print(f"Número total de embeddings: {len(embeddings_list)}") - - if embeddings_list: - print(f"Tamanho do primeiro embedding: {len(embeddings_list[0])}") - - print("\n Exemplo de Chunk e seu Embedding:") - print(f"Chunk: {chunks[0]}") - print(f"Embedding (primeiros 10 valores): {embeddings_list[0][:10]}") - -def debug_faiss(vector_store, query, embeddings, top_k=3): - query_embedding = embeddings.embed_query(query) - print(f"\n DEBUG: Tamanho do vetor da pergunta: {len(query_embedding)}") - - docs = vector_store.similarity_search(query, k=top_k) - print("\n DEBUG: Resultados da busca FAISS") - print(f"Número de chunks retornados: {len(docs)}") - - for i, doc in enumerate(docs): - print(f"\n Chunk {i+1}:") - print(doc.page_content[:200]) # Mostra os primeiros 200 caracteres do chunk - -def generate_response(query, vector_store, embeddings): - docs = search_faiss(vector_store, query, embeddings) - context = "\n".join([doc.page_content for doc in docs]) - - response = client.chat.completions.create( - model="gpt-3.5-turbo", - messages=[ - {"role": "system", "content": "Use o contexto abaixo para responder."}, - {"role": "system", "content": context}, - {"role": "user", "content": query} - ] - ) - return response.choices[0].message.content - -pdf_file = "teste.pdf" -texto_extraido = extract_text_from_pdf(pdf_file) -chunks = criarChunk(texto_extraido) - -embeddings = OpenAIEmbeddings() - -debug_embeddings(chunks, embeddings) - -vector_store = create_faiss_index(chunks, embeddings) -debug_faiss(vector_store, "Exemplo de pesquisa", embeddings) - -print("Chatbot: Olá! Como te posso ajudar?") -while True: - user_input = input("Você: ") - if user_input.lower() in ["sair", "exit", "quit"]: - print("Chatbot: Até logo!") - break - - debug_faiss(vector_store, user_input, embeddings) - resposta = generate_response(user_input, vector_store, embeddings) - print("Chatbot:", resposta) diff --git a/ChatBot-Python/debugAPI.py b/ChatBot-Python/debugAPI.py deleted file mode 100644 index 3163b3b..0000000 --- a/ChatBot-Python/debugAPI.py +++ /dev/null @@ -1,13 +0,0 @@ -import openai -import os -from dotenv import load_dotenv - -load_dotenv() - -openai.api_key = os.getenv("OPENAI_API_KEY") - -try: - response = openai.models.list() - print("API funciona!") -except openai.OpenAIError as e: - print("Erro ao conectar a API:", e) diff --git a/ChatBot-Python/package-lock.json b/ChatBot-Python/package-lock.json deleted file mode 100644 index 4c1e26c..0000000 --- a/ChatBot-Python/package-lock.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "name": "ChatBot-Python", - "lockfileVersion": 3, - "requires": true, - "packages": {} -} diff --git a/ChatBot-Python/requirements.txt b/ChatBot-Python/requirements.txt new file mode 100644 index 0000000..7eec745 --- /dev/null +++ b/ChatBot-Python/requirements.txt @@ -0,0 +1,4 @@ +Flask==2.0.1 +openai==0.11.3 +python-dotenv==0.19.1 +pyodbc==4.0.30 \ No newline at end of file diff --git a/ChatBot-Python/src/.env b/ChatBot-Python/src/.env new file mode 100644 index 0000000..3776dcc --- /dev/null +++ b/ChatBot-Python/src/.env @@ -0,0 +1,6 @@ +OPENAI_API_KEY=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 +DB_SERVER=TECHX-DEV1\SQLENERGYMSDEV # Exemplo: localhost\SQLEXPRESS +DB_NAME=EnergyMS_CMBarcelos +DB_USER=sa # Deixa vazio se usares autenticação Windows +DB_PASSWORD=EnergyMS+DEV # Deixa vazio se usares autenticação Windows +#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 \ No newline at end of file diff --git a/ChatBot-Python/__pycache__/main.cpython-313.pyc b/ChatBot-Python/src/__pycache__/main.cpython-313.pyc similarity index 54% rename from ChatBot-Python/__pycache__/main.cpython-313.pyc rename to ChatBot-Python/src/__pycache__/main.cpython-313.pyc index e03822cda6c049a4870fe36f6a13c7bbababb4da..700110c31ad56ad89dfa4f750493c22bdc9a2f49 100644 GIT binary patch delta 10123 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 Xbz8@DUco=qY?{_<|FL<~v`PCvki+Cm delta 4317 zcmeGeYiv{J^}CL*;|Fmbw)1dIxOo!WNgM||2_XcM07*t|vwX|i!tKgUViME%vCmF{ zu+>Ffp`zV*d{R|S?YeeVCDiewv($@0|0U@BaFl;m`L3`&U&}Rsz3;k>Tat$`yN~F=Hp>O5fZ030mnUCk0kT zC#gln?9}QetePI6+f=%SRsmg04^q2I*U>|?TE!f6iq@)_6Qp%2<^s&2V)cMIRjh%! zz(XUQru8b_B)e(D$Ww$&nL^6@gvi_4NEp+DA8VGIyET+3O(65M5!x6QG?ql<=Gi@b zJnET_nN?BKhKRbUncCp5nR@7U+Cq0wiPq3o+D6;^s#r@4k#~Tzj*I$o+%C$yO>)x&mM(3IJs11 zX_EEXtXDrE`)3u*hTpHQa-na3$(jXYwQ|dBoM(*A&<0ZumeN_YK{`0ieI=%UKo`Aaz9{DDpz#f3zj99?dJRg zo%;XPEO)DPxR^i%7LXlq-~mW#mzq?dR1Bqhy%-@mU?()Jj+C)vE0LHDN+l9HD0f{o zH>{7*kS@7zM=SU>qg4_4&Et>OM_=U7=iu%xNG1My=wRd zy%HlaU9WcQdQ~%^;A__S^;>^Rj9kO4d4ph@O>ou_9o;RgFTA~S2)4=CrfpJ(n&nRn?Iqmz@G~}LiuhnT?k{J1tIjo} z>5gxq3vZTO1M$nB3M{OycfTAC8?+S;3*Hogb-pBcXZTUatT4>4JO17=M>*!3&}NsFi=_^b5ADt}{YA59T{~(bd*zIe2;@p2;QBsiK%+iB!&OC>j^i zsZ>#jXECHC6=S@$ewruiw_9EZ#*5^R!Nz(0l6c2XYMk8M(9;+iA0odWvQ9Krd?MH< zoE4uqMZgyf{M>WS!tsV*RS0X`*!{8j{Q2-*>JAm~J} z6G1IM>L04{BC5qh%*WsHH@f_Y_8@?_jIcfg0e;gTwvGUb|Jkp2>uE

bWV}ua=t|+0fqMo=-BKpkyQ?qUn_Fe`>E8a}K$yQ49v4RW&G0_MU#+})@KY09 z6v8I>=Yt+k9iT;HE|H8E#q3Hvo?%!c;GD_CdG%0Cj01U|FAVhwzQWa^Ib#e<@oz(~ z#99DhFzn=7C96kVLZFsFJ-vEnwJd7cP5MKlrh3R;J~$e%C4hbd{F_}TI1eUA)fVetMFmPD{o>F1Dy+fa`D$ML-h5{!?snR8w3bBj-%e zdiLZ$W+YUF4}ZecI&2gOt5`Iji02lT)2aCSqyq1nb?Ykf51u+}eF+o-80b$hmCnL} zr^n3@=e**rIfWrkBRGR#jeo3+31|6z<@FH>8IMYV{TQT0@x)46$rUS<`Rqm}>;-;v zeoVXo3g0i(F6=a!v><#e5pDeuaJ}ZOQ^j~bem!x>`YNF3FyCf1-`bP^F+$m^(xIMF zkgUR&8|jgJzM0gg*B26LpKnBBLEx>~kb5YRR5^!vGTjK9=^`A0E)(w*>sBS zUtI5P~J$gmp>Ys2tt>^K7Tp5iuA z`(Csp)2ZC@vq>dW6cee;YK~2U4Ayn}RGh(G$b+J`0ny|D%8ZLjPGMuJT2g^=X5%QE zK!zn5&pxa9*^f;14&Z)(QA?$Zm5J;8U0U0JAE5 Y8##r~F1=x_o--0#^_?Iwy448(10VDg!2kdN diff --git a/ChatBot-Python/src/chatbot.py b/ChatBot-Python/src/chatbot.py new file mode 100644 index 0000000..ee307bb --- /dev/null +++ b/ChatBot-Python/src/chatbot.py @@ -0,0 +1,22 @@ +class Chatbot: + def __init__(self, db_connector): + self.db_connector = db_connector + + def get_database_structure(self): + schema = self.db_connector.get_schema() + return self.format_schema(schema) + + def format_schema(self, schema): + formatted_schema = "" + for table in schema: + formatted_schema += f"Table: {table['table']}\n" + for column in table['columns']: + formatted_schema += f" - Column: {column['name']} (Type: {column['type']})\n" + formatted_schema += "\n" + return formatted_schema + + def respond_to_query(self, query): + if "database structure" in query.lower(): + return self.get_database_structure() + else: + return "I'm sorry, I can only provide information about the database structure." \ No newline at end of file diff --git a/ChatBot-Python/src/db/__init__.py b/ChatBot-Python/src/db/__init__.py new file mode 100644 index 0000000..82789f2 --- /dev/null +++ b/ChatBot-Python/src/db/__init__.py @@ -0,0 +1 @@ +# This file is intentionally left blank. \ No newline at end of file diff --git a/ChatBot-Python/src/db/connector.py b/ChatBot-Python/src/db/connector.py new file mode 100644 index 0000000..b7543f1 --- /dev/null +++ b/ChatBot-Python/src/db/connector.py @@ -0,0 +1,17 @@ +class DatabaseConnector: + def __init__(self, connection_string): + self.connection_string = connection_string + self.connection = None + + def connect(self): + import pyodbc + try: + self.connection = pyodbc.connect(self.connection_string) + print("Database connection established.") + except Exception as e: + print(f"Error connecting to the database: {e}") + + def disconnect(self): + if self.connection: + self.connection.close() + print("Database connection closed.") \ No newline at end of file diff --git a/ChatBot-Python/src/db/schema.py b/ChatBot-Python/src/db/schema.py new file mode 100644 index 0000000..0f561c5 --- /dev/null +++ b/ChatBot-Python/src/db/schema.py @@ -0,0 +1,25 @@ +class DatabaseSchema: + def __init__(self, connector): + self.connector = connector + + def get_schema(self): + conn = self.connector.connect() + if not conn: + return "Error: Unable to connect to the database." + + try: + cursor = conn.cursor() + cursor.execute("SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = 'BASE TABLE'") + tables = cursor.fetchall() + + schema = {} + for (table_name,) in tables: + cursor.execute(f"SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = ?", table_name) + columns = [column[0] for column in cursor.fetchall()] + schema[table_name] = columns + + return schema + except Exception as e: + return f"Error retrieving schema: {e}" + finally: + conn.close() \ No newline at end of file diff --git a/ChatBot-Python/main.py b/ChatBot-Python/src/main.py similarity index 83% rename from ChatBot-Python/main.py rename to ChatBot-Python/src/main.py index bac07a5..9f648da 100644 --- a/ChatBot-Python/main.py +++ b/ChatBot-Python/src/main.py @@ -21,6 +21,7 @@ def connect_db(): f"UID={os.getenv('DB_USER')};" f"PWD={os.getenv('DB_PASSWORD')};" ) + print("Conexão com o banco de dados estabelecida com sucesso.") return conn except Exception as e: print(f"Erro ao conectar à base de dados: {e}") @@ -55,6 +56,32 @@ def get_data(atributes=None, limit=20): finally: conn.close() +def get_schema_with_examples(limit=10): + conn = connect_db() + if not conn: + return "Erro: Não foi possível conectar à base de dados." + try: + cursor = conn.cursor() + tables = [] + cursor.execute("SELECT TABLE_SCHEMA, TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = 'BASE TABLE'") + for schema_name, table_name in cursor.fetchall(): + columns = [] + cursor.execute(f"SELECT COLUMN_NAME, DATA_TYPE FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = ?", table_name) + for col_name, col_type in cursor.fetchall(): + columns.append({'name': col_name, 'type': col_type}) + try: + cursor.execute(f"SELECT TOP {limit} * FROM [{schema_name}].[{table_name}]") + example = cursor.fetchone() + example_dict = dict(zip([col['name'] for col in columns], example)) if example else {} + except Exception: + example_dict = {} + tables.append({'table': table_name, 'columns': columns, 'example': example_dict}) + conn.close() + return tables + except Exception as e: + conn.close() + return f"Erro ao buscar schema: {e}" + def get_filtered_data(cunit_id=None, date_billling_begin=None, date_billing_end=None, limit=2): conn = connect_db() if not conn: @@ -135,7 +162,7 @@ def chat_with_gpt(prompt, attempts=3): for i in range(attempts): try: response = client.chat.completions.create( - model="gpt-3.5-turbo-0125", + model="gpt-3.5-turbo", messages=[{"role": "user", "content": prompt}], max_tokens=300 ) @@ -478,6 +505,47 @@ def get_total_kwh_by_building_type(building_type=None): finally: conn.close() +column_mapping = { + "id": "Id", + "tipo de energia": "EnergyTypesId", + "unidade de consumo": "CUnitId", + "tipo de fatura": "CUnitBillsInvoiceTypeId", + "tipo de documento": "DocumentTypeId", + "contrato eletricidade": "CUnitContractElectId", + "número": "Number", + "data de receção da fatura": "DateBillReceipt", + "data": "Date", + "data inicial": "DateBilllingBegin", + "data final": "DateBillingEnd", + "prazo de pagamento": "PaymentDeadline", + "total": "Total", + "mb entidade": "MBEnt", + "mb referência": "MBRef", + "saldo anterior": "PreviousBalance", + "saldo anterior dc": "PreviousBalanceDC", + "pagamentos efetuados": "PaymentsMade", + "saldo de pagamentos efetuados": "PaymentsMadeBalance", + "saldo de pagamentos efetuados dc": "PaymentsMadeBalanceDC", + "faturado": "Billed", + "saldo faturado": "BilledBalance", + "saldo faturado dc": "BilledBalanceDC", + "saldo atual": "CurrentBalance", + "saldo atual dc": "CurrentBalanceDC", + "fator de potência": "PowerFactor", + "potência tomada": "PotTomada", + "total sem iva normal": "TotalExcludingNormalVAT", + "total sem iva reduzido": "TotalExcludingReducedVAT", + "total iva normal": "TotalNormalVAT", + "total iva reduzido": "TotalReducedVAT", + "emissão co2": "CO2Emission", + "consumo médio período faturação": "AvgConsBillingPeriod", + "consumo médio últimos 12m": "AvgConsLast12M", + "informação adicional": "AddicionalInfo", + "data de revisão": "RevisionDate", + "número normalizado": "NormalizedNumber", + "data de pagamento": "PaymentDate" +} + if __name__ == "__main__": conn = connect_db() @@ -507,6 +575,37 @@ if __name__ == "__main__": if user_input.lower() in ["quit", "exit", "bye"]: break + if any(word in user_input.lower() for word in ["tabela", "coluna", "campos", "estrutura", "schema"]): + schema_info = get_schema_with_examples() + if isinstance(schema_info, str): + print(f"Chatbot: {schema_info}") + else: + if re.search(r"tabelas|todas as tabelas", user_input.lower()): + nomes = [t['table'] for t in schema_info] + print(f"Chatbot: As tabelas disponíveis são: {', '.join(nomes)}") + else: + for t in schema_info: + if t['table'].lower() in user_input.lower(): + colunas = ", ".join([ + next((k for k, v in column_mapping.items() if v == c['name']), c['name']) + for c in t['columns'] + ]) + print(f"Chatbot: A tabela '{t['table']}' tem as colunas: {colunas}.") + if t['example']: + exemplo_traduzido = { + next((k for k, v in column_mapping.items() if v == k or v == k or v == col), col): val + for col, val in t['example'].items() + } + exemplo_traduzido = { + next((k for k, v in column_mapping.items() if v == col), col): val + for col, val in t['example'].items() + } + print(f"Exemplo de linha: {exemplo_traduzido}") + break + else: + print("Chatbot: Não encontrei essa tabela. Pergunte por outra ou peça 'tabelas' para ver todas.") + continue + cunit_id, date_billling_begin, date_billing_end, total_requested = parse_user_input(user_input) if total_requested and cunit_id: diff --git a/ChatBot-Python/server.py b/ChatBot-Python/src/server.py similarity index 58% rename from ChatBot-Python/server.py rename to ChatBot-Python/src/server.py index 6d71de7..46669b9 100644 --- a/ChatBot-Python/server.py +++ b/ChatBot-Python/src/server.py @@ -1,13 +1,16 @@ from flask import Flask, request, jsonify from flask_cors import CORS +import json +import os from main import ( chat_with_gpt, parse_user_input, get_total_by_cunit, get_filtered_data, get_price_comparison, compare_current_vs_previous_year, get_top_consumers, - compare_kwh_current_vs_previous_year, get_invoices_by_month_year, + compare_kwh_current_vs_previous_year, get_invoices_by_month_year,get_schema_with_examples, get_invoices_from_inactive_units, get_total_kwh_by_building_type, get_data ) import re from datetime import datetime +import pyodbc app = Flask(__name__) CORS(app) @@ -17,10 +20,103 @@ month_map = { "julho": 7, "agosto": 8, "setembro": 9, "outubro": 10, "novembro": 11, "dezembro": 12 } +column_mapping = { + "id": "Id", + "tipo de energia": "EnergyTypesId", + "unidade de consumo": "CUnitId", + "tipo de fatura": "CUnitBillsInvoiceTypeId", + "tipo de documento": "DocumentTypeId", + "contrato eletricidade": "CUnitContractElectId", + "número": "Number", + "data de receção da fatura": "DateBillReceipt", + "data": "Date", + "data inicial": "DateBilllingBegin", + "data final": "DateBillingEnd", + "prazo de pagamento": "PaymentDeadline", + "total": "Total", + "mb entidade": "MBEnt", + "mb referência": "MBRef", + "saldo anterior": "PreviousBalance", + "saldo anterior dc": "PreviousBalanceDC", + "pagamentos efetuados": "PaymentsMade", + "saldo de pagamentos efetuados": "PaymentsMadeBalance", + "saldo de pagamentos efetuados dc": "PaymentsMadeBalanceDC", + "faturado": "Billed", + "saldo faturado": "BilledBalance", + "saldo faturado dc": "BilledBalanceDC", + "saldo atual": "CurrentBalance", + "saldo atual dc": "CurrentBalanceDC", + "fator de potência": "PowerFactor", + "potência tomada": "PotTomada", + "total sem iva normal": "TotalExcludingNormalVAT", + "total sem iva reduzido": "TotalExcludingReducedVAT", + "total iva normal": "TotalNormalVAT", + "total iva reduzido": "TotalReducedVAT", + "emissão co2": "CO2Emission", + "consumo médio período faturação": "AvgConsBillingPeriod", + "consumo médio últimos 12m": "AvgConsLast12M", + "informação adicional": "AddicionalInfo", + "data de revisão": "RevisionDate", + "número normalizado": "NormalizedNumber", + "data de pagamento": "PaymentDate" +} + +def get_db_schema(): + conn = pyodbc.connect('DRIVER={SQL Server};SERVER=SEU_SERVIDOR;DATABASE=SEU_BANCO;UID=USUARIO;PWD=SENHA') + cursor = conn.cursor() + tables = [] + for row in cursor.tables(tableType='TABLE'): + table_name = row.table_name + columns = [] + for col in cursor.columns(table=table_name): + columns.append({'name': col.column_name, 'type': col.type_name}) + tables.append({'table': table_name, 'columns': columns}) + conn.close() + return tables + +def save_schema_to_file(schema, mapping, folder='schema'): + os.makedirs(folder, exist_ok=True) + with open(os.path.join(folder, 'schema.json'), 'w', encoding='utf-8') as f: + json.dump({'schema': schema, 'mapping': mapping}, f, ensure_ascii=False, indent=2) + +def answer_schema_question(question): + schema = get_schema_with_examples() + question = question.lower() + if "tabelas" in question: + return "As tabelas disponíveis são: " + ", ".join([t['table'] for t in schema]) + for t in schema: + if t['table'].lower() in question: + cols = ", ".join([c['name'] for c in t['columns']]) + return f"A tabela {t['table']} tem as colunas: {cols}.\nExemplo: {t['example']}" + return "Não consegui encontrar informação sobre essa tabela ou coluna." + +@app.route('/api/schema-exemplo', methods=['GET']) +def schema_exemplo(): + schema_info = get_schema_with_examples() + return jsonify({'schema': schema_info}) + +@app.route('/api/schema', methods=['GET']) +def schema(): + schema_info = get_db_schema() + save_schema_to_file(schema_info, column_mapping) + return jsonify({'schema': schema_info, 'mapping': column_mapping}) + @app.route('/api/chat', methods=['POST']) def chat(): data = request.json - user_input = data.get('message', '') + user_input = data.get('message', '').lower() + + if "tabela" in user_input or "coluna" in user_input: + resposta = answer_schema_question(user_input) + return jsonify({'reply': resposta}) + + if "colunas" in user_input and ("fatura" in user_input or "cunitbills" in user_input): + schema_info = get_db_schema() + for table in schema_info: + if table['table'].lower() == 'cunitbills': + colunas = [col['name'] for col in table['columns']] + return jsonify({'reply': "As colunas da tabela de faturas são:\n" + ", ".join(colunas)}) + return jsonify({'reply': "Tabela de faturas não encontrada no banco de dados."}) cunit_id, date_billling_begin, date_billing_end, total_requested = parse_user_input(user_input) diff --git a/ChatBot-Python/src/utils/helpers.py b/ChatBot-Python/src/utils/helpers.py new file mode 100644 index 0000000..33a6b93 --- /dev/null +++ b/ChatBot-Python/src/utils/helpers.py @@ -0,0 +1,13 @@ +def format_response(data): + formatted = "" + for table in data: + formatted += f"**Table:** {table['name']}\n" + formatted += "Columns:\n" + for column in table['columns']: + formatted += f"- {column['name']} ({column['type']})\n" + formatted += "\n" + return formatted + +def parse_user_input(user_input): + # This function can be expanded to include more complex parsing logic + return user_input.strip() \ No newline at end of file