Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions Backend.Tests/Controllers/StatisticsControllerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -132,5 +132,21 @@ public async Task TestGetSemanticDomainUserCounts()
var result = await _statsController.GetSemanticDomainUserCounts(_projId);
Assert.That(result, Is.InstanceOf<OkObjectResult>());
}

[Test]
public async Task TestGetDomainProgressProportionNoPermission()
{
_statsController.ControllerContext.HttpContext = PermissionServiceMock.UnauthorizedHttpContext();

var result = await _statsController.GetDomainProgressProportion(_projId, "1");
Assert.That(result, Is.InstanceOf<ForbidResult>());
}

[Test]
public async Task TestGetDomainProgressProportion()
{
var result = await _statsController.GetDomainProgressProportion(_projId, "1");
Assert.That(result, Is.InstanceOf<OkObjectResult>());
}
}
}
4 changes: 4 additions & 0 deletions Backend.Tests/Mocks/StatisticsServiceMock.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,5 +28,9 @@ public Task<List<SemanticDomainUserCount>> GetSemanticDomainUserCounts(string pr
{
return Task.FromResult(new List<SemanticDomainUserCount>());
}
public Task<double> GetDomainProgressProportion(string projectId, string domainId)
{
return Task.FromResult(0.0);
}
}
}
11 changes: 8 additions & 3 deletions Backend.Tests/Mocks/WordRepositoryMock.cs
Original file line number Diff line number Diff line change
Expand Up @@ -132,11 +132,16 @@ public Task<Word> Add(Word word)
return Task.FromResult(word);
}

public Task<bool> FrontierHasWordsWithDomain(string projectId, string domainId)
{
return Task.FromResult(_frontier.Any(
w => w.ProjectId == projectId && w.Senses.Any(s => s.SemanticDomains.Any(sd => sd.Id == domainId))));
}

public Task<int> CountFrontierWordsWithDomain(string projectId, string domainId)
{
var count = _frontier.Count(
w => w.ProjectId == projectId && w.Senses.Any(s => s.SemanticDomains.Any(sd => sd.Id == domainId)));
return Task.FromResult(count);
return Task.FromResult(_frontier.Count(
w => w.ProjectId == projectId && w.Senses.Any(s => s.SemanticDomains.Any(sd => sd.Id == domainId))));
}
}
}
17 changes: 17 additions & 0 deletions Backend/Controllers/StatisticsController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -118,5 +118,22 @@ public async Task<IActionResult> GetSemanticDomainUserCounts(string projectId)

return Ok(await _statService.GetSemanticDomainUserCounts(projectId));
}

/// <summary> Get the proportion of descendant domains that have at least one entry </summary>
/// <returns> A double value between 0 and 1 </returns>
[HttpGet("GetDomainProgressProportion", Name = "GetDomainProgressProportion")]
[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(double))]
[ProducesResponseType(StatusCodes.Status403Forbidden)]
public async Task<IActionResult> GetDomainProgressProportion(string projectId, string domainId)
{
using var activity = OtelService.StartActivityWithTag(otelTagName, "getting domain progress proportion");

if (!await _permissionService.HasProjectPermission(HttpContext, Permission.WordEntry, projectId))
{
return Forbid();
}

return Ok(await _statService.GetDomainProgressProportion(projectId, domainId));
}
}
}
1 change: 1 addition & 0 deletions Backend/Interfaces/IStatisticsService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ public interface IStatisticsService
Task<ChartRootData> GetProgressEstimationLineChartRoot(string projectId, List<DateTime> schedule);
Task<ChartRootData> GetLineChartRootData(string projectId);
Task<List<SemanticDomainUserCount>> GetSemanticDomainUserCounts(string projectId);
Task<double> GetDomainProgressProportion(string projectId, string domainId);
}

}
1 change: 1 addition & 0 deletions Backend/Interfaces/IWordRepository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ public interface IWordRepository
Task<List<Word>> AddFrontier(List<Word> words);
Task<bool> DeleteFrontier(string projectId, string wordId);
Task<long> DeleteFrontier(string projectId, List<string> wordIds);
Task<bool> FrontierHasWordsWithDomain(string projectId, string domainId);
Task<int> CountFrontierWordsWithDomain(string projectId, string domainId);
}
}
18 changes: 18 additions & 0 deletions Backend/Repositories/WordRepository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,24 @@ public async Task<long> DeleteFrontier(string projectId, List<string> wordIds)
return deleted.DeletedCount;
}

