Skip to content

Commit daa79a0

Browse files
committed
add cs solution
1 parent 1907093 commit daa79a0

28 files changed

+779
-0
lines changed
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
4+
<head>
5+
<meta charset="utf-8" />
6+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
7+
<base href="/" />
8+
<link rel="stylesheet" href="_content/DevExpress.Blazor.Themes/blazing-berry.bs5.min.css" />
9+
<link href="_content/DevExpress.Blazor.Reporting.Viewer/css/dx-blazor-reporting-components.bs5.css" rel="stylesheet" />
10+
<link href="_content/DevExpress.Blazor.RichEdit/dx-blazor-richedit.css" rel="stylesheet" />
11+
<link rel="stylesheet" href="site.css" />
12+
@DxResourceManager.RegisterScripts()
13+
14+
</head>
15+
16+
<body>
17+
<Routes @rendermode="InteractiveServer" />
18+
<script src="_framework/blazor.web.js"></script>
19+
</body>
20+
21+
</html>
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
@inherits LayoutComponentBase
2+
<div class="main-layout">
3+
@Body
4+
</div>
Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
@page "/grid"
2+
@page "/"
3+
@using DevExpress.AI.Samples.Blazor.Data;
4+
@using DevExpress.AI.Samples.Blazor.Services
5+
@using DevExpress.AIIntegration.Blazor.Chat
6+
@using DevExpress.AIIntegration.OpenAI.Services
7+
8+
@inject IssuesDataService IssuesDataService
9+
10+
<DxGrid @ref="grid" Data="@DataSource" CssClass="my-grid" ShowGroupPanel="true" TextWrapEnabled="false" AutoExpandAllGroupRows="true"
11+
CustomizeFilterRowEditor="Grid_CustomizeFilterRowEditor" FilterMenuButtonDisplayMode="GridFilterMenuButtonDisplayMode.Always"
12+
ShowSearchBox="true" ColumnResizeMode="GridColumnResizeMode.NextColumn"
13+
ShowAllRows="true" AllowSelectRowByClick="true" @bind-SearchText="@GridSearchText"
14+
HighlightRowOnHover="true">
15+
<Columns>
16+
<DxGridSelectionColumn Width="75px" />
17+
<DxGridDataColumn FieldName="Name" Caption="Subject" MinWidth="220" AllowGroup="false">
18+
<CellDisplayTemplate>
19+
@GetIssueTypeIconHtml(((Issue)context.DataItem).Type)
20+
@context.HighlightedDisplayText
21+
</CellDisplayTemplate>
22+
</DxGridDataColumn>
23+
<DxGridDataColumn FieldName="ProjectID" Caption="Project" GroupIndex="0" Width="220px">
24+
<EditSettings>
25+
<DxComboBoxSettings Data="ProjectList" ValueFieldName="ID" TextFieldName="Name"
26+
SearchFilterCondition="ListSearchFilterCondition.Contains" />
27+
</EditSettings>
28+
</DxGridDataColumn>
29+
<DxGridDataColumn FieldName="CreatorID" Caption="Owner" Width="140px" MinWidth="100">
30+
<EditSettings>
31+
<DxComboBoxSettings Data="UserList" ValueFieldName="ID" TextFieldName="FullName"
32+
SearchFilterCondition="ListSearchFilterCondition.Contains" />
33+
</EditSettings>
34+
</DxGridDataColumn>
35+
<DxGridDataColumn FieldName="OwnerID" Caption="Assignee" Width="140px" MinWidth="100">
36+
<EditSettings>
37+
<DxComboBoxSettings Data="UserList" ValueFieldName="ID" TextFieldName="FullName"
38+
SearchFilterCondition="ListSearchFilterCondition.Contains" />
39+
</EditSettings>
40+
</DxGridDataColumn>
41+
<DxGridDataColumn FieldName="Status" Caption="Status" Width="140px" MinWidth="140"
42+
TextAlignment="GridTextAlignment.Left">
43+
<EditSettings>
44+
<DxComboBoxSettings Data="StatusList" />
45+
</EditSettings>
46+
<CellDisplayTemplate>
47+
<div class="d-flex align-items-center">
48+
@GetIssueStatusIcon((context.DataItem as Issue).Status)
49+
@context.HighlightedDisplayText
50+
</div>
51+
</CellDisplayTemplate>
52+
</DxGridDataColumn>
53+
<DxGridDataColumn FieldName="CreatedDate" Caption="Created" Width="120px" MinWidth="120" />
54+
<DxGridDataColumn FieldName="ModifiedDate" Caption="Modified" Width="120px" MinWidth="120" />
55+
<DxGridDataColumn FieldName="FixedDate" Caption="Fixed" Width="120px" MinWidth="120" />
56+
<DxGridDataColumn FieldName="Priority" Caption="Priority" Width="90px" TextAlignment="GridTextAlignment.Left"
57+
AllowGroup="false" AllowSort="false">
58+
<FilterRowCellTemplate Context="filterContext">
59+
<DxButton RenderStyle="ButtonRenderStyle.Link" CssClass="p-0 w-100" Enabled="IsGridFiltered()"
60+
Click="@(() => grid.ClearFilter())">Clear</DxButton>
61+
</FilterRowCellTemplate>
62+
<CellDisplayTemplate>
63+
<div>@GetIssuePriorityIconHtml((context.DataItem as Issue).Priority)</div>
64+
</CellDisplayTemplate>
65+
</DxGridDataColumn>
66+
</Columns>
67+
<GroupSummary>
68+
<DxGridSummaryItem FieldName="ID" SummaryType="GridSummaryItemType.Count" />
69+
</GroupSummary>
70+
<TotalSummary>
71+
<DxGridSummaryItem FieldName="ID" SummaryType="GridSummaryItemType.Count" FooterColumnName="Name" />
72+
</TotalSummary>
73+
</DxGrid>
74+
<DxAIChat @ref="chat" CssClass="my-grid-chat">
75+
<MessageContentTemplate>
76+
<div class="my-chat-content">
77+
@ToHtml(context.Content)
78+
</div>
79+
</MessageContentTemplate>
80+
</DxAIChat>
81+
82+
@code {
83+
IGrid grid;
84+
IAIChat chat;
85+
MarkupString ToHtml(string text) {
86+
return (MarkupString)Markdig.Markdown.ToHtml(text);
87+
}
88+
IEnumerable<Issue> DataSource { get; set; }
89+
IEnumerable<Project> ProjectList { get; set; }
90+
IEnumerable<User> UserList { get; set; }
91+
static List<IssueStatus?> StatusList { get; set; } =
92+
((IssueStatus[])Enum.GetValues(typeof(IssueStatus))).Cast<IssueStatus?>().ToList();
93+
string GridSearchText = "";
94+
[Parameter]
95+
public SizeMode SizeMode { get; set; }
96+
[Parameter]
97+
public EventCallback<Issue> GotoDetailsView { get; set; }
98+
protected override async Task OnInitializedAsync() {
99+
ProjectList = (await IssuesDataService.GetProjectsAsync())
100+
.OrderBy(i => i.Name)
101+
.ToList();
102+
UserList = (await IssuesDataService.GetUsersAsync())
103+
.OrderBy(i => i.FullName)
104+
.ToList();
105+
DataSource = await IssuesDataService.GetIssuesAsync();
106+
}
107+
void Grid_CustomizeFilterRowEditor(GridCustomizeFilterRowEditorEventArgs e) {
108+
if(e.FieldName == "CreatedDate" || e.FieldName == "ModifiedDate" || e.FieldName == "FixedDate")
109+
((ITextEditSettings)e.EditSettings).ClearButtonDisplayMode = DataEditorClearButtonDisplayMode.Never;
110+
}
111+
public MarkupString GetIssueStatusIcon(IssueStatus status) {
112+
string statusIconName = status switch {
113+
IssueStatus.Fixed => "fixed",
114+
IssueStatus.Postponed => "postponed",
115+
IssueStatus.Rejected => "rejected",
116+
IssueStatus.New => "new",
117+
_ => throw new NotSupportedException()
118+
};
119+
string html = string.Format("<span class='status-icon status-icon-{0} me-1 rounded-circle d-inline-block'></span>",
120+
statusIconName);
121+
return new MarkupString(html);
122+
}
123+
public MarkupString GetIssuePriorityIconHtml(IssuePriority priority) {
124+
string priorytyClass = "warning";
125+
string title = "Medium";
126+
if(priority == IssuePriority.High) {
127+
priorytyClass = "danger";
128+
title = " High ";
129+
}
130+
if(priority == IssuePriority.Low) {
131+
priorytyClass = "info";
132+
title = " Low ";
133+
}
134+
string html = string.Format("<span class='badge priority-{0} py-1 px-2' title='{1} Priority'>{1}</span>", priorytyClass,
135+
title);
136+
return new MarkupString(html);
137+
}
138+
public MarkupString GetIssueTypeIconHtml(IssueType type) {
139+
string html = "";
140+
if(type == IssueType.Bug)
141+
html = "<span class='bug-icon d-inline-block me-1' title='Bug'></span>";
142+
return new MarkupString(html);
143+
}
144+
public bool IsGridFiltered() {
145+
return !object.ReferenceEquals(grid.GetFilterCriteria(), null);
146+
}
147+
148+
protected override async Task OnAfterRenderAsync(bool firstRender) {
149+
if(firstRender) {
150+
using(MemoryStream ms = new MemoryStream()) {
151+
grid.BeginUpdate();
152+
grid.ShowGroupedColumns = true;
153+
await grid.ExportToXlsxAsync(ms, new GridXlExportOptions() {
154+
ExportDisplayText = true
155+
});
156+
await chat.SetupAssistantAsync(new OpenAIAssistantOptions("grid_data.xlsx", ms) {
157+
Instructions = AssistantHelper.GetAIAssistantInstructions("xlsx"),
158+
UseFileSearchTool = false
159+
});
160+
grid.ShowGroupedColumns = false;
161+
grid.EndUpdate();
162+
}
163+
}
164+
await base.OnAfterRenderAsync(firstRender);
165+
}
166+
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
@page "/report"
2+
@using Azure.AI.OpenAI.Assistants
3+
@using DevExpress.Blazor.Reporting
4+
@using DevExpress.XtraReports
5+
@using DevExpress.XtraReports.UI;
6+
@using DevExpress.XtraReports.Parameters;
7+
@using DevExpress.Blazor;
8+
@using DevExpress.AI.Samples.Blazor.Components.Reporting
9+
@using DevExpress.AI.Samples.Blazor.Services;
10+
@using DevExpress.AI.Samples.Blazor.Models;
11+
@using System.IO;
12+
@using DevExpress.Blazor.Reporting.Models;
13+
@using DevExpress.Blazor.Reporting.EditingFields;
14+
15+
<DxListBox Data="@DemoReportList" CssClass="my-list" Value="@DemoReportName" ValueChanged="@(async (string name) => await DemoReportNameChanged(name))"></DxListBox>
16+
<DxReportViewer @ref="Viewer" CssClass="my-report" OnCustomizeTabs="OnCustomizeTabs">
17+
</DxReportViewer>
18+
19+
@code {
20+
[Inject] IDemoReportSource DemoReportSource { get; set; }
21+
List<string> DemoReportList { get; set; }
22+
string DemoReportName { get; set; }
23+
XtraReport CurrentReport { get; set; }
24+
25+
DxReportViewer Viewer { get; set; }
26+
public enum Gender { Male, Female }
27+
28+
async Task DemoReportNameChanged(string name) {
29+
DemoReportName = name;
30+
await UpdateReportAsync(name);
31+
}
32+
33+
async Task UpdateReportAsync(string reportName) {
34+
CurrentReport = GetReport(reportName);
35+
await Viewer.OpenReportAsync(CurrentReport);
36+
}
37+
XtraReport GetReport(string reportName) {
38+
XtraReport report = DemoReportSource.GetReport(reportName);
39+
return report;
40+
}
41+
protected override Task OnInitializedAsync() {
42+
DemoReportList = DemoReportSource.GetReportList().Keys.ToList();
43+
DemoReportName = "Market Share Report";
44+
return base.OnInitializedAsync();
45+
}
46+
47+
protected override async Task OnAfterRenderAsync(bool firstRender) {
48+
if(firstRender) {
49+
await UpdateReportAsync(DemoReportName);
50+
}
51+
base.OnAfterRender(firstRender);
52+
}
53+
54+
void OnCustomizeTabs(List<TabModel> tabs) {
55+
tabs.Add(new TabModel(new UserAssistantTabContentModel(() => CurrentReport), "AI", "AI Assistant") {
56+
TabTemplate = (tabModel) => {
57+
return (builder) => {
58+
builder.OpenComponent<AITabRenderer>(0);
59+
builder.AddComponentParameter(1, "Model", tabModel.ContentModel);
60+
builder.CloseComponent();
61+
};
62+
}
63+
});
64+
}
65+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
@using System.Text.RegularExpressions
2+
@using DevExpress.AI.Samples.Blazor.Models
3+
@using AIIntegration.Services.Chat
4+
@using DevExpress.AIIntegration.Blazor.Chat
5+
@using DevExpress.AIIntegration.OpenAI.Services
6+
@using Markdig
7+
8+
<DxAIChat CssClass="my-report-chat" ResponseContentFormat="ResponseContentFormat.Markdown" Initialized="ChatInitialized">
9+
<MessageContentTemplate>
10+
<div class="my-chat-content">
11+
@ToHtml(context.Content)
12+
</div>
13+
</MessageContentTemplate>
14+
</DxAIChat>
15+
16+
@code {
17+
[Parameter] public UserAssistantTabContentModel Model { get; set; }
18+
string ClearAnnotations(string text) {
19+
//To clear out the annotations in a response from assistant.
20+
return Regex.Replace(text, @"\【.*?】", "");
21+
}
22+
23+
MarkupString ToHtml(string text) {
24+
text = ClearAnnotations(text);
25+
return (MarkupString)Markdown.ToHtml(text);
26+
}
27+
28+
async Task ChatInitialized(IAIChat aIChat) {
29+
using (MemoryStream ms = Model.GetReportData()) {
30+
await aIChat.SetupAssistantAsync(new OpenAIAssistantOptions("report.pdf", ms) {
31+
Instructions = AssistantHelper.GetAIAssistantInstructions("pdf")
32+
});
33+
}
34+
}
35+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<Router AppAssembly="typeof(Program).Assembly">
2+
<Found Context="routeData">
3+
<RouteView RouteData="routeData" DefaultLayout="typeof(Layout.MainLayout)" />
4+
<FocusOnNavigate RouteData="routeData" Selector="h1" />
5+
</Found>
6+
</Router>
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
@using System.Net.Http
2+
@using System.Net.Http.Json
3+
@using Microsoft.AspNetCore.Components.Forms
4+
@using Microsoft.AspNetCore.Components.Routing
5+
@using Microsoft.AspNetCore.Components.Web
6+
@using static Microsoft.AspNetCore.Components.Web.RenderMode
7+
@using Microsoft.AspNetCore.Components.Web.Virtualization
8+
@using Microsoft.JSInterop
9+
@using DevExpress.AI.Samples.Blazor
10+
@using DevExpress.AI.Samples.Blazor.Components
11+
@using DevExpress.AIIntegration.Blazor
12+
@using DevExpress.Blazor
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
namespace DevExpress.AI.Samples.Blazor {
2+
public static class AssistantHelper {
3+
public static string GetAIAssistantInstructions(string documentFormat) => $"""
4+
You are an analytics assistant specialized in analyzing {documentFormat} files. You use all available methods for parse this data. Your role is to assist users by providing accurate answers to their questions about data contained within these files.
5+
6+
### Tasks:
7+
- Perform various types of data analyses, including summaries, calculations, data filtering, and trend identification.
8+
- Clearly explain your analysis process to ensure users understand how you arrived at your answers.
9+
- Always provide precise and accurate information based on the Excel data.
10+
- If you cannot find an answer based on the provided data, explicitly state: "The requested information cannot be found in the data provided."
11+
12+
### Examples:
13+
1. **Summarization:**
14+
- **User Question:** "What is the average sales revenue for Q1?"
15+
- **Response:** "The average sales revenue for Q1 is calculated as $45,000, based on the data in Sheet1, Column C."
16+
17+
2. **Data Filtering:**
18+
- **User Question:** "Which products had sales over $10,000 in June?"
19+
- **Response:** "The products with sales over $10,000 in June are listed in Sheet2, Column D, and they include Product A, Product B, and Product C."
20+
21+
3. **Insufficient Data:**
22+
- **User Question:** "What is the market trend for Product Z over the past 5 years?"
23+
- **Response:** "The requested information cannot be found in the data provided, as the dataset only includes data for the current year."
24+
25+
### Additional Instructions:
26+
- Format your responses to clearly indicate which sheet and column the data was extracted from when necessary.
27+
- Avoid providing any answers if the data in the file is insufficient for a reliable response.
28+
- Ask clarifying questions if the user's query is ambiguous or lacks detail.
29+
30+
Remember, your primary goal is to provide helpful, data-driven insights that directly answer the user's questions. Do not assume or infer information not present in the dataset.
31+
""";
32+
}
33+
}
Binary file not shown.
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
namespace DevExpress.AI.Samples.Blazor.Data {
2+
public enum IssueType { Request, Bug }
3+
public enum IssueStatus { New, Postponed, Fixed, Rejected }
4+
public enum IssuePriority { Low, Medium, High }
5+
public partial class Issue {
6+
public long ID { get; set; }
7+
public string Name { get; set; }
8+
public IssueType Type { get; set; }
9+
public Nullable<long> ProjectID { get; set; }
10+
public IssuePriority Priority { get; set; }
11+
public IssueStatus Status { get; set; }
12+
public Nullable<long> CreatorID { get; set; }
13+
public Nullable<System.DateTime> CreatedDate { get; set; }
14+
public Nullable<long> OwnerID { get; set; }
15+
public Nullable<System.DateTime> ModifiedDate { get; set; }
16+
public Nullable<System.DateTime> FixedDate { get; set; }
17+
public string Description { get; set; }
18+
public string Resolution { get; set; }
19+
}
20+
}

0 commit comments

Comments
 (0)