diff --git a/frontend/src/app/data.service.ts b/frontend/src/app/data.service.ts index e8a077a..b6d2508 100644 --- a/frontend/src/app/data.service.ts +++ b/frontend/src/app/data.service.ts @@ -188,6 +188,24 @@ export class DataService { return repoData; } + async loadAllInstitutionsSummaries() { + const institutionData = await this.http + .get<{ + institutionsSummaries: InstitutionSummary[]; + total: number; + }>(`${environment.api}api/completeInstitutionSummaries`, {}) + .pipe( + catchError(error => { + if (error.status === 401) { + this.toastr.error("Ihre Sitzung ist abgelaufen. Bitte melden Sie sich erneut an.", "Login timeout", {disableTimeOut: true}); + } + return throwError(error); + }) + ) + .toPromise(); + return institutionData; + } + // request to get all users, repositories, institutions and organizations, // these are used to get all the data for excel export and is restricted to logged in users // ***************************************************************************************************** diff --git a/frontend/src/app/ranking/ranking.component.html b/frontend/src/app/ranking/ranking.component.html index 7c2fdcc..6440c0d 100644 --- a/frontend/src/app/ranking/ranking.component.html +++ b/frontend/src/app/ranking/ranking.component.html @@ -58,6 +58,11 @@

(change)="includeForksChange($event.checked)" >Include forks + +

Information on OSS Benchmark updated: {{ latestUdpate | date }}

