Skip to content

Commit fc196a3

Browse files
committed
MMI-3371 Fix Transcript Service (bcgov#2526)
1 parent 64ee13b commit fc196a3

File tree

3 files changed

+60
-52
lines changed

3 files changed

+60
-52
lines changed

api/net/Areas/Editor/Controllers/ContentController.cs

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -609,16 +609,18 @@ public async Task<IActionResult> DownloadFileAsync(long id)
609609
public async Task<IActionResult> StreamAsync([FromQuery] string path)
610610
{
611611
path = string.IsNullOrWhiteSpace(path) ? "" : HttpUtility.UrlDecode(path).MakeRelativePath();
612-
//find file from s3
613-
var stream = await _s3StorageService.DownloadFromS3Async(path);
614-
if (stream != null)
615-
{
616-
return File(stream, "application/octet-stream");
617-
}
618-
//find file from local
612+
613+
// find file from local
619614
path = string.IsNullOrWhiteSpace(path) ? "" : HttpUtility.UrlDecode(path).MakeRelativePath();
620615
var safePath = Path.Combine(_storageOptions.GetUploadPath(), path);
621-
if (!safePath.FileExists()) throw new NoContentException("File does not exist");
616+
617+
if (!safePath.FileExists())
618+
{
619+
// find file from s3
620+
var stream = await _s3StorageService.DownloadFromS3Async(path);
621+
if (stream != null) return File(stream, "application/octet-stream");
622+
else throw new NoContentException("File does not exist");
623+
}
622624

623625
var info = new ItemModel(safePath);
624626
var fileStream = System.IO.File.OpenRead(safePath);

api/net/Areas/Helpers/WorkOrderHelper.cs

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -167,13 +167,6 @@ public bool HasExistingTranscript(long contentId)
167167
if (this.Content == null || this.Content.Id != contentId)
168168
this.Content = _contentService.FindById(contentId) ?? throw new NoContentException("Content does not exist");
169169
if (String.IsNullOrWhiteSpace(_kafkaOptions.TranscriptionTopic)) throw new ConfigurationException("Kafka transcription topic not configured.");
170-
bool skipKafka = false;
171-
string skipDescription = "User request transcription while editor is working on it. Automatic transcription is bypassed";
172-
173-
if (HasExistingTranscript(contentId) && !this.Content.IsApproved)
174-
{
175-
skipKafka = true;
176-
}
177170

178171
if (this.Content.IsApproved && force == false) throw new InvalidOperationException("Content is already approved");
179172
// Only allow one work order transcript request at a time.
@@ -191,15 +184,12 @@ public bool HasExistingTranscript(long contentId)
191184
new Entities.WorkOrder(
192185
Entities.WorkOrderType.Transcription,
193186
requestor,
194-
skipKafka ? skipDescription : "",
187+
"",
195188
this.Content,
196189
configuration
197190
));
198191

199-
if (!skipKafka)
200-
{
201-
await _kafkaMessenger.SendMessageAsync(_kafkaOptions.TranscriptionTopic, new TNO.Kafka.Models.TranscriptRequestModel(workOrder));
202-
}
192+
await _kafkaMessenger.SendMessageAsync(_kafkaOptions.TranscriptionTopic, new TNO.Kafka.Models.TranscriptRequestModel(workOrder));
203193
return workOrder;
204194
}
205195
return workOrders.OrderByDescending(w => w.CreatedOn).First();

services/net/transcription/TranscriptionManager.cs

Lines changed: 48 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ public class TranscriptionManager : ServiceManager<TranscriptionOptions>
2929
private CancellationTokenSource? _cancelToken;
3030
private Task? _consumer;
3131
private readonly TaskStatus[] _notRunning = new TaskStatus[] { TaskStatus.Canceled, TaskStatus.Faulted, TaskStatus.RanToCompletion };
32-
private readonly WorkOrderStatus[] _ignoreWorkOrders = new WorkOrderStatus[] { WorkOrderStatus.Completed };
32+
private readonly WorkOrderStatus[] _ignoreWorkOrders = new WorkOrderStatus[] { WorkOrderStatus.Completed, WorkOrderStatus.Cancelled };
3333
private int _retries = 0;
3434

