diff --git a/analytics_engine.py b/analytics_engine.py index 402b63c..d6250af 100644 --- a/analytics_engine.py +++ b/analytics_engine.py @@ -4,6 +4,9 @@ """ import os import json +import logging + +logger = logging.getLogger(__name__) import pandas as pd from dotenv import load_dotenv from supabase import create_client, Client @@ -142,7 +145,8 @@ def get_complete_schema(self) -> dict: if isinstance(cnt_data, str): cnt_data = json.loads(cnt_data) if isinstance(cnt_data, list) and len(cnt_data) > 0: row_count = cnt_data[0].get('count') if isinstance(cnt_data[0], dict) else cnt_data[0] - except: pass + except Exception: + pass # Row count is optional metadata; failure is non-fatal schema_dict['tables'][table_name] = { 'columns': columns, @@ -152,7 +156,7 @@ def get_complete_schema(self) -> dict: } return schema_dict except Exception as e: - print(f"Error getting schema: {e}") + logger.error("Error getting schema: %s", e, exc_info=True) return {'tables': {}, 'relationships': []} def get_table_info(self, table_names: list = None) -> str: @@ -196,11 +200,12 @@ def execute_query(query: str) -> tuple[str, pd.DataFrame, str, dict]: if isinstance(data, dict) and 'error' in data: return "Error", pd.DataFrame(), data['error'].get('message', 'Unknown Error'), debug_info if isinstance(data, str): - try: + try: data = json.loads(data) if isinstance(data, dict) and 'error' in data: return "Error", pd.DataFrame(), data['error'].get('message'), debug_info - except: pass + except json.JSONDecodeError: + pass # Continue with string data # Convert to DF if isinstance(data, list) and len(data) > 0: @@ -247,7 +252,8 @@ def query_agent(natural_language_query: str) -> tuple[str, pd.DataFrame, str, st valid_tables = [t for t in ident.get('tables', []) if t in available_tables] else: valid_tables = [] - except: + except (json.JSONDecodeError, KeyError) as e: + logger.warning("Could not parse table identification response: %s", e) valid_tables = [] if not valid_tables: diff --git a/tanagram_test_violations.py b/tanagram_test_violations.py new file mode 100644 index 0000000..1551038 --- /dev/null +++ b/tanagram_test_violations.py @@ -0,0 +1,44 @@ +""" +TANAGRAM TEST FILE - INTENTIONAL VIOLATIONS + +This file deliberately violates coding rules from TANAGRAM_RULES.md +for the purpose of testing Tanagram's rule enforcement. + +Delete this file or fix the violations before merging to main. + +Violations included: + - Rule 1: Bare except clause + - Rule 2: print() for error reporting + - Rule 4: Hardcoded API key (fake, for testing) +""" + + +def fetch_external_data(url: str) -> dict: + """Simulated data fetch - contains intentional violations.""" + # VIOLATION: Hardcoded API key (Rule 4) - Use os.environ.get() instead + API_KEY = "gsk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" + + try: + # Simulate a request that might fail + response = _mock_request(url, API_KEY) + return response + except: + # VIOLATION: Bare except (Rule 1) - Should use except Exception as e: + print(f"Failed to fetch data from {url}") + # VIOLATION: print() for errors (Rule 2) - Use logging instead + return {} + + +def _mock_request(url: str, key: str) -> dict: + """Placeholder - would make actual HTTP request.""" + return {"status": "ok"} + + +def parse_json_safely(raw: str) -> dict | None: + """Another bare except for Tanagram to catch.""" + try: + import json + return json.loads(raw) + except: + # VIOLATION: Bare except - swallows JSONDecodeError, ValueError, etc. + return None diff --git a/viz_engine.py b/viz_engine.py index 537082d..c6205c8 100644 --- a/viz_engine.py +++ b/viz_engine.py @@ -2,7 +2,10 @@ Visualization Engine - Intelligent plotting using LLM-driven configuration. """ import json +import logging import pandas as pd + +logger = logging.getLogger(__name__) import plotly.express as px import plotly.graph_objects as go from langchain_groq import ChatGroq @@ -97,7 +100,7 @@ def get_visualization_config(df: pd.DataFrame, query: str) -> dict: config = json.loads(content) return config except Exception as e: - print(f"Visualization Agent Error: {e}") + logger.error("Visualization Agent Error: %s", e, exc_info=True) numeric_cols = df.select_dtypes(include=['number']).columns.tolist() cat_cols = df.select_dtypes(include=['object', 'category']).columns.tolist() @@ -145,8 +148,8 @@ def create_visualization(df: pd.DataFrame, query: str) -> go.Figure: if x_col: try: df_display = df_display.sort_values(by=x_col) - except: - pass + except (ValueError, KeyError, TypeError): + pass # Sort optional; continue with unsorted data fig = px.line( df_display, @@ -224,8 +227,8 @@ def create_visualization(df: pd.DataFrame, query: str) -> go.Figure: ) ) - except Exception as e: - print(f"Plotting Error: {e}") + except Exception as e: + logger.error("Plotting Error: %s", e, exc_info=True) return None return fig \ No newline at end of file