Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions angular/src/app/app.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@ import { Component, ViewChild } from '@angular/core';
import { RouterOutlet } from '@angular/router';
import { Router } from '@angular/router';
import { SidebarAllModule, TreeViewAllModule, NodeSelectEventArgs, ToolbarAllModule, SidebarComponent } from '@syncfusion/ej2-angular-navigations';

// Import Syncfusion Chart modules
import { ChartAllModule, LineSeriesService, CategoryService, DateTimeService, TooltipService, LegendService } from '@syncfusion/ej2-angular-charts';
@Component({
selector: 'app-root',
standalone: true,
imports: [RouterOutlet, SidebarAllModule, TreeViewAllModule, ToolbarAllModule],
imports: [RouterOutlet, SidebarAllModule, TreeViewAllModule, ToolbarAllModule, ChartAllModule ],
templateUrl: './app.component.html',
styleUrl: './app.component.css'
styleUrl: './app.component.css'
})
export class AppComponent {
@ViewChild('sidebarhome') sidebar!: SidebarComponent;
Expand All @@ -17,6 +18,7 @@ export class AppComponent {
{ id: '1', name: 'Smart Paste', route: '/smart-paste' },
{ id: '2', name: 'Smart TexArea', route: '/smart-textarea' },
{ id: '11', name: 'ComboBox', route: 'combobox/local-embedding' },
{ id: '22', name: 'Chart', route: 'chart/data-preprocessing' },
{ id: '4', name: 'Schedule', url: '', route: '/scheduler/smart-scheduler' },
{ id: '14', name: 'Rich Text Editor', url: '', route: 'rich-text-editor' },
{ id: '16', name: 'SpreadSheet', url: '', route: 'spreadsheet' },
Expand Down Expand Up @@ -69,8 +71,6 @@ export class AppComponent {

]
},
{ id: '11', name: '' }


];
public fields: Object = { dataSource: this.treeData, id: 'id', text: 'name', child: 'subItems' };
Expand Down
2 changes: 2 additions & 0 deletions angular/src/app/app.routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { comboboxRoutes } from './combo-box';
import { diagramRoutes } from './diagram';
import { RouterModule, Routes } from '@angular/router';
import { mapsRoutes } from './maps';
import { chartRoutes } from './chart';
import {SmartFileManager} from './filemanager/smart-filemanager/file-manager.component'
import {pivotRoutes} from './pivot-table';
import { queryRoutes} from './query-builder';
Expand Down Expand Up @@ -41,6 +42,7 @@ export const routes: Routes = [
...schedulerRoutes,
...ganttRoutes,
...mapsRoutes,
...chartRoutes,
...pivotRoutes,
{
path: 'smart-treegrid', component: AdaptiveDataStructureComponent
Expand Down
35 changes: 35 additions & 0 deletions angular/src/app/chart/data-preproccessing/azure-ai.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { lastValueFrom } from 'rxjs';

@Injectable({
providedIn: 'root'
})
export class AzureAIService {
private endpoint = 'YOUR_AZURE_OPENAI_ENDPOINT'; // e.g. https://<your-resource>.openai.azure.com/
private apiKey = 'YOUR_API_KEY';
private deploymentName = 'YOUR_DEPLOYMENT_NAME'; // model deployment name

constructor(private http: HttpClient) {}

public async getCompletion(prompt: string): Promise<string> {
const url = `${this.endpoint}/openai/deployments/${this.deploymentName}/completions?api-version=2023-07-01-preview`;

const headers = new HttpHeaders({
'Content-Type': 'application/json',
'api-key': this.apiKey
});

const body = {
prompt: prompt,
max_tokens: 500,
temperature: 0.5
};

const response$ = this.http.post<any>(url, body, { headers });
const response = await lastValueFrom(response$);

// Azure returns choices array
return response.choices && response.choices.length > 0 ? response.choices[0].text : '';
}
}
32 changes: 32 additions & 0 deletions angular/src/app/chart/data-preproccessing/chart-data.model.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
export interface ChartData {
Time: Date;
Visitors: number | null;
Color?: string | null;
}

export const originalChartData: ChartData[] = [
{ Time: new Date(2024, 6, 1, 0, 0, 0), Visitors: 150 },
{ Time: new Date(2024, 6, 1, 1, 0, 0), Visitors: 160 },
{ Time: new Date(2024, 6, 1, 2, 0, 0), Visitors: 155 },
{ Time: new Date(2024, 6, 1, 3, 0, 0), Visitors: null },
{ Time: new Date(2024, 6, 1, 4, 0, 0), Visitors: 170 },
{ Time: new Date(2024, 6, 1, 5, 0, 0), Visitors: 175 },
{ Time: new Date(2024, 6, 1, 6, 0, 0), Visitors: 145 },
{ Time: new Date(2024, 6, 1, 7, 0, 0), Visitors: 180 },
{ Time: new Date(2024, 6, 1, 8, 0, 0), Visitors: null },
{ Time: new Date(2024, 6, 1, 9, 0, 0), Visitors: 185 },
{ Time: new Date(2024, 6, 1, 10, 0, 0), Visitors: 200 },
{ Time: new Date(2024, 6, 1, 11, 0, 0), Visitors: null },
{ Time: new Date(2024, 6, 1, 12, 0, 0), Visitors: 220 },
{ Time: new Date(2024, 6, 1, 13, 0, 0), Visitors: 230 },
{ Time: new Date(2024, 6, 1, 14, 0, 0), Visitors: null },
{ Time: new Date(2024, 6, 1, 15, 0, 0), Visitors: 250 },
{ Time: new Date(2024, 6, 1, 16, 0, 0), Visitors: 260 },
{ Time: new Date(2024, 6, 1, 17, 0, 0), Visitors: 270 },
{ Time: new Date(2024, 6, 1, 18, 0, 0), Visitors: null },
{ Time: new Date(2024, 6, 1, 19, 0, 0), Visitors: 280 },
{ Time: new Date(2024, 6, 1, 20, 0, 0), Visitors: 250 },
{ Time: new Date(2024, 6, 1, 21, 0, 0), Visitors: 290 },
{ Time: new Date(2024, 6, 1, 22, 0, 0), Visitors: 300 },
{ Time: new Date(2024, 6, 1, 23, 0, 0), Visitors: null },
];
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
.chart-wrapper {
position: relative;
margin: 0 auto;
width: 90%;
}

.chart-action-button {
position: absolute;
top: 10px;
right: 10px;
display: flex;
align-items: center;
justify-content: center;
}

.chart-action-button .e-icons {
color: white;
margin-top: 3%;
margin-bottom: 3%;
}

.description-container {
margin: 10px auto;
width: 85%;
padding: 10px;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<h4 style="text-align: center;">Data Preprocessing</h4>

<div class="description-container e-card">
<div class="e-card-content">
<p>
The <b>Data Preprocessing</b> sample uses Syncfusion Angular Charts to clean and visualize datasets
containing missing or inconsistent values. With a single click on the AI Assist button, raw data is
automatically processed to fill gaps and correct anomalies. The updated dataset is instantly reflected
in the chart, offering a complete and accurate view.
</p>
<p>
Select a dataset with missing values and click AI Assist to clean and visualize complete data instantly.
Know more <a target="_blank" href="https://github.com/syncfusion/smart-ai-samples/blob/master/blazor/SyncfusionAISamples/Components/Pages/Charts/Readme.md">here</a>.
</p>
</div>
</div>

<div id="chart-wrapper" class="chart-wrapper">
<ejs-chart #chart id="data-pre-chart" [primaryXAxis]="{ valueType: 'DateTime', labelFormat: 'h tt', edgeLabelPlacement: 'Shift' }"
[primaryYAxis]="{ minimum: 140, maximum: 320, interval: 30 }"
[title]="'E-Commerce Website Traffic Data'"
[subTitle]="'AI-powered data cleaning and preprocessing for tracking hourly website visitors'">
<e-series-collection>
<e-series [dataSource]="chartData" xName="Time" yName="Visitors"
type="MultiColoredLine" pointColorMapping="Color" name="Visitors">
</e-series>
</e-series-collection>
</ejs-chart>

<button ejs-button cssClass="chart-action-button e-primary" iconCss="e-icons e-ai-chat"
(click)="processChartData()">AI Assist</button>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
import { Component, ViewChild } from '@angular/core';
import { ChartComponent, ChartAllModule, SeriesCollectionDirective, SeriesDirective } from '@syncfusion/ej2-angular-charts';
import { ButtonComponent } from '@syncfusion/ej2-angular-buttons';
import { createSpinner, showSpinner, hideSpinner } from '@syncfusion/ej2-popups';
import { getAzureChatAIRequest } from '../../ai-models/azure_openai'; // your AI service
import { ButtonModule } from '@syncfusion/ej2-angular-buttons';

interface ChartData {
Time: Date;
Visitors: number | null;
Color?: string | null;
}


@Component({
selector: 'app-data-preprocessing',
standalone: true,

imports: [ChartAllModule, ButtonModule],
templateUrl: './data-preprocessing.component.html',
styleUrls: ['./data-preprocessing.component.css']
})



export class DataPreprocessingComponent {
@ViewChild('chart', { static: true }) chart!: ChartComponent;

public chartData: ChartData[] = [];
public originalData: ChartData[] = [
{ Time: new Date(2024, 6, 1, 0, 0, 0), Visitors: 150 },
{ Time: new Date(2024, 6, 1, 1, 0, 0), Visitors: 160 },
{ Time: new Date(2024, 6, 1, 2, 0, 0), Visitors: 155 },
{ Time: new Date(2024, 6, 1, 3, 0, 0), Visitors: null },
{ Time: new Date(2024, 6, 1, 4, 0, 0), Visitors: 170 },
{ Time: new Date(2024, 6, 1, 5, 0, 0), Visitors: 175 },
{ Time: new Date(2024, 6, 1, 6, 0, 0), Visitors: 145 },
{ Time: new Date(2024, 6, 1, 7, 0, 0), Visitors: 180 },
{ Time: new Date(2024, 6, 1, 8, 0, 0), Visitors: null },
{ Time: new Date(2024, 6, 1, 9, 0, 0), Visitors: 185 },
{ Time: new Date(2024, 6, 1, 10, 0, 0), Visitors: 200 },
{ Time: new Date(2024, 6, 1, 11, 0, 0), Visitors: null },
{ Time: new Date(2024, 6, 1, 12, 0, 0), Visitors: 220 },
{ Time: new Date(2024, 6, 1, 13, 0, 0), Visitors: 230 },
{ Time: new Date(2024, 6, 1, 14, 0, 0), Visitors: null },
{ Time: new Date(2024, 6, 1, 15, 0, 0), Visitors: 250 },
{ Time: new Date(2024, 6, 1, 16, 0, 0), Visitors: 260 },
{ Time: new Date(2024, 6, 1, 17, 0, 0), Visitors: 270 },
{ Time: new Date(2024, 6, 1, 18, 0, 0), Visitors: null },
{ Time: new Date(2024, 6, 1, 19, 0, 0), Visitors: 280 },
{ Time: new Date(2024, 6, 1, 20, 0, 0), Visitors: 250 },
{ Time: new Date(2024, 6, 1, 21, 0, 0), Visitors: 290 },
{ Time: new Date(2024, 6, 1, 22, 0, 0), Visitors: 300 },
{ Time: new Date(2024, 6, 1, 23, 0, 0), Visitors: null },
];

ngOnInit(): void {
this.loadOriginalData();
}

loadOriginalData(): void {
this.chartData = [...this.originalData];
}

// async processChartData(): Promise<void> {
// const chartContainer = document.getElementById('chart-wrapper') as HTMLElement;
// createSpinner({ target: chartContainer });
// showSpinner(chartContainer);

// const prompt = this.generatePrompt(this.originalData);
// let response: string = await getAzureChatAIRequest({
// messages: [{ role: 'user', content: prompt }]
// });

// response = response.replace('```json', '').replace('```', '');
// const processed = this.convertAIResponseToChartData(response, this.originalData);

// if (processed.length) {
// this.chartData = [...processed];
// }

// hideSpinner(chartContainer);
// }

async processChartData(): Promise<void> {
const chartContainer = document.getElementById('chart-wrapper') as HTMLElement;
createSpinner({ target: chartContainer });
showSpinner(chartContainer);

// Hide spinner after 3 seconds, regardless of AI response
setTimeout(() => {
hideSpinner(chartContainer);
}, 1000);

const prompt = this.generatePrompt(this.originalData);
let response: string = await getAzureChatAIRequest({
messages: [{ role: 'user', content: prompt }]
});

response = response.replace('```json', '').replace('```', '');
const processed = this.convertAIResponseToChartData(response, this.originalData);

if (processed.length) {
this.chartData = [...processed];
}
}


generatePrompt(data: ChartData[]): string {
let prompt = 'Clean the following e-commerce website traffic data, resolve outliers and fill missing values:\n';
for (let d of data) {
prompt += `${d.Time.getFullYear()}-${(d.Time.getMonth() + 1)
.toString().padStart(2, '0')}-${d.Time.getDate().toString().padStart(2, '0')}-${d.Time.getHours().toString().padStart(2, '0')}-00-00: ${d.Visitors}\n`;
}
prompt += 'and the output cleaned data should be in the yyyy-MM-dd-HH-m-ss:Value format, no other explanation required';
return prompt;
}

convertAIResponseToChartData(response: string, original: ChartData[]): ChartData[] {
const lines = response.split('\n').filter(l => l.trim().length > 0);
const result: ChartData[] = [];
let count = 0;

for (let line of lines) {
const parts = line.split(':');
if (parts.length === 2) {
const dateStr = parts[0].trim();
const valueStr = parts[1].trim();

const [yyyy, MM, dd, HH, mm, ss] = dateStr.split('-').map(Number);
const date = new Date(yyyy, MM - 1, dd, HH, mm, ss);
const visitors = Number(valueStr);

const isCurrNull = original[count]?.Visitors == null;
const isNextNull = original[count + 1]?.Visitors == null;
const color = (isCurrNull || isNextNull) ? '#D84227' : null;

result.push({ Time: date, Visitors: visitors, Color: color });
count++;
}
}
return result;
}
}
6 changes: 6 additions & 0 deletions angular/src/app/chart/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { Routes } from '@angular/router';
import { DataPreprocessingComponent } from './data-preproccessing/data-preprocessing.component';

export const chartRoutes:Routes = [{
path: 'chart/data-preprocessing',component: DataPreprocessingComponent
}];
Loading