3535
#endregion
@@ -369,43 +369,60 @@ private async Task UpdateTranscriptionAsync(TranscriptRequestModel request, Cont
369369
if (!String.IsNullOrEmpty(safePath))
370370
{
371371
this.Logger.LogInformation("Validating work order. Content ID: {Id}, Path: {file}", requestContentId, safePath);
372-
var hasWorkOrder = await UpdateWorkOrderAsync(request, WorkOrderStatus.InProgress);
373-
374-
if (hasWorkOrder)
372+
var workOrder = await UpdateWorkOrderAsync(request, content.IsApproved ? WorkOrderStatus.Cancelled : WorkOrderStatus.InProgress);
373+
if (workOrder?.Status == WorkOrderStatus.InProgress)
375374
{
376375
var original = content.Body;
377376
var fileBytes = File.ReadAllBytes(safePath);
378377
var transcript = await RequestTranscriptionAsync(requestContentId, fileBytes); // TODO: Extract language from data source.
379378

380-
// Fetch content again because it may have been updated by an external source.
381-
// This can introduce issues if the transcript has been edited as now it will overwrite what was changed.
382-
content = (await this.Api.FindContentByIdAsync(requestContentId))!;
383-
if (content != null && !String.IsNullOrWhiteSpace(transcript))
384-
{
385-
// The transcription may have been edited during this process and now those changes will be lost.
386-
if (String.CompareOrdinal(original, content.Body) != 0) this.Logger.LogWarning("Transcription will be overwritten. Content ID: {Id}", requestContentId);
387-
388-
content.Body = GetFormattedTranscript(transcript);
389-
await this.Api.UpdateContentAsync(content); // TODO: This can result in an editor getting a optimistic concurrency error.
390-
this.Logger.LogInformation("Transcription updated. Content ID: {Id}", requestContentId);
391-
392-
await UpdateWorkOrderAsync(request, WorkOrderStatus.Completed);
393-
}
394-
else if (String.IsNullOrWhiteSpace(transcript))
379+
// Fetch latest work order to ensure they haven't changed during transcription process.
380+
workOrder = await this.Api.FindWorkOrderAsync(workOrder.Id);
381+
if (workOrder?.Status == WorkOrderStatus.Cancelled)
395382
{
396-
this.Logger.LogWarning("Content did not generate a transcript. Content ID: {Id}", requestContentId);
397-
var emptyTranscriptException = new EmptyTranscriptException(requestContentId);
398-
await UpdateWorkOrderAsync(request, WorkOrderStatus.Failed, emptyTranscriptException);
383+
this.Logger.LogWarning("Work order has been cancelled during transcription process. Content ID: {id}, File: {path}", requestContentId, safePath);
399384
}
400385
else
401386
{
402-
// The content is no longer available for some reason.
403-
this.Logger.LogError("Content no longer exists. Content ID: {Id}", requestContentId);
404-
var contentNotFoundException = new ContentNotFoundException(requestContentId);
405-
await UpdateWorkOrderAsync(request, WorkOrderStatus.Failed, contentNotFoundException);
387+
// Fetch content again because it may have been updated by an external source.
388+
// This can introduce issues if the transcript has been edited as now it will overwrite what was changed.
389+
content = (await this.Api.FindContentByIdAsync(requestContentId))!;
390+
if (content != null && !String.IsNullOrWhiteSpace(transcript) && !content.IsApproved)
391+
{
392+
// The transcription may have been edited during this process and now those changes will be lost.
393+
if (String.CompareOrdinal(original, content.Body) != 0) this.Logger.LogWarning("Transcription will be overwritten. Content ID: {Id}", requestContentId);
394+
395+
content.Body = GetFormattedTranscript(transcript);
396+
await this.Api.UpdateContentAsync(content); // TODO: This can result in an editor getting a optimistic concurrency error.
397+
this.Logger.LogInformation("Transcription updated. Content ID: {Id}", requestContentId);
398+
399+
await UpdateWorkOrderAsync(request, WorkOrderStatus.Completed);
400+
}
401+
else if (content != null && content.IsApproved)
402+
{
403+
this.Logger.LogWarning("Content is approved, transcription will not be updated. Content ID: {Id}", requestContentId);
404+
await UpdateWorkOrderAsync(request, WorkOrderStatus.Cancelled);
405+
}
406+
else if (String.IsNullOrWhiteSpace(transcript))
407+
{
408+
this.Logger.LogWarning("Content did not generate a transcript. Content ID: {Id}", requestContentId);
409+
var emptyTranscriptException = new EmptyTranscriptException(requestContentId);
410+
await UpdateWorkOrderAsync(request, WorkOrderStatus.Failed, emptyTranscriptException);
411+
}
412+
else
413+
{
414+
// The content is no longer available for some reason.
415+
this.Logger.LogError("Content no longer exists. Content ID: {Id}", requestContentId);
416+
var contentNotFoundException = new ContentNotFoundException(requestContentId);
417+
await UpdateWorkOrderAsync(request, WorkOrderStatus.Failed, contentNotFoundException);
418+
}
406419
}
407420
}
408-
else
421+
else if (workOrder?.Status == WorkOrderStatus.Cancelled)
422+
{
423+
this.Logger.LogWarning("Work order has been cancelled. Content ID: {id}, File: {path}", requestContentId, safePath);
424+
}
425+
else if (workOrder == null)
409426
{
410427
this.Logger.LogWarning("Request ignored because it does not have a work order. Content ID: {id}, File: {path}", requestContentId, safePath);
411428
}
@@ -455,26 +472,25 @@ private static string GetFormattedTranscript(string transcript)
455472
/// <param name="request"></param>
456473
/// <param name="status"></param>
457474
/// <returns>Whether a work order exists or is not required.</returns>
458-
private async Task<bool> UpdateWorkOrderAsync(TranscriptRequestModel request, WorkOrderStatus status, Exception? reason = null)
475+
private async Task<API.Areas.Services.Models.WorkOrder.WorkOrderModel?> UpdateWorkOrderAsync(TranscriptRequestModel request, WorkOrderStatus status, Exception? reason = null)
459476
{
460477
if (request.WorkOrderId > 0)
461478
{
462479
var workOrder = await this.Api.FindWorkOrderAsync(request.WorkOrderId);
463480
if (workOrder != null && !_ignoreWorkOrders.Contains(workOrder.Status))
464481
{
465482
workOrder.Status = status;
466-
await this.Api.UpdateWorkOrderAsync(workOrder);
483+
workOrder = await this.Api.UpdateWorkOrderAsync(workOrder);
467484

468485
if (status == WorkOrderStatus.Failed && reason != null)
469486
{
470487
await this.SendErrorEmailAsync($"Work order failed for Content ID: {request.ContentId}", reason);
471488
this.Logger.LogError(reason, "Work order failed for Content ID: {ContentId}", request.ContentId);
472489
}
473-
474-
return true;
475490
}
491+
return workOrder;
476492
}
477-
return !this.Options.AcceptOnlyWorkOrders;
493+
return null;
478494
}
479495

480496
/// <summary>

0 commit comments

Comments
 (0)