From 16f6344080b347d03e72efeaf0b723b318252be4 Mon Sep 17 00:00:00 2001 From: Christian Chimezie Date: Sat, 25 Oct 2025 03:32:37 +0100 Subject: [PATCH 1/8] test --- .github/ISSUE_TEMPLATE/feature_request.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index 4ece4bd..77e3f07 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -80,4 +80,4 @@ Related to # - [ ] I have searched existing issues/PRs for similar feature requests - [ ] I have provided a clear use case for this feature - [ ] I have considered how this fits with PlotSense's mission -- [ ] I have provided example usage code +- [ ] I have provided example usage code. From be341e79cbec1059891746df4dc135fe92c60dfe Mon Sep 17 00:00:00 2001 From: Christian Chimezie Date: Thu, 13 Nov 2025 16:57:50 +0000 Subject: [PATCH 2/8] fixed errors --- plotsense/visual_suggestion/suggestions.py | 3 +- pytest.ini | 6 ++-- test_logs/pytest.log | 34 +++++++++++----------- 3 files changed, 20 insertions(+), 23 deletions(-) diff --git a/plotsense/visual_suggestion/suggestions.py b/plotsense/visual_suggestion/suggestions.py index dcb9743..7ea0fb6 100644 --- a/plotsense/visual_suggestion/suggestions.py +++ b/plotsense/visual_suggestion/suggestions.py @@ -84,8 +84,7 @@ def _validate_keys(self): link = service_links.get( service, f"the {service.upper()} website") message = ( - f"Enter { - service.upper()} API key (get it at {link}): ") + f"Enter {service.upper()} API key (get it at {link}): ") self.api_keys[service] = builtins.input( message).strip() if not self.api_keys[service]: diff --git a/pytest.ini b/pytest.ini index c7e184a..9ef3569 100644 --- a/pytest.ini +++ b/pytest.ini @@ -26,12 +26,10 @@ addopts = --strict-config # Show warnings -W default - # Coverage options (when --cov is used) - --cov-report=term-missing:skip-covered - --cov-report=html - --cov-branch # Performance --durations=10 + # Note: Coverage options removed from default addopts + # To run with coverage: pytest --cov=plotsense --cov-report=term-missing --cov-report=html --cov-branch # Markers for organizing tests markers = diff --git a/test_logs/pytest.log b/test_logs/pytest.log index d46fbdc..260cc32 100644 --- a/test_logs/pytest.log +++ b/test_logs/pytest.log @@ -390,45 +390,45 @@ DEBUG matplotlib.font_manager:font_manager.py:1483 findfont: score(FontEntry( DEBUG matplotlib.font_manager:font_manager.py:1483 findfont: score(FontEntry(fname='C:\\Windows\\Fonts\\ntailu.ttf', name='Microsoft New Tai Lue', style='normal', variant='normal', weight=400, stretch='normal', size='scalable')) = 10.05 DEBUG matplotlib.font_manager:font_manager.py:1483 findfont: score(FontEntry(fname='C:\\Windows\\Fonts\\BOD_BI.TTF', name='Bodoni MT', style='italic', variant='normal', weight=700, stretch='normal', size='scalable')) = 11.335 DEBUG matplotlib.font_manager:font_manager.py:1515 findfont: Matching sans\-serif:style=normal:variant=normal:weight=normal:stretch=normal:size=10.0 to DejaVu Sans ('C:\\Users\\chime\\PlotSense_Recloned\\PlotSenseAI\\venv\\Lib\\site-packages\\matplotlib\\mpl-data\\fonts\\ttf\\DejaVuSans.ttf') with score of 0.050000. -DEBUG matplotlib.colorbar:colorbar.py:867 locator: -DEBUG matplotlib.colorbar:colorbar.py:867 locator: -DEBUG matplotlib.colorbar:colorbar.py:867 locator: -DEBUG matplotlib.colorbar:colorbar.py:867 locator: -DEBUG matplotlib.colorbar:colorbar.py:867 locator: -DEBUG matplotlib.colorbar:colorbar.py:867 locator: +DEBUG matplotlib.colorbar:colorbar.py:867 locator: +DEBUG matplotlib.colorbar:colorbar.py:867 locator: +DEBUG matplotlib.colorbar:colorbar.py:867 locator: +DEBUG matplotlib.colorbar:colorbar.py:867 locator: +DEBUG matplotlib.colorbar:colorbar.py:867 locator: +DEBUG matplotlib.colorbar:colorbar.py:867 locator: INFO matplotlib.category:category.py:224 Using categorical units to plot a list of strings that are all parsable as floats or dates. If these strings should be plotted as numbers, cast to the appropriate data type before plotting. INFO matplotlib.category:category.py:224 Using categorical units to plot a list of strings that are all parsable as floats or dates. If these strings should be plotted as numbers, cast to the appropriate data type before plotting. INFO matplotlib.category:category.py:224 Using categorical units to plot a list of strings that are all parsable as floats or dates. If these strings should be plotted as numbers, cast to the appropriate data type before plotting. INFO matplotlib.category:category.py:224 Using categorical units to plot a list of strings that are all parsable as floats or dates. If these strings should be plotted as numbers, cast to the appropriate data type before plotting. -DEBUG groq._base_client:_base_client.py:451 Request options: {'method': 'post', 'url': '/openai/v1/chat/completions', 'timeout': 30, 'files': None, 'json_data': {'messages': [{'role': 'user', 'content': '\n You are a data visualization expert analyzing this dataset:\n\n DataFrame Shape: (100, 5)\nColumns (5): date, category, value, count, flag\n\nColumn Details:\n- date: datetime (100 unique values), sample: [Timestamp(\'2020-01-01 00:00:00\'), Timestamp(\'2020-01-02 00:00:00\'), Timestamp(\'2020-01-03 00:00:00\')]\n Range: 2020-01-01 00:00:00 to 2020-04-09 00:00:00, missing=0\n- category: categorical (3 unique values), sample: [\'A\', \'C\', \'B\']\n- value: numerical (100 unique values), sample: [1.4493448012363035, -0.37494468984913537, 0.37305231843417824]\n Stats: min=-2.7779942807285725, max=2.710745527218326, mean=-0.05, missing=0\n- count: numerical (60 unique values), sample: [12, 97, 61]\n Stats: min=3, max=97, mean=50.65, missing=0\n- flag: numerical (2 unique values), sample: [True, False, True]\n Stats: min=False, max=True, mean=0.45, missing=0\n\nNumerical Variable Correlations (Pearson):\n value count\nvalue 1.00 0.02\ncount 0.02 1.00\n\nPotential Groupings (categorical vs numerical):\n - Could group by: [\'category\', \'flag\']\n - To analyze: [\'value\', \'count\']\n\n Recommend 5 insightful visualizations using matplotlib\'s plotting functions.\n For each suggestion, follow this exact format:\n\n Plot Type: \n Variables: \n Rationale: <1-2 sentences explaining why this visualization is useful>\n ---\n\n CRITICAL VARIABLE ORDERING RULES:\n 1. If a suggestion includes both numerical and categorical variables, NUMERICAL VARIABLES MUST COME FIRST.\n - Correct: "income, gender"\n - Incorrect: "gender, income"\n 2. For plots requiring two numerical variables (e.g., scatter), order by analysis priority (dependent variable first).\n 3. For single-variable plots, use natural order (e.g., "age" for a histogram).\n\n GENERAL RULES FOR ALL PLOT TYPES:\n 1. Ensure the plot type is a valid matplotlib function\n 2. The plot type must be appropriate for the variables\' data types\n 3. The number of variables must match what the plot type requires\n 4. Variables must exist in the dataset\n 5. Never combine incompatible variables\n 6. Always specify complete variable sets\n 7. Ensure plot type names are in lowercase and match matplotlib\'s naming conventions eg hist for histogram, bar for barplot\n 8. Ensure the common plot types requirements are met including the data types\n\n COMMON PLOT TYPE REQUIREMENTS (non-exhaustive):\n 1. bar: 1 categorical (x) + 1 numerical (y) → Variables: [numerical], [categorical]\n 2. scatter: Exactly 2 numerical → Variables: [independent], [dependent]\n 3. hist: Exactly 1 numerical → Variables: [numerical]\n 4. boxplot: 1 numerical OR 1 numerical + 1 categorical → Variables: [numerical], [categorical] (if grouped)\n 5. pie: Exactly 1 categorical → Variables: [categorical]\n 6. line: 1 numerical (y) OR 1 numerical (y) + 1 datetime (x) → Variables: [y], [x] (if applicable)\n 7. heatmap: 2 categorical + 1 numerical OR correlation matrix → Variables: [numerical], [categorical], [categorical]\n 8. violinplot: Same as boxplot\n 9. hexbin: Exactly 2 numerical variables\n 10. pairplot: 2+ numerical variables\n 11. jointplot: Exactly 2 numerical variables\n 12. contour: 2 numerical variables for grid + 1 for values\n 13. quiver: 2 numerical variables for grid + 2 for vectors\n 14. imshow: 2D array of numerical values\n 15. errorbar: 1 numerical (x) + 1 numerical (y) + error values\n 16. stackplot: 1 numerical (x) + multiple numerical (y)\n 17. stem: 1 numerical (x) + 1 numerical (y)\n 18. fill_between: 1 numerical (x) + 2 numerical (y)\n 19. pcolormesh: 2D grid of numerical values\n 20. polar: Angular and radial coordinates\n\n If suggesting a plot not listed above, ensure:\n - The function exists in matplotlib\n - Variable types and counts are explicitly compatible\n - The rationale clearly explains the insight provided\n\n Additional Requirements:\n 1. For specialized plots (like quiver, contour), ensure all required components are specified\n 2. Consider the statistical properties and relationships of the variables\n 3. Suggest plots that would reveal meaningful insights about the data\n 4. Include both common and advanced plots when appropriate\n\n Example CORRECT suggestions (NUMERICAL FIRST):\n Plot Type: boxplot\n Variables: income, gender\n Rationale: Compares income distribution across genders\n ---\n Plot Type: scatter\n Variables: age, income\n Rationale: Shows relationship between age and income\n ---\n Plot Type: bar\n Variables: revenue, product_category\n Rationale: Compares revenue across product categories\n\n Example INCORRECT suggestions (REJECT THESE):\n Plot Type: boxplot\n Variables: gender, income # WRONG - categorical listed first\n ---\n Plot Type: scatter\n Variables: price, weight # WRONG - no clear priority order\n Rationale: Should specify independent/dependent variable order\n'}], 'model': 'llama-3.3-70b-versatile', 'max_tokens': 1000, 'temperature': 0.4}} DEBUG groq._base_client:_base_client.py:451 Request options: {'method': 'post', 'url': '/openai/v1/chat/completions', 'timeout': 30, 'files': None, 'json_data': {'messages': [{'role': 'user', 'content': '\n You are a data visualization expert analyzing this dataset:\n\n DataFrame Shape: (100, 5)\nColumns (5): date, category, value, count, flag\n\nColumn Details:\n- date: datetime (100 unique values), sample: [Timestamp(\'2020-01-01 00:00:00\'), Timestamp(\'2020-01-02 00:00:00\'), Timestamp(\'2020-01-03 00:00:00\')]\n Range: 2020-01-01 00:00:00 to 2020-04-09 00:00:00, missing=0\n- category: categorical (3 unique values), sample: [\'A\', \'C\', \'B\']\n- value: numerical (100 unique values), sample: [1.4493448012363035, -0.37494468984913537, 0.37305231843417824]\n Stats: min=-2.7779942807285725, max=2.710745527218326, mean=-0.05, missing=0\n- count: numerical (60 unique values), sample: [12, 97, 61]\n Stats: min=3, max=97, mean=50.65, missing=0\n- flag: numerical (2 unique values), sample: [True, False, True]\n Stats: min=False, max=True, mean=0.45, missing=0\n\nNumerical Variable Correlations (Pearson):\n value count\nvalue 1.00 0.02\ncount 0.02 1.00\n\nPotential Groupings (categorical vs numerical):\n - Could group by: [\'category\', \'flag\']\n - To analyze: [\'value\', \'count\']\n\n Recommend 5 insightful visualizations using matplotlib\'s plotting functions.\n For each suggestion, follow this exact format:\n\n Plot Type: \n Variables: \n Rationale: <1-2 sentences explaining why this visualization is useful>\n ---\n\n CRITICAL VARIABLE ORDERING RULES:\n 1. If a suggestion includes both numerical and categorical variables, NUMERICAL VARIABLES MUST COME FIRST.\n - Correct: "income, gender"\n - Incorrect: "gender, income"\n 2. For plots requiring two numerical variables (e.g., scatter), order by analysis priority (dependent variable first).\n 3. For single-variable plots, use natural order (e.g., "age" for a histogram).\n\n GENERAL RULES FOR ALL PLOT TYPES:\n 1. Ensure the plot type is a valid matplotlib function\n 2. The plot type must be appropriate for the variables\' data types\n 3. The number of variables must match what the plot type requires\n 4. Variables must exist in the dataset\n 5. Never combine incompatible variables\n 6. Always specify complete variable sets\n 7. Ensure plot type names are in lowercase and match matplotlib\'s naming conventions eg hist for histogram, bar for barplot\n 8. Ensure the common plot types requirements are met including the data types\n\n COMMON PLOT TYPE REQUIREMENTS (non-exhaustive):\n 1. bar: 1 categorical (x) + 1 numerical (y) → Variables: [numerical], [categorical]\n 2. scatter: Exactly 2 numerical → Variables: [independent], [dependent]\n 3. hist: Exactly 1 numerical → Variables: [numerical]\n 4. boxplot: 1 numerical OR 1 numerical + 1 categorical → Variables: [numerical], [categorical] (if grouped)\n 5. pie: Exactly 1 categorical → Variables: [categorical]\n 6. line: 1 numerical (y) OR 1 numerical (y) + 1 datetime (x) → Variables: [y], [x] (if applicable)\n 7. heatmap: 2 categorical + 1 numerical OR correlation matrix → Variables: [numerical], [categorical], [categorical]\n 8. violinplot: Same as boxplot\n 9. hexbin: Exactly 2 numerical variables\n 10. pairplot: 2+ numerical variables\n 11. jointplot: Exactly 2 numerical variables\n 12. contour: 2 numerical variables for grid + 1 for values\n 13. quiver: 2 numerical variables for grid + 2 for vectors\n 14. imshow: 2D array of numerical values\n 15. errorbar: 1 numerical (x) + 1 numerical (y) + error values\n 16. stackplot: 1 numerical (x) + multiple numerical (y)\n 17. stem: 1 numerical (x) + 1 numerical (y)\n 18. fill_between: 1 numerical (x) + 2 numerical (y)\n 19. pcolormesh: 2D grid of numerical values\n 20. polar: Angular and radial coordinates\n\n If suggesting a plot not listed above, ensure:\n - The function exists in matplotlib\n - Variable types and counts are explicitly compatible\n - The rationale clearly explains the insight provided\n\n Additional Requirements:\n 1. For specialized plots (like quiver, contour), ensure all required components are specified\n 2. Consider the statistical properties and relationships of the variables\n 3. Suggest plots that would reveal meaningful insights about the data\n 4. Include both common and advanced plots when appropriate\n\n Example CORRECT suggestions (NUMERICAL FIRST):\n Plot Type: boxplot\n Variables: income, gender\n Rationale: Compares income distribution across genders\n ---\n Plot Type: scatter\n Variables: age, income\n Rationale: Shows relationship between age and income\n ---\n Plot Type: bar\n Variables: revenue, product_category\n Rationale: Compares revenue across product categories\n\n Example INCORRECT suggestions (REJECT THESE):\n Plot Type: boxplot\n Variables: gender, income # WRONG - categorical listed first\n ---\n Plot Type: scatter\n Variables: price, weight # WRONG - no clear priority order\n Rationale: Should specify independent/dependent variable order\n'}], 'model': 'llama-3.1-8b-instant', 'max_tokens': 1000, 'temperature': 0.4}} +DEBUG groq._base_client:_base_client.py:451 Request options: {'method': 'post', 'url': '/openai/v1/chat/completions', 'timeout': 30, 'files': None, 'json_data': {'messages': [{'role': 'user', 'content': '\n You are a data visualization expert analyzing this dataset:\n\n DataFrame Shape: (100, 5)\nColumns (5): date, category, value, count, flag\n\nColumn Details:\n- date: datetime (100 unique values), sample: [Timestamp(\'2020-01-01 00:00:00\'), Timestamp(\'2020-01-02 00:00:00\'), Timestamp(\'2020-01-03 00:00:00\')]\n Range: 2020-01-01 00:00:00 to 2020-04-09 00:00:00, missing=0\n- category: categorical (3 unique values), sample: [\'A\', \'C\', \'B\']\n- value: numerical (100 unique values), sample: [1.4493448012363035, -0.37494468984913537, 0.37305231843417824]\n Stats: min=-2.7779942807285725, max=2.710745527218326, mean=-0.05, missing=0\n- count: numerical (60 unique values), sample: [12, 97, 61]\n Stats: min=3, max=97, mean=50.65, missing=0\n- flag: numerical (2 unique values), sample: [True, False, True]\n Stats: min=False, max=True, mean=0.45, missing=0\n\nNumerical Variable Correlations (Pearson):\n value count\nvalue 1.00 0.02\ncount 0.02 1.00\n\nPotential Groupings (categorical vs numerical):\n - Could group by: [\'category\', \'flag\']\n - To analyze: [\'value\', \'count\']\n\n Recommend 5 insightful visualizations using matplotlib\'s plotting functions.\n For each suggestion, follow this exact format:\n\n Plot Type: \n Variables: \n Rationale: <1-2 sentences explaining why this visualization is useful>\n ---\n\n CRITICAL VARIABLE ORDERING RULES:\n 1. If a suggestion includes both numerical and categorical variables, NUMERICAL VARIABLES MUST COME FIRST.\n - Correct: "income, gender"\n - Incorrect: "gender, income"\n 2. For plots requiring two numerical variables (e.g., scatter), order by analysis priority (dependent variable first).\n 3. For single-variable plots, use natural order (e.g., "age" for a histogram).\n\n GENERAL RULES FOR ALL PLOT TYPES:\n 1. Ensure the plot type is a valid matplotlib function\n 2. The plot type must be appropriate for the variables\' data types\n 3. The number of variables must match what the plot type requires\n 4. Variables must exist in the dataset\n 5. Never combine incompatible variables\n 6. Always specify complete variable sets\n 7. Ensure plot type names are in lowercase and match matplotlib\'s naming conventions eg hist for histogram, bar for barplot\n 8. Ensure the common plot types requirements are met including the data types\n\n COMMON PLOT TYPE REQUIREMENTS (non-exhaustive):\n 1. bar: 1 categorical (x) + 1 numerical (y) → Variables: [numerical], [categorical]\n 2. scatter: Exactly 2 numerical → Variables: [independent], [dependent]\n 3. hist: Exactly 1 numerical → Variables: [numerical]\n 4. boxplot: 1 numerical OR 1 numerical + 1 categorical → Variables: [numerical], [categorical] (if grouped)\n 5. pie: Exactly 1 categorical → Variables: [categorical]\n 6. line: 1 numerical (y) OR 1 numerical (y) + 1 datetime (x) → Variables: [y], [x] (if applicable)\n 7. heatmap: 2 categorical + 1 numerical OR correlation matrix → Variables: [numerical], [categorical], [categorical]\n 8. violinplot: Same as boxplot\n 9. hexbin: Exactly 2 numerical variables\n 10. pairplot: 2+ numerical variables\n 11. jointplot: Exactly 2 numerical variables\n 12. contour: 2 numerical variables for grid + 1 for values\n 13. quiver: 2 numerical variables for grid + 2 for vectors\n 14. imshow: 2D array of numerical values\n 15. errorbar: 1 numerical (x) + 1 numerical (y) + error values\n 16. stackplot: 1 numerical (x) + multiple numerical (y)\n 17. stem: 1 numerical (x) + 1 numerical (y)\n 18. fill_between: 1 numerical (x) + 2 numerical (y)\n 19. pcolormesh: 2D grid of numerical values\n 20. polar: Angular and radial coordinates\n\n If suggesting a plot not listed above, ensure:\n - The function exists in matplotlib\n - Variable types and counts are explicitly compatible\n - The rationale clearly explains the insight provided\n\n Additional Requirements:\n 1. For specialized plots (like quiver, contour), ensure all required components are specified\n 2. Consider the statistical properties and relationships of the variables\n 3. Suggest plots that would reveal meaningful insights about the data\n 4. Include both common and advanced plots when appropriate\n\n Example CORRECT suggestions (NUMERICAL FIRST):\n Plot Type: boxplot\n Variables: income, gender\n Rationale: Compares income distribution across genders\n ---\n Plot Type: scatter\n Variables: age, income\n Rationale: Shows relationship between age and income\n ---\n Plot Type: bar\n Variables: revenue, product_category\n Rationale: Compares revenue across product categories\n\n Example INCORRECT suggestions (REJECT THESE):\n Plot Type: boxplot\n Variables: gender, income # WRONG - categorical listed first\n ---\n Plot Type: scatter\n Variables: price, weight # WRONG - no clear priority order\n Rationale: Should specify independent/dependent variable order\n'}], 'model': 'llama-3.3-70b-versatile', 'max_tokens': 1000, 'temperature': 0.4}} DEBUG groq._base_client:_base_client.py:950 Sending HTTP Request: POST https://api.groq.com/openai/v1/chat/completions DEBUG httpcore.connection:_trace.py:47 connect_tcp.started host='api.groq.com' port=443 local_address=None timeout=30 socket_options=None DEBUG groq._base_client:_base_client.py:950 Sending HTTP Request: POST https://api.groq.com/openai/v1/chat/completions DEBUG httpcore.connection:_trace.py:47 connect_tcp.started host='api.groq.com' port=443 local_address=None timeout=30 socket_options=None -DEBUG httpcore.connection:_trace.py:47 connect_tcp.complete return_value= -DEBUG httpcore.connection:_trace.py:47 connect_tcp.complete return_value= -DEBUG httpcore.connection:_trace.py:47 start_tls.started ssl_context= server_hostname='api.groq.com' timeout=30 -DEBUG httpcore.connection:_trace.py:47 start_tls.started ssl_context= server_hostname='api.groq.com' timeout=30 -DEBUG httpcore.connection:_trace.py:47 start_tls.complete return_value= +DEBUG httpcore.connection:_trace.py:47 connect_tcp.complete return_value= +DEBUG httpcore.connection:_trace.py:47 start_tls.started ssl_context= server_hostname='api.groq.com' timeout=30 +DEBUG httpcore.connection:_trace.py:47 connect_tcp.complete return_value= +DEBUG httpcore.connection:_trace.py:47 start_tls.started ssl_context= server_hostname='api.groq.com' timeout=30 +DEBUG httpcore.connection:_trace.py:47 start_tls.complete return_value= DEBUG httpcore.http11:_trace.py:47 send_request_headers.started request= DEBUG httpcore.http11:_trace.py:47 send_request_headers.complete DEBUG httpcore.http11:_trace.py:47 send_request_body.started request= DEBUG httpcore.http11:_trace.py:47 send_request_body.complete DEBUG httpcore.http11:_trace.py:47 receive_response_headers.started request= -DEBUG httpcore.connection:_trace.py:47 start_tls.complete return_value= +DEBUG httpcore.connection:_trace.py:47 start_tls.complete return_value= DEBUG httpcore.http11:_trace.py:47 send_request_headers.started request= DEBUG httpcore.http11:_trace.py:47 send_request_headers.complete DEBUG httpcore.http11:_trace.py:47 send_request_body.started request= DEBUG httpcore.http11:_trace.py:47 send_request_body.complete DEBUG httpcore.http11:_trace.py:47 receive_response_headers.started request= -DEBUG httpcore.http11:_trace.py:47 receive_response_headers.complete return_value=(b'HTTP/1.1', 401, b'Unauthorized', [(b'Date', b'Sat, 25 Oct 2025 01:58:34 GMT'), (b'Content-Type', b'application/json'), (b'Content-Length', b'96'), (b'Connection', b'keep-alive'), (b'Cache-Control', b'private, max-age=0, no-store, no-cache, must-revalidate'), (b'Server', b'cloudflare'), (b'vary', b'Origin'), (b'x-groq-region', b'fra'), (b'x-request-id', b'req_01k8chdhqmemcvjdwk67cnbtdz'), (b'via', b'1.1 google'), (b'cf-cache-status', b'DYNAMIC'), (b'Set-Cookie', b'__cf_bm=b66yrI.gqNCW4fVikPD0rkECgQXWLutBciOi5gao1Ts-1761357514.4726043-1.0.1.1-y4WZvDohmunPNzTp1k7tD1dfKYid_OHB1IwcBjTZx_fKEjxO435ggm_aiVjheo99GTJXgUXvKeJMHCmDap2pQUzdBnAtFyUc7_82xEzqSwlRy68GYA3l9yASp9kVqApO; HttpOnly; Secure; Path=/; Expires=Sat, 25 Oct 2025 02:28:34 GMT'), (b'CF-RAY', b'993e1c1178692b0b-LHR'), (b'alt-svc', b'h3=":443"; ma=86400')]) +DEBUG httpcore.http11:_trace.py:47 receive_response_headers.complete return_value=(b'HTTP/1.1', 401, b'Unauthorized', [(b'Date', b'Thu, 13 Nov 2025 16:56:25 GMT'), (b'Content-Type', b'application/json'), (b'Content-Length', b'96'), (b'Connection', b'keep-alive'), (b'Cache-Control', b'private, max-age=0, no-store, no-cache, must-revalidate'), (b'Server', b'cloudflare'), (b'vary', b'Origin'), (b'x-groq-region', b'fra'), (b'x-request-id', b'req_01k9z2b785fsxtsb2b2me1wk7r'), (b'via', b'1.1 google'), (b'cf-cache-status', b'DYNAMIC'), (b'Set-Cookie', b'__cf_bm=zvk3jI266DE9a8P2UwH02Xnz64QJIkK310etCN3vMcU-1763052985.5876882-1.0.1.1-KtxYx5eaVCsyqlDC8P0FKEhdlc3fGHeFcrzhrKXLpqU9xLK.8wJHsNDrgWoawgXs4H8A914itgee.I8va9JyM6G..qaviTFxwSrXelP.0Ls9ef6q7acm6pCCsQRgzKCp; HttpOnly; Secure; Path=/; Domain=groq.com; Expires=Thu, 13 Nov 2025 17:26:25 GMT'), (b'CF-RAY', b'99dfcd67ebf460fa-LHR'), (b'alt-svc', b'h3=":443"; ma=86400')]) INFO httpx:_client.py:1025 HTTP Request: POST https://api.groq.com/openai/v1/chat/completions "HTTP/1.1 401 Unauthorized" DEBUG httpcore.http11:_trace.py:47 receive_response_body.started request= DEBUG httpcore.http11:_trace.py:47 receive_response_body.complete DEBUG httpcore.http11:_trace.py:47 response_closed.started DEBUG httpcore.http11:_trace.py:47 response_closed.complete -DEBUG groq._base_client:_base_client.py:989 HTTP Response: POST https://api.groq.com/openai/v1/chat/completions "401 Unauthorized" Headers({'date': 'Sat, 25 Oct 2025 01:58:34 GMT', 'content-type': 'application/json', 'content-length': '96', 'connection': 'keep-alive', 'cache-control': 'private, max-age=0, no-store, no-cache, must-revalidate', 'server': 'cloudflare', 'vary': 'Origin', 'x-groq-region': 'fra', 'x-request-id': 'req_01k8chdhqmemcvjdwk67cnbtdz', 'via': '1.1 google', 'cf-cache-status': 'DYNAMIC', 'set-cookie': '__cf_bm=b66yrI.gqNCW4fVikPD0rkECgQXWLutBciOi5gao1Ts-1761357514.4726043-1.0.1.1-y4WZvDohmunPNzTp1k7tD1dfKYid_OHB1IwcBjTZx_fKEjxO435ggm_aiVjheo99GTJXgUXvKeJMHCmDap2pQUzdBnAtFyUc7_82xEzqSwlRy68GYA3l9yASp9kVqApO; HttpOnly; Secure; Path=/; Expires=Sat, 25 Oct 2025 02:28:34 GMT', 'cf-ray': '993e1c1178692b0b-LHR', 'alt-svc': 'h3=":443"; ma=86400'}) +DEBUG groq._base_client:_base_client.py:989 HTTP Response: POST https://api.groq.com/openai/v1/chat/completions "401 Unauthorized" Headers({'date': 'Thu, 13 Nov 2025 16:56:25 GMT', 'content-type': 'application/json', 'content-length': '96', 'connection': 'keep-alive', 'cache-control': 'private, max-age=0, no-store, no-cache, must-revalidate', 'server': 'cloudflare', 'vary': 'Origin', 'x-groq-region': 'fra', 'x-request-id': 'req_01k9z2b785fsxtsb2b2me1wk7r', 'via': '1.1 google', 'cf-cache-status': 'DYNAMIC', 'set-cookie': '__cf_bm=zvk3jI266DE9a8P2UwH02Xnz64QJIkK310etCN3vMcU-1763052985.5876882-1.0.1.1-KtxYx5eaVCsyqlDC8P0FKEhdlc3fGHeFcrzhrKXLpqU9xLK.8wJHsNDrgWoawgXs4H8A914itgee.I8va9JyM6G..qaviTFxwSrXelP.0Ls9ef6q7acm6pCCsQRgzKCp; HttpOnly; Secure; Path=/; Domain=groq.com; Expires=Thu, 13 Nov 2025 17:26:25 GMT', 'cf-ray': '99dfcd67ebf460fa-LHR', 'alt-svc': 'h3=":443"; ma=86400'}) DEBUG groq._base_client:_base_client.py:1001 Encountered httpx.HTTPStatusError Traceback (most recent call last): File "C:\Users\chime\PlotSense_Recloned\PlotSenseAI\venv\Lib\site-packages\groq\_base_client.py", line 999, in _request @@ -438,7 +438,7 @@ Traceback (most recent call last): raise HTTPStatusError(message, request=request, response=self) httpx.HTTPStatusError: Client error '401 Unauthorized' for url 'https://api.groq.com/openai/v1/chat/completions' For more information check: https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/401 -DEBUG httpcore.http11:_trace.py:47 receive_response_headers.complete return_value=(b'HTTP/1.1', 401, b'Unauthorized', [(b'Date', b'Sat, 25 Oct 2025 01:58:34 GMT'), (b'Content-Type', b'application/json'), (b'Content-Length', b'96'), (b'Connection', b'keep-alive'), (b'Cache-Control', b'private, max-age=0, no-store, no-cache, must-revalidate'), (b'Server', b'cloudflare'), (b'vary', b'Origin'), (b'x-groq-region', b'fra'), (b'x-request-id', b'req_01k8chdhqqegbbxh76yfk4amrz'), (b'via', b'1.1 google'), (b'cf-cache-status', b'DYNAMIC'), (b'Set-Cookie', b'__cf_bm=gHUAY446hZoYVG2FXMFy.LGFC.Kw.PzDsVyWZCjk2lc-1761357514.4735234-1.0.1.1-erpRAJ.ifk8LuHGOXz75CyX46PP8No38mImVeIGq67SVIf5oaTrFxtN8.X8NLJ99Dzm324LgWreSHVn8h5YEX93qUDHaClcavAAVRqKSH7Is3uvRdZHqROKn0sktB93b; HttpOnly; Secure; Path=/; Expires=Sat, 25 Oct 2025 02:28:34 GMT'), (b'CF-RAY', b'993e1c117d2848b9-LHR'), (b'alt-svc', b'h3=":443"; ma=86400')]) +DEBUG httpcore.http11:_trace.py:47 receive_response_headers.complete return_value=(b'HTTP/1.1', 401, b'Unauthorized', [(b'Date', b'Thu, 13 Nov 2025 16:56:25 GMT'), (b'Content-Type', b'application/json'), (b'Content-Length', b'96'), (b'Connection', b'keep-alive'), (b'Cache-Control', b'private, max-age=0, no-store, no-cache, must-revalidate'), (b'Server', b'cloudflare'), (b'vary', b'Origin'), (b'x-groq-region', b'fra'), (b'x-request-id', b'req_01k9z2b78xeb2sbs08a23kxpvp'), (b'via', b'1.1 google'), (b'cf-cache-status', b'DYNAMIC'), (b'Set-Cookie', b'__cf_bm=ul3BLUZj0Ckt3TXJwZKWrqfPk5zAhYOuGcjcFpMl0R8-1763052985.6141908-1.0.1.1-3V7LATgJkfBKiJGouxEMF.HkfwRlm_SzX9dtEfqjUAjEwep9uXVSiRo2hMvJnSq0ElY0du2FHiWCl6E67ozUCODcRVs086uRKtVB0ESQzDGEl55mqI4BY_wzfMBs7lnn; HttpOnly; Secure; Path=/; Domain=groq.com; Expires=Thu, 13 Nov 2025 17:26:25 GMT'), (b'CF-RAY', b'99dfcd681cb3c66a-LHR'), (b'alt-svc', b'h3=":443"; ma=86400')]) DEBUG groq._base_client:_base_client.py:741 Not retrying INFO httpx:_client.py:1025 HTTP Request: POST https://api.groq.com/openai/v1/chat/completions "HTTP/1.1 401 Unauthorized" DEBUG groq._base_client:_base_client.py:1019 Re-raising status error @@ -446,7 +446,7 @@ DEBUG httpcore.http11:_trace.py:47 receive_response_body.started request= Date: Thu, 13 Nov 2025 17:20:09 +0000 Subject: [PATCH 3/8] fixed errors --- .github/workflows/python-tests.yml | 7 ++--- .gitignore | 49 ++++++++++++++++++++++++++---- setup.py | 21 ++++++++++++- 3 files changed, 66 insertions(+), 11 deletions(-) diff --git a/.github/workflows/python-tests.yml b/.github/workflows/python-tests.yml index 03c4271..b43238b 100644 --- a/.github/workflows/python-tests.yml +++ b/.github/workflows/python-tests.yml @@ -27,8 +27,8 @@ jobs: - name: Install dependencies run: | python -m pip install --upgrade pip - pip install -e . - pip install pytest pytest-cov pytest-mock flake8 + pip install -e ".[test]" + pip install flake8 - name: Lint with flake8 run: | @@ -68,8 +68,7 @@ jobs: - name: Install dependencies run: | python -m pip install --upgrade pip - pip install -e . - pip install pytest pytest-mock + pip install -e ".[test]" - name: Run unit tests only (fast) run: | diff --git a/.gitignore b/.gitignore index d611e12..5617b89 100644 --- a/.gitignore +++ b/.gitignore @@ -1,11 +1,48 @@ +# Virtual environments /venv +/.venv +venv/ +env/ +ENV/ + +# Environment variables .env *.env -/__pycache__ -/.venv -*.egg-info + +# Python cache +__pycache__/ +*.py[cod] +*$py.class +*.so + +# Distribution / packaging +*.egg-info/ *.egg -*.pyc -*.pyo -*.pyd +build/ +dist/ +wheels/ +*.whl + +# Testing +.pytest_cache/ +.coverage +coverage.xml +htmlcov/ +.tox/ +test_logs/ + +# IDE +.vscode/ +.idea/ +*.swp +*.swo +*~ + +# OS +.DS_Store +Thumbs.db + +# Database *.db +*.sqlite +*.sqlite3 diff --git a/setup.py b/setup.py index 600ee5c..abb5997 100644 --- a/setup.py +++ b/setup.py @@ -1,12 +1,16 @@ from setuptools import setup, find_packages +# Read README with explicit UTF-8 encoding +with open("README.md", "r", encoding="utf-8") as fh: + long_description = fh.read() + setup( name="plotsense", version="0.1.3", author="Christian Chimezie, Toluwaleke Ogidan, Grace Farayola, Amaka Iduwe, Nelson Ogbeide, Onyekachukwu Ojumah, Olamilekan Ajao", author_email="chimeziechristiancc@gmail.com, gbemilekeogidan@gmail.com, gracefarayola@gmail.com, nwaamaka_iduwe@yahoo.com, Ogbeide331@gmail.com, Onyekaojumah22@gmail.com, olamilekan011@gmail.com", description="An intelligent plotting package with suggestions and explanations", - long_description=open("README.md").read(), + long_description=long_description, long_description_content_type="text/markdown", url="https://github.com/christianchimezie/PlotSenseAI", project_urls={ @@ -35,6 +39,21 @@ "python-dotenv", "groq", "requests", + "Pillow>=9.0.0", ], + extras_require={ + "dev": [ + "pytest>=7.0", + "pytest-cov>=4.0", + "pytest-mock>=3.10", + "flake8>=6.0", + "autopep8>=2.0", + ], + "test": [ + "pytest>=7.0", + "pytest-cov>=4.0", + "pytest-mock>=3.10", + ], + }, license="Apache License 2.0", ) From 7c6f86c08ee65c85d9b07e0ee477fef02a04aa39 Mon Sep 17 00:00:00 2001 From: Christian Chimezie Date: Thu, 13 Nov 2025 17:22:14 +0000 Subject: [PATCH 4/8] fixed errors --- .coverage | Bin 53248 -> 86016 bytes plotsense.egg-info/PKG-INFO | 204 +++++++++++++++++++++++++++---- plotsense.egg-info/SOURCES.txt | 10 +- plotsense.egg-info/requires.txt | 18 ++- plotsense.egg-info/top_level.txt | 1 + test_logs/pytest.log | 48 ++++---- 6 files changed, 236 insertions(+), 45 deletions(-) diff --git a/.coverage b/.coverage index 5b66007fa92afe4f0bc5aece13a3fb0402c8ecc5..bf8dae6d56f01dc1ada3a3c9471cabe68e647437 100644 GIT binary patch literal 86016 zcmeIb2Y?hs-v3=yJw07rlL(STU_lX(43e{16a-N~MPY#j7M5LLmzFVy8shRox`WL!p(!_%+ z>nh6T)GS$8URTziHDR2y(Pd?fF(3bHf&ZC*9z?_d{-2lVUy?rSb^K0=)MOTqp2Ske zBo0aSjGv#_J@!<5p?`dAlUMG~!a(>hoPlr#!Wjr>;Q!wu_XAL*!$3Z#~ikA*h zUr~Hb7^Ca?6>;9N841Z!;O?OX!PMR-<>sGyT8% zX`?5L=TJ7Ys%B>6&sy;lQ(J*A2yB7bZO+o_S@cz@rPna4W?99O^0^hg^k`rf{%?Pf z@X_h(M_cXN;g>!-_9HO8uazp@ChE0n+m>hi;>Sq8Tl)XJMjs*bPW~5W)C?`GD6d_* z#2AuaGm_?rpRA<$j_47*oSt5^)tI)w^m6om)B9>6(_X}D)uIJY2j2*?OY}dt(Tkuh zKYay$<_P}zr@yGYZeH0xK6l!TC*H-4wi?#rm!3F1h3S3iZ!~+9C_d%-HpSUr2%}H` za~eHm+Vay=KB!`4&t>IRuqKPjE0^Fa3mY7GfHK-~b~$z~pINiCuK4*SEPg!6_x$vH zgA@G3X6~_9x8m;jdQYgSt*wMxCAU>O)NC#5GOxUrtWa&2vY9nCRTbscfp5SO2iD>- zD{IS&&*2}v(x{)#QM;1PF?U&);&U?2h0Oe6_|lp07sSGa<<+ybzalproE5g8zMS+p zff2!hWnJ*4ti@OKXXi90)uUvw%=vpQ!ko}q7naWszV7s$-yo!G~UEq!ODX*)j zt6W%7M!OWBO!0%Yyu7w-){+Vg!k2dI;{4jjfZna?ed#Yn$&xPR(jB841_Zf&OkT=;S7W`5Y9k21OIt5;Bdh`ivLCG zO_usA{)GR+83<<}oPlr#!Wjr>Ae@142ErK#XCRz`a0bE|2xs7b=M2P!(=KINJo=`% z)3%jv0vOb5aIZcJuS%poV5tvMU;XcVf#I_bXCRz`a0bE|2xlOifp7-G83<<}oPlr# z!Wjr>Ae;d+197JvH(3CooVLDB0#NAe@142L2~!V2T%E>Hd2UopyLFZiSyVYaVV;pEjYYrfyP2b#2A; zLn~%gVfl^OKW!Rw;Iu^;uBG9knm)Ip8cSQ$)hwB274DLs9L0(I{)&OYd0clMGezCXvQoB!5Rmz;>||6XDfOWm5hB6&#S?}<&R zqmvE)lOOW%vBDV$XCRz`a0bE|2xlOifp7-G83<<}oPlr#7@HbN_vgPg%A-ucFU0@e zlt{Yozv{##(PZ?9NP56;K5uEr|MU3YJv@@``L8*1$@7gI9Z8S)&1e1Z;(zDJNP5t} z@a!c|TAe@142ErK#XCRz`a0bE|2xs6w zdIlWsumsirdFls-|Azm<83<<}oPlr#!Wjr>Ae@142ErK#XCRz`a0bE|2xs6wa0VPF z;ZyyeHTe&Gap6k}XCRz`a0bE|2xlOifp7-G83<<}oPlr#!Wjr>Ae;d^162PH<9|CH zp$KOnoPlr#!Wjr>Ae@142ErK#XCRz`a0bE|2xs6wat2(yDkt?By|xyq?^0i;K1)5G zx<7SW>e|#Lsq<5(rH)IjOD#<;NR_9iq{gN8P7O)*PIXSTO*Kt<$sd#3lOHBuPd=A? zEO~G8=Hwre7bVY5o}A1jS0xuE=Om{k4^1AB9GToRxks`r*)kbVio`dG&l2w>{*u_1 zcqnma;)cZKiA{+OiQ^N;CYB|t5;GH16XO&6B!(vXBz8~il4zEY@g4Cm;~&M}h(8~H zJbqvN*7!B?E%9^Xr^XBMHSs0!dGTZ7hsDRnN5==od&E1%TgQ{}NbI}V-(v5@UXE>x zJrKJ!_J`Q!*oN3~u{E(pvDvYyv4dlyV|&KB#X7{A$9(mp`dq!EUQmy!yVP~+Vs)na zom!)6)J%1_IzSCmy;MiFlk)uU{7?Ni{Ac`!{M-CL_!s!6`FVeZzra7npXl%75Ayf$ z+xbm=M}93ol&{Dq<$dxddAU4Ko-EU{PR^A_%5ieE>@RnhZDibQ@V@Zg^hM{7Z z%5>DwPYhL=G7Up$w+LMr7|t_NKbk_I|aaB_^IRhguQ7er5$NoaV6Kc_Nr4Nr5-K}^F_{Ara@8n*GL zRL0lvINzo+Qo|Gc36=3Q{FxwXNB*obt~~}L5j*m@$~YPxp<{%Gt^5&{;Tj(1TUCZ> zc!)o&((5%mz#mfSbs8Sz52*B7JMy4PuhDQf-=fm1HQdARR_Rq5?&bHW^h!H&uS&15 z$6#c+hP&vPWg2ehx2g0}4Y%>zRl3f?ttwrs;a1vhiH2J^Miy(hgJ6+`J5{-4|h8y_}Dm~AR+^EuXHC#_4b2MDX zuUF{`4Oj5%RC>0C%lQ>5Jxjw?G%{1emHaA|F1Nc~snRnvT*@z3>FF9SqhpS-BbTZ4 z(HgeUn>EeCr7C@th70M%9jRe6zfh%*&~O3YtkP38Y{LgOJw?O$`~sDptl>OHF4a4~$l^&^KD8UFjGE}99+mYca zJxs$ej*+2uWSB}1(J+Wc25T5XXB%WkhN$#F4TEW9fE^jE(tBFKy!E$$2koa}z>cp} zy03=Lysb+2(Xa>atkS(TbmM!dbT18Ec{i2rsi6z+s?t3)?9RKWbaxG%`R*#chlX}^ zwr(0a@pdY$-4EZDcT(x1`w6k;$Ahiy%KW0& z!7a0=q?PtL>_m2o(k_Rc#7^!*c8drQHq7vz*f2h8@T9N;?}? z&yG{t*RTSsSK8HJ|ANw<2It8u?PypU=h1$K9m~=_xfw2;G&|NOCj+^johHe};AN~n zQIdawTDDq}bAej1Op<4TTDd}!TOqYll24)0m699^)bbUQyb08@<&soGsJiu%JP1_HnUdTGRCSFc-vPC-T9V^{s#++?Ye1b@CCOz#&99T>FQ8`MEXi3w z&8?8+DWEE5OL7yW=1TGrv|oiJ2LW~D1WDcjYU+`aTm#e*QziKY+IEB_rvNp1sw9s9 zHD$6ScK~(R6iL1S>hQxPIRe^txFjzCHR&)(E&%G#Ns{b8so9c@Kh(rSC0Tx`2{%hJ z`?PI>B%4p$CQ35+P~#4kWbL62o*>E8L)|=1lAVVdJ71EKhuZsXNfsVz>hU(N#l6{8i*h!LchAQhQ$uiS^Ws=M?RQnE+Y%)~4_L2-Tsj-r* zF{w_HOfjjBKG|V>$eW9elB_T`?$kk&35II1lO+2K)vARg;|tZYl_bkcqb((wU8v?Q zB-vc3Pn%0JxKK@+OR~05i6)XvEmS-q$<9K>;*yLkl!{5Ruu#5|WL}}9FUht-c~X*L zg^GHTtSXcn^~j|9h=e4WQ*4U3l58oI6Om*{p@id+73IFuAjyJqE?k^BP&j~WC$>2a z9vM!|LBu1Yi4d#7b6srUZUf>%pN2fAxWPeG*1Rwaf1Bp^$KNKIt^)t(q#nm#wi9Ci zR}lR_oVqJ@L+Y~B`6BN=;d|=XSVioOc&EewbyD&(WCZ4>>Qie{i&N856I1)8hNb!- zAFy*Om5L<4O}?Gnj4Z${$rqDPB=1YssY{Y)B#%$7Pj*RGCi^9iN$!%Ilx&Kez=&it z@qOZNiFc6+crx)o;UnjidIWL**{W7ms42?vzw+PrUs4CE zrilG}st*2R{$2jH{)K*}f0W9o-M3&E1#Xd)({Yi`{y6rMtjA+C2nOeP_3S z?r#a)0Ee$T^YABIStc*G9TUY9ehT38%sNJTfUV$Ui-@FESR(owuEh zi0rR&9(HbXPH}3Tv{T{l>P&GCbcQ({#QRQ9@s-omal}jR8R9Wf?MUY@Xon3u;bA?W2XONe^8vV^dg`<4**a%l;HFZV1x!T-#omLA8LYv~F8IFDFD z@Cz%UXM!X8<-!ueU(PKd{^iUP0$|o)2@x>cVd(+>Ap6l0Vqo@zB?Q6jdrOFd*>{%i z;rFs{E!~Z=Z!94YW`DPYNSJ+X3866is)TN3Us^&i%(h!XG|axRgm9RBUP5=Wzga>+ z%>HT#5i$GB5<+73sU^h3>=R2j^Sju`mJk)Qk1Qc9W*=I*f#1kJu!O*vy>AJTF?-Jv zLSy!>CB(+;9ZLv~+1r*79kaJAUCys$Z(2fp%-*ns0GYjR=~8|fd(9F;WcF$aUCLgu zgdmx{Yza{^`->%n$?PRdh?CiimJlej7c3!CX3twfsLY--M6oh^)(}Uq%$~7?Xqi22 z3E?t($`aybw$0K+K7l=HX#)2Av!!tud%_Z8X7;!x1kLO*OXK+=>`_bOF!o3Zox`?T zLg36EwuH!;J!A=?GkeeyVrTY%B?QmxeoKg+*?pD}KC?erLj26`wS)kg-D3$6G`ri< za6XjXWoalM&hE5?Ae!A_X(%7YZnuOmn%!mzaWuQt(jY#B-C_xmG`rc-AU>GgR6^s} zA4_OByU`M&X?BApgwyPLONgi0b(RoNvuiCOqGs1vLP*W7wuG3P{lOA~YIc<+MAhs{ zO9-pk6_yZJv&$_Zux6K8LS)S@wS>@`U1ABbHQQnd!8N_SV3ui0ix z2(Z}&mJngHO_mU1vyGMzW3%%uA;@OuSwfV}&NW0~Hao`<#o6p^O9-^tS%xUmW@lPL zsLjr>gjkzxu!LZnonAt(veOK4gxl=*mJn~VQ!OFjW~UgUh?|{k2_ZK-$r562cA_N& z-RuNQh`QPFmJoKc-<8k>?6?xz#Of^}@@54~2)$XpgwAIHBuiwPrg7;cLwHc4ez$bTD>77{1ExgE)Mp1q9+N zEFcnJZULeAG7E^sms&tDUS|Q(c&!D5<4Y_c9$#z$0r?^eh{$U!ASAE0fS7zCJ!y{0 z2ZHh{qY;%aFt2O%iSvzIwz|^D70c!sxpKu^D_71ja>dFDBbTq3ZRE1$vy5E2Y^ISX zE-g2*Zv6}+YtEc*WOdCkMlP&A+Q_Pf(~LZ`>L?@U*Bxo(?3<4;a&E;`BP(W4v2yNY zBWG6}Zsd^@4l{D)|$Ic&P?Tl>Eau*|;w`gnRr_FaZvPttcMkboHHZq=QWn?Vg(nu9+VWf|eu(npxZ*HU~ zn;9APni}awoA}rlrp*y4voYc(jdUUjBZU*EUGX2MA*MGX>{mu2o;MO{0ex0P{>6h3 zl{G8Q7Xtph_Yv2CQ`+>p>}+5ffa>Q$EdKJ|6#^VBD)_fu~H!++rn zgfkG%KsW>841_Zf&OkT=;S7W`5Y9k21K|vWGw{D<1{@D7{5w(l;q`r2hgYz-ygO3R>dm(r;*on zCH^wh;;9Z_gL?d=$i9(5sL1PjJZkXI73=R;p%#A%D)6;Vg|5K&L}xu!-@g*?i|HFM_t@a@S*#%zpLK@-43Ty*FdrQf7CD5|9@7i|39hDmsB`^R{w8YRUF4l zYlV&LmW`{Cjcbhmo~q)$e7a4f{d$eDw2s)g;`sAAVrh-BaaFN#ZL)Dy@#l5KU#=WB zu1z+sMmDZLHm))LJF1F}>z2P(M{Hbi{Owi6(kkY!*BA%zqwRb9Z?8But}#-}?oV~Z z9gpx4{223GQ2)DzPc}ri)bOsBaNh}UX(*`w`z?5D>dir~N@?%P|4Fw>Nxc(o#-+V0 zKkrs4?PO`(yYg%ODUCZ>8h5KS?s;k4yYlN*)6yQ4Mm1EO@AseK=wvDF zPifrO(zsitabL@?^rr;%e}5F)rT7VH+~?J(cPyy?`^#BU?^)7GM!ojGUjN4x$1u_TEr?#eUPyHdaDRpWpld4P2Nli_S zO$|@=N|mLW;qHL1lOH7ig8KsQNnW44821I#Cs!sHB#%xWk{pxlpX{7$mGp5>z^94V z6Hg`XPu!HaEOAcagv8oJ4ekY)l-M^hDA6s^Hj#)o#6OR}jnx3Q#&3`RA-)Os0c7HJ z@j3CSSOs8syjQ#|-V7@Md>i{L_Ezk9+y!uF>>Awve_AXTTNbN~9UYq(+c!2O)-%>I z)&h3`G^p+Bef6?>Qr)j^QCF!8)ak0AR;g;-|36ucRU=hDwY%C`C6(~M_CNAp^Pk2F z0JrX(%a;n<`ukEUbQz9D*=r4Mtc3c z-MyW?q$i?ZM?Z?b7JVxEVDz@=)zJ&1XGH6<7Qlk&(a}SqW1{_|oujRy%KgFp%zeXs z+I;{k0bK5$>%8nd;oRd~?_BJhfxGxuIt!emokN^4PJgGf)5`J1_u^CWI_}}WU)&@v z6X%E%#9C1!%EctHuNWk{(Y3N`JEC3oT|+0)T1Of#`<%Te+E{vzeJ5I#(C5O$Brbc0 zy(XI5vA03ZEWHJ4Vd+iKPL|#PHMR5__eA0+V(*A}3B4_1CG?gs%`PtDsy9WGlCd|$ zPpvVpG#n(99ebI*B793Pu$P6j^elTuL@hnTo)vBhJuEy++t`!BvGgR4i`|PP+p$M++>e&FvPZeOC%|P7vlqCzUw~++*shUQ`qU;+&tj2li4Z!d3%sIndjb6KwKVtNnm3*twt5@=e>1A+Otd;Cm{-7CHxt>2@^*VmP z)ob~ERxbsfLm>RNuA z(KQ?SJw{j8@SBaUs^&KtJ-?d&(df!5euLHX`He=;nZ>U)x?&c;#^_m#`E^#$;a6K- z!T(@%`E-7z(KE{V6-G~=!7sOZ3%|{`6yL97uqdRxuIiovuOTO0VmV^1NR=4H1SiLh}Z*?2K!s^z1xzWv=@j9cM zHRrWfH|0x>ZrYkJF*?xO|?`PK3`j8kx~zC=GMr3LdoG`@(JD6=shwlDx*~kJ|IQjovzppJMdY zhx1KFZ#jlvYV^iRe!bDBEaUeYyHw9IV!1Js4Xa@M%nes=!$IsWvDl7v!lhNMp&boe zi9VLL@u6awrL%Zi4Au`8aUDL}XuR}UMmr6>csBrEZY08ky8(O=Y2Y*Tc^q-6*vt>N zbO~spr7hwTKB0s*^TRA%EVl4NN@z2mWa%PtF&|$-oB5%ZF2o~Dv$R=U%8x9eOZX8b zw1ppBLKpLcO6Vd!wuCO^M`>T?h|LXroE_RIF5(ATI_Jkz_yLwq7pwXHmQE9=^LLSLTmhWZhL~#NiZRrGY5+7AUC-M;`bOIk~={OuW+)}+b zjt?!NEFWg6Ao6^$rMxKcK_!&sLoDS)J)c@ac|OomR-DEMlu(XOv6K;6zNe+M$ngG_ z)`>LlYiX@m!~0oUBi8ZWCA60Jvb0*9!+Vy{T5dk(j#zd#GJbY?Jzi|D(aQ#18(;!^`&q0VDuKyRV|L1VjeZ%$t z_#xr?f8qLn;rf5*9u3$3!z6_3|Ap)Sh3o&J86#Z(50mh}VEsSW6UBu9g%jl@{DuVL z{|@3HmfDW!|0Tr!_h9Y6OHya2PQbeVi&L|)>i^i(2(0n9YpP|+PyUenYw|6u_rEoH zNAjBFg~<)5;;%|BOwPc%e)}bdBzs_;|K?cR@7u&DsN+AKcrbBm;wr50|NBG^tNP7P z9G#eu*gG*W(Ie3*(JB#7@c7^3pTyrRuI>M)_|5UF;+x}V#E*}!M@7F9EBjB1AAp*E z-}vtFw(%x$7wh`}HTF*IrP!Zi_hViEt78|%&WfEFOQW*CAT}d*cx-HJRB>hh_E_0p zVrBm?P}_e+J*6H}cVLabOVqjQ6qQpeQQe=VrmAsjFEvQ@!213zRZKCg{P&Uny8o>I zh<~?#qkp--5o`R{`)mBgsPG@3g8Sm2{ry9vWM&-n@QLE#{1ZN-Fq7A0N(0dg$n=gy_~n)o9`X%P4M>i272AR zUC^2DpuYcM^p)t7(fgt|MK6z@7d<(ej@F?!|H$aL=xEgUcaOG-#?jgFh5N4iqWhS8 zH}25b;-2Lm@2*Af#w>TTd!Req?d^7QTeuP{0)7^GGxBWYVRUa?9oZZ?JyM9Qj8sLY zM-Ii^2!kWtBkdzi(ZBI`=OgD;XPa}sbF*`Wb3X1z$T&-#dCpPJcytNu>2z`0I!T9% zFL6i0U&IsQUU4Hb#pj3<#d@(sREVkg_|QL!SwHPvvNdRDy0h8dQ!}NhRos z<)d@7P%1$W-c#jhtyF^Uyobusa;XG+@a`%{E2a|A`qMdDG!?J|DRn!u3s@=BbU zR!*h8D)}Onqs3DLs8KmuKQ(}Am7^t816ZhXw2CT0Q?68w7E&ch@}??BYpDV#tFLmj zoGOiQTwCX8Mb!YL%F&{#1d3h`t*aV9QsroARRYPm%F*ho!7-l7(E_UkQ5vB&R)dkK z%F!~b1RjmhN~;8TSe2v2R)gJKmD^22ghpt|RT^;!I@#SKDn|>i2FExmM@z2)*berc z%F*hp1V6GJDn|>j5}eJhQaM_KHGs=ij+S91_<{YXaXn0Q*ZH5hqEwd(7=BTVr>_G1VGwq1_g0xQX4WvQ-vtQP~9+ZuhhE3AoFys#n=c zJ#aZYTV>~IxQJb-vU4?Dh~>qzb2Myb7pZK8h6}LPcy_jiO>DEu&eE`vou{%hHP8a* z*>Vl%v2#^+hK6(LL8ogt2Me5MkI{f85S2Yz1A0JIcA5sXfT-+I8qfivvPWt_1BjnJ zf&j1W1iZGXdH`)4Dmz638befevIg{psO;ey&=#VyhiO0yhssVefW^qOhiX8Jh{{gX zfZh(3oe%(3H&yl!J%Vl#l^w4E%_1s0&K`r^4%UDM5Scv)fF8#6D`j?UAlE@27|6Ad z2Ly5rBsm@IyBd;Q4&=(!ewG{#>bmFxky-LLw0XVElD8qbPGx5z9x56JTAGE1%laz?q#lH;HqX2>kL z4aj4#133-IEyu_#xeUl@I4?O2$YrOoQ&K7Z5=7IX2`HZwuB_J zPO`P1A)}6O8QL~vhHN@EH)|?0WYHm;H&vf+?2CNpHgA+e}>hU_;aR#nfC^@fDbkn#4pD`bW&H#TCO^bFZ; zNUW@$A*&6ErPVWJv$^j!$P5{5&e1m^Gi0tI=_oSRkXT+lL#EotRxd+#n){Ikl?jZr z!$tR3@;}4&4Z-GJ%yYHf^X(V5J?gg&rrc(vG;8Ah6PoxQHOI z(vCoDhsp$2nvTJP23DGm*`hLmm8N4Z_A^B*jg{**<5dSnnw}^2A|p*@vd!XRl_4`7 zz(p!UhME8k7b-)hngIP4DnrJa0PPkkL*|;`Gy*c%0i3QfWU&cO7AL6;+3WyLRvEI| z1Sg5C%8=a-03&3%3D9SuGGx06&}N}BWW58x2-)udPE;AP-~{NgP#Ln}1od=kvf=?8 zr!r*63Gy^TmYe|H7AixwJQyjc3|Vu69F34YCqO%e%8*4TK>viwkWD8*`-IAnRVP5d zgvyXzCqOHO%8+FzSS{A74B2)9G*YMxS@!@iLiRn_?HrXM3r~Q)36&umPk^=wl_4u1 zz%rE~J5PY73FYV6<7ikAA0W1ST-+_L6Bi-c8}E!p1eifIc(e1J^NI7Cvn^8QwssXd z-akPP{5JnS|BwEqYML6aMx#T1H$>1j#uu^~V&iUw(?l;kc-B+R`-9JbBxc5do zMR#&L&$nOkH@+lkGTntor%@O=HT}X z8qIOz`EUH*l)h{J@sYCPySfeUE8gM6)p)1p72oq@>F0nk-8<&(vyU*nrNxyc&GxzikN@(fln-xQga)TEb;Cf5Q^4qxtKW5QmEP=J~(USD5BKZ?|z`qumP^ z@BAIJSEud5beeh{I|XmgA=h!0<2=MnTArj#4n&aD*i8Jj8q6hxI8EM;2Y_KC2 zu?zVJW~5D9ahe@LEW$rDBdywqQ+1;gMJ)Uy(~9V|XeCaz$K1qj=O35QZG3wP-O4{L zp3QrMVVYMxnGmLV)$^n<&8yzG9;_X@ zv2lgq31NCyy@)4F@2clSgy~WBV2{=p*EaQq*C5vEClrlFglS8~Z(#`2>KctSz{Uk< zkGf)&J~G-MR+<-vWYZuKECr2g6yE0xv9x4pu&A??<~gy%5JTk1KeKcbds{3r#B)2& z64jQD!*L5O)$`*-m8Al&7Yi)qc|pv#l;e3(X(@xV%(Il`88O#VhG)ebOKFT%SX#s@ z#cWH}pjnm{f@WH(0+m~;ixOzUVd11d~gDec)`m_|~fe!?`87L%aDw2l_Dpu)737So`@G?Etcpu)6{ z789Yuw3QY!p<;-!G01)j(??oNe+ttpN>n3EuP9NqFx{a<3x(+pEv7()=?5+5K!xcQ zE#^Ii=?*25qL+P(kogp*JCyRENn!dyi@8r>`a$V9PndqtV)j#XvuEKvD!N)i{!^It z&|(5qn1)a~iz`gCsLPQ86{bD3m;)82J(Tv02-6-)25ZsUK|^YJC{cl&tV+4GL+mUY>` z`$jphVE}vFX=$I!YwX{9>z~&!f<53QEy+%5ADCWPCR9{FIp_N z8C|nTJZW@Qjrg66P*J?|@f?xl7R7{{Ug`4D|NfU)+H6w5IdyH&&S2 zlDvKP6W3Awe;2XOzpnn@A=xbHCca60oOliQ@jsBbrC9$zEs;;GPApE$NgRa^fc+A~ z5`7Y#aW{XHgd6`Z{#mgD;Ia7K_-(%}@w1R6p!)yZ__X*T@qOZh;yvOWa8JJ%`yuwZ zuKxcCzv_2c?A+K%v14OPWAkIvV~52KjE#)-k9Cc;L(aidKd8^ud$_NEn|e^)uC7&E z)Hw>}47EZn#GU<9)WK?u8mPLfGSxyUzrp{~{}6eNXZ(l#JN@hZOa1fwQ?c&dD!;~` z?H^IRyMK`1!|&*~^kcF?ej(qLFUrSocmMTr3x5Cac)3`^nC-wM3g9)k zuYUvX>t7XJ7@ZNF6x}a6B-$g|A=(V9@_ple?7rqcg**Fiaj$eYx~IBXcbQx1PQ$MO z?&S_}ySi<$Y6Fja8F@eQm&g;5dn13uuK}JLIVo~%q!#No91%GuI#CKR9;0^JNcu3qP{va+u zx-O3d6XFCh5g!~X{1+&1if<@CoKIB+%A67$#t&Bo%AFD%$`4ZoN}m!;;D@S0kU=dv zGgSyOs2(5B)2a|;P(6O|j#pJ7$e?=s0DiD41Q}G1@6QiVg&>3Kar#y5LXbiAIQ^=2 zA;_S5xc1*y6@m<^$M?psY8QeGs>et3y;UK|pn7~1eqy^2WKao4sX~xJ_4qJ?AcN}h z;e41X1Q}Es8LkRJ2Gv8010cwtdI$~%sls^uO8fJHs!+_J;$`>a{Z%1Ip?Y-Pt_nd4 z)#F{!;!p@us2<-Pzjj>+Qm7uMAGj_ADb%8;SA`&j>ftx7$E!k+LiKpZ22T}&6sm`6 zKu1*wQm7s-VO>=SQm7uP_z_hIQmD8H#*dd4f)uL98S49mAcg9&9USjzkV5rPp5LJg zK?)Vu7AVgbf)r};QbQGj6spI*#Sdv0f)r};vO^Vu6e>~${7F>^Qm6p-tttd5RF8et z&_or26spI*WM8R5kV5s?cJ`$z1SwRHeZjV?LXbi&Uc>l>VhWXe>~A<(kU{m>Ck<^> zA;_S5>|^$cDg+r+kA1{GR)rvg>ah>mN2<^(czf6f>_b%uGN{EXZB+;|s2+Qt{Y@2u z464W8Z75TPAcN|mbKqT72r{T1ItSiSg|7N+Z?U&kp^Jt$*;}fxdjNEatqPs>$Q$f^ zRoG3#D-GRMVOI^*5>V)*;V)M>194PFpVH&+GB3zA%Oo3^*ie0Vp>or`7zL)$u4Og%$ zRer68%h?qwzedAl>~fV~t>IF3naZ!ya0$CqGs=Iev#ge3u%?F34pGnRlZt} z;4<3JFC@TMal;uhUj>b$+qGBu1$rM`L#zCJ4Y-6>`AQAcU67xrfqpSPKUV|&WO{y% z2I^JFS7^Z1v&zrbfQx69pQQoU&ML1x4_!K|e7PRMm9xsv2!Jk}RbG1@{9Jm*&y(lD zbrCL`Wu6=lp8vY_GEZ&?a_u^qC#M6sX06PV%Yj_IM&`-kK(1Qt=gHmR6u4lPdGa>c zykeEildpkn)mrAs(?Bj;A@k&CAo0uTdGa!lbxUQQd<ArJ$*hVOIv`R0&y(YToHSYH$!$R5*VXgnG$8S#>UnY*kcZqT z^W-ofZ=5Led2RniJJCVqwfz@6i*_Q(|D!i-jGP z3rxC8^$nFPnsmH&ls8naDmVd0bwlL>tL}0XH&iaL>MloZL*)XijuY^_$^};4<*00^ zTwv8*j>3k@1y-F#vMN`hPmQvM$^};4<)~_?Twv8*j-rOj&D6W0rlE4>8c@jWrgs2mw~0#q|pj!ZiNiWw?L#+?AQ43#7EPJmK| z%8`L5KqW)v$ifq#kfCy9;|Wm5P&u;l1Sn&u9NBpSR54VJEIk2=7%E4$o&Yrrl_P6U zfD(qvk-aBC1w-Y?;uD~Np>kyN2~fXKIkNf$C|{`D!5UD#P&qREG=k!V%8}_OKDp4o;0u z?V0MD+9lNl>-c?>{3Q8C^4a9WxCh``tmJoA@`U92WNmV8^2p@)I&uKZ6P1Z+iSdciiGGRQ5-k%F(f=p$*W%ma_r?DhzchX} zdjHqNtK&1`ha&PH81EY2IUbMgi2V&~`8^wZ2tEH-#?FtOjHrJ}Y<6sN?10$NSdUoy zSd*AgUn1UrQ9Y{eRM)7@>NK?7E>rW=k%;t1sy?a{ZqN7utMa`mpOFvBTjUk;Jb98_ zFBc4)1F30`%%MC`r2j$U)mjs6{- z`Y%VHh~5*uK6-KEb@<%x(W&p#T72&7?x*hkh}Um&&v7qvPjL4|pMH&7?oM(Cx!n+} zCn61z&m(U~okZ}6&C5oCjD=4cfu?FpZ$CN z8~iO;d+<19C#w8oa9_e+{+|Bsert-w!++rn{P#0p(#rg+9le|lmbUY+oYO6R&bK?K z{X`<}{NCiu`O^)~DR$@u{oO(-7^5>j_rN?$0>*Osx!XI;TmLBGhI9W^g@rRv^r9bieoV2BT`JbF)E#1ZM zb=F(DgWu(>vveK5!&z%-3%|};W9d9Rm(`Xw^G(hwOBe9Xj_FPC_$I!^Sz*UE@(Y~h zmd@qpp)p7oiLmDRM#nTRc>G*`j#F1McCJ%vX#*a8iKR2J=VD8z^9|0T5<0`Fv2<*M zbQb z?OBjLbWGzyF@5Nm#)V@3&@qh*#RQ@=*&c@sqGK8tiYY|rFgu1EqGK8tib+K0(2}v? z&O}Q{BRUf-A&=-BVhM>v$22Y!Gl|YPJBC!EbFd}k5}kuAA(`lmwS;Vz0yA-uB}8W8LQ4qE#AZu~&BO(k5Qd5KEg{SjXIny? zCC)0Loy8fJ5K{?jpMxTx9Yg#itVyOQOZ?uBA#4)X2vbBQtbwK|N?4OjVU(~YnIa}( z%`Rn5o!;i0lsa`xn?^BR>h!Q<$d@|ZEg@m*>|qHRQ>U9Hq)eTzmXI@bx>!P<)ah&q ziBe}bOURTuyIMl3)ahgixl*U2B_vCoGE2yoIvp$_L+Z4%gcPZ>izVbpowk;cBy~)! z++)a+I&JJ2(xgsnOURQttt=ro>a?(gOGuQ8?<^rxD!whDOT;&p zkSi5`x3rmED89CYY^nIl64IsOOH0U?itUz=Fcn`|YSeGQ{;|Wr?)d-Nt^&%3{!jK1 z{4>1+|7^d2Xp# zAe@142L97$K-E+K1U1H@9HZ)W2L(qp zM%7aX1&yE>qv~}J1^-;cRXz1kV1#ZI@#~9S6j<2l=>}C#O%!wp$}*~6w^49ZWmLUx zqX0ao>UA3hM@>f6>oy9Gl8maSHVQfcDl)2`8Yu`+kWuxzl>#HrsCsIppb?Z~R6X@l z5TF{P>ZzN80L2(pPyG}GsKuyy6rGEJQjDsnrV1KCB}Ua#TLl3MF{(g~6$Gfmr~Pt#R7~fP^Sfr zp#Gu?L8}FoSk6<0Vygv4P<>H_px2^Ud{Kp<*MdgQRfV9}!s95ts6xN9pDN0a)g{asU7T delta 82 zcmZozz}m2Yd4e=62Ll5G??eTAAVaULdt=H1c{WD=_YC~+Hwy|p<)8dsUlhpy%E141 bvtYq%er_&iY0k3LqT -License: MIT -Project-URL: Homepage, https://github.com/christianchimezie/PlotSenseAI +Home-page: https://github.com/christianchimezie/PlotSenseAI +Author: Christian Chimezie, Toluwaleke Ogidan, Grace Farayola, Amaka Iduwe, Nelson Ogbeide, Onyekachukwu Ojumah, Olamilekan Ajao +Author-email: chimeziechristiancc@gmail.com, gbemilekeogidan@gmail.com, gracefarayola@gmail.com, nwaamaka_iduwe@yahoo.com, Ogbeide331@gmail.com, Onyekaojumah22@gmail.com, olamilekan011@gmail.com +License: Apache License 2.0 Project-URL: Documentation, https://github.com/christianchimezie/PlotSenseAI/blob/main/README.md Classifier: Development Status :: 3 - Alpha Classifier: Intended Audience :: Science/Research @@ -21,19 +22,53 @@ Requires-Python: >=3.8 Description-Content-Type: text/markdown License-File: LICENCE License-File: NOTICE -Requires-Dist: matplotlib>=3.0 +Requires-Dist: matplotlib>=3.8.0 Requires-Dist: seaborn>=0.11 Requires-Dist: pandas>=1.0 Requires-Dist: numpy>=1.18 +Requires-Dist: python-dotenv +Requires-Dist: groq +Requires-Dist: requests +Requires-Dist: Pillow>=9.0.0 +Provides-Extra: dev +Requires-Dist: pytest>=7.0; extra == "dev" +Requires-Dist: pytest-cov>=4.0; extra == "dev" +Requires-Dist: pytest-mock>=3.10; extra == "dev" +Requires-Dist: flake8>=6.0; extra == "dev" +Requires-Dist: autopep8>=2.0; extra == "dev" +Provides-Extra: test +Requires-Dist: pytest>=7.0; extra == "test" +Requires-Dist: pytest-cov>=4.0; extra == "test" +Requires-Dist: pytest-mock>=3.10; extra == "test" +Dynamic: author +Dynamic: author-email +Dynamic: classifier +Dynamic: description +Dynamic: description-content-type +Dynamic: home-page +Dynamic: license Dynamic: license-file +Dynamic: project-url +Dynamic: provides-extra +Dynamic: requires-dist +Dynamic: requires-python +Dynamic: summary # 🌟 PlotSense: AI-Powered Data Visualization Assistant +[![Python Tests](https://github.com/PlotSenseAI/PlotSenseAI/actions/workflows/python-tests.yml/badge.svg)](https://github.com/PlotSenseAI/PlotSenseAI/actions/workflows/python-tests.yml) +[![Frontend Tests](https://github.com/PlotSenseAI/PlotSenseAI/actions/workflows/frontend-tests.yml/badge.svg)](https://github.com/PlotSenseAI/PlotSenseAI/actions/workflows/frontend-tests.yml) +[![codecov](https://codecov.io/gh/PlotSenseAI/PlotSenseAI/branch/main/graph/badge.svg)](https://codecov.io/gh/PlotSenseAI/PlotSenseAI) +[![Python Version](https://img.shields.io/badge/python-3.9%2B-blue.svg)](https://www.python.org/downloads/) +[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) +[![Code style: flake8](https://img.shields.io/badge/code%20style-flake8-black.svg)](https://flake8.pycqa.org/) + ## 📌 Overview **PlotSense** is an AI-powered assistant that helps data professionals and analysts make smarter, faster, and more explainable data visualizations. Whether you're exploring a new dataset or building dashboards, PlotSense simplifies the process with: - ✅ Smart Visualization Suggestions - Recommends the best plots based on your data structure and relationships. +- 📊 Visualization Plot - Generates suggested plot with ease. - 🧠 Natural Language Explanations – Automatically explains charts in plain English. - 🔗 Seamless Integration – Works out of the box with pandas, matplotlib, and seaborn. @@ -51,7 +86,7 @@ pip install plotsense ```bash import plotsense as ps -from plotsense import recommender, generate_plot, explainer, +from plotsense import recommender, plotgen, explainer ``` ### 🔐 Authenticate with Groq API: Get your free API key from Groq Cloud https://console.groq.com/home @@ -77,24 +112,55 @@ import pandas as pd df = pd.read_csv("data.csv") # Get AI-recommended visualizations -suggestions = ps.recommender(df) # default number of suggestions is 5 +suggestions = recommender(df) # default number of suggestions is 5 print(suggestions) ``` ### 📊 Sample Output: -![alt text](suggestions_table.png) +![alt text](image.png) 🎛️ Want more suggestions? ``` bash -suggestions = ps.recommender(df, n=10) +suggestions = recommender(df, n=10) +``` + +### 📈 2. One-Click Plot Generation +Generate recommended charts instantly using .iloc + +```bash +plot1 = plotgen(df, suggestions.iloc[0]) # This will plot a bar chart with variables 'survived', 'pclass' +plot2 = plotgen(df, suggestions.iloc[1]) # This will plot a bar chart with variables 'survived', 'sex' +plot3 = plotgen(df, suggestions.iloc[2]) # This will plot a histogram with variable 'age' ``` -### 🧾 2. AI-Powered Plot Explanation +or Generate recommended charts instantly using three argurments + +```bash +plot1 = plotgen(df, 0, suggestions) # This will plot a bar chart with variables 'survived', 'pclass' +plot2 = plotgen(df, 1, suggestions) # This will plot a bar chart with variables 'survived', 'sex' +plot3 = plotgen(df, 2, suggestions) # This will plot a histogram with variable 'age' +``` +🎛️ Want more control? + +``` bash +plot1 = plotgen(df, suggestions.iloc[0], x='pclass', y='survived') +``` +Supported Plots +- scatter +- bar +- barh +- histogram +- boxplot +- violinplot +- pie +- hexbin + +### 🧾 3. AI-Powered Plot Explanation Turn your visualizations into stories with natural language insights: ``` bash -explanation = ps.explainer(plot1) +explanation = explainer(plot1) print(explanation) ``` @@ -103,7 +169,7 @@ print(explanation) - Custom Prompts: You can provide your own prompt to guide the explanation ``` bash -explanation = refine_plot_explanation( +explanation = explainer( fig, prompt="Explain the key trends in this sales data visualization" ) @@ -111,33 +177,126 @@ explanation = refine_plot_explanation( - Multiple Refinement Iterations: Increase the number of refinement cycles for more polished explanations: ```bash -explanation = refine_plot_explanation(fig, iterations=3) # Default is 2 +explanation = explainer(fig, max_iterations=3) # Default is 2 +``` + +## 🔄 Combined Workflow: Suggest → Plot → Explain +``` bash +suggestions = recommender(df) +plot = plotgen(df, suggestions.iloc[0]) +insight = explainer(plot) +``` + +## 🧪 Testing + +PlotSenseAI has comprehensive test coverage for both Python and frontend code. + +### Quick Test Commands + +**Python Tests:** +```bash +# Run all tests +pytest + +# Run with coverage +pytest --cov=plotsense --cov-report=html + +# Run fast tests only (skip slow tests) +pytest -m "not slow" +``` + +**Frontend Tests:** +```bash +cd web + +# Run all tests +npm test + +# Run with coverage +npm run test:coverage + +# Run in watch mode +npm test -- --watch +``` + +### Test Infrastructure + +- **Backend**: pytest with 950+ lines of tests covering: + - Unit tests for individual functions + - Integration tests for component interaction + - End-to-end workflow tests + - Mock external API calls (Groq) + +- **Frontend**: Vitest + React Testing Library + - Component tests for UI elements + - User interaction testing + - Accessibility testing + +- **CI/CD**: GitHub Actions runs tests automatically on every push and PR +- **Pre-commit Hooks**: Automated linting and quick tests before commits + +For detailed testing documentation, see [TESTING.md](TESTING.md). + +### Setting Up Development Environment + +```bash +# Install Python dependencies +pip install -e . +pip install pytest pytest-cov pytest-mock + +# Install frontend dependencies +cd web && npm install + +# Install pre-commit hooks (optional but recommended) +pip install pre-commit +pre-commit install ``` ## 🤝 Contributing -We welcome contributions! + +We welcome contributions! Please see our [Contributing Guide](CONTRIBUTING.md) for details. + +### Quick Start for Contributors + +1. **Fork and clone** the repository +2. **Set up development environment:** + ```bash + pip install -e . + pip install pytest pytest-cov pytest-mock pre-commit + pre-commit install + ``` +3. **Create a feature branch:** `git checkout -b feature/your-feature` +4. **Make changes and add tests** +5. **Run tests:** `pytest` and `cd web && npm test` +6. **Submit a Pull Request** ### Branching Strategy -- main → The stable production-ready version of PlotSense. -- dev → Active development -- feature/ → Branches for specific features (e.g., feature/ai-visualization-suggestions). +- `main` → Production-ready version +- `dev` → Active development +- `feature/` → New features +- `fix/` → Bug fixes ### 💡 How to Help -- 🐞 **Bug Reports** → GitHub Issues -- 💡 **Suggest features** → Open a discussion -- 🚀 **Submit PRs** → Fork → Branch → Test → Pull Request +- 🐞 **Bug Reports** → [Open an issue](https://github.com/PlotSenseAI/PlotSenseAI/issues/new?template=bug_report.md) +- 💡 **Feature Requests** → [Request a feature](https://github.com/PlotSenseAI/PlotSenseAI/issues/new?template=feature_request.md) +- ❓ **Questions** → [Ask a question](https://github.com/PlotSenseAI/PlotSenseAI/issues/new?template=question.md) +- 🚀 **Submit PRs** → See [CONTRIBUTING.md](CONTRIBUTING.md) ### 📅 Roadmap - More model integrations - Automated insight highlighting - Jupyter widget support +- Features/target analysis +- More supported plots +- PlotSense web interface +- PlotSense customised notebook template ### 📥 Install or Update ``` bash pip install --upgrade plotsense # Get the latest features! ``` ## 🛡 License -MIT License (Open Source) +Apache License 2.0 ## 🔐 API & Privacy Notes - Your API key is securely held in memory for your current Python session. @@ -146,3 +305,6 @@ MIT License (Open Source) Let your data speak—with clarity, power, and PlotSense. 📊✨ + +## Your Feedback +[Feedback Form](https://forms.gle/QEjipzHiMagpAQU99) diff --git a/plotsense.egg-info/SOURCES.txt b/plotsense.egg-info/SOURCES.txt index babe714..ad6ed30 100644 --- a/plotsense.egg-info/SOURCES.txt +++ b/plotsense.egg-info/SOURCES.txt @@ -1,5 +1,8 @@ +LICENCE +NOTICE README.md pyproject.toml +setup.cfg setup.py plotsense/__init__.py plotsense.egg-info/PKG-INFO @@ -11,5 +14,10 @@ plotsense/explanations/__init__.py plotsense/explanations/explanations.py plotsense/plot_generator/__init__.py plotsense/plot_generator/generator.py -plotsense/visual_suggestion/suggestions.py plotsense/visual_suggestion/__init__.py +plotsense/visual_suggestion/suggestions.py +test/__init__.py +test/conftest.py +test/test_explanations.py +test/test_plotgen.py +test/test_suggestions.py diff --git a/plotsense.egg-info/requires.txt b/plotsense.egg-info/requires.txt index 6fbc6f2..dca5248 100644 --- a/plotsense.egg-info/requires.txt +++ b/plotsense.egg-info/requires.txt @@ -1,4 +1,20 @@ -matplotlib>=3.0 +matplotlib>=3.8.0 seaborn>=0.11 pandas>=1.0 numpy>=1.18 +python-dotenv +groq +requests +Pillow>=9.0.0 + +[dev] +pytest>=7.0 +pytest-cov>=4.0 +pytest-mock>=3.10 +flake8>=6.0 +autopep8>=2.0 + +[test] +pytest>=7.0 +pytest-cov>=4.0 +pytest-mock>=3.10 diff --git a/plotsense.egg-info/top_level.txt b/plotsense.egg-info/top_level.txt index 100c7e8..f5522c3 100644 --- a/plotsense.egg-info/top_level.txt +++ b/plotsense.egg-info/top_level.txt @@ -1 +1,2 @@ plotsense +test diff --git a/test_logs/pytest.log b/test_logs/pytest.log index 260cc32..a0435a2 100644 --- a/test_logs/pytest.log +++ b/test_logs/pytest.log @@ -390,45 +390,48 @@ DEBUG matplotlib.font_manager:font_manager.py:1483 findfont: score(FontEntry( DEBUG matplotlib.font_manager:font_manager.py:1483 findfont: score(FontEntry(fname='C:\\Windows\\Fonts\\ntailu.ttf', name='Microsoft New Tai Lue', style='normal', variant='normal', weight=400, stretch='normal', size='scalable')) = 10.05 DEBUG matplotlib.font_manager:font_manager.py:1483 findfont: score(FontEntry(fname='C:\\Windows\\Fonts\\BOD_BI.TTF', name='Bodoni MT', style='italic', variant='normal', weight=700, stretch='normal', size='scalable')) = 11.335 DEBUG matplotlib.font_manager:font_manager.py:1515 findfont: Matching sans\-serif:style=normal:variant=normal:weight=normal:stretch=normal:size=10.0 to DejaVu Sans ('C:\\Users\\chime\\PlotSense_Recloned\\PlotSenseAI\\venv\\Lib\\site-packages\\matplotlib\\mpl-data\\fonts\\ttf\\DejaVuSans.ttf') with score of 0.050000. -DEBUG matplotlib.colorbar:colorbar.py:867 locator: -DEBUG matplotlib.colorbar:colorbar.py:867 locator: -DEBUG matplotlib.colorbar:colorbar.py:867 locator: -DEBUG matplotlib.colorbar:colorbar.py:867 locator: -DEBUG matplotlib.colorbar:colorbar.py:867 locator: -DEBUG matplotlib.colorbar:colorbar.py:867 locator: +DEBUG matplotlib.colorbar:colorbar.py:867 locator: +DEBUG matplotlib.colorbar:colorbar.py:867 locator: +DEBUG matplotlib.colorbar:colorbar.py:867 locator: +DEBUG matplotlib.colorbar:colorbar.py:867 locator: +DEBUG matplotlib.colorbar:colorbar.py:867 locator: +DEBUG matplotlib.colorbar:colorbar.py:867 locator: INFO matplotlib.category:category.py:224 Using categorical units to plot a list of strings that are all parsable as floats or dates. If these strings should be plotted as numbers, cast to the appropriate data type before plotting. INFO matplotlib.category:category.py:224 Using categorical units to plot a list of strings that are all parsable as floats or dates. If these strings should be plotted as numbers, cast to the appropriate data type before plotting. INFO matplotlib.category:category.py:224 Using categorical units to plot a list of strings that are all parsable as floats or dates. If these strings should be plotted as numbers, cast to the appropriate data type before plotting. INFO matplotlib.category:category.py:224 Using categorical units to plot a list of strings that are all parsable as floats or dates. If these strings should be plotted as numbers, cast to the appropriate data type before plotting. -DEBUG groq._base_client:_base_client.py:451 Request options: {'method': 'post', 'url': '/openai/v1/chat/completions', 'timeout': 30, 'files': None, 'json_data': {'messages': [{'role': 'user', 'content': '\n You are a data visualization expert analyzing this dataset:\n\n DataFrame Shape: (100, 5)\nColumns (5): date, category, value, count, flag\n\nColumn Details:\n- date: datetime (100 unique values), sample: [Timestamp(\'2020-01-01 00:00:00\'), Timestamp(\'2020-01-02 00:00:00\'), Timestamp(\'2020-01-03 00:00:00\')]\n Range: 2020-01-01 00:00:00 to 2020-04-09 00:00:00, missing=0\n- category: categorical (3 unique values), sample: [\'A\', \'C\', \'B\']\n- value: numerical (100 unique values), sample: [1.4493448012363035, -0.37494468984913537, 0.37305231843417824]\n Stats: min=-2.7779942807285725, max=2.710745527218326, mean=-0.05, missing=0\n- count: numerical (60 unique values), sample: [12, 97, 61]\n Stats: min=3, max=97, mean=50.65, missing=0\n- flag: numerical (2 unique values), sample: [True, False, True]\n Stats: min=False, max=True, mean=0.45, missing=0\n\nNumerical Variable Correlations (Pearson):\n value count\nvalue 1.00 0.02\ncount 0.02 1.00\n\nPotential Groupings (categorical vs numerical):\n - Could group by: [\'category\', \'flag\']\n - To analyze: [\'value\', \'count\']\n\n Recommend 5 insightful visualizations using matplotlib\'s plotting functions.\n For each suggestion, follow this exact format:\n\n Plot Type: \n Variables: \n Rationale: <1-2 sentences explaining why this visualization is useful>\n ---\n\n CRITICAL VARIABLE ORDERING RULES:\n 1. If a suggestion includes both numerical and categorical variables, NUMERICAL VARIABLES MUST COME FIRST.\n - Correct: "income, gender"\n - Incorrect: "gender, income"\n 2. For plots requiring two numerical variables (e.g., scatter), order by analysis priority (dependent variable first).\n 3. For single-variable plots, use natural order (e.g., "age" for a histogram).\n\n GENERAL RULES FOR ALL PLOT TYPES:\n 1. Ensure the plot type is a valid matplotlib function\n 2. The plot type must be appropriate for the variables\' data types\n 3. The number of variables must match what the plot type requires\n 4. Variables must exist in the dataset\n 5. Never combine incompatible variables\n 6. Always specify complete variable sets\n 7. Ensure plot type names are in lowercase and match matplotlib\'s naming conventions eg hist for histogram, bar for barplot\n 8. Ensure the common plot types requirements are met including the data types\n\n COMMON PLOT TYPE REQUIREMENTS (non-exhaustive):\n 1. bar: 1 categorical (x) + 1 numerical (y) → Variables: [numerical], [categorical]\n 2. scatter: Exactly 2 numerical → Variables: [independent], [dependent]\n 3. hist: Exactly 1 numerical → Variables: [numerical]\n 4. boxplot: 1 numerical OR 1 numerical + 1 categorical → Variables: [numerical], [categorical] (if grouped)\n 5. pie: Exactly 1 categorical → Variables: [categorical]\n 6. line: 1 numerical (y) OR 1 numerical (y) + 1 datetime (x) → Variables: [y], [x] (if applicable)\n 7. heatmap: 2 categorical + 1 numerical OR correlation matrix → Variables: [numerical], [categorical], [categorical]\n 8. violinplot: Same as boxplot\n 9. hexbin: Exactly 2 numerical variables\n 10. pairplot: 2+ numerical variables\n 11. jointplot: Exactly 2 numerical variables\n 12. contour: 2 numerical variables for grid + 1 for values\n 13. quiver: 2 numerical variables for grid + 2 for vectors\n 14. imshow: 2D array of numerical values\n 15. errorbar: 1 numerical (x) + 1 numerical (y) + error values\n 16. stackplot: 1 numerical (x) + multiple numerical (y)\n 17. stem: 1 numerical (x) + 1 numerical (y)\n 18. fill_between: 1 numerical (x) + 2 numerical (y)\n 19. pcolormesh: 2D grid of numerical values\n 20. polar: Angular and radial coordinates\n\n If suggesting a plot not listed above, ensure:\n - The function exists in matplotlib\n - Variable types and counts are explicitly compatible\n - The rationale clearly explains the insight provided\n\n Additional Requirements:\n 1. For specialized plots (like quiver, contour), ensure all required components are specified\n 2. Consider the statistical properties and relationships of the variables\n 3. Suggest plots that would reveal meaningful insights about the data\n 4. Include both common and advanced plots when appropriate\n\n Example CORRECT suggestions (NUMERICAL FIRST):\n Plot Type: boxplot\n Variables: income, gender\n Rationale: Compares income distribution across genders\n ---\n Plot Type: scatter\n Variables: age, income\n Rationale: Shows relationship between age and income\n ---\n Plot Type: bar\n Variables: revenue, product_category\n Rationale: Compares revenue across product categories\n\n Example INCORRECT suggestions (REJECT THESE):\n Plot Type: boxplot\n Variables: gender, income # WRONG - categorical listed first\n ---\n Plot Type: scatter\n Variables: price, weight # WRONG - no clear priority order\n Rationale: Should specify independent/dependent variable order\n'}], 'model': 'llama-3.1-8b-instant', 'max_tokens': 1000, 'temperature': 0.4}} DEBUG groq._base_client:_base_client.py:451 Request options: {'method': 'post', 'url': '/openai/v1/chat/completions', 'timeout': 30, 'files': None, 'json_data': {'messages': [{'role': 'user', 'content': '\n You are a data visualization expert analyzing this dataset:\n\n DataFrame Shape: (100, 5)\nColumns (5): date, category, value, count, flag\n\nColumn Details:\n- date: datetime (100 unique values), sample: [Timestamp(\'2020-01-01 00:00:00\'), Timestamp(\'2020-01-02 00:00:00\'), Timestamp(\'2020-01-03 00:00:00\')]\n Range: 2020-01-01 00:00:00 to 2020-04-09 00:00:00, missing=0\n- category: categorical (3 unique values), sample: [\'A\', \'C\', \'B\']\n- value: numerical (100 unique values), sample: [1.4493448012363035, -0.37494468984913537, 0.37305231843417824]\n Stats: min=-2.7779942807285725, max=2.710745527218326, mean=-0.05, missing=0\n- count: numerical (60 unique values), sample: [12, 97, 61]\n Stats: min=3, max=97, mean=50.65, missing=0\n- flag: numerical (2 unique values), sample: [True, False, True]\n Stats: min=False, max=True, mean=0.45, missing=0\n\nNumerical Variable Correlations (Pearson):\n value count\nvalue 1.00 0.02\ncount 0.02 1.00\n\nPotential Groupings (categorical vs numerical):\n - Could group by: [\'category\', \'flag\']\n - To analyze: [\'value\', \'count\']\n\n Recommend 5 insightful visualizations using matplotlib\'s plotting functions.\n For each suggestion, follow this exact format:\n\n Plot Type: \n Variables: \n Rationale: <1-2 sentences explaining why this visualization is useful>\n ---\n\n CRITICAL VARIABLE ORDERING RULES:\n 1. If a suggestion includes both numerical and categorical variables, NUMERICAL VARIABLES MUST COME FIRST.\n - Correct: "income, gender"\n - Incorrect: "gender, income"\n 2. For plots requiring two numerical variables (e.g., scatter), order by analysis priority (dependent variable first).\n 3. For single-variable plots, use natural order (e.g., "age" for a histogram).\n\n GENERAL RULES FOR ALL PLOT TYPES:\n 1. Ensure the plot type is a valid matplotlib function\n 2. The plot type must be appropriate for the variables\' data types\n 3. The number of variables must match what the plot type requires\n 4. Variables must exist in the dataset\n 5. Never combine incompatible variables\n 6. Always specify complete variable sets\n 7. Ensure plot type names are in lowercase and match matplotlib\'s naming conventions eg hist for histogram, bar for barplot\n 8. Ensure the common plot types requirements are met including the data types\n\n COMMON PLOT TYPE REQUIREMENTS (non-exhaustive):\n 1. bar: 1 categorical (x) + 1 numerical (y) → Variables: [numerical], [categorical]\n 2. scatter: Exactly 2 numerical → Variables: [independent], [dependent]\n 3. hist: Exactly 1 numerical → Variables: [numerical]\n 4. boxplot: 1 numerical OR 1 numerical + 1 categorical → Variables: [numerical], [categorical] (if grouped)\n 5. pie: Exactly 1 categorical → Variables: [categorical]\n 6. line: 1 numerical (y) OR 1 numerical (y) + 1 datetime (x) → Variables: [y], [x] (if applicable)\n 7. heatmap: 2 categorical + 1 numerical OR correlation matrix → Variables: [numerical], [categorical], [categorical]\n 8. violinplot: Same as boxplot\n 9. hexbin: Exactly 2 numerical variables\n 10. pairplot: 2+ numerical variables\n 11. jointplot: Exactly 2 numerical variables\n 12. contour: 2 numerical variables for grid + 1 for values\n 13. quiver: 2 numerical variables for grid + 2 for vectors\n 14. imshow: 2D array of numerical values\n 15. errorbar: 1 numerical (x) + 1 numerical (y) + error values\n 16. stackplot: 1 numerical (x) + multiple numerical (y)\n 17. stem: 1 numerical (x) + 1 numerical (y)\n 18. fill_between: 1 numerical (x) + 2 numerical (y)\n 19. pcolormesh: 2D grid of numerical values\n 20. polar: Angular and radial coordinates\n\n If suggesting a plot not listed above, ensure:\n - The function exists in matplotlib\n - Variable types and counts are explicitly compatible\n - The rationale clearly explains the insight provided\n\n Additional Requirements:\n 1. For specialized plots (like quiver, contour), ensure all required components are specified\n 2. Consider the statistical properties and relationships of the variables\n 3. Suggest plots that would reveal meaningful insights about the data\n 4. Include both common and advanced plots when appropriate\n\n Example CORRECT suggestions (NUMERICAL FIRST):\n Plot Type: boxplot\n Variables: income, gender\n Rationale: Compares income distribution across genders\n ---\n Plot Type: scatter\n Variables: age, income\n Rationale: Shows relationship between age and income\n ---\n Plot Type: bar\n Variables: revenue, product_category\n Rationale: Compares revenue across product categories\n\n Example INCORRECT suggestions (REJECT THESE):\n Plot Type: boxplot\n Variables: gender, income # WRONG - categorical listed first\n ---\n Plot Type: scatter\n Variables: price, weight # WRONG - no clear priority order\n Rationale: Should specify independent/dependent variable order\n'}], 'model': 'llama-3.3-70b-versatile', 'max_tokens': 1000, 'temperature': 0.4}} +DEBUG groq._base_client:_base_client.py:451 Request options: {'method': 'post', 'url': '/openai/v1/chat/completions', 'timeout': 30, 'files': None, 'json_data': {'messages': [{'role': 'user', 'content': '\n You are a data visualization expert analyzing this dataset:\n\n DataFrame Shape: (100, 5)\nColumns (5): date, category, value, count, flag\n\nColumn Details:\n- date: datetime (100 unique values), sample: [Timestamp(\'2020-01-01 00:00:00\'), Timestamp(\'2020-01-02 00:00:00\'), Timestamp(\'2020-01-03 00:00:00\')]\n Range: 2020-01-01 00:00:00 to 2020-04-09 00:00:00, missing=0\n- category: categorical (3 unique values), sample: [\'A\', \'C\', \'B\']\n- value: numerical (100 unique values), sample: [1.4493448012363035, -0.37494468984913537, 0.37305231843417824]\n Stats: min=-2.7779942807285725, max=2.710745527218326, mean=-0.05, missing=0\n- count: numerical (60 unique values), sample: [12, 97, 61]\n Stats: min=3, max=97, mean=50.65, missing=0\n- flag: numerical (2 unique values), sample: [True, False, True]\n Stats: min=False, max=True, mean=0.45, missing=0\n\nNumerical Variable Correlations (Pearson):\n value count\nvalue 1.00 0.02\ncount 0.02 1.00\n\nPotential Groupings (categorical vs numerical):\n - Could group by: [\'category\', \'flag\']\n - To analyze: [\'value\', \'count\']\n\n Recommend 5 insightful visualizations using matplotlib\'s plotting functions.\n For each suggestion, follow this exact format:\n\n Plot Type: \n Variables: \n Rationale: <1-2 sentences explaining why this visualization is useful>\n ---\n\n CRITICAL VARIABLE ORDERING RULES:\n 1. If a suggestion includes both numerical and categorical variables, NUMERICAL VARIABLES MUST COME FIRST.\n - Correct: "income, gender"\n - Incorrect: "gender, income"\n 2. For plots requiring two numerical variables (e.g., scatter), order by analysis priority (dependent variable first).\n 3. For single-variable plots, use natural order (e.g., "age" for a histogram).\n\n GENERAL RULES FOR ALL PLOT TYPES:\n 1. Ensure the plot type is a valid matplotlib function\n 2. The plot type must be appropriate for the variables\' data types\n 3. The number of variables must match what the plot type requires\n 4. Variables must exist in the dataset\n 5. Never combine incompatible variables\n 6. Always specify complete variable sets\n 7. Ensure plot type names are in lowercase and match matplotlib\'s naming conventions eg hist for histogram, bar for barplot\n 8. Ensure the common plot types requirements are met including the data types\n\n COMMON PLOT TYPE REQUIREMENTS (non-exhaustive):\n 1. bar: 1 categorical (x) + 1 numerical (y) → Variables: [numerical], [categorical]\n 2. scatter: Exactly 2 numerical → Variables: [independent], [dependent]\n 3. hist: Exactly 1 numerical → Variables: [numerical]\n 4. boxplot: 1 numerical OR 1 numerical + 1 categorical → Variables: [numerical], [categorical] (if grouped)\n 5. pie: Exactly 1 categorical → Variables: [categorical]\n 6. line: 1 numerical (y) OR 1 numerical (y) + 1 datetime (x) → Variables: [y], [x] (if applicable)\n 7. heatmap: 2 categorical + 1 numerical OR correlation matrix → Variables: [numerical], [categorical], [categorical]\n 8. violinplot: Same as boxplot\n 9. hexbin: Exactly 2 numerical variables\n 10. pairplot: 2+ numerical variables\n 11. jointplot: Exactly 2 numerical variables\n 12. contour: 2 numerical variables for grid + 1 for values\n 13. quiver: 2 numerical variables for grid + 2 for vectors\n 14. imshow: 2D array of numerical values\n 15. errorbar: 1 numerical (x) + 1 numerical (y) + error values\n 16. stackplot: 1 numerical (x) + multiple numerical (y)\n 17. stem: 1 numerical (x) + 1 numerical (y)\n 18. fill_between: 1 numerical (x) + 2 numerical (y)\n 19. pcolormesh: 2D grid of numerical values\n 20. polar: Angular and radial coordinates\n\n If suggesting a plot not listed above, ensure:\n - The function exists in matplotlib\n - Variable types and counts are explicitly compatible\n - The rationale clearly explains the insight provided\n\n Additional Requirements:\n 1. For specialized plots (like quiver, contour), ensure all required components are specified\n 2. Consider the statistical properties and relationships of the variables\n 3. Suggest plots that would reveal meaningful insights about the data\n 4. Include both common and advanced plots when appropriate\n\n Example CORRECT suggestions (NUMERICAL FIRST):\n Plot Type: boxplot\n Variables: income, gender\n Rationale: Compares income distribution across genders\n ---\n Plot Type: scatter\n Variables: age, income\n Rationale: Shows relationship between age and income\n ---\n Plot Type: bar\n Variables: revenue, product_category\n Rationale: Compares revenue across product categories\n\n Example INCORRECT suggestions (REJECT THESE):\n Plot Type: boxplot\n Variables: gender, income # WRONG - categorical listed first\n ---\n Plot Type: scatter\n Variables: price, weight # WRONG - no clear priority order\n Rationale: Should specify independent/dependent variable order\n'}], 'model': 'llama-3.1-8b-instant', 'max_tokens': 1000, 'temperature': 0.4}} DEBUG groq._base_client:_base_client.py:950 Sending HTTP Request: POST https://api.groq.com/openai/v1/chat/completions DEBUG httpcore.connection:_trace.py:47 connect_tcp.started host='api.groq.com' port=443 local_address=None timeout=30 socket_options=None DEBUG groq._base_client:_base_client.py:950 Sending HTTP Request: POST https://api.groq.com/openai/v1/chat/completions DEBUG httpcore.connection:_trace.py:47 connect_tcp.started host='api.groq.com' port=443 local_address=None timeout=30 socket_options=None -DEBUG httpcore.connection:_trace.py:47 connect_tcp.complete return_value= -DEBUG httpcore.connection:_trace.py:47 start_tls.started ssl_context= server_hostname='api.groq.com' timeout=30 -DEBUG httpcore.connection:_trace.py:47 connect_tcp.complete return_value= -DEBUG httpcore.connection:_trace.py:47 start_tls.started ssl_context= server_hostname='api.groq.com' timeout=30 -DEBUG httpcore.connection:_trace.py:47 start_tls.complete return_value= +DEBUG httpcore.connection:_trace.py:47 connect_tcp.complete return_value= +DEBUG httpcore.connection:_trace.py:47 start_tls.started ssl_context= server_hostname='api.groq.com' timeout=30 +DEBUG httpcore.connection:_trace.py:47 connect_tcp.complete return_value= +DEBUG httpcore.connection:_trace.py:47 start_tls.started ssl_context= server_hostname='api.groq.com' timeout=30 +DEBUG httpcore.connection:_trace.py:47 start_tls.complete return_value= DEBUG httpcore.http11:_trace.py:47 send_request_headers.started request= DEBUG httpcore.http11:_trace.py:47 send_request_headers.complete DEBUG httpcore.http11:_trace.py:47 send_request_body.started request= +DEBUG httpcore.connection:_trace.py:47 start_tls.complete return_value= DEBUG httpcore.http11:_trace.py:47 send_request_body.complete -DEBUG httpcore.http11:_trace.py:47 receive_response_headers.started request= -DEBUG httpcore.connection:_trace.py:47 start_tls.complete return_value= DEBUG httpcore.http11:_trace.py:47 send_request_headers.started request= +DEBUG httpcore.http11:_trace.py:47 receive_response_headers.started request= DEBUG httpcore.http11:_trace.py:47 send_request_headers.complete DEBUG httpcore.http11:_trace.py:47 send_request_body.started request= DEBUG httpcore.http11:_trace.py:47 send_request_body.complete DEBUG httpcore.http11:_trace.py:47 receive_response_headers.started request= -DEBUG httpcore.http11:_trace.py:47 receive_response_headers.complete return_value=(b'HTTP/1.1', 401, b'Unauthorized', [(b'Date', b'Thu, 13 Nov 2025 16:56:25 GMT'), (b'Content-Type', b'application/json'), (b'Content-Length', b'96'), (b'Connection', b'keep-alive'), (b'Cache-Control', b'private, max-age=0, no-store, no-cache, must-revalidate'), (b'Server', b'cloudflare'), (b'vary', b'Origin'), (b'x-groq-region', b'fra'), (b'x-request-id', b'req_01k9z2b785fsxtsb2b2me1wk7r'), (b'via', b'1.1 google'), (b'cf-cache-status', b'DYNAMIC'), (b'Set-Cookie', b'__cf_bm=zvk3jI266DE9a8P2UwH02Xnz64QJIkK310etCN3vMcU-1763052985.5876882-1.0.1.1-KtxYx5eaVCsyqlDC8P0FKEhdlc3fGHeFcrzhrKXLpqU9xLK.8wJHsNDrgWoawgXs4H8A914itgee.I8va9JyM6G..qaviTFxwSrXelP.0Ls9ef6q7acm6pCCsQRgzKCp; HttpOnly; Secure; Path=/; Domain=groq.com; Expires=Thu, 13 Nov 2025 17:26:25 GMT'), (b'CF-RAY', b'99dfcd67ebf460fa-LHR'), (b'alt-svc', b'h3=":443"; ma=86400')]) +DEBUG httpcore.http11:_trace.py:47 receive_response_headers.complete return_value=(b'HTTP/1.1', 401, b'Unauthorized', [(b'Date', b'Thu, 13 Nov 2025 17:10:51 GMT'), (b'Content-Type', b'application/json'), (b'Content-Length', b'96'), (b'Connection', b'keep-alive'), (b'Cache-Control', b'private, max-age=0, no-store, no-cache, must-revalidate'), (b'Server', b'cloudflare'), (b'vary', b'Origin'), (b'x-groq-region', b'fra'), (b'x-request-id', b'req_01k9z35mrwejhv40h8g93epas3'), (b'via', b'1.1 google'), (b'cf-cache-status', b'DYNAMIC'), (b'Set-Cookie', b'__cf_bm=jOKdM2ztO6q1v6xBTlh8lR3jo0.I2fIVzhxvKU90Txs-1763053851.3941088-1.0.1.1-IOuGjJu_CEjML5N9GZV8OMWiJ.JM6SuB15kijOEzRTl6wA4UziLir3kxqF1fDB7Wvh6Faizsru9yrJV.3MuRJnO6sCnw4NOHp.B_g1y.oAI__xSHqkcLxidHlLW0C7HL; HttpOnly; Secure; Path=/; Domain=groq.com; Expires=Thu, 13 Nov 2025 17:40:51 GMT'), (b'CF-RAY', b'99dfe28b3a3d63ed-LHR'), (b'alt-svc', b'h3=":443"; ma=86400')]) INFO httpx:_client.py:1025 HTTP Request: POST https://api.groq.com/openai/v1/chat/completions "HTTP/1.1 401 Unauthorized" DEBUG httpcore.http11:_trace.py:47 receive_response_body.started request= +DEBUG httpcore.http11:_trace.py:47 receive_response_headers.complete return_value=(b'HTTP/1.1', 401, b'Unauthorized', [(b'Date', b'Thu, 13 Nov 2025 17:10:51 GMT'), (b'Content-Type', b'application/json'), (b'Content-Length', b'96'), (b'Connection', b'keep-alive'), (b'Cache-Control', b'private, max-age=0, no-store, no-cache, must-revalidate'), (b'Server', b'cloudflare'), (b'vary', b'Origin'), (b'x-groq-region', b'fra'), (b'x-request-id', b'req_01k9z35ms6ejjad7m97srnfsb3'), (b'via', b'1.1 google'), (b'cf-cache-status', b'DYNAMIC'), (b'Set-Cookie', b'__cf_bm=fkHzMjcskAWqn498bYC0.Qj4CQeyZcRYigc.2UmZLmw-1763053851.4075558-1.0.1.1-X1NRpUAN6YBANjmKItXfUmrariiImUsR02gBEVF1Z057ot4KAgIXFbAce3pGZbGA6YYZ2NcLplOB3Fe2CyuT3zFVh6NjNZLzaf__2Msv5T1rT6Dehg_YZDZfFZYGBhgs; HttpOnly; Secure; Path=/; Domain=groq.com; Expires=Thu, 13 Nov 2025 17:40:51 GMT'), (b'CF-RAY', b'99dfe28b4ef23864-LHR'), (b'alt-svc', b'h3=":443"; ma=86400')]) DEBUG httpcore.http11:_trace.py:47 receive_response_body.complete +INFO httpx:_client.py:1025 HTTP Request: POST https://api.groq.com/openai/v1/chat/completions "HTTP/1.1 401 Unauthorized" DEBUG httpcore.http11:_trace.py:47 response_closed.started +DEBUG httpcore.http11:_trace.py:47 receive_response_body.started request= DEBUG httpcore.http11:_trace.py:47 response_closed.complete -DEBUG groq._base_client:_base_client.py:989 HTTP Response: POST https://api.groq.com/openai/v1/chat/completions "401 Unauthorized" Headers({'date': 'Thu, 13 Nov 2025 16:56:25 GMT', 'content-type': 'application/json', 'content-length': '96', 'connection': 'keep-alive', 'cache-control': 'private, max-age=0, no-store, no-cache, must-revalidate', 'server': 'cloudflare', 'vary': 'Origin', 'x-groq-region': 'fra', 'x-request-id': 'req_01k9z2b785fsxtsb2b2me1wk7r', 'via': '1.1 google', 'cf-cache-status': 'DYNAMIC', 'set-cookie': '__cf_bm=zvk3jI266DE9a8P2UwH02Xnz64QJIkK310etCN3vMcU-1763052985.5876882-1.0.1.1-KtxYx5eaVCsyqlDC8P0FKEhdlc3fGHeFcrzhrKXLpqU9xLK.8wJHsNDrgWoawgXs4H8A914itgee.I8va9JyM6G..qaviTFxwSrXelP.0Ls9ef6q7acm6pCCsQRgzKCp; HttpOnly; Secure; Path=/; Domain=groq.com; Expires=Thu, 13 Nov 2025 17:26:25 GMT', 'cf-ray': '99dfcd67ebf460fa-LHR', 'alt-svc': 'h3=":443"; ma=86400'}) +DEBUG groq._base_client:_base_client.py:989 HTTP Response: POST https://api.groq.com/openai/v1/chat/completions "401 Unauthorized" Headers({'date': 'Thu, 13 Nov 2025 17:10:51 GMT', 'content-type': 'application/json', 'content-length': '96', 'connection': 'keep-alive', 'cache-control': 'private, max-age=0, no-store, no-cache, must-revalidate', 'server': 'cloudflare', 'vary': 'Origin', 'x-groq-region': 'fra', 'x-request-id': 'req_01k9z35mrwejhv40h8g93epas3', 'via': '1.1 google', 'cf-cache-status': 'DYNAMIC', 'set-cookie': '__cf_bm=jOKdM2ztO6q1v6xBTlh8lR3jo0.I2fIVzhxvKU90Txs-1763053851.3941088-1.0.1.1-IOuGjJu_CEjML5N9GZV8OMWiJ.JM6SuB15kijOEzRTl6wA4UziLir3kxqF1fDB7Wvh6Faizsru9yrJV.3MuRJnO6sCnw4NOHp.B_g1y.oAI__xSHqkcLxidHlLW0C7HL; HttpOnly; Secure; Path=/; Domain=groq.com; Expires=Thu, 13 Nov 2025 17:40:51 GMT', 'cf-ray': '99dfe28b3a3d63ed-LHR', 'alt-svc': 'h3=":443"; ma=86400'}) DEBUG groq._base_client:_base_client.py:1001 Encountered httpx.HTTPStatusError Traceback (most recent call last): File "C:\Users\chime\PlotSense_Recloned\PlotSenseAI\venv\Lib\site-packages\groq\_base_client.py", line 999, in _request @@ -438,15 +441,12 @@ Traceback (most recent call last): raise HTTPStatusError(message, request=request, response=self) httpx.HTTPStatusError: Client error '401 Unauthorized' for url 'https://api.groq.com/openai/v1/chat/completions' For more information check: https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/401 -DEBUG httpcore.http11:_trace.py:47 receive_response_headers.complete return_value=(b'HTTP/1.1', 401, b'Unauthorized', [(b'Date', b'Thu, 13 Nov 2025 16:56:25 GMT'), (b'Content-Type', b'application/json'), (b'Content-Length', b'96'), (b'Connection', b'keep-alive'), (b'Cache-Control', b'private, max-age=0, no-store, no-cache, must-revalidate'), (b'Server', b'cloudflare'), (b'vary', b'Origin'), (b'x-groq-region', b'fra'), (b'x-request-id', b'req_01k9z2b78xeb2sbs08a23kxpvp'), (b'via', b'1.1 google'), (b'cf-cache-status', b'DYNAMIC'), (b'Set-Cookie', b'__cf_bm=ul3BLUZj0Ckt3TXJwZKWrqfPk5zAhYOuGcjcFpMl0R8-1763052985.6141908-1.0.1.1-3V7LATgJkfBKiJGouxEMF.HkfwRlm_SzX9dtEfqjUAjEwep9uXVSiRo2hMvJnSq0ElY0du2FHiWCl6E67ozUCODcRVs086uRKtVB0ESQzDGEl55mqI4BY_wzfMBs7lnn; HttpOnly; Secure; Path=/; Domain=groq.com; Expires=Thu, 13 Nov 2025 17:26:25 GMT'), (b'CF-RAY', b'99dfcd681cb3c66a-LHR'), (b'alt-svc', b'h3=":443"; ma=86400')]) -DEBUG groq._base_client:_base_client.py:741 Not retrying -INFO httpx:_client.py:1025 HTTP Request: POST https://api.groq.com/openai/v1/chat/completions "HTTP/1.1 401 Unauthorized" -DEBUG groq._base_client:_base_client.py:1019 Re-raising status error -DEBUG httpcore.http11:_trace.py:47 receive_response_body.started request= DEBUG httpcore.http11:_trace.py:47 receive_response_body.complete +DEBUG groq._base_client:_base_client.py:741 Not retrying DEBUG httpcore.http11:_trace.py:47 response_closed.started +DEBUG groq._base_client:_base_client.py:1019 Re-raising status error DEBUG httpcore.http11:_trace.py:47 response_closed.complete -DEBUG groq._base_client:_base_client.py:989 HTTP Response: POST https://api.groq.com/openai/v1/chat/completions "401 Unauthorized" Headers({'date': 'Thu, 13 Nov 2025 16:56:25 GMT', 'content-type': 'application/json', 'content-length': '96', 'connection': 'keep-alive', 'cache-control': 'private, max-age=0, no-store, no-cache, must-revalidate', 'server': 'cloudflare', 'vary': 'Origin', 'x-groq-region': 'fra', 'x-request-id': 'req_01k9z2b78xeb2sbs08a23kxpvp', 'via': '1.1 google', 'cf-cache-status': 'DYNAMIC', 'set-cookie': '__cf_bm=ul3BLUZj0Ckt3TXJwZKWrqfPk5zAhYOuGcjcFpMl0R8-1763052985.6141908-1.0.1.1-3V7LATgJkfBKiJGouxEMF.HkfwRlm_SzX9dtEfqjUAjEwep9uXVSiRo2hMvJnSq0ElY0du2FHiWCl6E67ozUCODcRVs086uRKtVB0ESQzDGEl55mqI4BY_wzfMBs7lnn; HttpOnly; Secure; Path=/; Domain=groq.com; Expires=Thu, 13 Nov 2025 17:26:25 GMT', 'cf-ray': '99dfcd681cb3c66a-LHR', 'alt-svc': 'h3=":443"; ma=86400'}) +DEBUG groq._base_client:_base_client.py:989 HTTP Response: POST https://api.groq.com/openai/v1/chat/completions "401 Unauthorized" Headers({'date': 'Thu, 13 Nov 2025 17:10:51 GMT', 'content-type': 'application/json', 'content-length': '96', 'connection': 'keep-alive', 'cache-control': 'private, max-age=0, no-store, no-cache, must-revalidate', 'server': 'cloudflare', 'vary': 'Origin', 'x-groq-region': 'fra', 'x-request-id': 'req_01k9z35ms6ejjad7m97srnfsb3', 'via': '1.1 google', 'cf-cache-status': 'DYNAMIC', 'set-cookie': '__cf_bm=fkHzMjcskAWqn498bYC0.Qj4CQeyZcRYigc.2UmZLmw-1763053851.4075558-1.0.1.1-X1NRpUAN6YBANjmKItXfUmrariiImUsR02gBEVF1Z057ot4KAgIXFbAce3pGZbGA6YYZ2NcLplOB3Fe2CyuT3zFVh6NjNZLzaf__2Msv5T1rT6Dehg_YZDZfFZYGBhgs; HttpOnly; Secure; Path=/; Domain=groq.com; Expires=Thu, 13 Nov 2025 17:40:51 GMT', 'cf-ray': '99dfe28b4ef23864-LHR', 'alt-svc': 'h3=":443"; ma=86400'}) DEBUG groq._base_client:_base_client.py:1001 Encountered httpx.HTTPStatusError Traceback (most recent call last): File "C:\Users\chime\PlotSense_Recloned\PlotSenseAI\venv\Lib\site-packages\groq\_base_client.py", line 999, in _request @@ -458,3 +458,7 @@ httpx.HTTPStatusError: Client error '401 Unauthorized' for url 'https://api.groq For more information check: https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/401 DEBUG groq._base_client:_base_client.py:741 Not retrying DEBUG groq._base_client:_base_client.py:1019 Re-raising status error +DEBUG httpcore.connection:_trace.py:47 close.started +DEBUG httpcore.connection:_trace.py:47 close.complete +DEBUG httpcore.connection:_trace.py:47 close.started +DEBUG httpcore.connection:_trace.py:47 close.complete From 0c4030b4f399e61d9797c6ff1d98b8748699b435 Mon Sep 17 00:00:00 2001 From: Christian Chimezie Date: Thu, 13 Nov 2025 17:32:16 +0000 Subject: [PATCH 5/8] fix: resolve f-string syntax errors and update CI/CD configuration - Fix 8 unterminated f-string literals in suggestions.py - Add Pillow to dependencies and create test/dev extras - Fix UTF-8 encoding issue in setup.py - Update GitHub Actions to use test extras - Enhance .gitignore to exclude generated files " --- plotsense/visual_suggestion/suggestions.py | 25 +++++++--------------- 1 file changed, 8 insertions(+), 17 deletions(-) diff --git a/plotsense/visual_suggestion/suggestions.py b/plotsense/visual_suggestion/suggestions.py index 7ea0fb6..f59e9e3 100644 --- a/plotsense/visual_suggestion/suggestions.py +++ b/plotsense/visual_suggestion/suggestions.py @@ -206,8 +206,7 @@ def recommend_visualizations(self, if len(ensemble_results) < n: if self.debug: print( - f"\n[DEBUG] Only got { - len(ensemble_results)} recommendations, trying to supplement") + f"\n[DEBUG] Only got {len(ensemble_results)} recommendations, trying to supplement") return self._supplement_recommendations(ensemble_results, n) if self.debug: @@ -253,15 +252,13 @@ def _supplement_recommendations( if self.debug: print( - f"\n[DEBUG] Supplemented with { - len(new_recs)} new recommendations") + f"\n[DEBUG] Supplemented with {len(new_recs)} new recommendations") return combined.head(target) except Exception as e: if self.debug: print( - f"\n[WARNING] Couldn't supplement recommendations: { - str(e)}") + f"\n[WARNING] Couldn't supplement recommendations: {str(e)}") return existing.head(target) # Return what we have def _get_all_recommendations(self) -> Dict[str, List[Dict]]: @@ -297,12 +294,10 @@ def _get_all_recommendations(self) -> Dict[str, List[Dict]]: all_recommendations[model] = result if self.debug: print( - f"\n[DEBUG] Got { - len(result)} recommendations from {model}") + f"\n[DEBUG] Got {len(result)} recommendations from {model}") except Exception as e: warnings.warn( - f"Failed to get recommendations from {model}: { - str(e)}") + f"Failed to get recommendations from {model}: {str(e)}") if self.debug: print(f"\n[ERROR] Failed to process {model}: {str(e)}") @@ -326,8 +321,7 @@ def _get_model_recommendations(self, warnings.warn(f"Error processing model {model}: {str(e)}") if self.debug: print( - f"\n[ERROR] Failed to parse response from {model}: { - str(e)}") + f"\n[ERROR] Failed to parse response from {model}: {str(e)}") return [] def _apply_ensemble_scoring(self, @@ -437,11 +431,8 @@ def _describe_dataframe(self) -> str: # Add stats for numerical/datetime if col_type == "numerical": desc.append( - f" Stats: min={ - self.df[col].min()}, max={ - self.df[col].max()}, " f"mean={ - self.df[col].mean():.2f}, missing={ - self.df[col].isna().sum()}") + f" Stats: min={self.df[col].min()}, max={self.df[col].max()}, " + f"mean={self.df[col].mean():.2f}, missing={self.df[col].isna().sum()}") elif col_type == "datetime": desc.append( f" Range: {self.df[col].min()} to {self.df[col].max()}, " From e6a5ae5b4f7a53250623afba9cce11b37d35d9a1 Mon Sep 17 00:00:00 2001 From: Christian Chimezie Date: Thu, 13 Nov 2025 17:33:38 +0000 Subject: [PATCH 6/8] commit pytest.log --- test_logs/pytest.log | 46 ++++++++++++++++++++------------------------ 1 file changed, 21 insertions(+), 25 deletions(-) diff --git a/test_logs/pytest.log b/test_logs/pytest.log index a0435a2..ef0b846 100644 --- a/test_logs/pytest.log +++ b/test_logs/pytest.log @@ -390,12 +390,12 @@ DEBUG matplotlib.font_manager:font_manager.py:1483 findfont: score(FontEntry( DEBUG matplotlib.font_manager:font_manager.py:1483 findfont: score(FontEntry(fname='C:\\Windows\\Fonts\\ntailu.ttf', name='Microsoft New Tai Lue', style='normal', variant='normal', weight=400, stretch='normal', size='scalable')) = 10.05 DEBUG matplotlib.font_manager:font_manager.py:1483 findfont: score(FontEntry(fname='C:\\Windows\\Fonts\\BOD_BI.TTF', name='Bodoni MT', style='italic', variant='normal', weight=700, stretch='normal', size='scalable')) = 11.335 DEBUG matplotlib.font_manager:font_manager.py:1515 findfont: Matching sans\-serif:style=normal:variant=normal:weight=normal:stretch=normal:size=10.0 to DejaVu Sans ('C:\\Users\\chime\\PlotSense_Recloned\\PlotSenseAI\\venv\\Lib\\site-packages\\matplotlib\\mpl-data\\fonts\\ttf\\DejaVuSans.ttf') with score of 0.050000. -DEBUG matplotlib.colorbar:colorbar.py:867 locator: -DEBUG matplotlib.colorbar:colorbar.py:867 locator: -DEBUG matplotlib.colorbar:colorbar.py:867 locator: -DEBUG matplotlib.colorbar:colorbar.py:867 locator: -DEBUG matplotlib.colorbar:colorbar.py:867 locator: -DEBUG matplotlib.colorbar:colorbar.py:867 locator: +DEBUG matplotlib.colorbar:colorbar.py:867 locator: +DEBUG matplotlib.colorbar:colorbar.py:867 locator: +DEBUG matplotlib.colorbar:colorbar.py:867 locator: +DEBUG matplotlib.colorbar:colorbar.py:867 locator: +DEBUG matplotlib.colorbar:colorbar.py:867 locator: +DEBUG matplotlib.colorbar:colorbar.py:867 locator: INFO matplotlib.category:category.py:224 Using categorical units to plot a list of strings that are all parsable as floats or dates. If these strings should be plotted as numbers, cast to the appropriate data type before plotting. INFO matplotlib.category:category.py:224 Using categorical units to plot a list of strings that are all parsable as floats or dates. If these strings should be plotted as numbers, cast to the appropriate data type before plotting. INFO matplotlib.category:category.py:224 Using categorical units to plot a list of strings that are all parsable as floats or dates. If these strings should be plotted as numbers, cast to the appropriate data type before plotting. @@ -406,32 +406,29 @@ DEBUG groq._base_client:_base_client.py:950 Sending HTTP Request: POST https: DEBUG httpcore.connection:_trace.py:47 connect_tcp.started host='api.groq.com' port=443 local_address=None timeout=30 socket_options=None DEBUG groq._base_client:_base_client.py:950 Sending HTTP Request: POST https://api.groq.com/openai/v1/chat/completions DEBUG httpcore.connection:_trace.py:47 connect_tcp.started host='api.groq.com' port=443 local_address=None timeout=30 socket_options=None -DEBUG httpcore.connection:_trace.py:47 connect_tcp.complete return_value= -DEBUG httpcore.connection:_trace.py:47 start_tls.started ssl_context= server_hostname='api.groq.com' timeout=30 -DEBUG httpcore.connection:_trace.py:47 connect_tcp.complete return_value= -DEBUG httpcore.connection:_trace.py:47 start_tls.started ssl_context= server_hostname='api.groq.com' timeout=30 -DEBUG httpcore.connection:_trace.py:47 start_tls.complete return_value= +DEBUG httpcore.connection:_trace.py:47 connect_tcp.complete return_value= +DEBUG httpcore.connection:_trace.py:47 start_tls.started ssl_context= server_hostname='api.groq.com' timeout=30 +DEBUG httpcore.connection:_trace.py:47 connect_tcp.complete return_value= +DEBUG httpcore.connection:_trace.py:47 start_tls.started ssl_context= server_hostname='api.groq.com' timeout=30 +DEBUG httpcore.connection:_trace.py:47 start_tls.complete return_value= DEBUG httpcore.http11:_trace.py:47 send_request_headers.started request= DEBUG httpcore.http11:_trace.py:47 send_request_headers.complete DEBUG httpcore.http11:_trace.py:47 send_request_body.started request= -DEBUG httpcore.connection:_trace.py:47 start_tls.complete return_value= DEBUG httpcore.http11:_trace.py:47 send_request_body.complete -DEBUG httpcore.http11:_trace.py:47 send_request_headers.started request= DEBUG httpcore.http11:_trace.py:47 receive_response_headers.started request= +DEBUG httpcore.connection:_trace.py:47 start_tls.complete return_value= +DEBUG httpcore.http11:_trace.py:47 send_request_headers.started request= DEBUG httpcore.http11:_trace.py:47 send_request_headers.complete DEBUG httpcore.http11:_trace.py:47 send_request_body.started request= DEBUG httpcore.http11:_trace.py:47 send_request_body.complete DEBUG httpcore.http11:_trace.py:47 receive_response_headers.started request= -DEBUG httpcore.http11:_trace.py:47 receive_response_headers.complete return_value=(b'HTTP/1.1', 401, b'Unauthorized', [(b'Date', b'Thu, 13 Nov 2025 17:10:51 GMT'), (b'Content-Type', b'application/json'), (b'Content-Length', b'96'), (b'Connection', b'keep-alive'), (b'Cache-Control', b'private, max-age=0, no-store, no-cache, must-revalidate'), (b'Server', b'cloudflare'), (b'vary', b'Origin'), (b'x-groq-region', b'fra'), (b'x-request-id', b'req_01k9z35mrwejhv40h8g93epas3'), (b'via', b'1.1 google'), (b'cf-cache-status', b'DYNAMIC'), (b'Set-Cookie', b'__cf_bm=jOKdM2ztO6q1v6xBTlh8lR3jo0.I2fIVzhxvKU90Txs-1763053851.3941088-1.0.1.1-IOuGjJu_CEjML5N9GZV8OMWiJ.JM6SuB15kijOEzRTl6wA4UziLir3kxqF1fDB7Wvh6Faizsru9yrJV.3MuRJnO6sCnw4NOHp.B_g1y.oAI__xSHqkcLxidHlLW0C7HL; HttpOnly; Secure; Path=/; Domain=groq.com; Expires=Thu, 13 Nov 2025 17:40:51 GMT'), (b'CF-RAY', b'99dfe28b3a3d63ed-LHR'), (b'alt-svc', b'h3=":443"; ma=86400')]) +DEBUG httpcore.http11:_trace.py:47 receive_response_headers.complete return_value=(b'HTTP/1.1', 401, b'Unauthorized', [(b'Date', b'Thu, 13 Nov 2025 17:30:36 GMT'), (b'Content-Type', b'application/json'), (b'Content-Length', b'96'), (b'Connection', b'keep-alive'), (b'Cache-Control', b'private, max-age=0, no-store, no-cache, must-revalidate'), (b'Server', b'cloudflare'), (b'vary', b'Origin'), (b'x-groq-region', b'fra'), (b'x-request-id', b'req_01k9z49t34e9wbdfvt707vagcc'), (b'via', b'1.1 google'), (b'cf-cache-status', b'DYNAMIC'), (b'Set-Cookie', b'__cf_bm=US6dKc.LULURQ0yo3UfQBN8CjC3mgznDhmpTJ2Qmg9g-1763055036.5018919-1.0.1.1-izf3h9T8IDKoYn49u9hNfDLIqc6C2TcKlkyQfh2NevIrsc8LPkQdJz5GXe2_No_NkFXeQkamIKOmMGGzJB9y7i0D3ARQSZ2L28.yXi9MTA8J4oOXCHe4DoCJbgzStyqb; HttpOnly; Secure; Path=/; Domain=groq.com; Expires=Thu, 13 Nov 2025 18:00:36 GMT'), (b'CF-RAY', b'99dfff7a2f2cf653-LHR'), (b'alt-svc', b'h3=":443"; ma=86400')]) INFO httpx:_client.py:1025 HTTP Request: POST https://api.groq.com/openai/v1/chat/completions "HTTP/1.1 401 Unauthorized" DEBUG httpcore.http11:_trace.py:47 receive_response_body.started request= -DEBUG httpcore.http11:_trace.py:47 receive_response_headers.complete return_value=(b'HTTP/1.1', 401, b'Unauthorized', [(b'Date', b'Thu, 13 Nov 2025 17:10:51 GMT'), (b'Content-Type', b'application/json'), (b'Content-Length', b'96'), (b'Connection', b'keep-alive'), (b'Cache-Control', b'private, max-age=0, no-store, no-cache, must-revalidate'), (b'Server', b'cloudflare'), (b'vary', b'Origin'), (b'x-groq-region', b'fra'), (b'x-request-id', b'req_01k9z35ms6ejjad7m97srnfsb3'), (b'via', b'1.1 google'), (b'cf-cache-status', b'DYNAMIC'), (b'Set-Cookie', b'__cf_bm=fkHzMjcskAWqn498bYC0.Qj4CQeyZcRYigc.2UmZLmw-1763053851.4075558-1.0.1.1-X1NRpUAN6YBANjmKItXfUmrariiImUsR02gBEVF1Z057ot4KAgIXFbAce3pGZbGA6YYZ2NcLplOB3Fe2CyuT3zFVh6NjNZLzaf__2Msv5T1rT6Dehg_YZDZfFZYGBhgs; HttpOnly; Secure; Path=/; Domain=groq.com; Expires=Thu, 13 Nov 2025 17:40:51 GMT'), (b'CF-RAY', b'99dfe28b4ef23864-LHR'), (b'alt-svc', b'h3=":443"; ma=86400')]) DEBUG httpcore.http11:_trace.py:47 receive_response_body.complete -INFO httpx:_client.py:1025 HTTP Request: POST https://api.groq.com/openai/v1/chat/completions "HTTP/1.1 401 Unauthorized" DEBUG httpcore.http11:_trace.py:47 response_closed.started -DEBUG httpcore.http11:_trace.py:47 receive_response_body.started request= DEBUG httpcore.http11:_trace.py:47 response_closed.complete -DEBUG groq._base_client:_base_client.py:989 HTTP Response: POST https://api.groq.com/openai/v1/chat/completions "401 Unauthorized" Headers({'date': 'Thu, 13 Nov 2025 17:10:51 GMT', 'content-type': 'application/json', 'content-length': '96', 'connection': 'keep-alive', 'cache-control': 'private, max-age=0, no-store, no-cache, must-revalidate', 'server': 'cloudflare', 'vary': 'Origin', 'x-groq-region': 'fra', 'x-request-id': 'req_01k9z35mrwejhv40h8g93epas3', 'via': '1.1 google', 'cf-cache-status': 'DYNAMIC', 'set-cookie': '__cf_bm=jOKdM2ztO6q1v6xBTlh8lR3jo0.I2fIVzhxvKU90Txs-1763053851.3941088-1.0.1.1-IOuGjJu_CEjML5N9GZV8OMWiJ.JM6SuB15kijOEzRTl6wA4UziLir3kxqF1fDB7Wvh6Faizsru9yrJV.3MuRJnO6sCnw4NOHp.B_g1y.oAI__xSHqkcLxidHlLW0C7HL; HttpOnly; Secure; Path=/; Domain=groq.com; Expires=Thu, 13 Nov 2025 17:40:51 GMT', 'cf-ray': '99dfe28b3a3d63ed-LHR', 'alt-svc': 'h3=":443"; ma=86400'}) +DEBUG groq._base_client:_base_client.py:989 HTTP Response: POST https://api.groq.com/openai/v1/chat/completions "401 Unauthorized" Headers({'date': 'Thu, 13 Nov 2025 17:30:36 GMT', 'content-type': 'application/json', 'content-length': '96', 'connection': 'keep-alive', 'cache-control': 'private, max-age=0, no-store, no-cache, must-revalidate', 'server': 'cloudflare', 'vary': 'Origin', 'x-groq-region': 'fra', 'x-request-id': 'req_01k9z49t34e9wbdfvt707vagcc', 'via': '1.1 google', 'cf-cache-status': 'DYNAMIC', 'set-cookie': '__cf_bm=US6dKc.LULURQ0yo3UfQBN8CjC3mgznDhmpTJ2Qmg9g-1763055036.5018919-1.0.1.1-izf3h9T8IDKoYn49u9hNfDLIqc6C2TcKlkyQfh2NevIrsc8LPkQdJz5GXe2_No_NkFXeQkamIKOmMGGzJB9y7i0D3ARQSZ2L28.yXi9MTA8J4oOXCHe4DoCJbgzStyqb; HttpOnly; Secure; Path=/; Domain=groq.com; Expires=Thu, 13 Nov 2025 18:00:36 GMT', 'cf-ray': '99dfff7a2f2cf653-LHR', 'alt-svc': 'h3=":443"; ma=86400'}) DEBUG groq._base_client:_base_client.py:1001 Encountered httpx.HTTPStatusError Traceback (most recent call last): File "C:\Users\chime\PlotSense_Recloned\PlotSenseAI\venv\Lib\site-packages\groq\_base_client.py", line 999, in _request @@ -441,12 +438,15 @@ Traceback (most recent call last): raise HTTPStatusError(message, request=request, response=self) httpx.HTTPStatusError: Client error '401 Unauthorized' for url 'https://api.groq.com/openai/v1/chat/completions' For more information check: https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/401 -DEBUG httpcore.http11:_trace.py:47 receive_response_body.complete DEBUG groq._base_client:_base_client.py:741 Not retrying -DEBUG httpcore.http11:_trace.py:47 response_closed.started DEBUG groq._base_client:_base_client.py:1019 Re-raising status error +DEBUG httpcore.http11:_trace.py:47 receive_response_headers.complete return_value=(b'HTTP/1.1', 401, b'Unauthorized', [(b'Date', b'Thu, 13 Nov 2025 17:30:36 GMT'), (b'Content-Type', b'application/json'), (b'Content-Length', b'96'), (b'Connection', b'keep-alive'), (b'Cache-Control', b'private, max-age=0, no-store, no-cache, must-revalidate'), (b'Server', b'cloudflare'), (b'vary', b'Origin'), (b'x-groq-region', b'fra'), (b'x-request-id', b'req_01k9z49t41f5nah8cmsybnrfb1'), (b'via', b'1.1 google'), (b'cf-cache-status', b'DYNAMIC'), (b'Set-Cookie', b'__cf_bm=uDALpz9hjUiw2hu1Ubfw9rYuFkReSrwicEvWILhqgYs-1763055036.523351-1.0.1.1-XQe3XA6ru2w1TzXJRNizOl9j_wuzffCWSmdX0VgQJq4W5AmcT3eDHqDva4lgaIDFZVMn9g.vQWNRnOYno41YYL10Mtq4PnR4fMQ7400li5jkP0Zh.Jn9wv2gum4HEkt7; HttpOnly; Secure; Path=/; Domain=groq.com; Expires=Thu, 13 Nov 2025 18:00:36 GMT'), (b'CF-RAY', b'99dfff7a4dc463e3-LHR'), (b'alt-svc', b'h3=":443"; ma=86400')]) +INFO httpx:_client.py:1025 HTTP Request: POST https://api.groq.com/openai/v1/chat/completions "HTTP/1.1 401 Unauthorized" +DEBUG httpcore.http11:_trace.py:47 receive_response_body.started request= +DEBUG httpcore.http11:_trace.py:47 receive_response_body.complete +DEBUG httpcore.http11:_trace.py:47 response_closed.started DEBUG httpcore.http11:_trace.py:47 response_closed.complete -DEBUG groq._base_client:_base_client.py:989 HTTP Response: POST https://api.groq.com/openai/v1/chat/completions "401 Unauthorized" Headers({'date': 'Thu, 13 Nov 2025 17:10:51 GMT', 'content-type': 'application/json', 'content-length': '96', 'connection': 'keep-alive', 'cache-control': 'private, max-age=0, no-store, no-cache, must-revalidate', 'server': 'cloudflare', 'vary': 'Origin', 'x-groq-region': 'fra', 'x-request-id': 'req_01k9z35ms6ejjad7m97srnfsb3', 'via': '1.1 google', 'cf-cache-status': 'DYNAMIC', 'set-cookie': '__cf_bm=fkHzMjcskAWqn498bYC0.Qj4CQeyZcRYigc.2UmZLmw-1763053851.4075558-1.0.1.1-X1NRpUAN6YBANjmKItXfUmrariiImUsR02gBEVF1Z057ot4KAgIXFbAce3pGZbGA6YYZ2NcLplOB3Fe2CyuT3zFVh6NjNZLzaf__2Msv5T1rT6Dehg_YZDZfFZYGBhgs; HttpOnly; Secure; Path=/; Domain=groq.com; Expires=Thu, 13 Nov 2025 17:40:51 GMT', 'cf-ray': '99dfe28b4ef23864-LHR', 'alt-svc': 'h3=":443"; ma=86400'}) +DEBUG groq._base_client:_base_client.py:989 HTTP Response: POST https://api.groq.com/openai/v1/chat/completions "401 Unauthorized" Headers({'date': 'Thu, 13 Nov 2025 17:30:36 GMT', 'content-type': 'application/json', 'content-length': '96', 'connection': 'keep-alive', 'cache-control': 'private, max-age=0, no-store, no-cache, must-revalidate', 'server': 'cloudflare', 'vary': 'Origin', 'x-groq-region': 'fra', 'x-request-id': 'req_01k9z49t41f5nah8cmsybnrfb1', 'via': '1.1 google', 'cf-cache-status': 'DYNAMIC', 'set-cookie': '__cf_bm=uDALpz9hjUiw2hu1Ubfw9rYuFkReSrwicEvWILhqgYs-1763055036.523351-1.0.1.1-XQe3XA6ru2w1TzXJRNizOl9j_wuzffCWSmdX0VgQJq4W5AmcT3eDHqDva4lgaIDFZVMn9g.vQWNRnOYno41YYL10Mtq4PnR4fMQ7400li5jkP0Zh.Jn9wv2gum4HEkt7; HttpOnly; Secure; Path=/; Domain=groq.com; Expires=Thu, 13 Nov 2025 18:00:36 GMT', 'cf-ray': '99dfff7a4dc463e3-LHR', 'alt-svc': 'h3=":443"; ma=86400'}) DEBUG groq._base_client:_base_client.py:1001 Encountered httpx.HTTPStatusError Traceback (most recent call last): File "C:\Users\chime\PlotSense_Recloned\PlotSenseAI\venv\Lib\site-packages\groq\_base_client.py", line 999, in _request @@ -458,7 +458,3 @@ httpx.HTTPStatusError: Client error '401 Unauthorized' for url 'https://api.groq For more information check: https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/401 DEBUG groq._base_client:_base_client.py:741 Not retrying DEBUG groq._base_client:_base_client.py:1019 Re-raising status error -DEBUG httpcore.connection:_trace.py:47 close.started -DEBUG httpcore.connection:_trace.py:47 close.complete -DEBUG httpcore.connection:_trace.py:47 close.started -DEBUG httpcore.connection:_trace.py:47 close.complete From b07a7f842651e36f33dda67317f7addc15419af1 Mon Sep 17 00:00:00 2001 From: Christian Chimezie Date: Thu, 13 Nov 2025 17:37:42 +0000 Subject: [PATCH 7/8] fix: add required blank line in test/conftest.py for PEP 8 compliance --- test/conftest.py | 1 + 1 file changed, 1 insertion(+) diff --git a/test/conftest.py b/test/conftest.py index 7f76bbd..f58d458 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -29,6 +29,7 @@ # Session-level fixtures (shared across all tests) # ============================================================================ + @pytest.fixture(scope="session") def test_api_key(): """Provide a test API key for testing.""" From 8d541b1e71f9b940c4d816869c7c8dca2157c249 Mon Sep 17 00:00:00 2001 From: Christian Chimezie Date: Thu, 13 Nov 2025 17:38:15 +0000 Subject: [PATCH 8/8] commit pytest --- test_logs/pytest.log | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/test_logs/pytest.log b/test_logs/pytest.log index ef0b846..4ad4ead 100644 --- a/test_logs/pytest.log +++ b/test_logs/pytest.log @@ -390,12 +390,12 @@ DEBUG matplotlib.font_manager:font_manager.py:1483 findfont: score(FontEntry( DEBUG matplotlib.font_manager:font_manager.py:1483 findfont: score(FontEntry(fname='C:\\Windows\\Fonts\\ntailu.ttf', name='Microsoft New Tai Lue', style='normal', variant='normal', weight=400, stretch='normal', size='scalable')) = 10.05 DEBUG matplotlib.font_manager:font_manager.py:1483 findfont: score(FontEntry(fname='C:\\Windows\\Fonts\\BOD_BI.TTF', name='Bodoni MT', style='italic', variant='normal', weight=700, stretch='normal', size='scalable')) = 11.335 DEBUG matplotlib.font_manager:font_manager.py:1515 findfont: Matching sans\-serif:style=normal:variant=normal:weight=normal:stretch=normal:size=10.0 to DejaVu Sans ('C:\\Users\\chime\\PlotSense_Recloned\\PlotSenseAI\\venv\\Lib\\site-packages\\matplotlib\\mpl-data\\fonts\\ttf\\DejaVuSans.ttf') with score of 0.050000. -DEBUG matplotlib.colorbar:colorbar.py:867 locator: -DEBUG matplotlib.colorbar:colorbar.py:867 locator: -DEBUG matplotlib.colorbar:colorbar.py:867 locator: -DEBUG matplotlib.colorbar:colorbar.py:867 locator: -DEBUG matplotlib.colorbar:colorbar.py:867 locator: -DEBUG matplotlib.colorbar:colorbar.py:867 locator: +DEBUG matplotlib.colorbar:colorbar.py:867 locator: +DEBUG matplotlib.colorbar:colorbar.py:867 locator: +DEBUG matplotlib.colorbar:colorbar.py:867 locator: +DEBUG matplotlib.colorbar:colorbar.py:867 locator: +DEBUG matplotlib.colorbar:colorbar.py:867 locator: +DEBUG matplotlib.colorbar:colorbar.py:867 locator: INFO matplotlib.category:category.py:224 Using categorical units to plot a list of strings that are all parsable as floats or dates. If these strings should be plotted as numbers, cast to the appropriate data type before plotting. INFO matplotlib.category:category.py:224 Using categorical units to plot a list of strings that are all parsable as floats or dates. If these strings should be plotted as numbers, cast to the appropriate data type before plotting. INFO matplotlib.category:category.py:224 Using categorical units to plot a list of strings that are all parsable as floats or dates. If these strings should be plotted as numbers, cast to the appropriate data type before plotting. @@ -406,29 +406,23 @@ DEBUG groq._base_client:_base_client.py:950 Sending HTTP Request: POST https: DEBUG httpcore.connection:_trace.py:47 connect_tcp.started host='api.groq.com' port=443 local_address=None timeout=30 socket_options=None DEBUG groq._base_client:_base_client.py:950 Sending HTTP Request: POST https://api.groq.com/openai/v1/chat/completions DEBUG httpcore.connection:_trace.py:47 connect_tcp.started host='api.groq.com' port=443 local_address=None timeout=30 socket_options=None -DEBUG httpcore.connection:_trace.py:47 connect_tcp.complete return_value= -DEBUG httpcore.connection:_trace.py:47 start_tls.started ssl_context= server_hostname='api.groq.com' timeout=30 -DEBUG httpcore.connection:_trace.py:47 connect_tcp.complete return_value= -DEBUG httpcore.connection:_trace.py:47 start_tls.started ssl_context= server_hostname='api.groq.com' timeout=30 -DEBUG httpcore.connection:_trace.py:47 start_tls.complete return_value= +DEBUG httpcore.connection:_trace.py:47 connect_tcp.complete return_value= +DEBUG httpcore.connection:_trace.py:47 start_tls.started ssl_context= server_hostname='api.groq.com' timeout=30 +DEBUG httpcore.connection:_trace.py:47 connect_tcp.complete return_value= +DEBUG httpcore.connection:_trace.py:47 start_tls.started ssl_context= server_hostname='api.groq.com' timeout=30 +DEBUG httpcore.connection:_trace.py:47 start_tls.complete return_value= DEBUG httpcore.http11:_trace.py:47 send_request_headers.started request= DEBUG httpcore.http11:_trace.py:47 send_request_headers.complete DEBUG httpcore.http11:_trace.py:47 send_request_body.started request= DEBUG httpcore.http11:_trace.py:47 send_request_body.complete DEBUG httpcore.http11:_trace.py:47 receive_response_headers.started request= -DEBUG httpcore.connection:_trace.py:47 start_tls.complete return_value= -DEBUG httpcore.http11:_trace.py:47 send_request_headers.started request= -DEBUG httpcore.http11:_trace.py:47 send_request_headers.complete -DEBUG httpcore.http11:_trace.py:47 send_request_body.started request= -DEBUG httpcore.http11:_trace.py:47 send_request_body.complete -DEBUG httpcore.http11:_trace.py:47 receive_response_headers.started request= -DEBUG httpcore.http11:_trace.py:47 receive_response_headers.complete return_value=(b'HTTP/1.1', 401, b'Unauthorized', [(b'Date', b'Thu, 13 Nov 2025 17:30:36 GMT'), (b'Content-Type', b'application/json'), (b'Content-Length', b'96'), (b'Connection', b'keep-alive'), (b'Cache-Control', b'private, max-age=0, no-store, no-cache, must-revalidate'), (b'Server', b'cloudflare'), (b'vary', b'Origin'), (b'x-groq-region', b'fra'), (b'x-request-id', b'req_01k9z49t34e9wbdfvt707vagcc'), (b'via', b'1.1 google'), (b'cf-cache-status', b'DYNAMIC'), (b'Set-Cookie', b'__cf_bm=US6dKc.LULURQ0yo3UfQBN8CjC3mgznDhmpTJ2Qmg9g-1763055036.5018919-1.0.1.1-izf3h9T8IDKoYn49u9hNfDLIqc6C2TcKlkyQfh2NevIrsc8LPkQdJz5GXe2_No_NkFXeQkamIKOmMGGzJB9y7i0D3ARQSZ2L28.yXi9MTA8J4oOXCHe4DoCJbgzStyqb; HttpOnly; Secure; Path=/; Domain=groq.com; Expires=Thu, 13 Nov 2025 18:00:36 GMT'), (b'CF-RAY', b'99dfff7a2f2cf653-LHR'), (b'alt-svc', b'h3=":443"; ma=86400')]) +DEBUG httpcore.http11:_trace.py:47 receive_response_headers.complete return_value=(b'HTTP/1.1', 401, b'Unauthorized', [(b'Date', b'Thu, 13 Nov 2025 17:37:03 GMT'), (b'Content-Type', b'application/json'), (b'Content-Length', b'96'), (b'Connection', b'keep-alive'), (b'Cache-Control', b'private, max-age=0, no-store, no-cache, must-revalidate'), (b'Server', b'cloudflare'), (b'vary', b'Origin'), (b'x-groq-region', b'fra'), (b'x-request-id', b'req_01k9z4nkjde4st09n44hm8tjp8'), (b'via', b'1.1 google'), (b'cf-cache-status', b'DYNAMIC'), (b'Set-Cookie', b'__cf_bm=LVIaRobBN1PJhPWpI_ThvnipI4NTlMKRzTAG5nq1.0A-1763055423.0405674-1.0.1.1-Konb9eRsioMJnK67NHofpA6A7sjR9MgG62u.Tb42zkBDIu1nKbs3.JE3dod.mQWgjy6vB9FdPE0BFHzYIDfbUmFfESqT4aiTP25hsePNe8MKs4Z_f7L1sZ2J5rf1j_5Q; HttpOnly; Secure; Path=/; Domain=groq.com; Expires=Thu, 13 Nov 2025 18:07:03 GMT'), (b'CF-RAY', b'99e008e9f9e3593c-LHR'), (b'alt-svc', b'h3=":443"; ma=86400')]) INFO httpx:_client.py:1025 HTTP Request: POST https://api.groq.com/openai/v1/chat/completions "HTTP/1.1 401 Unauthorized" DEBUG httpcore.http11:_trace.py:47 receive_response_body.started request= DEBUG httpcore.http11:_trace.py:47 receive_response_body.complete DEBUG httpcore.http11:_trace.py:47 response_closed.started DEBUG httpcore.http11:_trace.py:47 response_closed.complete -DEBUG groq._base_client:_base_client.py:989 HTTP Response: POST https://api.groq.com/openai/v1/chat/completions "401 Unauthorized" Headers({'date': 'Thu, 13 Nov 2025 17:30:36 GMT', 'content-type': 'application/json', 'content-length': '96', 'connection': 'keep-alive', 'cache-control': 'private, max-age=0, no-store, no-cache, must-revalidate', 'server': 'cloudflare', 'vary': 'Origin', 'x-groq-region': 'fra', 'x-request-id': 'req_01k9z49t34e9wbdfvt707vagcc', 'via': '1.1 google', 'cf-cache-status': 'DYNAMIC', 'set-cookie': '__cf_bm=US6dKc.LULURQ0yo3UfQBN8CjC3mgznDhmpTJ2Qmg9g-1763055036.5018919-1.0.1.1-izf3h9T8IDKoYn49u9hNfDLIqc6C2TcKlkyQfh2NevIrsc8LPkQdJz5GXe2_No_NkFXeQkamIKOmMGGzJB9y7i0D3ARQSZ2L28.yXi9MTA8J4oOXCHe4DoCJbgzStyqb; HttpOnly; Secure; Path=/; Domain=groq.com; Expires=Thu, 13 Nov 2025 18:00:36 GMT', 'cf-ray': '99dfff7a2f2cf653-LHR', 'alt-svc': 'h3=":443"; ma=86400'}) +DEBUG groq._base_client:_base_client.py:989 HTTP Response: POST https://api.groq.com/openai/v1/chat/completions "401 Unauthorized" Headers({'date': 'Thu, 13 Nov 2025 17:37:03 GMT', 'content-type': 'application/json', 'content-length': '96', 'connection': 'keep-alive', 'cache-control': 'private, max-age=0, no-store, no-cache, must-revalidate', 'server': 'cloudflare', 'vary': 'Origin', 'x-groq-region': 'fra', 'x-request-id': 'req_01k9z4nkjde4st09n44hm8tjp8', 'via': '1.1 google', 'cf-cache-status': 'DYNAMIC', 'set-cookie': '__cf_bm=LVIaRobBN1PJhPWpI_ThvnipI4NTlMKRzTAG5nq1.0A-1763055423.0405674-1.0.1.1-Konb9eRsioMJnK67NHofpA6A7sjR9MgG62u.Tb42zkBDIu1nKbs3.JE3dod.mQWgjy6vB9FdPE0BFHzYIDfbUmFfESqT4aiTP25hsePNe8MKs4Z_f7L1sZ2J5rf1j_5Q; HttpOnly; Secure; Path=/; Domain=groq.com; Expires=Thu, 13 Nov 2025 18:07:03 GMT', 'cf-ray': '99e008e9f9e3593c-LHR', 'alt-svc': 'h3=":443"; ma=86400'}) DEBUG groq._base_client:_base_client.py:1001 Encountered httpx.HTTPStatusError Traceback (most recent call last): File "C:\Users\chime\PlotSense_Recloned\PlotSenseAI\venv\Lib\site-packages\groq\_base_client.py", line 999, in _request @@ -440,13 +434,19 @@ httpx.HTTPStatusError: Client error '401 Unauthorized' for url 'https://api.groq For more information check: https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/401 DEBUG groq._base_client:_base_client.py:741 Not retrying DEBUG groq._base_client:_base_client.py:1019 Re-raising status error -DEBUG httpcore.http11:_trace.py:47 receive_response_headers.complete return_value=(b'HTTP/1.1', 401, b'Unauthorized', [(b'Date', b'Thu, 13 Nov 2025 17:30:36 GMT'), (b'Content-Type', b'application/json'), (b'Content-Length', b'96'), (b'Connection', b'keep-alive'), (b'Cache-Control', b'private, max-age=0, no-store, no-cache, must-revalidate'), (b'Server', b'cloudflare'), (b'vary', b'Origin'), (b'x-groq-region', b'fra'), (b'x-request-id', b'req_01k9z49t41f5nah8cmsybnrfb1'), (b'via', b'1.1 google'), (b'cf-cache-status', b'DYNAMIC'), (b'Set-Cookie', b'__cf_bm=uDALpz9hjUiw2hu1Ubfw9rYuFkReSrwicEvWILhqgYs-1763055036.523351-1.0.1.1-XQe3XA6ru2w1TzXJRNizOl9j_wuzffCWSmdX0VgQJq4W5AmcT3eDHqDva4lgaIDFZVMn9g.vQWNRnOYno41YYL10Mtq4PnR4fMQ7400li5jkP0Zh.Jn9wv2gum4HEkt7; HttpOnly; Secure; Path=/; Domain=groq.com; Expires=Thu, 13 Nov 2025 18:00:36 GMT'), (b'CF-RAY', b'99dfff7a4dc463e3-LHR'), (b'alt-svc', b'h3=":443"; ma=86400')]) +DEBUG httpcore.connection:_trace.py:47 start_tls.complete return_value= +DEBUG httpcore.http11:_trace.py:47 send_request_headers.started request= +DEBUG httpcore.http11:_trace.py:47 send_request_headers.complete +DEBUG httpcore.http11:_trace.py:47 send_request_body.started request= +DEBUG httpcore.http11:_trace.py:47 send_request_body.complete +DEBUG httpcore.http11:_trace.py:47 receive_response_headers.started request= +DEBUG httpcore.http11:_trace.py:47 receive_response_headers.complete return_value=(b'HTTP/1.1', 401, b'Unauthorized', [(b'Date', b'Thu, 13 Nov 2025 17:37:03 GMT'), (b'Content-Type', b'application/json'), (b'Content-Length', b'96'), (b'Connection', b'keep-alive'), (b'Cache-Control', b'private, max-age=0, no-store, no-cache, must-revalidate'), (b'Server', b'cloudflare'), (b'vary', b'Origin'), (b'x-groq-region', b'fra'), (b'x-request-id', b'req_01k9z4nkscfq8anrv8baxgrwm0'), (b'via', b'1.1 google'), (b'cf-cache-status', b'DYNAMIC'), (b'Set-Cookie', b'__cf_bm=pp8dX29TH8wbgOphZua71qlVOIQHnCiDD.R81mnj9Zw-1763055423.2502484-1.0.1.1-Lv7NTiG06vhIzlPmWQtKR2.fAfxBsdrvTcJtmQilI29xavsa7hqAHAvgRNTSuC5x2S17.HDx6yyl_pC3l5ZjoHuZrp4JcN1E30YHcyKbo.fW9Y5YLKqtto7BGqQoujl6; HttpOnly; Secure; Path=/; Domain=groq.com; Expires=Thu, 13 Nov 2025 18:07:03 GMT'), (b'CF-RAY', b'99e008eb4aa7942b-LHR'), (b'alt-svc', b'h3=":443"; ma=86400')]) INFO httpx:_client.py:1025 HTTP Request: POST https://api.groq.com/openai/v1/chat/completions "HTTP/1.1 401 Unauthorized" DEBUG httpcore.http11:_trace.py:47 receive_response_body.started request= DEBUG httpcore.http11:_trace.py:47 receive_response_body.complete DEBUG httpcore.http11:_trace.py:47 response_closed.started DEBUG httpcore.http11:_trace.py:47 response_closed.complete -DEBUG groq._base_client:_base_client.py:989 HTTP Response: POST https://api.groq.com/openai/v1/chat/completions "401 Unauthorized" Headers({'date': 'Thu, 13 Nov 2025 17:30:36 GMT', 'content-type': 'application/json', 'content-length': '96', 'connection': 'keep-alive', 'cache-control': 'private, max-age=0, no-store, no-cache, must-revalidate', 'server': 'cloudflare', 'vary': 'Origin', 'x-groq-region': 'fra', 'x-request-id': 'req_01k9z49t41f5nah8cmsybnrfb1', 'via': '1.1 google', 'cf-cache-status': 'DYNAMIC', 'set-cookie': '__cf_bm=uDALpz9hjUiw2hu1Ubfw9rYuFkReSrwicEvWILhqgYs-1763055036.523351-1.0.1.1-XQe3XA6ru2w1TzXJRNizOl9j_wuzffCWSmdX0VgQJq4W5AmcT3eDHqDva4lgaIDFZVMn9g.vQWNRnOYno41YYL10Mtq4PnR4fMQ7400li5jkP0Zh.Jn9wv2gum4HEkt7; HttpOnly; Secure; Path=/; Domain=groq.com; Expires=Thu, 13 Nov 2025 18:00:36 GMT', 'cf-ray': '99dfff7a4dc463e3-LHR', 'alt-svc': 'h3=":443"; ma=86400'}) +DEBUG groq._base_client:_base_client.py:989 HTTP Response: POST https://api.groq.com/openai/v1/chat/completions "401 Unauthorized" Headers({'date': 'Thu, 13 Nov 2025 17:37:03 GMT', 'content-type': 'application/json', 'content-length': '96', 'connection': 'keep-alive', 'cache-control': 'private, max-age=0, no-store, no-cache, must-revalidate', 'server': 'cloudflare', 'vary': 'Origin', 'x-groq-region': 'fra', 'x-request-id': 'req_01k9z4nkscfq8anrv8baxgrwm0', 'via': '1.1 google', 'cf-cache-status': 'DYNAMIC', 'set-cookie': '__cf_bm=pp8dX29TH8wbgOphZua71qlVOIQHnCiDD.R81mnj9Zw-1763055423.2502484-1.0.1.1-Lv7NTiG06vhIzlPmWQtKR2.fAfxBsdrvTcJtmQilI29xavsa7hqAHAvgRNTSuC5x2S17.HDx6yyl_pC3l5ZjoHuZrp4JcN1E30YHcyKbo.fW9Y5YLKqtto7BGqQoujl6; HttpOnly; Secure; Path=/; Domain=groq.com; Expires=Thu, 13 Nov 2025 18:07:03 GMT', 'cf-ray': '99e008eb4aa7942b-LHR', 'alt-svc': 'h3=":443"; ma=86400'}) DEBUG groq._base_client:_base_client.py:1001 Encountered httpx.HTTPStatusError Traceback (most recent call last): File "C:\Users\chime\PlotSense_Recloned\PlotSenseAI\venv\Lib\site-packages\groq\_base_client.py", line 999, in _request