Skip to content

Commit 76660db

Browse files
authored
Merge pull request #10 from palladiumkenya/feature/data
allow export data.
2 parents dd0a870 + f37a3db commit 76660db

File tree

3 files changed

+187
-0
lines changed

3 files changed

+187
-0
lines changed
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
using CSharpFunctionalExtensions;
2+
using LiveDWAPI.Application.Cs.Dto;
3+
using LiveDWAPI.Domain.Cs;
4+
using MediatR;
5+
using Microsoft.EntityFrameworkCore;
6+
using Serilog;
7+
8+
namespace LiveDWAPI.Application.Cs.Queries;
9+
10+
public class GetRealtimeFilteredExportQuery:IRequest<Result<IAsyncEnumerable<FactRealtimeIndicator>>>
11+
{
12+
public FilterDto Filter { get; }
13+
14+
public GetRealtimeFilteredExportQuery(FilterDto filter)
15+
{
16+
Filter = filter;
17+
}
18+
}
19+
20+
public class GetRealtimeFilteredExportQueryHandler:IRequestHandler<GetRealtimeFilteredExportQuery,Result<IAsyncEnumerable<FactRealtimeIndicator>>>
21+
{
22+
private readonly ICsContext _context;
23+
24+
public GetRealtimeFilteredExportQueryHandler(ICsContext context)
25+
{
26+
_context = context;
27+
}
28+
29+
public async Task<Result<IAsyncEnumerable<FactRealtimeIndicator>>> Handle(GetRealtimeFilteredExportQuery request, CancellationToken cancellationToken)
30+
{
31+
try
32+
{
33+
IQueryable<FactRealtimeIndicator> query = _context.FactRealtimeIndicators;
34+
35+
// Indicator
36+
if (request.Filter.HasIndicator())
37+
query = query.Where(x =>
38+
x.Indicator != null &&
39+
x.Indicator.ToLower()==request.Filter.Indicator!.ToLower());
40+
41+
// Time
42+
if (request.Filter.HasStartPeriod() && request.Filter.HasEndPeriod())
43+
query = query.Where(x =>
44+
x.AssessmentPeriod>=request.Filter.StartPeriod! &&
45+
x.AssessmentPeriod<=request.Filter.EndPeriod!);
46+
47+
// Place
48+
if (request.Filter.HasCounty())
49+
query = query.Where(x =>request.Filter.County!.Contains(x.County));
50+
51+
if (request.Filter.HasSubCounty())
52+
query = query.Where(x =>request.Filter.SubCounty!.Contains(x.SubCounty));
53+
54+
if (request.Filter.HasWard())
55+
query = query.Where(x =>request.Filter.Ward!.Contains(x.Ward));
56+
57+
if (request.Filter.HasFacilityName())
58+
query = query.Where(x =>request.Filter.FacilityName!.Contains(x.FacilityName));
59+
60+
// Person
61+
if (request.Filter.HasSex())
62+
query = query.Where(x =>request.Filter.Sex!.Contains(x.Sex));
63+
64+
if (request.Filter.HasAgeGroup())
65+
query = query.Where(x =>request.Filter.AgeGroup!.Contains(x.AgeGroup));
66+
67+
// Partner
68+
if (request.Filter.HasAgency())
69+
query = query.Where(x =>request.Filter.Agency!.Contains(x.Agency));
70+
71+
if (request.Filter.HasPartnerName())
72+
query = query.Where(x =>request.Filter.PartnerName!.Contains(x.PartnerName));
73+
74+
if (request.Filter.Limit > 0)
75+
query = query.Take(request.Filter.Limit);
76+
77+
var indicators = query.AsAsyncEnumerable();
78+
79+
return Result.Success(indicators);
80+
}
81+
catch (Exception e)
82+
{
83+
Log.Error(e,"Load Realtime Indicators error!");
84+
return Result.Failure<IAsyncEnumerable<FactRealtimeIndicator>>(e.Message);
85+
}
86+
}
87+
}
88+
89+
// public class GetRealtimeFilteredExportQueryValidator: AbstractValidator<GetRealtimeFilteredExportQuery>
90+
// {
91+
// public GetRealtimeFilteredExportQueryValidator()
92+
// {
93+
// RuleFor(x => x.Indicator).NotEmpty()
94+
// .WithMessage("Indicator is required");
95+
// }
96+
// }

