452 lines
15 KiB
Markdown
452 lines
15 KiB
Markdown
|
|
# Add New Report - DataPRO Prompt Template
|
||
|
|
|
||
|
|
## Context
|
||
|
|
DataPRO reports are implemented as Prism modules in two locations:
|
||
|
|
- `DataPRO/Modules/Reports/` - Full DataPRO reports
|
||
|
|
- `DTS Viewer/DTS.Viewer.Reports/` - Viewer-specific reports
|
||
|
|
|
||
|
|
Reports follow MVVM with separate Input and Output views for parameter collection and results display.
|
||
|
|
|
||
|
|
## System Architecture
|
||
|
|
```
|
||
|
|
DataPRO/Modules/Reports/PedestrianAndHeadReports/
|
||
|
|
├── Classes/ # Report generation logic
|
||
|
|
│ ├── ReportBase.cs # Base class for reports
|
||
|
|
│ ├── ExportBase.cs # Export functionality
|
||
|
|
│ └── {ReportName}Export.cs # Specific export logic
|
||
|
|
├── View/
|
||
|
|
│ ├── {ReportName}InputView.xaml # Parameter input UI
|
||
|
|
│ └── {ReportName}OutputView.xaml # Results display UI
|
||
|
|
├── ViewModel/
|
||
|
|
│ └── {ReportName}ViewModel.cs
|
||
|
|
├── Resources/ # Localization
|
||
|
|
└── {ReportName}Module.cs # Module registration
|
||
|
|
```
|
||
|
|
|
||
|
|
## Step-by-Step Instructions
|
||
|
|
|
||
|
|
### 1. Create the Report Module Class
|
||
|
|
**File:** `DataPRO/Modules/Reports/{ReportName}/{ReportName}Module.cs`
|
||
|
|
|
||
|
|
```csharp
|
||
|
|
using System;
|
||
|
|
using System.ComponentModel.Composition;
|
||
|
|
using System.Windows.Media.Imaging;
|
||
|
|
using DTS.Common;
|
||
|
|
using DTS.Common.Interface;
|
||
|
|
using Microsoft.Practices.Prism.Modularity;
|
||
|
|
using Microsoft.Practices.Unity;
|
||
|
|
|
||
|
|
namespace {REPORT_NAME}
|
||
|
|
{
|
||
|
|
[Export(typeof(IModule))]
|
||
|
|
[Module(ModuleName = "{REPORT_NAME}Module")]
|
||
|
|
public class {REPORT_NAME}Module : IModule
|
||
|
|
{
|
||
|
|
private readonly IUnityContainer _unityContainer;
|
||
|
|
|
||
|
|
public {REPORT_NAME}Module(IUnityContainer unityContainer)
|
||
|
|
{
|
||
|
|
_unityContainer = unityContainer;
|
||
|
|
}
|
||
|
|
|
||
|
|
public void Initialize()
|
||
|
|
{
|
||
|
|
_unityContainer.RegisterType<I{REPORT_NAME}InputView, {REPORT_NAME}InputView>();
|
||
|
|
_unityContainer.RegisterType<I{REPORT_NAME}OutputView, {REPORT_NAME}OutputView>();
|
||
|
|
_unityContainer.RegisterType<I{REPORT_NAME}ViewModel, {REPORT_NAME}ViewModel>();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = false)]
|
||
|
|
public class {REPORT_NAME}ImageAttribute : ImageAttribute
|
||
|
|
{
|
||
|
|
private BitmapImage _img;
|
||
|
|
|
||
|
|
public {REPORT_NAME}ImageAttribute() : this(null) { }
|
||
|
|
|
||
|
|
public override BitmapImage AssemblyImage
|
||
|
|
{
|
||
|
|
get { _img = AssemblyInfo.GetImage(AssemblyNames.DB.ToString()); return _img; }
|
||
|
|
}
|
||
|
|
|
||
|
|
public {REPORT_NAME}ImageAttribute(string s)
|
||
|
|
{
|
||
|
|
_img = AssemblyInfo.GetImage(AssemblyNames.DB.ToString());
|
||
|
|
}
|
||
|
|
|
||
|
|
public override Type GetAttributeType() => typeof(ImageAttribute);
|
||
|
|
public override BitmapImage GetAssemblyImage() => AssemblyImage;
|
||
|
|
|
||
|
|
private string _name;
|
||
|
|
public override string AssemblyName
|
||
|
|
{
|
||
|
|
get { _name = AssemblyNames.{REPORT_GROUP}.ToString(); return _name; }
|
||
|
|
}
|
||
|
|
|
||
|
|
public override string GetAssemblyName() => AssemblyName;
|
||
|
|
|
||
|
|
private string _group;
|
||
|
|
public override string AssemblyGroup
|
||
|
|
{
|
||
|
|
get { _group = eAssemblyGroups.Administrative.ToString(); return _group; }
|
||
|
|
}
|
||
|
|
|
||
|
|
public override string GetAssemblyGroup() => AssemblyGroup;
|
||
|
|
|
||
|
|
public override eAssemblyRegion GetAssemblyRegion()
|
||
|
|
{
|
||
|
|
throw new NotImplementedException();
|
||
|
|
}
|
||
|
|
public override eAssemblyRegion AssemblyRegion => throw new NotImplementedException();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### 2. Create the Report Base Class
|
||
|
|
**File:** `DataPRO/Modules/Reports/{ReportName}/Classes/{ReportName}.cs`
|
||
|
|
|
||
|
|
```csharp
|
||
|
|
using System;
|
||
|
|
using System.Collections.Generic;
|
||
|
|
using DTS.Common.Storage;
|
||
|
|
|
||
|
|
namespace {REPORT_NAME}
|
||
|
|
{
|
||
|
|
public class {REPORT_NAME}Report
|
||
|
|
{
|
||
|
|
public string ReportTitle { get; set; }
|
||
|
|
public DateTime GeneratedDate { get; set; }
|
||
|
|
public List<ReportChannel> Channels { get; set; }
|
||
|
|
|
||
|
|
public {REPORT_NAME}Report()
|
||
|
|
{
|
||
|
|
Channels = new List<ReportChannel>();
|
||
|
|
GeneratedDate = DateTime.Now;
|
||
|
|
}
|
||
|
|
|
||
|
|
public void Generate(TestSetup testSetup, ReportParameters parameters)
|
||
|
|
{
|
||
|
|
// Validate inputs
|
||
|
|
if (testSetup == null)
|
||
|
|
throw new ArgumentNullException(nameof(testSetup));
|
||
|
|
|
||
|
|
// Generate report data
|
||
|
|
ProcessData(testSetup, parameters);
|
||
|
|
}
|
||
|
|
|
||
|
|
private void ProcessData(TestSetup testSetup, ReportParameters parameters)
|
||
|
|
{
|
||
|
|
// Implementation specific to report type
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
public class ReportChannel
|
||
|
|
{
|
||
|
|
public string ChannelName { get; set; }
|
||
|
|
public double PeakValue { get; set; }
|
||
|
|
public double Duration { get; set; }
|
||
|
|
}
|
||
|
|
|
||
|
|
public class ReportParameters
|
||
|
|
{
|
||
|
|
public DateTime StartTime { get; set; }
|
||
|
|
public DateTime EndTime { get; set; }
|
||
|
|
public double Threshold { get; set; }
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### 3. Create the Export Class
|
||
|
|
**File:** `DataPRO/Modules/Reports/{ReportName}/Classes/{ReportName}Export.cs`
|
||
|
|
|
||
|
|
```csharp
|
||
|
|
using System;
|
||
|
|
using System.Data;
|
||
|
|
using System.IO;
|
||
|
|
using DTS.Common.Storage;
|
||
|
|
|
||
|
|
namespace {REPORT_NAME}
|
||
|
|
{
|
||
|
|
public class {REPORT_NAME}Export : ExportBase
|
||
|
|
{
|
||
|
|
public {REPORT_NAME}Report Report { get; set; }
|
||
|
|
|
||
|
|
public void ExportToCSV(string filePath)
|
||
|
|
{
|
||
|
|
if (Report == null)
|
||
|
|
throw new InvalidOperationException("Report not generated");
|
||
|
|
|
||
|
|
using (var writer = new StreamWriter(filePath))
|
||
|
|
{
|
||
|
|
WriteHeader(writer);
|
||
|
|
WriteData(writer);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
public void ExportToExcel(string filePath)
|
||
|
|
{
|
||
|
|
// Excel export implementation
|
||
|
|
}
|
||
|
|
|
||
|
|
private void WriteHeader(StreamWriter writer)
|
||
|
|
{
|
||
|
|
writer.WriteLine($"Report,{Report.ReportTitle}");
|
||
|
|
writer.WriteLine($"Generated,{Report.GeneratedDate:yyyy-MM-dd HH:mm:ss}");
|
||
|
|
writer.WriteLine();
|
||
|
|
writer.WriteLine("Channel,Peak Value,Duration");
|
||
|
|
}
|
||
|
|
|
||
|
|
private void WriteData(StreamWriter writer)
|
||
|
|
{
|
||
|
|
foreach (var channel in Report.Channels)
|
||
|
|
{
|
||
|
|
writer.WriteLine($"{channel.ChannelName},{channel.PeakValue},{channel.Duration}");
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### 4. Create Input View (XAML)
|
||
|
|
**File:** `DataPRO/Modules/Reports/{ReportName}/View/{ReportName}InputView.xaml`
|
||
|
|
|
||
|
|
```xml
|
||
|
|
<UserControl x:Class="{REPORT_NAME}.{REPORT_NAME}InputView"
|
||
|
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||
|
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||
|
|
xmlns:resx="clr-namespace:{REPORT_NAME}.Resources">
|
||
|
|
<Grid>
|
||
|
|
<Grid.RowDefinitions>
|
||
|
|
<RowDefinition Height="Auto"/>
|
||
|
|
<RowDefinition Height="Auto"/>
|
||
|
|
<RowDefinition Height="*"/>
|
||
|
|
</Grid.RowDefinitions>
|
||
|
|
|
||
|
|
<TextBlock Grid.Row="0" Text="{x:Static resx:StringResources.ReportParameters}"
|
||
|
|
Style="{StaticResource HeaderStyle}"/>
|
||
|
|
|
||
|
|
<StackPanel Grid.Row="1" Orientation="Vertical" Margin="5">
|
||
|
|
<DatePicker SelectedDate="{Binding StartDate, Mode=TwoWay}"
|
||
|
|
Header="{x:Static resx:StringResources.StartDate}"/>
|
||
|
|
<DatePicker SelectedDate="{Binding EndDate, Mode=TwoWay}"
|
||
|
|
Header="{x:Static resx:StringResources.EndDate}"/>
|
||
|
|
<TextBox Text="{Binding Threshold, Mode=TwoWay}"
|
||
|
|
Header="{x:Static resx:StringResources.Threshold}"/>
|
||
|
|
</StackPanel>
|
||
|
|
|
||
|
|
<Button Grid.Row="2" Content="{x:Static resx:StringResources.Generate}"
|
||
|
|
Command="{Binding GenerateCommand}"
|
||
|
|
HorizontalAlignment="Right" Margin="5"/>
|
||
|
|
</Grid>
|
||
|
|
</UserControl>
|
||
|
|
```
|
||
|
|
|
||
|
|
### 5. Create Output View (XAML)
|
||
|
|
**File:** `DataPRO/Modules/Reports/{ReportName}/View/{ReportName}OutputView.xaml`
|
||
|
|
|
||
|
|
```xml
|
||
|
|
<UserControl x:Class="{REPORT_NAME}.{REPORT_NAME}OutputView"
|
||
|
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||
|
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||
|
|
xmlns:resx="clr-namespace:{REPORT_NAME}.Resources">
|
||
|
|
<Grid>
|
||
|
|
<Grid.RowDefinitions>
|
||
|
|
<RowDefinition Height="Auto"/>
|
||
|
|
<RowDefinition Height="*"/>
|
||
|
|
<RowDefinition Height="Auto"/>
|
||
|
|
</Grid.RowDefinitions>
|
||
|
|
|
||
|
|
<TextBlock Grid.Row="0" Text="{Binding ReportTitle}"
|
||
|
|
Style="{StaticResource HeaderStyle}"/>
|
||
|
|
|
||
|
|
<DataGrid Grid.Row="1" ItemsSource="{Binding ReportData}"
|
||
|
|
AutoGenerateColumns="False" IsReadOnly="True">
|
||
|
|
<DataGrid.Columns>
|
||
|
|
<DataGridTextColumn Header="Channel" Binding="{Binding ChannelName}"/>
|
||
|
|
<DataGridTextColumn Header="Peak Value" Binding="{Binding PeakValue}"/>
|
||
|
|
<DataGridTextColumn Header="Duration" Binding="{Binding Duration}"/>
|
||
|
|
</DataGrid.Columns>
|
||
|
|
</DataGrid>
|
||
|
|
|
||
|
|
<StackPanel Grid.Row="2" Orientation="Horizontal" HorizontalAlignment="Right">
|
||
|
|
<Button Content="{x:Static resx:StringResources.ExportCSV}"
|
||
|
|
Command="{Binding ExportCSVCommand}" Margin="5"/>
|
||
|
|
<Button Content="{x:Static resx:StringResources.ExportExcel}"
|
||
|
|
Command="{Binding ExportExcelCommand}" Margin="5"/>
|
||
|
|
</StackPanel>
|
||
|
|
</Grid>
|
||
|
|
</UserControl>
|
||
|
|
```
|
||
|
|
|
||
|
|
### 6. Create the ViewModel
|
||
|
|
**File:** `DataPRO/Modules/Reports/{ReportName}/ViewModel/{ReportName}ViewModel.cs`
|
||
|
|
|
||
|
|
```csharp
|
||
|
|
using System;
|
||
|
|
using System.Collections.ObjectModel;
|
||
|
|
using System.ComponentModel;
|
||
|
|
using System.ComponentModel.Composition;
|
||
|
|
using System.Windows.Input;
|
||
|
|
using DTS.Common.Interface;
|
||
|
|
using Microsoft.Practices.Prism.Commands;
|
||
|
|
using Microsoft.Practices.Unity;
|
||
|
|
|
||
|
|
namespace {REPORT_NAME}
|
||
|
|
{
|
||
|
|
public class {REPORT_NAME}ViewModel : I{REPORT_NAME}ViewModel, INotifyPropertyChanged
|
||
|
|
{
|
||
|
|
private readonly IUnityContainer _unityContainer;
|
||
|
|
|
||
|
|
public I{REPORT_NAME}InputView InputView { get; set; }
|
||
|
|
public I{REPORT_NAME}OutputView OutputView { get; set; }
|
||
|
|
|
||
|
|
private DateTime _startDate;
|
||
|
|
public DateTime StartDate
|
||
|
|
{
|
||
|
|
get => _startDate;
|
||
|
|
set { _startDate = value; OnPropertyChanged(nameof(StartDate)); }
|
||
|
|
}
|
||
|
|
|
||
|
|
private DateTime _endDate;
|
||
|
|
public DateTime EndDate
|
||
|
|
{
|
||
|
|
get => _endDate;
|
||
|
|
set { _endDate = value; OnPropertyChanged(nameof(EndDate)); }
|
||
|
|
}
|
||
|
|
|
||
|
|
private double _threshold;
|
||
|
|
public double Threshold
|
||
|
|
{
|
||
|
|
get => _threshold;
|
||
|
|
set { _threshold = value; OnPropertyChanged(nameof(Threshold)); }
|
||
|
|
}
|
||
|
|
|
||
|
|
public ObservableCollection<ReportChannel> ReportData { get; set; }
|
||
|
|
|
||
|
|
public ICommand GenerateCommand { get; }
|
||
|
|
public ICommand ExportCSVCommand { get; }
|
||
|
|
public ICommand ExportExcelCommand { get; }
|
||
|
|
|
||
|
|
public event PropertyChangedEventHandler PropertyChanged;
|
||
|
|
protected void OnPropertyChanged(string propertyName)
|
||
|
|
{
|
||
|
|
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||
|
|
}
|
||
|
|
|
||
|
|
public {REPORT_NAME}ViewModel(
|
||
|
|
I{REPORT_NAME}InputView inputView,
|
||
|
|
I{REPORT_NAME}OutputView outputView,
|
||
|
|
IUnityContainer unityContainer)
|
||
|
|
{
|
||
|
|
InputView = inputView;
|
||
|
|
InputView.DataContext = this;
|
||
|
|
OutputView = outputView;
|
||
|
|
OutputView.DataContext = this;
|
||
|
|
_unityContainer = unityContainer;
|
||
|
|
|
||
|
|
ReportData = new ObservableCollection<ReportChannel>();
|
||
|
|
|
||
|
|
GenerateCommand = new DelegateCommand(OnGenerate);
|
||
|
|
ExportCSVCommand = new DelegateCommand(OnExportCSV);
|
||
|
|
ExportExcelCommand = new DelegateCommand(OnExportExcel);
|
||
|
|
|
||
|
|
InitializeDefaults();
|
||
|
|
}
|
||
|
|
|
||
|
|
private void InitializeDefaults()
|
||
|
|
{
|
||
|
|
StartDate = DateTime.Now.AddDays(-7);
|
||
|
|
EndDate = DateTime.Now;
|
||
|
|
Threshold = 0.0;
|
||
|
|
}
|
||
|
|
|
||
|
|
private void OnGenerate()
|
||
|
|
{
|
||
|
|
// Generate report logic
|
||
|
|
var report = new {REPORT_NAME}Report();
|
||
|
|
// ... generate data
|
||
|
|
}
|
||
|
|
|
||
|
|
private void OnExportCSV()
|
||
|
|
{
|
||
|
|
// Export to CSV
|
||
|
|
}
|
||
|
|
|
||
|
|
private void OnExportExcel()
|
||
|
|
{
|
||
|
|
// Export to Excel
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### 7. Add Localization Resources
|
||
|
|
**File:** `DataPRO/Modules/Reports/{ReportName}/Resources/StringResources.resx`
|
||
|
|
|
||
|
|
Add required strings:
|
||
|
|
- `ReportParameters`
|
||
|
|
- `StartDate`
|
||
|
|
- `EndDate`
|
||
|
|
- `Threshold`
|
||
|
|
- `Generate`
|
||
|
|
- `ExportCSV`
|
||
|
|
- `ExportExcel`
|
||
|
|
|
||
|
|
## For DTS Viewer Reports
|
||
|
|
If creating a Viewer report, use this location:
|
||
|
|
```
|
||
|
|
DTS Viewer/DTS.Viewer.Reports/DTS.Viewer.{ReportName}/
|
||
|
|
```
|
||
|
|
|
||
|
|
The module class differs slightly:
|
||
|
|
```csharp
|
||
|
|
[Module(ModuleName = "{REPORT_NAME}")]
|
||
|
|
public class {REPORT_NAME}Module : I{REPORT_NAME}Module
|
||
|
|
{
|
||
|
|
public bool SessionStarted { get; private set; }
|
||
|
|
|
||
|
|
public void StartSession()
|
||
|
|
{
|
||
|
|
var eventAggregator = _unityContainer.Resolve<IEventAggregator>();
|
||
|
|
eventAggregator.GetEvent<LoadViewModulEvent>().Publish(new LoadViewModulArg());
|
||
|
|
SessionStarted = true;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
## Files to Create Summary
|
||
|
|
|
||
|
|
| File | Action |
|
||
|
|
|------|--------|
|
||
|
|
| `{ReportName}/{ReportName}Module.cs` | Create |
|
||
|
|
| `{ReportName}/Classes/{ReportName}.cs` | Create |
|
||
|
|
| `{ReportName}/Classes/{ReportName}Export.cs` | Create |
|
||
|
|
| `{ReportName}/View/{ReportName}InputView.xaml` | Create |
|
||
|
|
| `{ReportName}/View/{ReportName}InputView.xaml.cs` | Create |
|
||
|
|
| `{ReportName}/View/{ReportName}OutputView.xaml` | Create |
|
||
|
|
| `{ReportName}/View/{ReportName}OutputView.xaml.cs` | Create |
|
||
|
|
| `{ReportName}/ViewModel/{ReportName}ViewModel.cs` | Create |
|
||
|
|
| `{ReportName}/Resources/StringResources.resx` | Create |
|
||
|
|
| `DTS.Common/Interface/{ReportName}Interfaces.cs` | Create |
|
||
|
|
|
||
|
|
## Validation Checklist
|
||
|
|
|
||
|
|
- [ ] Module registered with `[Module]` attribute
|
||
|
|
- [ ] Assembly image attribute defined
|
||
|
|
- [ ] Input/Output views follow naming convention
|
||
|
|
- [ ] ViewModel implements `INotifyPropertyChanged`
|
||
|
|
- [ ] Commands use `DelegateCommand` from Prism
|
||
|
|
- [ ] Export methods handle file I/O properly
|
||
|
|
- [ ] Localization strings for all UI text
|
||
|
|
- [ ] Report generation validates inputs
|
||
|
|
- [ ] Error handling implemented
|
||
|
|
- [ ] Assembly group set appropriately (`eAssemblyGroups.Administrative`)
|
||
|
|
|
||
|
|
## Common Patterns
|
||
|
|
|
||
|
|
1. **Two-View Pattern:** Reports use separate Input and Output views
|
||
|
|
2. **ExportBase Inheritance:** Export classes inherit from `ExportBase`
|
||
|
|
3. **ObservableCollection:** Use for data binding in ViewModels
|
||
|
|
4. **DelegateCommand:** Use Prism's `DelegateCommand` for ICommand implementation
|