-
Notifications
You must be signed in to change notification settings - Fork 2
CC-76: AI Reporting Dashboard Proof of Concept #85
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
ec0c0f4
feat: add initial AI PoC
kevbang b45ee08
chore: adding documentation
kevbang 18219ee
chore: export Gemini api key to lightsail instance
kevbang 7724f35
fix: properly register Gemini API in DI workflow
kevbang 070a638
fix: inject AI reporting service
kevbang 367b7eb
fix: unit tests
kevbang 27b178b
fix: use official playwright setup
kevbang 7d90eb4
revert changes
kevbang File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
63 changes: 63 additions & 0 deletions
63
CulinaryCommandApp/AIDashboard/DTOs/AIAnalysisResultDTO.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,63 @@ | ||
| namespace CulinaryCommandApp.AIDashboard.Services.DTOs | ||
| { | ||
| using System; | ||
| using System.Collections.Generic; | ||
| using System.Text.Json.Serialization; | ||
|
|
||
| public class AIAnalysisResultDTO | ||
| { | ||
| [JsonPropertyName("title")] | ||
| public string? Title { get; set; } | ||
|
|
||
| [JsonPropertyName("summary")] | ||
| public string? Summary { get; set; } | ||
|
|
||
| [JsonPropertyName("metrics")] | ||
| public List<Metric>? Metrics { get; set; } | ||
|
|
||
| [JsonPropertyName("sections")] | ||
| public List<Section>? Sections { get; set; } | ||
|
|
||
| [JsonPropertyName("anomalies")] | ||
| public List<Anomaly>? Anomalies { get; set; } | ||
|
|
||
| [JsonPropertyName("recommendations")] | ||
| public List<string>? Recommendations { get; set; } | ||
|
|
||
| [JsonPropertyName("generatedAt")] | ||
| public DateTimeOffset? GeneratedAt { get; set; } | ||
|
|
||
| [JsonPropertyName("confidence")] | ||
| public double? Confidence { get; set; } | ||
| } | ||
|
|
||
| public class Metric | ||
| { | ||
| [JsonPropertyName("label")] | ||
| public string? Label { get; set; } | ||
|
|
||
| [JsonPropertyName("value")] | ||
| public string? Value { get; set; } | ||
|
|
||
| [JsonPropertyName("unit")] | ||
| public string? Unit { get; set; } | ||
| } | ||
| public class Section | ||
| { | ||
| [JsonPropertyName("heading")] | ||
| public string? Heading { get; set; } | ||
|
|
||
| [JsonPropertyName("body")] | ||
| public string? Body { get; set; } | ||
| } | ||
|
|
||
| public class Anomaly | ||
| { | ||
| [JsonPropertyName("row")] | ||
| public string? Row { get; set; } | ||
|
|
||
| [JsonPropertyName("reason")] | ||
| public string? Reason { get; set; } | ||
|
|
||
| } | ||
| } | ||
124 changes: 124 additions & 0 deletions
124
CulinaryCommandApp/AIDashboard/Services/Reporting/AIReportingService.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,124 @@ | ||
| namespace CulinaryCommandApp.AIDashboard.Services.Reporting | ||
| { | ||
| using Google.GenAI; | ||
| using Google.GenAI.Types; | ||
| using System; | ||
| using System.Threading.Tasks; | ||
| using System.Linq; | ||
| using System.Text; | ||
| using System.Globalization; | ||
| using System.Text.Json.Serialization; | ||
| using System.Text.Json; | ||
| using CulinaryCommandApp.AIDashboard.Services.DTOs; | ||
|
|
||
|
|
||
| public class AIReportingService | ||
| { | ||
| private readonly Client _client; | ||
|
|
||
| public AIReportingService(Client client) | ||
| { | ||
| _client = client ?? throw new ArgumentNullException(nameof(client)); | ||
| } | ||
|
|
||
| public async Task<string> AnalyzeCsvAsync(string? csvPath = null) | ||
| { | ||
| const string PayloadSchema = @" You are an expert restaurant data analyst. | ||
| Analyze the CSV I give you and return ONLY a JSON object | ||
| that exactly follows this SCHEMA: | ||
| { | ||
| ""title"": string, | ||
| ""summary"": string, | ||
| ""metrics"": [ { ""label"": string, ""value"": string, ""unit"": string|null } ], | ||
| ""sections"": [ { ""heading"": string, ""body"": string } ], | ||
| ""anomalies"": [ { ""row"": string, ""reason"": string } ], | ||
| ""recommendations"": [ string ], | ||
| ""generatedAt"": ""ISO-8601 datetime string"", | ||
| ""confidence"": ""number"" (0.0 to 1.0) | ||
| } | ||
| Constraints: | ||
| - Return only JSON (no surrounding text, no markdown fences). | ||
| - For long text blocks, try to be concise in the ""body"" fields. | ||
| - Use ISO-8601 for generatedAt. | ||
| - Provide a numeric confidence between 0 and 1. | ||
|
|
||
| Analysis requirements: | ||
| - Use business/user friendly tone. | ||
| - If there are anomalies, prompt the user to investigate. | ||
| - Include top seller focus analysis. | ||
| - Provide quick action items if appropriate. | ||
| Now analyze the CSV content below and return the JSON. | ||
| "; | ||
|
|
||
|
|
||
| if (string.IsNullOrWhiteSpace(csvPath)) | ||
| { | ||
| Console.WriteLine("CSV path not provided."); | ||
| return "CSV path not provided."; | ||
| } | ||
|
|
||
| if (!System.IO.File.Exists(csvPath)) | ||
| { | ||
| Console.WriteLine($"CSV not found at: {csvPath}"); | ||
| return $"CSV not found at: {csvPath}"; | ||
| } | ||
|
|
||
| var lines = System.IO.File.ReadAllLines(csvPath).ToList(); | ||
|
kevbang marked this conversation as resolved.
|
||
|
|
||
| if (lines.Count <= 1) | ||
| { | ||
| Console.WriteLine("CSV empty or only header."); | ||
| return "CSV empty or only header."; | ||
| } | ||
|
|
||
| var header = lines[0]; | ||
| var rows = lines.Skip(1).ToList(); | ||
|
|
||
| /**** Build payload to send to Gemini model****/ | ||
| var geminiPayload = new StringBuilder(); | ||
|
|
||
| geminiPayload.AppendLine(PayloadSchema); | ||
| geminiPayload.AppendLine(header); | ||
| foreach (var r in rows) geminiPayload.AppendLine(r); | ||
|
kevbang marked this conversation as resolved.
|
||
|
|
||
| Console.WriteLine(geminiPayload); | ||
|
|
||
| /***** Make API call to Gemini with payload ******/ | ||
| var response = await _client.Models.GenerateContentAsync( | ||
| model: "gemini-3-flash-preview", | ||
|
kevbang marked this conversation as resolved.
|
||
| contents: geminiPayload.ToString() | ||
| ); | ||
|
|
||
| Console.WriteLine(response.Candidates[0].Content.Parts[0].Text); | ||
|
kevbang marked this conversation as resolved.
|
||
|
|
||
| var AIAnalysisResponse = response?.Candidates?.FirstOrDefault()?.Content?.Parts?.FirstOrDefault()?.Text; | ||
|
kevbang marked this conversation as resolved.
|
||
|
|
||
| /*** Deserialize ***/ | ||
|
|
||
| if (!string.IsNullOrWhiteSpace(AIAnalysisResponse)) | ||
| { | ||
| try | ||
| { | ||
| var options = new System.Text.Json.JsonSerializerOptions | ||
| { | ||
| PropertyNameCaseInsensitive = true, | ||
| }; | ||
|
|
||
| var result = System.Text.Json.JsonSerializer.Deserialize<AIAnalysisResultDTO>(AIAnalysisResponse, options); | ||
|
|
||
| if (result != null) | ||
| { | ||
| return System.Text.Json.JsonSerializer.Serialize(result, new System.Text.Json.JsonSerializerOptions { WriteIndented = true }); | ||
| } | ||
| } | ||
| catch (JsonException) | ||
| { | ||
| // just catch the exception and move on | ||
| } | ||
|
kevbang marked this conversation as resolved.
|
||
| } | ||
|
|
||
| return AIAnalysisResponse ?? "(no analysis returned)"; | ||
| } | ||
|
|
||
| } | ||
| } | ||
101 changes: 101 additions & 0 deletions
101
CulinaryCommandApp/AIDashboard/Services/Reporting/test_data.csv
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,101 @@ | ||
| date,dish_ordered,dish_price | ||
| 2025-08-09,Beef Tacos,9.75 | ||
| 2025-08-21,Beef Tacos,9.75 | ||
| 2025-12-26,Grilled Chicken Salad,10.49 | ||
| 2025-01-12,Spaghetti Bolognese,12.99 | ||
| 2025-09-08,Beef Tacos,9.75 | ||
| 2025-01-25,Grilled Chicken Salad,10.49 | ||
| 2025-02-14,Fish and Chips,11.99 | ||
| 2025-05-29,Sushi Platter,15.99 | ||
| 2025-11-26,Fish and Chips,11.99 | ||
| 2025-05-26,Fish and Chips,11.99 | ||
| 2025-02-27,Chicken Alfredo,13.5 | ||
| 2025-09-29,Spaghetti Bolognese,12.99 | ||
| 2025-08-05,Sushi Platter,15.99 | ||
| 2025-12-11,Spaghetti Bolognese,12.99 | ||
| 2025-04-22,Beef Tacos,9.75 | ||
| 2025-04-28,Fish and Chips,11.99 | ||
| 2025-08-31,Spaghetti Bolognese,12.99 | ||
| 2025-11-02,Grilled Chicken Salad,10.49 | ||
| 2025-09-18,Margherita Pizza,11.25 | ||
| 2025-12-24,Margherita Pizza,11.25 | ||
| 2025-11-09,Margherita Pizza,11.25 | ||
| 2025-03-10,Sushi Platter,15.99 | ||
| 2025-12-31,Margherita Pizza,11.25 | ||
| 2025-06-27,Chicken Alfredo,13.5 | ||
| 2025-02-28,Chicken Alfredo,13.5 | ||
| 2025-09-16,Chicken Alfredo,13.5 | ||
| 2025-12-28,Beef Tacos,9.75 | ||
| 2025-11-27,Chicken Alfredo,13.5 | ||
| 2025-07-18,Spaghetti Bolognese,12.99 | ||
| 2025-05-29,Chicken Alfredo,13.5 | ||
| 2025-11-03,Veggie Burger,8.95 | ||
| 2025-05-09,Fish and Chips,11.99 | ||
| 2025-05-20,Spaghetti Bolognese,12.99 | ||
| 2025-04-23,Sushi Platter,15.99 | ||
| 2025-07-08,Sushi Platter,15.99 | ||
| 2025-08-06,Spaghetti Bolognese,12.99 | ||
| 2025-07-12,Grilled Chicken Salad,10.49 | ||
| 2025-04-13,Grilled Chicken Salad,10.49 | ||
| 2025-04-22,Fish and Chips,11.99 | ||
| 2025-03-09,Beef Tacos,9.75 | ||
| 2025-06-17,Spaghetti Bolognese,12.99 | ||
| 2025-02-26,Spaghetti Bolognese,12.99 | ||
| 2025-06-19,Fish and Chips,11.99 | ||
| 2025-06-16,Beef Tacos,9.75 | ||
| 2025-07-10,Spaghetti Bolognese,12.99 | ||
| 2025-02-03,Spaghetti Bolognese,12.99 | ||
| 2025-07-22,Chicken Alfredo,13.5 | ||
| 2025-03-17,Grilled Chicken Salad,10.49 | ||
| 2025-07-24,Fish and Chips,11.99 | ||
| 2025-01-29,Veggie Burger,8.95 | ||
| 2025-08-21,Veggie Burger,8.95 | ||
| 2025-12-30,Fish and Chips,11.99 | ||
| 2025-09-04,Fish and Chips,11.99 | ||
| 2025-05-04,Spaghetti Bolognese,12.99 | ||
| 2025-11-06,Sushi Platter,15.99 | ||
| 2035-05-15,Spaghetti Bolognese,999.99 | ||
| 2025-12-12,Chicken Alfredo,13.5 | ||
| 2025-11-15,Sushi Platter,15.99 | ||
| 2025-05-19,Chicken Alfredo,13.5 | ||
| 2025-04-01,Grilled Chicken Salad,10.49 | ||
| 2025-01-03,Beef Tacos,9.75 | ||
| 2025-08-22,Spaghetti Bolognese,12.99 | ||
| 2025-12-29,Grilled Chicken Salad,10.49 | ||
| 2025-03-22,Margherita Pizza,11.25 | ||
| 2025-04-29,Veggie Burger,8.95 | ||
| 2025-05-31,Grilled Chicken Salad,10.49 | ||
| 2025-02-07,Beef Tacos,9.75 | ||
| 2025-09-11,Veggie Burger,8.95 | ||
| 2025-12-22,Veggie Burger,8.95 | ||
| 2025-05-29,Margherita Pizza,11.25 | ||
| 2025-01-19,Beef Tacos,9.75 | ||
| 2025-07-12,Beef Tacos,9.75 | ||
| 2025-02-09,Sushi Platter,15.99 | ||
| 2025-10-05,Beef Tacos,9.75 | ||
| 2025-06-06,Beef Tacos,9.75 | ||
| 2025-08-29,Margherita Pizza,11.25 | ||
| 2025-09-08,Chicken Alfredo,13.5 | ||
| 2025-11-06,Beef Tacos,9.75 | ||
| 2025-03-27,Margherita Pizza,11.25 | ||
| 2025-03-08,Veggie Burger,8.95 | ||
| 2025-07-18,Margherita Pizza,11.25 | ||
| 2025-08-29,Beef Tacos,9.75 | ||
| 2025-03-02,Grilled Chicken Salad,10.49 | ||
| 2025-03-06,Margherita Pizza,11.25 | ||
| 2025-11-28,Beef Tacos,9.75 | ||
| 2025-11-15,Spaghetti Bolognese,12.99 | ||
| 2025-11-12,Grilled Chicken Salad,10.49 | ||
| 2025-08-05,Veggie Burger,8.95 | ||
| 2025-07-31,Sushi Platter,15.99 | ||
| 2025-01-14,Margherita Pizza,11.25 | ||
| 2025-04-01,Sushi Platter,15.99 | ||
| 2025-11-09,Spaghetti Bolognese,12.99 | ||
| 2025-12-23,Veggie Burger,8.95 | ||
| 2025-12-22,Veggie Burger,8.95 | ||
| 2025-05-25,Chicken Alfredo,13.5 | ||
| 2025-11-04,Grilled Chicken Salad,10.49 | ||
| 2025-04-13,Sushi Platter,15.99 | ||
| 2025-09-27,Fish and Chips,11.99 | ||
| 2025-09-29,Chicken Alfredo,13.5 | ||
| 2025-09-01,Chicken Alfredo,13.5 |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.