src/LiveDWAPI.Web/Controllers/CsRealtimePointController.cs

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
1+
using System.Globalization;
2+
using CsvHelper;
13
using LiveDWAPI.Application.Cs.Dto;
24
using LiveDWAPI.Application.Cs.Queries;
5+
using LiveDWAPI.Domain.Cs;
36
using MediatR;
47
using Microsoft.AspNetCore.Mvc;
58
using Serilog;
@@ -85,4 +88,91 @@ public async Task<IActionResult> GetDataByQuery([FromQuery] FilterDto filter)
8588
return StatusCode(500, e.Message);
8689
}
8790
}
91+
92+
[HttpPost("Data")]
93+
[ProducesResponseType(typeof(IndicatorDataDto), 200)]
94+
public async Task<IActionResult> GetDataByBody([FromBody] FilterDto filter)
95+
{
96+
try
97+
{
98+
filter.Limit = 50;
99+
var res = await _mediator.Send(new GetRealtimeFilteredQuery(filter));
100+
101+
if (res.IsSuccess)
102+
{
103+
var data = res.Value;
104+
return Ok(data);
105+
}
106+
107+
throw new Exception($"Error occured ${res.Error}");
108+
}
109+
catch (Exception e)
110+
{
111+
Log.Error(e, "Error loading");
112+
return StatusCode(500, e.Message);
113+
}
114+
}
115+
116+
[HttpGet("Data/Export")]
117+
public async Task<IActionResult> ExportDataByQuery([FromQuery] FilterDto filter)
118+
{
119+
// Set the content type and filename for the browser
120+
Response.ContentType = "text/csv";
121+
Response.Headers.Add("Content-Disposition", "attachment; filename=cs_export.csv");
122+
123+
// Use a StreamWriter on the response body to avoid creating a MemoryStream
124+
await using var streamWriter = new StreamWriter(Response.Body, leaveOpen: true);
125+
await using var csvWriter = new CsvWriter(streamWriter, CultureInfo.InvariantCulture);
126+
127+
// EF Core 6+ supports IAsyncEnumerable, which is crucial for streaming
128+
var res = await _mediator.Send(new GetRealtimeFilteredExportQuery(filter));
129+
130+
var data = res.Value;
131+
132+
// Write the CSV header
133+
csvWriter.WriteHeader<FactRealtimeIndicator>();
134+
await csvWriter.NextRecordAsync();
135+
136+
// Write records one at a time as they are retrieved from the database
137+
await csvWriter.WriteRecordsAsync(data);
138+
139+
// Explicitly flush to ensure all data is sent
140+
await streamWriter.FlushAsync();
141+
142+
// This indicates the response is complete
143+
return new EmptyResult();
144+
}
145+
146+
[HttpPost("Data/Export")]
147+
public async Task<IActionResult> ExportDataByBody([FromBody] FilterDto filter)
148+
{
149+
Log.Debug("Exporting...");
150+
// Set the content type and filename for the browser
151+
Response.ContentType = "text/csv";
152+
Response.Headers.Add("Content-Disposition", "attachment; filename=cs_export.csv");
153+
154+
// Use a StreamWriter on the response body to avoid creating a MemoryStream
155+
await using var streamWriter = new StreamWriter(Response.Body, leaveOpen: true);
156+
await using var csvWriter = new CsvWriter(streamWriter, CultureInfo.InvariantCulture);
157+
158+
// EF Core 6+ supports IAsyncEnumerable, which is crucial for streaming
159+
var res = await _mediator.Send(new GetRealtimeFilteredExportQuery(filter));
160+
161+
var data = res.Value;
162+
163+
// Write the CSV header
164+
csvWriter.WriteHeader<FactRealtimeIndicator>();
165+
await csvWriter.NextRecordAsync();
166+
167+
// Write records one at a time as they are retrieved from the database
168+
await csvWriter.WriteRecordsAsync(data);
169+
170+
// Explicitly flush to ensure all data is sent
171+
await streamWriter.FlushAsync();
172+
173+
Log.Debug("Exporting complete !!");
174+
175+
// This indicates the response is complete
176+
return new EmptyResult();
177+
}
88178
}

src/LiveDWAPI.Web/LiveDWAPI.Web.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
</PropertyGroup>
1212

1313
<ItemGroup>
14+
<PackageReference Include="CsvHelper" Version="33.1.0" />
1415
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="8.0.4" />
1516
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.15">
1617
<PrivateAssets>all</PrivateAssets>

0 commit comments

Comments
 (0)