Skip to content

Commit

Permalink
conflict server
Browse files Browse the repository at this point in the history
  • Loading branch information
dodoLaprovence committed Dec 12, 2024
1 parent c47f322 commit 1119fcd
Show file tree
Hide file tree
Showing 6 changed files with 244 additions and 2 deletions.
25 changes: 25 additions & 0 deletions server/entities/phaseHistory.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { Column, CreateDateColumn, DeleteDateColumn, Entity, Index, ManyToOne, PrimaryGeneratedColumn } from 'typeorm';

import type { VillagePhase } from './village';
import { Village } from './village';

@Entity()
@Index('IDX_PHASE_HISTORY', ['village', 'phase'], { unique: true })
export class PhaseHistory {
@PrimaryGeneratedColumn()
public id: number;

@ManyToOne(() => Village, (village) => village.phaseHistories, { onDelete: 'CASCADE' })
village: Village;

@Column({
type: 'tinyint',
})
phase: VillagePhase;

@CreateDateColumn({ type: 'datetime' })
public startingOn: Date;

@DeleteDateColumn({ type: 'datetime' })
public endingOn: Date;
}
4 changes: 4 additions & 0 deletions server/entities/village.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { Classroom } from './classroom';
import { Game } from './game';
import { GameResponse } from './gameResponse';
import { Image } from './image';
import { PhaseHistory } from './phaseHistory';
import { User } from './user';

export { VillagePhase };
Expand Down Expand Up @@ -39,6 +40,9 @@ export class Village implements VillageInterface {
})
public activePhase: number;

@OneToMany(() => PhaseHistory, (phaseHistory) => phaseHistory.village, { eager: true })
public phaseHistories: PhaseHistory[];

@OneToOne(() => Activity, { onDelete: 'SET NULL' })
@JoinColumn({ name: 'anthemId' })
public anthem: Activity | null;
Expand Down
37 changes: 35 additions & 2 deletions server/stats/classroomStats.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { UserType } from '../../types/user.type';
import type { Activity } from '../entities/activity';
import { Classroom } from '../entities/classroom';
import { Video } from '../entities/video';
import type { VillagePhase } from '../entities/village';
import { AppDataSource } from '../utils/data-source';
import { generateEmptyFilterParams, getChildrenCodesCount, getConnectedFamiliesCount, getFamiliesWithoutAccount } from './queryStatsByFilters';

const classroomRepository = AppDataSource.getRepository(Classroom);

Expand Down Expand Up @@ -135,3 +135,36 @@ export const normalizeForCountry = (inputData: any) => {

return result;
};

export const getChildrenCodesCountForClassroom = async (classroomId: number, phase: VillagePhase) => {
const classroom = await classroomRepository
.createQueryBuilder('classroom')
.innerJoin('classroom.village', 'village')
.where('classroom.id = :classroomId', { classroomId })
.getOne();

const villageId = classroom?.villageId;

if (!classroomId || !villageId) return 0;
let filterParams = generateEmptyFilterParams();
filterParams = { ...filterParams, villageId, classroomId, phase };
const whereClause = { clause: 'classroom.id = :classroomId', value: { classroomId } };
return await getChildrenCodesCount(filterParams, whereClause);
};

export const getConnectedFamiliesCountForClassroom = async (classroomId: number, phase: VillagePhase) => {
const classroom = await classroomRepository
.createQueryBuilder('classroom')
.innerJoin('classroom.village', 'village')
.where('classroom.id = :classroomId', { classroomId })
.getOne();

const villageId = classroom?.villageId;
let filterParams = generateEmptyFilterParams();
filterParams = { ...filterParams, villageId, classroomId, phase };
return await getConnectedFamiliesCount(filterParams);
};

export const getFamiliesWithoutAccountForClassroom = async (classroomId: number) => {
return getFamiliesWithoutAccount('classroom.id = :classroomId', { classroomId });
};
31 changes: 31 additions & 0 deletions server/stats/globalStats.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import {
generateEmptyFilterParams,
getChildrenCodesCount,
getConnectedFamiliesCount,
getFamiliesWithoutAccount,
getFamilyAccountsCount,
getFloatingAccounts,
} from './queryStatsByFilters';

export const getFamiliesWithoutAccountForGlobal = async () => {
return getFamiliesWithoutAccount();
};

export const getConnectedFamiliesCountForGlobal = async () => {
const filterParams = generateEmptyFilterParams();
return getConnectedFamiliesCount(filterParams);
};

export const getChildrenCodesCountForGlobal = async () => {
const filterParams = generateEmptyFilterParams();
return await getChildrenCodesCount(filterParams);
};
export const getFloatingAccountsForGlobal = async () => {
const filterParams = generateEmptyFilterParams();
return await getFloatingAccounts(filterParams);
};

export const getFamilyAccountsCountForGlobal = async () => {
const filterParams = generateEmptyFilterParams();
return await getFamilyAccountsCount(filterParams);
};
149 changes: 149 additions & 0 deletions server/stats/queryStatsByFilters.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
import type { StatsFilterParams } from '../../types/statistics.type';
import { PhaseHistory } from '../entities/phaseHistory';
import { Student } from '../entities/student';
import { User } from '../entities/user';
import { VillagePhase, Village } from '../entities/village';
import { AppDataSource } from '../utils/data-source';

