Skip to content
Merged
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
222 changes: 117 additions & 105 deletions client/src/api/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -961,6 +961,49 @@ export interface CountryStatDto {
*/
'count': number;
}
/**
*
* @export
* @interface CourseAggregateStatsDto
*/
export interface CourseAggregateStatsDto {
/**
*
* @type {CountriesStatsDto}
* @memberof CourseAggregateStatsDto
*/
'studentsCountries': CountriesStatsDto;
/**
*
* @type {CourseStatsDto}
* @memberof CourseAggregateStatsDto
*/
'studentsStats': CourseStatsDto;
/**
*
* @type {CountriesStatsDto}
* @memberof CourseAggregateStatsDto
*/
'mentorsCountries': CountriesStatsDto;
/**
*
* @type {CourseMentorsStatsDto}
* @memberof CourseAggregateStatsDto
*/
'mentorsStats': CourseMentorsStatsDto;
/**
*
* @type {Array<CourseTaskDto>}
* @memberof CourseAggregateStatsDto
*/
'courseTasks': Array<CourseTaskDto>;
/**
*
* @type {CountriesStatsDto}
* @memberof CourseAggregateStatsDto
*/
'studentsCertificatesCountries': CountriesStatsDto;
}
/**
*
* @export
Expand Down Expand Up @@ -1476,49 +1519,6 @@ export interface CourseStatsDto {
*/
'eligibleForCertificationCount': number;
}
/**
*
* @export
* @interface CoursesStatsDto
*/
export interface CoursesStatsDto {
/**
*
* @type {CountriesStatsDto}
* @memberof CoursesStatsDto
*/
'studentsCountries': CountriesStatsDto;
/**
*
* @type {CourseStatsDto}
* @memberof CourseStatsDto
*/
'studentsStats': CourseStatsDto;
/**
*
* @type {CountriesStatsDto}
* @memberof CoursesStatsDto
*/
'mentorsCountries': CountriesStatsDto;
/**
*
* @type {CourseMentorsStatsDto}
* @memberof CoursesStatsDto
*/
'mentorsStats': CourseMentorsStatsDto;
/**
*
* @type {CourseTaskDto[]}
* @memberof CoursesStatsDto
*/
'courseTasks': CourseTaskDto[];
/**
*
* @type {CountriesStatsDto}
* @memberof CoursesStatsDto
*/
'studentsCertificatesCountries': CountriesStatsDto;
}
/**
*
* @export
Expand Down Expand Up @@ -10174,47 +10174,6 @@ export const CourseStatsApiAxiosParamCreator = function (configuration?: Configu
options: localVarRequestOptions,
};
},
/**
*
* @param {number[]} ids
* @param {number} year
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
getCoursesStats: async (ids: number[] = [], year?: number, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
const localVarUrlObj = new URL('/courses/aggregate/stats', 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 (ids.length > 0) {
localVarQueryParameter.ids = ids
}

if (year) {
localVarQueryParameter.year = year;
}

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

return {
url: toPathString(localVarUrlObj),
options: localVarRequestOptions,
};
},
/**
*
* @param {number} courseId
Expand Down Expand Up @@ -10272,6 +10231,49 @@ export const CourseStatsApiAxiosParamCreator = function (configuration?: Configu



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

return {
url: toPathString(localVarUrlObj),
options: localVarRequestOptions,
};
},
/**
*
* @param {Array<number>} ids List of course IDs
* @param {number} year Year for which stats are fetched
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
getCoursesStats: async (ids: Array<number>, year: number, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
// verify required parameter 'ids' is not null or undefined
assertParamExists('getCoursesStats', 'ids', ids)
// verify required parameter 'year' is not null or undefined
assertParamExists('getCoursesStats', 'year', year)
const localVarPath = `/courses/aggregate/stats`;
// 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 (ids) {
localVarQueryParameter['ids'] = ids;
}

if (year !== undefined) {
localVarQueryParameter['year'] = year;
}



setSearchParams(localVarUrlObj, localVarQueryParameter);
let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
Expand Down Expand Up @@ -10358,17 +10360,6 @@ export const CourseStatsApiFp = function(configuration?: Configuration) {
const localVarAxiosArgs = await localVarAxiosParamCreator.getCourseStats(courseId, options);
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
},
/**
*
* @param {number[]} ids
* @param {number} year
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
async getCoursesStats(ids: number[], year: number, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<CoursesStatsDto>> {
const localVarAxiosArgs = await localVarAxiosParamCreator.getCoursesStats( ids, year, options);
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
},
/**
*
* @param {number} courseId
Expand All @@ -10389,6 +10380,17 @@ export const CourseStatsApiFp = function(configuration?: Configuration) {
const localVarAxiosArgs = await localVarAxiosParamCreator.getCourseStudentCountries(courseId, options);
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
},
/**
*
* @param {Array<number>} ids List of course IDs
* @param {number} year Year for which stats are fetched
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
async getCoursesStats(ids: Array<number>, year: number, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<CourseAggregateStatsDto>> {
const localVarAxiosArgs = await localVarAxiosParamCreator.getCoursesStats(ids, year, options);
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
},
/**
*
* @param {number} courseId
Expand Down Expand Up @@ -10455,6 +10457,16 @@ export const CourseStatsApiFactory = function (configuration?: Configuration, ba
getCourseStudentCountries(courseId: number, options?: any): AxiosPromise<CountriesStatsDto> {
return localVarFp.getCourseStudentCountries(courseId, options).then((request) => request(axios, basePath));
},
/**
*
* @param {Array<number>} ids List of course IDs
* @param {number} year Year for which stats are fetched
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
getCoursesStats(ids: Array<number>, year: number, options?: any): AxiosPromise<CourseAggregateStatsDto> {
return localVarFp.getCoursesStats(ids, year, options).then((request) => request(axios, basePath));
},
/**
*
* @param {number} courseId
Expand Down Expand Up @@ -10509,15 +10521,14 @@ export class CourseStatsApi extends BaseAPI {
}

/**
*
* @param {number[]} ids
* @param {number} year
*
* @param {number} courseId
* @param {*} [options] Override http request option.
* @throws {RequiredError}
* @memberof CourseStatsApi
*/
public getCoursesStats(ids: number[], year: number, options?: AxiosRequestConfig) {
return CourseStatsApiFp(this.configuration).getCoursesStats( ids, year, options).then((request) => request(this.axios, this.basePath));
public getCourseStudentCertificatesCountries(courseId: number, options?: AxiosRequestConfig) {
return CourseStatsApiFp(this.configuration).getCourseStudentCertificatesCountries(courseId, options).then((request) => request(this.axios, this.basePath));
}

/**
Expand All @@ -10527,19 +10538,20 @@ export class CourseStatsApi extends BaseAPI {
* @throws {RequiredError}
* @memberof CourseStatsApi
*/
public getCourseStudentCertificatesCountries(courseId: number, options?: AxiosRequestConfig) {
return CourseStatsApiFp(this.configuration).getCourseStudentCertificatesCountries(courseId, options).then((request) => request(this.axios, this.basePath));
public getCourseStudentCountries(courseId: number, options?: AxiosRequestConfig) {
return CourseStatsApiFp(this.configuration).getCourseStudentCountries(courseId, options).then((request) => request(this.axios, this.basePath));
}

/**
*
* @param {number} courseId
* @param {Array<number>} ids List of course IDs
* @param {number} year Year for which stats are fetched
* @param {*} [options] Override http request option.
* @throws {RequiredError}
* @memberof CourseStatsApi
*/
public getCourseStudentCountries(courseId: number, options?: AxiosRequestConfig) {
return CourseStatsApiFp(this.configuration).getCourseStudentCountries(courseId, options).then((request) => request(this.axios, this.basePath));
public getCoursesStats(ids: Array<number>, year: number, options?: AxiosRequestConfig) {
return CourseStatsApiFp(this.configuration).getCoursesStats(ids, year, options).then((request) => request(this.axios, this.basePath));
}

/**
Expand Down
2 changes: 1 addition & 1 deletion client/src/api/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/* tslint:disable */
/* eslint-disable */
/**
*
* No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { CoursesStatsDto } from '@client/api';
import { CourseAggregateStatsDto } from '@client/api';
import { StudentsCountriesCard } from '@client/modules/CourseStatistics/components/StudentsCountriesCard';
import { StudentsStatsCard } from '@client/modules/CourseStatistics/components/StudentsStatsCard';
import { MentorsCountriesCard } from '@client/modules/CourseStatistics/components/MentorsCountriesCard/MentorsCountriesCard';
Expand All @@ -12,7 +12,7 @@ import Masonry from 'react-masonry-css';
import css from 'styled-jsx/css';

type StatCardsProps = {
coursesData?: CoursesStatsDto;
coursesData?: CourseAggregateStatsDto;
};

const gapSize = 24;
Expand Down
21 changes: 8 additions & 13 deletions client/src/modules/CourseStatistics/hooks/useCourseStats.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useMessage } from '@client/hooks';
import { CoursesStatsDto, CourseStatsApi } from '@client/api';
import { CourseAggregateStatsDto, CourseStatsApi } from '@client/api';
import { useRequest } from 'ahooks';

const courseStatsApi = new CourseStatsApi();
Expand All @@ -9,30 +9,25 @@ type CourseStatsParams = {
year?: number;
};

async function fetchCourseStats({ ids = [], year = 0 }: CourseStatsParams): Promise<CoursesStatsDto | undefined> {
async function fetchCourseStats({ ids = [], year = 0 }: CourseStatsParams): Promise<CourseAggregateStatsDto> {
try {
const { data } = await courseStatsApi.getCoursesStats(ids, year);
return data;
} catch {
console.error("Couldn't get course(s) stats");
} catch (err) {
console.error("Couldn't get course(s) stats", err);
throw err;
}
}

export function useCoursesStats({ ids, year }: CourseStatsParams) {
const { message } = useMessage();

const service = async () => {
if (!ids?.length && !year) {
return;
}
return fetchCourseStats({ ids, year });
};

const { data, loading } = useRequest(service, {
const { data, loading } = useRequest(() => fetchCourseStats({ ids, year }), {
ready: Boolean((ids && ids.length) || year),
refreshDeps: [ids, year],
retryCount: 3,
onError: () => {
message.error("Can't load courses data. Please try latter.");
message.error("Can't load courses data. Please try later.");
},
});

Expand Down
18 changes: 15 additions & 3 deletions nestjs/src/courses/stats/course-stats.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
UseInterceptors,
} from '@nestjs/common';
import { CacheInterceptor, CacheTTL } from '@nestjs/cache-manager';
import { ApiBadRequestResponse, ApiOkResponse, ApiOperation, ApiTags } from '@nestjs/swagger';
import { ApiBadRequestResponse, ApiOkResponse, ApiOperation, ApiQuery, ApiTags } from '@nestjs/swagger';
import { CourseGuard, CurrentRequest, DefaultGuard } from '../../auth';
import { ONE_HOUR_CACHE_TTL } from '../../constants';
import { CourseAccessService } from '../course-access.service';
Expand All @@ -37,11 +37,23 @@ export class CourseStatsController {
@CacheTTL(ONE_HOUR_CACHE_TTL)
@UseInterceptors(CacheInterceptor)
@ApiOperation({ operationId: 'getCoursesStats' })
@ApiQuery({
name: 'ids',
required: true,
type: [Number],
description: 'List of course IDs',
})
@ApiQuery({
name: 'year',
required: true,
type: Number,
description: 'Year for which stats are fetched',
})
@ApiOkResponse({ type: CourseAggregateStatsDto })
public async getCoursesStats(
@Req() req: CurrentRequest,
@Query('ids', new ParseArrayPipe({ items: Number, optional: true })) ids: number[],
@Query('year', new ParseIntPipe({ optional: true })) year: number,
@Query('ids', new ParseArrayPipe({ items: Number })) ids: number[],
@Query('year', ParseIntPipe) year: number,
) {
const allowedCourseIds = await this.courseAccessService.getUserAllowedCourseIds(req.user, ids, year);
const data = await this.courseStatsService.getCoursesStats(allowedCourseIds);
Expand Down
Loading
Loading