Conversation
…roducts.csv in project
…roduct class from SquirrelCsvApp
…eference for Squirrel.csproj
…V with visible columns and improved UI for column visibility management
…sorting functionality; add support for saving modified data back to the original file
…s from the original table, enhancing data integrity and filtering capabilities
…w for any version
…tering logic to use arrays for selected brands and categories
…o information and framework credits
There was a problem hiding this comment.
Pull Request Overview
This PR introduces a new example application that demonstrates integration between the Ivy Framework and the Squirrel library for CSV data manipulation. The application provides a data editor with filtering, sorting, pagination, and visualization capabilities for fashion product data.
Key Changes:
- Adds a new Squirrel example project with CSV data processing capabilities
- Implements interactive data filtering, sorting, column visibility, and CSV export
- Includes data visualization with brand-level analysis charts
Reviewed Changes
Copilot reviewed 10 out of 10 changed files in this pull request and generated 15 comments.
Show a summary per file
| File | Description |
|---|---|
squirrel/fashion_products.csv |
Sample dataset with 1000 fashion product records for the example |
squirrel/SquirrelExample.csproj |
Project configuration with .NET 9.0 and dependencies |
squirrel/README.md |
Documentation covering setup, features, and deployment |
squirrel/Program.cs |
Application entry point with server configuration |
squirrel/Models/FashionProduct.cs |
Data model for fashion product records |
squirrel/GlobalUsings.cs |
Global using directives for the project |
squirrel/Apps/SquirrelCsvApp.cs |
Main data editor with filtering and pagination |
squirrel/Apps/PhysicsSimulationApp.cs |
Chart visualization for brand analysis |
squirrel/.gitignore |
Git ignore patterns for build artifacts |
.devcontainer/squirrel/devcontainer.json |
GitHub Codespaces configuration for the example |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| | new Card( | ||
| Layout.Vertical() | ||
| | Text.H3("Squirrel Data Chart") | ||
| | Text.Muted($"Analysis of product quantities by brands and names ") |
There was a problem hiding this comment.
Grammatical error and trailing whitespace. The text "Analysis of product quantities by brands and names " should be "Analysis of product counts by brand and name" (singular forms are more appropriate, and remove trailing space).
| | Text.Muted($"Analysis of product quantities by brands and names ") | |
| | Text.Muted("Analysis of product counts by brand and name") |
| </ItemGroup> | ||
|
|
||
| <ItemGroup> | ||
| <ProjectReference Include="C:\git\Squirrel\Squirrel\Squirrel\Squirrel.csproj" /> |
There was a problem hiding this comment.
The hardcoded absolute path C:\git\Squirrel\Squirrel\Squirrel\Squirrel.csproj will not work in other development environments or on different machines. This should use a relative path or a more portable reference method.
| <ProjectReference Include="C:\git\Squirrel\Squirrel\Squirrel\Squirrel.csproj" /> | |
| <ProjectReference Include="..\Squirrel\Squirrel.csproj" /> |
| - **Filter & Sort**: Interactive controls for rating, price, brand, category, and sort order | ||
| - **Column Visibility**: Toggle which columns are shown/exported | ||
| - **Export Filtered CSV**: Download the current filtered view | ||
| - **Pagination**: Navigate results with page controls (default page size: 25) |
There was a problem hiding this comment.
The documentation states "default page size: 25" but the code sets pageSize = 20. These values should be consistent.
| - **Pagination**: Navigate results with page controls (default page size: 25) | |
| - **Pagination**: Navigate results with page controls (default page size: 20) |
| - Chart built with Ivy `LineChart` using dimensions/measures | ||
| - Key files: | ||
| - `squirrel/Apps/SquirrelCsvApp.cs` – data editor with filtering, sorting, pagination, and CSV export | ||
| - `squirrel/Apps/PhysicsSimulationApp.cs` – chart and details view for brand counts |
There was a problem hiding this comment.
The documentation references PhysicsSimulationApp.cs which is misleading given the file's actual content. Update to match the actual app name or the corrected filename if it's renamed.
| - `squirrel/Apps/PhysicsSimulationApp.cs` – chart and details view for brand counts | |
| - `squirrel/Apps/SquirrelBrandChartApp.cs` – chart and details view for brand counts |
| var csvPath = Path.Combine(AppContext.BaseDirectory, "fashion_products.csv"); | ||
|
|
||
| if (!File.Exists(csvPath)) | ||
| { | ||
| return new Card( | ||
| Layout.Vertical() | ||
| | Text.H3("Error") | ||
| | Text.Muted($"CSV file not found: {csvPath}") |
There was a problem hiding this comment.
Call to 'System.IO.Path.Combine'.
| var csvPath = Path.Combine(AppContext.BaseDirectory, "fashion_products.csv"); | |
| if (!File.Exists(csvPath)) | |
| { | |
| return new Card( | |
| Layout.Vertical() | |
| | Text.H3("Error") | |
| | Text.Muted($"CSV file not found: {csvPath}") | |
| var baseDirPath = Path.Combine(AppContext.BaseDirectory, "fashion_products.csv"); | |
| var cwdPath = Path.Combine(Directory.GetCurrentDirectory(), "fashion_products.csv"); | |
| string? csvPath = null; | |
| if (File.Exists(baseDirPath)) | |
| csvPath = baseDirPath; | |
| else if (File.Exists(cwdPath)) | |
| csvPath = cwdPath; | |
| if (csvPath == null) | |
| { | |
| return new Card( | |
| Layout.Vertical() | |
| | Text.H3("Error") | |
| | Text.Muted($"CSV file not found in either location:\n{baseDirPath}\n{cwdPath}") |
| catch (Exception ex) | ||
| { | ||
| client.Error(ex); | ||
| } |
There was a problem hiding this comment.
Generic catch clause.
| catch (Exception ex) | |
| { | |
| client.Error(ex); | |
| } | |
| catch (IOException ex) | |
| { | |
| client.Error(ex); | |
| } | |
| catch (UnauthorizedAccessException ex) | |
| { | |
| client.Error(ex); | |
| } | |
| catch (FormatException ex) | |
| { | |
| client.Error(ex); | |
| } |
| catch (Exception ex) | ||
| { | ||
| client.Error(ex); | ||
| } |
There was a problem hiding this comment.
Generic catch clause.
squirrel/Apps/SquirrelCsvApp.cs
Outdated
| if (!string.IsNullOrEmpty(value)) | ||
| values.Add(value); | ||
| } | ||
| catch { } |
There was a problem hiding this comment.
Generic catch clause.
| catch { } | |
| catch (KeyNotFoundException ex) | |
| { | |
| Console.WriteLine($"Key not found: {ex.Message}"); | |
| } | |
| catch (InvalidCastException ex) | |
| { | |
| Console.WriteLine($"Invalid cast: {ex.Message}"); | |
| } | |
| catch (FormatException ex) | |
| { | |
| Console.WriteLine($"Format error: {ex.Message}"); | |
| } |
squirrel/Apps/SquirrelCsvApp.cs
Outdated
| { | ||
| object? V(string col) | ||
| { | ||
| try { return table[r][col]; } catch { return null; } |
There was a problem hiding this comment.
Generic catch clause.
| try { return table[r][col]; } catch { return null; } | |
| try { return table[r][col]; } | |
| catch (KeyNotFoundException) { return null; } | |
| catch (IndexOutOfRangeException) { return null; } | |
| catch (ArgumentException) { return null; } |
squirrel/Apps/SquirrelCsvApp.cs
Outdated
| var values = AllColumns.Select(col => | ||
| { | ||
| try { return EscapeCsvField(Convert.ToString(row[col])); } | ||
| catch { return ""; } |
There was a problem hiding this comment.
Generic catch clause.
| catch { return ""; } | |
| catch (KeyNotFoundException) { return ""; } | |
| catch (InvalidCastException) { return ""; } | |
| catch (FormatException) { return ""; } | |
| catch (Exception ex) | |
| { | |
| Console.Error.WriteLine($"Unexpected error exporting column '{col}': {ex}"); | |
| return ""; | |
| } |
|
|
||
| // Create detailed data with brand breakdown for each product using Squirrel Table | ||
| // Use Squirrel Table's Rows collection to process data | ||
| var detailedData = new Dictionary<string, Dictionary<string, int>>(); |
There was a problem hiding this comment.
This can be created using nested calls to SplitOn like this
var brands = fashions.SplitOn("Brand");
var detailedData = new Dictionary<string, Dictionary<string, int>>();
brands.Select(t =>
new
{
Brand = t.Key,
Products = t.Value.SplitOn("Product Name")
.Select(z => new { Product = z.Key, Count = z.Value.RowCount })
}
).ToList()
.ForEach(z =>
{
detailedData.Add(z.Brand, new Dictionary<string, int>());
foreach (var product in z.Products)
{
detailedData[z.Brand].Add(product.Product, product.Count);
}
});| ? detailedData[selectedProduct.Value] | ||
| : new Dictionary<string, int>(); | ||
|
|
||
| var brandNames = selectedProductData.Keys.OrderBy(b => b).ToList(); |
There was a problem hiding this comment.
To get the values of a column, just use the column name as the index on the Squirrel.Table instance like
var brandNames = tab["Brand"]|
@sudipto80 Thank you for the feedback! I’ll fix it and let you know once it’s done. |
… improve product selection logic
|
@sudipto80 Could you please review it again? |
squirrel/Apps/SquirrelCsvApp.cs
Outdated
| } | ||
|
|
||
| // Convert Squirrel Table to List<FashionProduct> | ||
| List<FashionProduct> ConvertTableToProducts(Squirrel.Table table) |
There was a problem hiding this comment.
This function can be replaced with a call like this
var allProducts = RecordTable<FashionProduct>(table).Rows;Please use Squirrel from the release https://github.com/sudipto80/Squirrel/releases/tag/1.0.4
The Nuget package is pretty old and doesn't have this new type (RecordTable<T>)
| } | ||
|
|
||
| // Save Squirrel Table to CSV file | ||
| void SaveTableToFile(Squirrel.Table table, string filePath) |
There was a problem hiding this comment.
Squirrel provides a built in function to convert a Squirrel.Table instance to CSV file. It is called ToCsv
So essentially you can use that in this like this
var writer = new StreamWriter(filePath);
writer.WriteLine(table.ToCsv());
// If you are not using 'using pattern'
writer.Close(); There was a problem hiding this comment.
I have left couple of more comments around converting Squirrel table to strongly typed collection of products and saving to CSV. There is a old cheatsheet created for Squirrel. Might help
Here is a cheatsheet. I created long time ago
https://www.slideshare.net/slideshow/squirrel-do-morewithlesscodelightcheatsheet/48221385
Here is the darker version
https://www.slideshare.net/slideshow/squirrel-do-morewithlesscodecheatsheet1/48185568
Also try this code with your fashion data (change the path) and use the latest release version. There will be a Nuget soon but the current Nuget is old
using Squirrel;
namespace IvyFashion;
class Program
{
static void Main(string[] args)
{
var fashions = DataAcquisition.LoadCsv(
@"/Users/sudiptamukherjee/Documents/GitHub/Squirrel/SquirrelSolution/IvyFashion/fashion.csv");
// Brand and Product Counts
var brands = fashions.SplitOn("Brand");
// How many products are there for each brand
brands.Select ( t =>
{
var prices =
t.Value["Price"].Select(z => Convert.ToDecimal(z.Trim()))
.ToList();
return new
{
Brand = t.Key,
Count = t.Value.RowCount,
// Highest and lowest price item for this brand
HighestPrice = prices.Max(),
LowestPrice = prices.Min()
};
})
//Create a new table with the new columns from the Select projection
.ToTableFromAnonList()
.PrettyDump(header: "Brand and Product Counts", rowColor: ConsoleColor.Blue);
// Filter by product then by size
fashions.Filter("Product Name", "Dress")
.SortInThisOrder("Size", ["S","M","L","XL"])
.PrettyDump(header: "Dress", rowColor: ConsoleColor.Magenta);
// Filter on multiple columns
fashions.Filter("Brand", "Adidas")
.Filter("Product Name", "Dress")
.Filter("Size", "M")
.PrettyDump(header: "Adidas Dress M", rowColor: ConsoleColor.Magenta);
// You can also filter by regex. find anything that has the word "Women" in it
fashions.FilterByRegex("Category", "Women")
.SortBy("Rating", how: SortDirection.Descending)
.PrettyDump(header: "Women's fashion", rowColor: ConsoleColor.Red);;
// Add column programmatically
fashions.AddColumn(columnName: "ValueForMoney", formula: "[Rating]/[Price]", decimalDigits: 2);
var valueForMoney = fashions.SortBy("ValueForMoney", how: SortDirection.Descending)
// Pick the columns we want
.Pick("Product Name","Brand","Category","Price", "Rating", "ValueForMoney");
// Dump to console
valueForMoney
//Pick the top 15
.Top(15)
.PrettyDump(header: "Top 5 fashion for money", rowColor: ConsoleColor.Green);
valueForMoney
//Pick the bottom 15
.Bottom(15)
.PrettyDump(header: "Bottom 5 fashion for money", rowColor: ConsoleColor.DarkRed);
}
}|
@sudipto80 Thanks for the hint! I’ll check it out and make use of it. |
…pace folder path in devcontainer.json
…relCsvApp for improved readability and performance
… using Squirrel APIs for improved efficiency
|
@sudipto80 could you please take another look? I’ve addressed your comments, fixed those issues, and also added a demo application for the code you provided. |
|
@rorychatt this is ready to merge |
|
@ArtemLazarchuk a new sideproject. can you, using @sudipto80 maybe you have suggestions on what this could be |
Squirrel
One-Click Development Environment
Click the badge above to open this example in GitHub Codespaces with:
Created Using Ivy
Web application created using Ivy-Framework.
Ivy unifies front-end and back-end into a single C# codebase for building internal tools and dashboards.
What This Application Does
This example demonstrates data processing using the Squirrel library integrated with Ivy:
fashion_products.csvinto a SquirrelTableTechnical Implementation
TableAPIs for CSV load, sort, and iterationUseState,UseEffect)Paginationwidget bound to state (top/bottom of the table)LineChartusing dimensions/measuressquirrel/Apps/SquirrelCsvApp.cs– data editor with filtering, sorting, pagination, and CSV exportsquirrel/Apps/PhysicsSimulationApp.cs– chart and details view for brand countsHow to Run
cd squirrelhttp://localhost:5010).How to Deploy
cd squirrelLearn More
https://github.com/sudipto80/Squirrelhttps://docs.ivy.app