const studentRepository = AppDataSource.getRepository(Student);
const villageRepository = AppDataSource.getRepository(Village);
const userRepository = AppDataSource.getRepository(User);
const phaseHistoryRepository = AppDataSource.getRepository(PhaseHistory);

export const getFamiliesWithoutAccount = async (condition?: string, conditionValue?: object) => {
const query = studentRepository
.createQueryBuilder('student')
.innerJoin('student.classroom', 'classroom')
.innerJoin('classroom.user', 'user')
.innerJoin('user.village', 'village')
.where('student.numLinkedAccount < 1');

if (condition && conditionValue) {
query.andWhere(condition, conditionValue);
}

query.select([
'classroom.name AS classroom_name',
'classroom.countryCode as classroom_country',
'student.firstname AS student_firstname',
'student.lastname AS student_lastname',
'student.id AS student_id',
'student.createdAt as student_creation_date',
'village.name AS village_name',
]);

return query.getRawMany();
};

export const getConnectedFamiliesCount = async (filterParams: StatsFilterParams) => {
const { villageId, classroomId, phase } = filterParams;
const query = studentRepository
.createQueryBuilder('student')
.innerJoin('classroom', 'classroom', 'classroom.id = student.classroomId')
.andWhere('student.numLinkedAccount >= 1');

if (classroomId) {
query.andWhere('classroom.id = :classroomId', { classroomId });
}

if (villageId) {
query.andWhere('classroom.villageId = :villageId', { villageId });

if (phaseWasSelected(phase)) {
const phaseValue = phase as number;
const { debut, end } = await getPhasePeriod(villageId, phaseValue);
query.andWhere('student.createdAt >= :debut', { debut });
if (phaseValue != (await villageRepository.findOne({ where: { id: villageId } }))?.activePhase) {
query.andWhere('student.createdAt <= :end', { end });
}
}
}

return await query.getCount();
};

export const getFloatingAccounts = async (filterParams: StatsFilterParams) => {
const { villageId } = filterParams;
const query = userRepository.createQueryBuilder('user').where('user.hasStudentLinked = 0').andWhere('user.type = 4');

if (villageId) query.andWhere('user.villageId = :villageId', { villageId });

query.select(['user.id', 'user.firstname', 'user.lastname', 'user.language', 'user.email', 'user.createdAt']);
const floatingAccounts = await query.getMany();
return floatingAccounts;
};

export const getFamilyAccountsCount = async (filterParams: { villageId: number | undefined; phase: VillagePhase | undefined }) => {
const { villageId, phase } = filterParams;
const village = await villageRepository.findOne({ where: { id: villageId } });
const query = userRepository
.createQueryBuilder('user')
.innerJoin('user.village', 'village')
.innerJoin('classroom', 'classroom', 'classroom.villageId = village.id')
.innerJoin('student', 'student', 'student.classroomId = classroom.id')
.where('user.type = 3');

if (villageId) {
query.andWhere('classroom.villageId = :villageId', { villageId });
if (phaseWasSelected(phase)) {
const phaseValue = phase as number;
const { debut, end } = await getPhasePeriod(villageId, phaseValue);
query.andWhere('user.createdAt >= :debut', { debut });
if (phaseValue != village?.activePhase) query.andWhere('student.createdAt <= :end', { end });
}
}

query.groupBy('user.id');
const familyAccountsCount = await query.getCount();
return familyAccountsCount;
};

export const generateEmptyFilterParams = (): StatsFilterParams => {
const filterParams: { [K in keyof StatsFilterParams]: StatsFilterParams[K] } = {
villageId: undefined,
classroomId: undefined,
phase: undefined,
};

return filterParams;
};
export type WhereClause = {
clause: string;
value: object;
};
export const getChildrenCodesCount = async (filterParams: StatsFilterParams, whereClause?: WhereClause) => {
const { villageId, phase } = filterParams;
const village = await villageRepository.findOne({ where: { id: villageId } });
const query = studentRepository.createQueryBuilder('student').innerJoin('student.classroom', 'classroom').innerJoin('classroom.village', 'village');
if (whereClause) query.where(whereClause.clause, whereClause.value);

if (phaseWasSelected(phase) && villageId) {
const phaseValue = phase as number;
const { debut, end } = await getPhasePeriod(villageId, phaseValue);
query.andWhere('student.createdAt >= :debut', { debut });
if (phaseValue != village?.activePhase) query.andWhere('student.createdAt <= :end', { end });
}

return await query.getCount();
};

export const getPhasePeriod = async (villageId: number, phase: number): Promise<{ debut: Date | undefined; end: Date | undefined }> => {
// Getting the debut and end dates for the given phase
const query = phaseHistoryRepository
.createQueryBuilder('phaseHistory')
.withDeleted()
.where('phaseHistory.villageId = :villageId', { villageId })
.andWhere('phaseHistory.phase = :phase', { phase });
query.select(['phaseHistory.startingOn', 'phaseHistory.endingOn']);
const result = await query.getOne();
const debut = result?.startingOn;
const end = result?.endingOn;
return {
debut,
end,
};
};

export const phaseWasSelected = (phase: number | undefined): boolean => {
return phase !== undefined && Object.values(VillagePhase).includes(+phase);
};
Empty file added server/stats/villageStats.ts
Empty file.

0 comments on commit 1119fcd

Please sign in to comment.