Skip to content

Commit dd357ac

Browse files
authored
Fix accumulate reports (#2409)
1 parent fb10bdc commit dd357ac

File tree

7 files changed

+73
-54
lines changed

7 files changed

+73
-54
lines changed

api/net/Areas/Services/Controllers/ReportController.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ public IActionResult FindById(int id)
8585
/// Make a request to Elasticsearch for content that matches the specified report 'id' filter.
8686
/// </summary>
8787
/// <param name="id"></param>
88+
/// <param name="instanceId"></param>
8889
/// <param name="requestorId"></param>
8990
/// <returns></returns>
9091
[HttpGet("{id}/content")]
@@ -93,11 +94,11 @@ public IActionResult FindById(int id)
9394
[ProducesResponseType(typeof(ErrorResponseModel), (int)HttpStatusCode.BadRequest)]
9495
[ProducesResponseType((int)HttpStatusCode.NoContent)]
9596
[SwaggerOperation(Tags = new[] { "Report" })]
96-
public async Task<IActionResult> FindContentForReportIdAsync(int id, int? requestorId)
97+
public async Task<IActionResult> FindContentForReportIdAsync(int id, long? instanceId, int? requestorId)
9798
{
9899
var report = _service.FindById(id);
99100
if (report == null) return NoContent();
100-
var results = await _service.FindContentWithElasticsearchAsync(report, null, requestorId);
101+
var results = await _service.FindContentWithElasticsearchAsync(report, instanceId, requestorId);
101102
return new JsonResult(results);
102103
}
103104

app/subscriber/src/features/my-reports/edit/view/ReportHistoryForm.tsx

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,7 @@ export const ReportHistoryForm = () => {
2828
setInstances(
2929
instances.filter(
3030
(i, index) =>
31-
!(
32-
index === 0 &&
33-
[ReportStatusName.Pending, ReportStatusName.Reopen].includes(i.status)
34-
),
31+
![ReportStatusName.Reopen, ReportStatusName.Completed].includes(i.status) && i.body,
3532
),
3633
);
3734
} catch {}
@@ -90,9 +87,15 @@ export const ReportHistoryForm = () => {
9087

9188
<div className="col-3">
9289
<Row gap="0.2em">
93-
{formatDate(instance.sentOn ?? '', false)}
94-
<FaRegClock size={18} className="" />
95-
{formatTime(instance.sentOn ?? '')}
90+
{instance.sentOn ? (
91+
<>
92+
{formatDate(instance.sentOn ?? '', false)}
93+
<FaRegClock size={18} className="" />
94+
{formatTime(instance.sentOn)}
95+
</>
96+
) : (
97+
<>Not Sent</>
98+
)}
9699
</Row>
97100
</div>
98101
<div className="col-4">

libs/net/dal/Services/ReportInstanceService.cs

Lines changed: 45 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,13 @@ public ReportInstance Update(ReportInstance entity, bool updateChildren = false)
161161
var originalInstanceContent = originalInstanceContents.FirstOrDefault(o => o.ContentId == ric.ContentId && o.SectionName == ric.SectionName);
162162
if (originalInstanceContent == null)
163163
{
164+
// Fetch the original content from the database to compare.
165+
originalContent = this.Context.Contents
166+
.AsNoTracking()
167+
.Include(c => c.TonePoolsManyToMany)
168+
.FirstOrDefault(c => c.Id == ric.ContentId);
169+
var addContent = originalContent != null;
170+
164171
// If the content is new too, upload it to the database.
165172
if (ric.Content?.Id == 0)
166173
{
@@ -171,46 +178,51 @@ public ReportInstance Update(ReportInstance entity, bool updateChildren = false)
171178
}
172179
else if (ric.Content != null)
173180
{
174-
// Fetch the original content from the database to compare.
175-
originalContent = this.Context.Contents
176-
.AsNoTracking()
177-
.Include(c => c.TonePoolsManyToMany)
178-
.FirstOrDefault(c => c.Id == ric.ContentId) ?? throw new InvalidOperationException($"Content in report instance does not exist {entity.Id}:{ric.ContentId}");
179-
180-
// Do not allow updating content not owned by the user.
181-
// Content can be in more than one section. Only the first copy that is modified will be used for the update. All other copies will be synced.
182-
if (!updatedContent.TryGetValue(ric.ContentId, out Content? reportContent) &&
183-
originalContent.IsPrivate &&
184-
(ric.Content.Headline != originalContent.Headline ||
185-
ric.Content.Summary != originalContent.Summary ||
186-
ric.Content.SourceId != originalContent.SourceId ||
187-
ric.Content.OtherSource != originalContent.OtherSource ||
188-
ric.Content.PublishedOn != originalContent.PublishedOn ||
189-
ric.Content.Byline != originalContent.Byline ||
190-
ric.Content.SourceUrl != originalContent.SourceUrl ||
191-
ric.Content.Uid != originalContent.Uid ||
192-
ric.Content.TonePoolsManyToMany.Any(tp =>
193-
{
194-
var otp = originalContent.TonePoolsManyToMany.FirstOrDefault(otp => otp.TonePoolId == tp.TonePoolId);
195-
return otp?.Value != tp.Value;
196-
})))
181+
if (originalContent == null)
197182
{
198-
updatedContent.Add(ric.ContentId, ric.Content);
199-
this.Context.Entry(ric.Content).State = EntityState.Modified;
183+
addContent = false;
184+
this.Logger.LogWarning("Content in report instance does not exist {entityId}:{contentId}", entity.Id, ric.ContentId);
200185
}
201186
else
202187
{
203-
// This content exists in another section and was updated so we need to make them the same content.
204-
// If the client incorrectly submits the same content in more than one section and updates both of them differently, it will only use the first entry.
205-
ric.Content = reportContent;
188+
// Do not allow updating content not owned by the user.
189+
// Content can be in more than one section. Only the first copy that is modified will be used for the update. All other copies will be synced.
190+
if (!updatedContent.TryGetValue(ric.ContentId, out Content? reportContent) &&
191+
originalContent.IsPrivate &&
192+
(ric.Content.Headline != originalContent.Headline ||
193+
ric.Content.Summary != originalContent.Summary ||
194+
ric.Content.SourceId != originalContent.SourceId ||
195+
ric.Content.OtherSource != originalContent.OtherSource ||
196+
ric.Content.PublishedOn != originalContent.PublishedOn ||
197+
ric.Content.Byline != originalContent.Byline ||
198+
ric.Content.SourceUrl != originalContent.SourceUrl ||
199+
ric.Content.Uid != originalContent.Uid ||
200+
ric.Content.TonePoolsManyToMany.Any(tp =>
201+
{
202+
var otp = originalContent.TonePoolsManyToMany.FirstOrDefault(otp => otp.TonePoolId == tp.TonePoolId);
203+
return otp?.Value != tp.Value;
204+
})))
205+
{
206+
updatedContent.Add(ric.ContentId, ric.Content);
207+
this.Context.Entry(ric.Content).State = EntityState.Modified;
208+
}
209+
else
210+
{
211+
// This content exists in another section and was updated so we need to make them the same content.
212+
// If the client incorrectly submits the same content in more than one section and updates both of them differently, it will only use the first entry.
213+
ric.Content = reportContent;
214+
}
206215
}
207216
}
208217

209-
// Add new content to the report.
210-
ric.Instance = null;
211-
ric.InstanceId = entity.Id;
212-
this.Context.Entry(ric).State = EntityState.Added;
213-
original.ContentManyToMany.Add(ric);
218+
if (addContent)
219+
{
220+
// Add new content to the report.
221+
ric.Instance = null;
222+
ric.InstanceId = entity.Id;
223+
this.Context.Entry(ric).State = EntityState.Added;
224+
original.ContentManyToMany.Add(ric);
225+
}
214226
}
215227
else
216228
{

libs/net/dal/Services/ReportService.cs

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -797,7 +797,8 @@ public IEnumerable<ReportInstance> GetLatestInstances(int id, int? ownerId = nul
797797
: Array.Empty<long>();
798798

799799
// When an auto report runs it may need to exclude content in the currently unsent report.
800-
if (currentInstance != null && currentInstance.SentOn.HasValue == false && reportSettings.Content.ExcludeContentInUnsentReport)
800+
if (currentInstance != null && currentInstance.SentOn.HasValue == false
801+
&& (reportSettings.Content.ExcludeContentInUnsentReport || !reportSettings.Content.ClearOnStartNewReport))
801802
excludeHistoricalContentIds = excludeHistoricalContentIds.AppendRange(currentInstance.ContentManyToMany.Select(c => c.ContentId)).Distinct().ToArray();
802803

803804
// Fetch other reports to exclude any content within them.
@@ -898,10 +899,6 @@ public IEnumerable<ReportInstance> GetLatestInstances(int id, int? ownerId = nul
898899
searchResults.Add(section.Name, content);
899900
excludeAboveSectionContentIds.AddRange(contentIds.Except(excludeAboveSectionContentIds));
900901
}
901-
else
902-
{
903-
904-
}
905902
}
906903

907904
return searchResults;

libs/net/services/Helpers/ApiService.cs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
using System.Net;
22
using System.Net.Http.Headers;
33
using System.Net.Http.Json;
4+
using System.Text;
45
using System.Text.Json;
56
using FTTLib;
6-
using Microsoft.AspNetCore.Http;
7+
using Microsoft.AspNetCore.WebUtilities;
78
using Microsoft.Extensions.Logging;
89
using Microsoft.Extensions.Options;
910
using TNO.Core.Exceptions;
@@ -752,11 +753,15 @@ public async Task<HttpResponseMessage> UploadFilesToS3Async(
752753
/// Make a request to the API to fetch the content for the specified report 'id'.
753754
/// </summary>
754755
/// <param name="id"></param>
756+
/// <param name="instanceId"></param>
755757
/// <param name="requestorId"></param>
756758
/// <returns></returns>
757-
public async Task<Dictionary<string, Elastic.Models.SearchResultModel<API.Areas.Services.Models.Content.ContentModel>>> FindContentForReportIdAsync(int id, int? requestorId)
759+
public async Task<Dictionary<string, Elastic.Models.SearchResultModel<API.Areas.Services.Models.Content.ContentModel>>> FindContentForReportIdAsync(int id, long? instanceId, int? requestorId)
758760
{
759-
var url = this.Options.ApiUrl.Append($"services/reports/{id}/content{(requestorId != null ? $"?requestorId={requestorId}" : "")}");
761+
var query = new Dictionary<string, string?>();
762+
if (instanceId.HasValue) query.Add("instanceId", instanceId.Value.ToString());
763+
if (requestorId.HasValue) query.Add("requestorId", requestorId.Value.ToString());
764+
var url = this.Options.ApiUrl.Append(QueryHelpers.AddQueryString($"services/reports/{id}/content", query));
760765
return await RetryRequestAsync(async () => await this.OpenClient.GetAsync<Dictionary<string, Elastic.Models.SearchResultModel<API.Areas.Services.Models.Content.ContentModel>>>(url)) ?? new Dictionary<string, Elastic.Models.SearchResultModel<API.Areas.Services.Models.Content.ContentModel>>();
761766
}
762767

libs/net/services/Helpers/IApiService.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -386,9 +386,10 @@ public Task<HttpResponseMessage> UploadFilesToS3Async(
386386
/// Make a request to the API to fetch the content for the specified report 'id'.
387387
/// </summary>
388388
/// <param name="id"></param>
389+
/// <param name="instanceId"></param>
389390
/// <param name="requestorId"></param>
390391
/// <returns></returns>
391-
Task<Dictionary<string, Elastic.Models.SearchResultModel<API.Areas.Services.Models.Content.ContentModel>>> FindContentForReportIdAsync(int id, int? requestorId);
392+
Task<Dictionary<string, Elastic.Models.SearchResultModel<API.Areas.Services.Models.Content.ContentModel>>> FindContentForReportIdAsync(int id, long? instanceId, int? requestorId);
392393

393394
/// <summary>
394395
/// Get the current instance for the specified report 'reportId'.

services/net/reporting/ReportingManager.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -447,11 +447,11 @@ private async Task<Dictionary<string, ReportSectionModel>> GetLinkedReportAsync(
447447
}
448448

449449
// Generate a new instance, or accumulate new content each time the report is run.
450-
if (reportInstanceModel == null || reportInstanceModel.SentOn.HasValue
451-
|| !(!report.Settings.Content.ClearOnStartNewReport && !report.Settings.Content.CopyPriorInstance))
450+
var workOnCurrentInstance = reportInstanceModel != null && !(!report.Settings.Content.ClearOnStartNewReport && !report.Settings.Content.CopyPriorInstance);
451+
if (reportInstanceModel == null || reportInstanceModel.SentOn.HasValue || workOnCurrentInstance)
452452
{
453453
// Fetch content for every section within the report. This will include folders and filters.
454-
var searchResults = await this.Api.FindContentForReportIdAsync(report.Id, request.RequestorId);
454+
var searchResults = await this.Api.FindContentForReportIdAsync(report.Id, workOnCurrentInstance ? reportInstanceModel?.Id : null, request.RequestorId);
455455
sectionContent = sections.ToDictionary(section => section.Name, section =>
456456
{
457457
if (searchResults.TryGetValue(section.Name, out SearchResultModel<TNO.API.Areas.Services.Models.Content.ContentModel>? results))

0 commit comments

Comments
 (0)