Skip to content

Commit 24af947

Browse files
Copilotimnasnainaec
andcommitted
Add backend statistics endpoints for domain sense count and progress proportion
Co-authored-by: imnasnainaec <6411521+imnasnainaec@users.noreply.github.com>
1 parent 0b08c94 commit 24af947

File tree

5 files changed

+161
-0
lines changed

5 files changed

+161
-0
lines changed

Backend.Tests/Controllers/StatisticsControllerTests.cs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,5 +132,37 @@ public async Task TestGetSemanticDomainUserCounts()
132132
var result = await _statsController.GetSemanticDomainUserCounts(_projId);
133133
Assert.That(result, Is.InstanceOf<OkObjectResult>());
134134
}
135+
136+
[Test]
137+
public async Task TestGetDomainSenseCountNoPermission()
138+
{
139+
_statsController.ControllerContext.HttpContext = PermissionServiceMock.UnauthorizedHttpContext();
140+
141+
var result = await _statsController.GetDomainSenseCount(_projId, "1");
142+
Assert.That(result, Is.InstanceOf<ForbidResult>());
143+
}
144+
145+
[Test]
146+
public async Task TestGetDomainSenseCount()
147+
{
148+
var result = await _statsController.GetDomainSenseCount(_projId, "1");
149+
Assert.That(result, Is.InstanceOf<OkObjectResult>());
150+
}
151+
152+
[Test]
153+
public async Task TestGetDomainProgressProportionNoPermission()
154+
{
155+
_statsController.ControllerContext.HttpContext = PermissionServiceMock.UnauthorizedHttpContext();
156+
157+
var result = await _statsController.GetDomainProgressProportion(_projId, "1", "en");
158+
Assert.That(result, Is.InstanceOf<ForbidResult>());
159+
}
160+
161+
[Test]
162+
public async Task TestGetDomainProgressProportion()
163+
{
164+
var result = await _statsController.GetDomainProgressProportion(_projId, "1", "en");
165+
Assert.That(result, Is.InstanceOf<OkObjectResult>());
166+
}
135167
}
136168
}

Backend.Tests/Mocks/StatisticsServiceMock.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,5 +28,13 @@ public Task<List<SemanticDomainUserCount>> GetSemanticDomainUserCounts(string pr
2828
{
2929
return Task.FromResult(new List<SemanticDomainUserCount>());
3030
}
31+
public Task<int> GetDomainSenseCount(string projectId, string domainId)
32+
{
33+
return Task.FromResult(0);
34+
}
35+
public Task<double> GetDomainProgressProportion(string projectId, string domainId, string lang)
36+
{
37+
return Task.FromResult(0.0);
38+
}
3139
}
3240
}

Backend/Controllers/StatisticsController.cs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,5 +118,39 @@ public async Task<IActionResult> GetSemanticDomainUserCounts(string projectId)
118118

119119
return Ok(await _statService.GetSemanticDomainUserCounts(projectId));
120120
}
121+
122+
/// <summary> Get the count of senses in a specific semantic domain </summary>
123+
/// <returns> An integer count </returns>
124+
[HttpGet("GetDomainSenseCount", Name = "GetDomainSenseCount")]
125+
[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(int))]
126+
[ProducesResponseType(StatusCodes.Status403Forbidden)]
127+
public async Task<IActionResult> GetDomainSenseCount(string projectId, string domainId)
128+
{
129+
using var activity = OtelService.StartActivityWithTag(otelTagName, "getting domain sense count");
130+
131+
if (!await _permissionService.HasProjectPermission(HttpContext, Permission.Statistics, projectId))
132+
{
133+
return Forbid();
134+
}
135+
136+
return Ok(await _statService.GetDomainSenseCount(projectId, domainId));
137+
}
138+
139+
/// <summary> Get the proportion of descendant domains that have at least one entry </summary>
140+
/// <returns> A double value between 0 and 1 </returns>
141+
[HttpGet("GetDomainProgressProportion", Name = "GetDomainProgressProportion")]
142+
[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(double))]
143+
[ProducesResponseType(StatusCodes.Status403Forbidden)]
144+
public async Task<IActionResult> GetDomainProgressProportion(string projectId, string domainId, string lang)
145+
{
146+
using var activity = OtelService.StartActivityWithTag(otelTagName, "getting domain progress proportion");
147+
148+
if (!await _permissionService.HasProjectPermission(HttpContext, Permission.Statistics, projectId))
149+
{
150+
return Forbid();
151+
}
152+
153+
return Ok(await _statService.GetDomainProgressProportion(projectId, domainId, lang));
154+
}
121155
}
122156
}