diff --git a/frontend/src/app/ranking/ranking.component.ts b/frontend/src/app/ranking/ranking.component.ts index 331211f..60f1649 100644 --- a/frontend/src/app/ranking/ranking.component.ts +++ b/frontend/src/app/ranking/ranking.component.ts @@ -36,6 +36,7 @@ export class RankingComponent implements OnInit { recordFilter = ''; state: Date; completeInstitutions: Institution[]; + allInstitutions: InstitutionSumary[] = []; institutions: InstitutionSumary[]; window: any = window; includeForks: boolean = false; @@ -45,6 +46,7 @@ export class RankingComponent implements OnInit { sortDirection: 'ASC' | 'DESC' = 'DESC'; latestUdpate: any; exportService: DataExportService = new DataExportService(); + isDownloading: boolean = false; searchTermRaw: string = ''; @@ -198,6 +200,33 @@ export class RankingComponent implements OnInit { this.reloadData(); } + isLoggedIn(): boolean { + return this.authService.isUserLoggedIn(); + } + + async downloadData(): Promise { + this.isDownloading = true; + try { + let institutionData = await this.dataService.loadAllInstitutionsSummaries(); + console.log('institutionData', institutionData); + this.allInstitutions = institutionData.institutionsSummaries; + + // Format the date fields + this.allInstitutions = this.allInstitutions.map((institution) => { + if (institution.created_at) { + institution.created_at = this.exportService.formatDate(institution.created_at); + } + return institution; + }); + + this.exportService.exportData(this.allInstitutions, 'InstitutionRanking'); + } catch (error) { + console.error('Error occurred while downloading data:', error); + } finally { + this.isDownloading = false; + } + } + @ViewChild(MatPaginator) paginator: MatPaginator; diff --git a/oss-api/src/api/api.controller.ts b/oss-api/src/api/api.controller.ts index 7cf7f15..ecec3f1 100644 --- a/oss-api/src/api/api.controller.ts +++ b/oss-api/src/api/api.controller.ts @@ -135,6 +135,32 @@ export class ApiController { return { users, total: users.length }; } + // Institutions Data + //@UseGuards(AuthGuard) + @Get('completeInstitutionSummaries') + async collectInstitutionSummaries() { + const queryConfig = { + sector: [], + search: '', + sort: 'num_repos', + direction: 'DESC', + page: 0, + count: -1, + sendStats: false, + includeForks: false, + }; + const institutionsSummaries = + await this.mongoDbService.findInstitutionsWithConditions( + queryConfig.sort, + queryConfig.direction == 'ASC' ? 1 : -1, + queryConfig.count, + queryConfig.page, + queryConfig.includeForks, + [{}], + ); + return { institutionsSummaries, total: institutionsSummaries.length }; + } + /***********************************Helper************************************************/ /** * Handle the instiution query with the given conditions @@ -150,7 +176,7 @@ export class ApiController { return queryConfig.sector.includes(sector); }); } - let conditions: Object[] = [ + const conditions: Object[] = [ { sector: { $in: sectorList }, }, @@ -160,7 +186,7 @@ export class ApiController { $text: { $search: queryConfig.search }, }); } - let institutions = await this.mongoDbService.findInstitutionsWithConditions( + const institutions = await this.mongoDbService.findInstitutionsWithConditions( queryConfig.sort, queryConfig.direction == 'ASC' ? 1 : -1, queryConfig.count, @@ -168,7 +194,7 @@ export class ApiController { queryConfig.includeForks, conditions, ); - let foundSectors = + const foundSectors = await this.mongoDbService.countInstitutionsWithConditions(conditions); let total = 0; const sectorcount = {}; diff --git a/oss-api/src/mongo-db/mongo-db.service.ts b/oss-api/src/mongo-db/mongo-db.service.ts index fa2288c..59c0f78 100644 --- a/oss-api/src/mongo-db/mongo-db.service.ts +++ b/oss-api/src/mongo-db/mongo-db.service.ts @@ -4,7 +4,7 @@ import { OnApplicationShutdown, OnModuleInit, } from '@nestjs/common'; -import { MongoClient, ConnectOptions, ObjectId } from 'mongodb'; +import { MongoClient, ConnectOptions, ObjectId, Document } from 'mongodb'; import { Institution, Repository, @@ -138,130 +138,132 @@ export class MongoDbService implements OnApplicationShutdown, OnModuleInit { // this.logger.log( // `Searching for institutions with these conditions: ${conditions.toString()}`, // ); - return this.client - .db(this.database) - .collection(Tables.institutions) - .aggregate( - [ - { - $match: { - $and: conditions, - }, - }, - { - $lookup: { - from: 'organisation', - localField: 'orgs', - foreignField: '_id', - as: 'orga', - }, - }, - { $unwind: { path: '$orga', preserveNullAndEmptyArrays: true } }, - { - $lookup: { - from: 'repositoriesNew', - localField: 'orga.repos', - foreignField: '_id', - as: 'repo', - }, - }, - { - $project: { - shortname: 1, - name_de: 1, - avatar: 1, - sector: 1, - orga: 1, - repo: { - $cond: [ - { $eq: [includeForks, true] }, - '$repo', - { - $filter: { - input: '$repo', - as: 'repository', - cond: { - $eq: ['$$repository.fork', false], - }, - }, - }, - ], - }, - }, - }, - { $unwind: { path: '$repo', preserveNullAndEmptyArrays: true } }, - // { $sort: { 'orga.created_at': 1 } }, // comment out to make "include forks" checkbox work - { - $group: { - _id: '$_id', - shortname: { $first: '$shortname' }, - name_de: { $first: '$name_de' }, - num_repos: { $count: {} }, - members: { $push: '$repo.contributors' }, - forks: { $push: '$repo.fork' }, - avatar: { $first: { $first: '$avatar' } }, - sector: { $first: '$sector' }, - repo_names: { $push: '$repo.name' }, - location: { $first: '$orga.locations' }, - created_at: { $first: '$orga.created_at' }, - }, - }, - { - $set: { - total_num_forks_in_repos: { - $sum: { - $size: { - $filter: { - input: '$forks', - cond: '$$this', - }, + const aggregateOptions: Document[] = [ + { + $match: { + $and: conditions, + }, + }, + { + $lookup: { + from: 'organisation', + localField: 'orgs', + foreignField: '_id', + as: 'orga', + }, + }, + { $unwind: { path: '$orga', preserveNullAndEmptyArrays: true } }, + { + $lookup: { + from: 'repositoriesNew', + localField: 'orga.repos', + foreignField: '_id', + as: 'repo', + }, + }, + { + $project: { + shortname: 1, + name_de: 1, + avatar: 1, + sector: 1, + orga: 1, + repo: { + $cond: [ + { $eq: [includeForks, true] }, + '$repo', + { + $filter: { + input: '$repo', + as: 'repository', + cond: { + $eq: ['$$repository.fork', false], }, }, }, - num_members: { - $size: { - $setUnion: [ - { - $reduce: { - input: '$members', - initialValue: [], - in: { $concatArrays: ['$$value', '$$this'] }, - }, - }, - [], - ], + ], + }, + }, + }, + { $unwind: { path: '$repo', preserveNullAndEmptyArrays: true } }, + // { $sort: { 'orga.created_at': 1 } }, // comment out to make "include forks" checkbox work + { + $group: { + _id: '$_id', + shortname: { $first: '$shortname' }, + name_de: { $first: '$name_de' }, + num_repos: { $count: {} }, + members: { $push: '$repo.contributors' }, + forks: { $push: '$repo.fork' }, + avatar: { $first: { $first: '$avatar' } }, + sector: { $first: '$sector' }, + repo_names: { $push: '$repo.name' }, + location: { $first: '$orga.locations' }, + created_at: { $first: '$orga.created_at' }, + }, + }, + { + $set: { + total_num_forks_in_repos: { + $sum: { + $size: { + $filter: { + input: '$forks', + cond: '$$this', }, }, }, }, - { - $project: { - _id: 1, - shortname: 1, - name_de: 1, - num_repos: 1, - num_members: 1, - total_num_forks_in_repos: 1, - avatar: 1, - sector: 1, - repo_names: 1, - location: 1, - created_at: 1, + num_members: { + $size: { + $setUnion: [ + { + $reduce: { + input: '$members', + initialValue: [], + in: { $concatArrays: ['$$value', '$$this'] }, + }, + }, + [], + ], }, }, - { - $sort: { [sortKey]: direction }, - }, - { - $skip: limit * page, - }, - { - $limit: limit, - }, - ], + }, + }, + { + $project: { + _id: 1, + shortname: 1, + name_de: 1, + num_repos: 1, + num_members: 1, + total_num_forks_in_repos: 1, + avatar: 1, + sector: 1, + repo_names: 1, + location: 1, + created_at: 1, + }, + }, + { + $sort: { [sortKey]: direction }, + }, + { + $skip: limit * page, + }, + ]; + + if(limit > 0) { + aggregateOptions.push({$limit: limit}) + } + + return this.client + .db(this.database) + .collection(Tables.institutions) + .aggregate(aggregateOptions, { allowDiskUse: true, - }, + } ) .toArray() as Promise; }