/// <summary>
/// Checks if the Frontier has any words that have the specified semantic domain.
/// </summary>
/// <param name="projectId"> The project id </param>
/// <param name="domainId"> The semantic domain id </param>
/// <returns> True if there is at least one word containing at least one sense with the specified domain. </returns>
public Task<bool> FrontierHasWordsWithDomain(string projectId, string domainId)
{
using var activity = OtelService.StartActivityWithTag(otelTagName, "checking frontier for words with domain");

var filterDef = new FilterDefinitionBuilder<Word>();
var filter = filterDef.And(
filterDef.Eq(w => w.ProjectId, projectId),
filterDef.ElemMatch(w => w.Senses, s => s.SemanticDomains.Any(sd => sd.Id == domainId)));

return _frontier.Find(filter).Limit(1).AnyAsync();
}

/// <summary>
/// Counts the number of Frontier words that have the specified semantic domain.
/// </summary>
Expand Down
41 changes: 41 additions & 0 deletions Backend/Services/StatisticsService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -367,5 +367,46 @@ public async Task<List<SemanticDomainUserCount>> GetSemanticDomainUserCounts(str
// return descending order by senseCount
return resUserMap.Values.ToList().OrderByDescending(t => t.WordCount).ToList();
}

/// <summary>
/// Get the proportion of descendant domains that have at least one entry
/// </summary>
/// <param name="projectId"> The project id </param>
/// <param name="domainId"> The semantic domain id </param>
/// <returns> A proportion value between 0 and 1 </returns>
public async Task<double> GetDomainProgressProportion(string projectId, string domainId)
{
using var activity = OtelService.StartActivityWithTag(otelTagName, "getting domain progress proportion");

if (string.IsNullOrEmpty(projectId) || string.IsNullOrEmpty(domainId) || !char.IsDigit(domainId[0]))
{
return 0.0;
}

var domains = await _domainRepo.GetAllSemanticDomainTreeNodes("en");
if (domains is null || domains.Count == 0)
{
return 0.0;
}

var domainAndDescendants = domains
.Where(dom => dom.Id.StartsWith(domainId, StringComparison.Ordinal)).ToList();

if (domainAndDescendants.Count == 0)
{
return 0.0;
}

var count = 0.0;
foreach (var dom in domainAndDescendants)
{
if (await _wordRepo.FrontierHasWordsWithDomain(projectId, dom.Id))
{
count++;
}
}

return count / domainAndDescendants.Count;
}
}
}
136 changes: 136 additions & 0 deletions src/api/api/statistics-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,58 @@ export const StatisticsApiAxiosParamCreator = function (
configuration?: Configuration
) {
return {
/**
*
* @param {string} projectId
* @param {string} [domainId]
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
getDomainProgressProportion: async (
projectId: string,
domainId?: string,
options: any = {}
): Promise<RequestArgs> => {
// verify required parameter 'projectId' is not null or undefined
assertParamExists("getDomainProgressProportion", "projectId", projectId);
const localVarPath =
`/v1/projects/{projectId}/statistics/GetDomainProgressProportion`.replace(
`{${"projectId"}}`,
encodeURIComponent(String(projectId))
);
// use dummy base URL string because the URL constructor only accepts absolute URLs.
const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL);
let baseOptions;
if (configuration) {
baseOptions = configuration.baseOptions;
}

const localVarRequestOptions = {
method: "GET",
...baseOptions,
...options,
};
const localVarHeaderParameter = {} as any;
const localVarQueryParameter = {} as any;

if (domainId !== undefined) {
localVarQueryParameter["domainId"] = domainId;
}

setSearchParams(localVarUrlObj, localVarQueryParameter, options.query);
let headersFromBaseOptions =
baseOptions && baseOptions.headers ? baseOptions.headers : {};
localVarRequestOptions.headers = {
...localVarHeaderParameter,
...headersFromBaseOptions,
...options.headers,
};

return {
url: toPathString(localVarUrlObj),
options: localVarRequestOptions,
};
},
/**
*
* @param {string} projectId
Expand Down Expand Up @@ -303,6 +355,33 @@ export const StatisticsApiFp = function (configuration?: Configuration) {
const localVarAxiosParamCreator =
StatisticsApiAxiosParamCreator(configuration);
return {
/**
*
* @param {string} projectId
* @param {string} [domainId]
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
async getDomainProgressProportion(
projectId: string,
domainId?: string,
options?: any
): Promise<
(axios?: AxiosInstance, basePath?: string) => AxiosPromise<number>
> {
const localVarAxiosArgs =
await localVarAxiosParamCreator.getDomainProgressProportion(
projectId,
domainId,
options
);
return createRequestFunction(
localVarAxiosArgs,
globalAxios,
BASE_PATH,
configuration
);
},
/**
*
* @param {string} projectId
Expand Down Expand Up @@ -449,6 +528,22 @@ export const StatisticsApiFactory = function (
) {
const localVarFp = StatisticsApiFp(configuration);
return {
/**
*
* @param {string} projectId
* @param {string} [domainId]
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
getDomainProgressProportion(
projectId: string,
domainId?: string,
options?: any
): AxiosPromise<number> {
return localVarFp
.getDomainProgressProportion(projectId, domainId, options)
.then((request) => request(axios, basePath));
},
/**
*
* @param {string} projectId
Expand Down Expand Up @@ -524,6 +619,27 @@ export const StatisticsApiFactory = function (
};
};

/**
* Request parameters for getDomainProgressProportion operation in StatisticsApi.
* @export
* @interface StatisticsApiGetDomainProgressProportionRequest
*/
export interface StatisticsApiGetDomainProgressProportionRequest {
/**
*
* @type {string}
* @memberof StatisticsApiGetDomainProgressProportion
*/
readonly projectId: string;

/**
*
* @type {string}
* @memberof StatisticsApiGetDomainProgressProportion
*/
readonly domainId?: string;
}

/**
* Request parameters for getLineChartRootData operation in StatisticsApi.
* @export
Expand Down Expand Up @@ -608,6 +724,26 @@ export interface StatisticsApiGetWordsPerDayPerUserCountsRequest {
* @extends {BaseAPI}
*/
export class StatisticsApi extends BaseAPI {
/**
*
* @param {StatisticsApiGetDomainProgressProportionRequest} requestParameters Request parameters.
* @param {*} [options] Override http request option.
* @throws {RequiredError}
* @memberof StatisticsApi
*/
public getDomainProgressProportion(
requestParameters: StatisticsApiGetDomainProgressProportionRequest,
options?: any
) {
return StatisticsApiFp(this.configuration)
.getDomainProgressProportion(
requestParameters.projectId,
requestParameters.domainId,
options
)
.then((request) => request(this.axios, this.basePath));
}

/**
*
* @param {StatisticsApiGetLineChartRootDataRequest} requestParameters Request parameters.
Expand Down
8 changes: 8 additions & 0 deletions src/backend/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -689,6 +689,14 @@ export async function getProgressEstimationLineChartRoot(
return response.data ?? undefined;
}

export async function getDomainProgress(domainId: string): Promise<number> {
const response = await statisticsApi.getDomainProgressProportion(
{ projectId: LocalStorage.getProjectId(), domainId },
defaultOptions()
);
return response.data;
}

/* UserController.cs */

export async function verifyCaptchaToken(token: string): Promise<boolean> {
Expand Down
Loading
Loading