Backend/Interfaces/IStatisticsService.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ public interface IStatisticsService
1212
Task<ChartRootData> GetProgressEstimationLineChartRoot(string projectId, List<DateTime> schedule);
1313
Task<ChartRootData> GetLineChartRootData(string projectId);
1414
Task<List<SemanticDomainUserCount>> GetSemanticDomainUserCounts(string projectId);
15+
Task<int> GetDomainSenseCount(string projectId, string domainId);
16+
Task<double> GetDomainProgressProportion(string projectId, string domainId, string lang);
1517
}
1618

1719
}

Backend/Services/StatisticsService.cs

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -355,5 +355,90 @@ public async Task<List<SemanticDomainUserCount>> GetSemanticDomainUserCounts(str
355355
// return descending order by senseCount
356356
return resUserMap.Values.ToList().OrderByDescending(t => t.WordCount).ToList();
357357
}
358+
359+
/// <summary>
360+
/// Get the count of senses in a specific semantic domain
361+
/// </summary>
362+
/// <param name="projectId"> The project id </param>
363+
/// <param name="domainId"> The semantic domain id </param>
364+
/// <returns> The count of senses with the specified domain </returns>
365+
public async Task<int> GetDomainSenseCount(string projectId, string domainId)
366+
{
367+
using var activity = OtelService.StartActivityWithTag(otelTagName, "getting domain sense count");
368+
369+
var wordList = await _wordRepo.GetFrontier(projectId);
370+
var count = 0;
371+
372+
foreach (var word in wordList)
373+
{
374+
foreach (var sense in word.Senses)
375+
{
376+
if (sense.SemanticDomains.Any(sd => sd.Id == domainId))
377+
{
378+
count++;
379+
}
380+
}
381+
}
382+
383+
return count;
384+
}
385+
386+
/// <summary>
387+
/// Get the proportion of descendant domains that have at least one entry
388+
/// </summary>
389+
/// <param name="projectId"> The project id </param>
390+
/// <param name="domainId"> The semantic domain id </param>
391+
/// <param name="lang"> The language code </param>
392+
/// <returns> A proportion value between 0 and 1 </returns>
393+
public async Task<double> GetDomainProgressProportion(string projectId, string domainId, string lang)
394+
{
395+
using var activity = OtelService.StartActivityWithTag(otelTagName, "getting domain progress proportion");
396+
397+
var domainTreeNodeList = await _domainRepo.GetAllSemanticDomainTreeNodes(lang);
398+
if (domainTreeNodeList is null || domainTreeNodeList.Count == 0)
399+
{
400+
return 0.0;
401+
}
402+
403+
// Get all descendant domain IDs
404+
var descendantIds = new List<string>();
405+
foreach (var node in domainTreeNodeList)
406+
{
407+
if (node.Id.StartsWith(domainId, StringComparison.Ordinal) && node.Id != domainId)
408+
{
409+
// Check if it's a direct or indirect child (not just a string prefix match)
410+
var relativePart = node.Id.Substring(domainId.Length);
411+
if (relativePart.StartsWith('.'))
412+
{
413+
descendantIds.Add(node.Id);
414+
}
415+
}
416+
}
417+
418+
if (descendantIds.Count == 0)
419+
{
420+
return 0.0;
421+
}
422+
423+
// Get word list and count which descendants have at least one entry
424+
var wordList = await _wordRepo.GetFrontier(projectId);
425+
var domainsWithEntries = new HashSet<string>();
426+
427+
foreach (var word in wordList)
428+
{
429+
foreach (var sense in word.Senses)
430+
{
431+
foreach (var sd in sense.SemanticDomains)
432+
{
433+
if (descendantIds.Contains(sd.Id))
434+
{
435+
domainsWithEntries.Add(sd.Id);
436+
}
437+
}
438+
}
439+
}
440+
441+
return (double)domainsWithEntries.Count / descendantIds.Count;
442+
}
358443
}
359444
}

0 commit comments

Comments
 (0)