From 7ca51c3670cce6110b52f52de003986c55f36d5b Mon Sep 17 00:00:00 2001 From: Julian Krauser Date: Wed, 18 Dec 2024 12:55:03 +0100 Subject: [PATCH] member data views --- src/data-source.ts | 10 ++ src/helpers/dynamicQueryBuilder.ts | 4 + src/index.ts | 3 +- .../1734520998539-memberDataViews.ts | 106 ++++++++++++++++++ src/views/memberExecutivePositionView.ts | 53 +++++++++ src/views/memberQualificationsView.ts | 53 +++++++++ src/views/memberView.ts | 37 ++++++ src/views/membershipsView.ts | 50 +++++++++ 8 files changed, 314 insertions(+), 2 deletions(-) create mode 100644 src/migrations/1734520998539-memberDataViews.ts create mode 100644 src/views/memberExecutivePositionView.ts create mode 100644 src/views/memberQualificationsView.ts create mode 100644 src/views/memberView.ts create mode 100644 src/views/membershipsView.ts diff --git a/src/data-source.ts b/src/data-source.ts index e4bb7af..685d05c 100644 --- a/src/data-source.ts +++ b/src/data-source.ts @@ -46,6 +46,11 @@ import { SMSAlarming1732696919191 } from "./migrations/1732696919191-SMSAlarming import { SecuringCalendarType1733249553766 } from "./migrations/1733249553766-securingCalendarType"; import { query } from "./entity/query"; import { QueryStore1734187754677 } from "./migrations/1734187754677-queryStore"; +import { memberView } from "./views/memberView"; +import { memberExecutivePositionsView } from "./views/memberExecutivePositionView"; +import { memberQualificationsView } from "./views/memberQualificationsView"; +import { membershipView } from "./views/membershipsView"; +import { MemberDataViews1734520998539 } from "./migrations/1734520998539-memberDataViews"; const dataSource = new DataSource({ type: DB_TYPE as any, @@ -85,6 +90,10 @@ const dataSource = new DataSource({ calendar, calendarType, query, + memberView, + memberExecutivePositionsView, + memberQualificationsView, + membershipView, ], migrations: [ Initial1724317398939, @@ -102,6 +111,7 @@ const dataSource = new DataSource({ SMSAlarming1732696919191, SecuringCalendarType1733249553766, QueryStore1734187754677, + MemberDataViews1734520998539, ], migrationsRun: true, migrationsTransactionMode: "each", diff --git a/src/helpers/dynamicQueryBuilder.ts b/src/helpers/dynamicQueryBuilder.ts index effab6e..ad0ba7b 100644 --- a/src/helpers/dynamicQueryBuilder.ts +++ b/src/helpers/dynamicQueryBuilder.ts @@ -16,6 +16,10 @@ export default abstract class DynamicQueryBuilder { "memberExecutivePositions", "memberQualifications", "membership", + "memberView", + "memberExecutivePositionsView", + "memberQualificationsView", + "membershipView", ]; public static getTableMeta(tableName: string): TableMeta { diff --git a/src/index.ts b/src/index.ts index 38374a4..b28e07a 100644 --- a/src/index.ts +++ b/src/index.ts @@ -4,6 +4,7 @@ import express from "express"; import { configCheck, SERVER_PORT } from "./env.defaults"; configCheck(); +import { PermissionObject } from "./type/permissionTypes"; declare global { namespace Express { export interface Request { @@ -16,12 +17,10 @@ declare global { } import { dataSource } from "./data-source"; - dataSource.initialize(); const app = express(); import router from "./routes/index"; -import { PermissionObject } from "./type/permissionTypes"; router(app); app.listen(process.env.NODE_ENV ? SERVER_PORT : 5000, () => { console.log(`listening on *:${SERVER_PORT}`); diff --git a/src/migrations/1734520998539-memberDataViews.ts b/src/migrations/1734520998539-memberDataViews.ts new file mode 100644 index 0000000..11b9656 --- /dev/null +++ b/src/migrations/1734520998539-memberDataViews.ts @@ -0,0 +1,106 @@ +import { DataSource, MigrationInterface, QueryRunner, View } from "typeorm"; +import { member } from "../entity/member"; +import { memberExecutivePositions } from "../entity/memberExecutivePositions"; +import { memberQualifications } from "../entity/memberQualifications"; +import { membership } from "../entity/membership"; + +export class MemberDataViews1734520998539 implements MigrationInterface { + name = "MemberDataViews1734520998539"; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.createView( + new View({ + name: "member_view", + expression: (datasource: DataSource) => + datasource + .getRepository(member) + .createQueryBuilder("member") + .addSelect("TIMESTAMPDIFF(YEAR, member.birthdate, CURDATE())", "todayAge") + .addSelect("YEAR(CURDATE()) - YEAR(member.birthdate)", "ageThisYear"), + }), + true + ); + await queryRunner.createView( + new View({ + name: "member_executive_positions_view", + expression: (datasource: DataSource) => + datasource + .getRepository(memberExecutivePositions) + .createQueryBuilder("memberExecutivePositions") + .select("executivePosition.id", "positionId") + .addSelect("executivePosition.position", "position") + .addSelect("member.id", "memberId") + .addSelect("member.salutation", "memberSalutation") + .addSelect("member.firstname", "memberFirstname") + .addSelect("member.lastname", "memberLastname") + .addSelect("member.nameaffix", "memberNameaffix") + .addSelect("member.birthdate", "memberBirthdate") + .addSelect( + "SUM(TIMESTAMPDIFF(DAY, memberExecutivePositions.start, COALESCE(memberExecutivePositions.end, CURRENT_DATE)))", + "durationInDays" + ) + .leftJoin("memberExecutivePositions.executivePosition", "executivePosition") + .leftJoin("memberExecutivePositions.member", "member") + .groupBy("executivePosition.id"), + }), + true + ); + await queryRunner.createView( + new View({ + name: "member_qualifications_view", + expression: (datasource: DataSource) => + datasource + .getRepository(memberQualifications) + .createQueryBuilder("memberQualifications") + .select("qualification.id", "qualificationId") + .addSelect("qualification.qualification", "qualification") + .addSelect("member.id", "memberId") + .addSelect("member.salutation", "memberSalutation") + .addSelect("member.firstname", "memberFirstname") + .addSelect("member.lastname", "memberLastname") + .addSelect("member.nameaffix", "memberNameaffix") + .addSelect("member.birthdate", "memberBirthdate") + .addSelect( + "SUM(TIMESTAMPDIFF(DAY, memberQualifications.start, COALESCE(memberQualifications.end, CURRENT_DATE)))", + "durationInDays" + ) + .leftJoin("memberQualifications.qualification", "qualification") + .leftJoin("memberQualifications.member", "member") + .groupBy("qualification.id"), + }), + true + ); + await queryRunner.createView( + new View({ + name: "membership_view", + expression: (datasource: DataSource) => + datasource + .getRepository(membership) + .createQueryBuilder("membership") + .select("status.id", "statusId") + .addSelect("status.status", "status") + .addSelect("member.id", "memberId") + .addSelect("member.salutation", "memberSalutation") + .addSelect("member.firstname", "memberFirstname") + .addSelect("member.lastname", "memberLastname") + .addSelect("member.nameaffix", "memberNameaffix") + .addSelect("member.birthdate", "memberBirthdate") + .addSelect( + "SUM(TIMESTAMPDIFF(DAY, membership.start, COALESCE(membership.end, CURRENT_DATE)))", + "durationInDays" + ) + .leftJoin("membership.status", "status") + .leftJoin("membership.member", "member") + .groupBy("status.id"), + }), + true + ); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.dropView("membership_view"); + await queryRunner.dropView("member_qualifications_view"); + await queryRunner.dropView("member_executive_positions_view"); + await queryRunner.dropView("member_view"); + } +} diff --git a/src/views/memberExecutivePositionView.ts b/src/views/memberExecutivePositionView.ts new file mode 100644 index 0000000..ffc859e --- /dev/null +++ b/src/views/memberExecutivePositionView.ts @@ -0,0 +1,53 @@ +import { DataSource, ViewColumn, ViewEntity } from "typeorm"; +import { memberExecutivePositions } from "../entity/memberExecutivePositions"; +import { Salutation } from "../enums/salutation"; + +@ViewEntity({ + expression: (datasource: DataSource) => + datasource + .getRepository(memberExecutivePositions) + .createQueryBuilder("memberExecutivePositions") + .select("executivePosition.id", "positionId") + .addSelect("executivePosition.position", "position") + .addSelect("member.id", "memberId") + .addSelect("member.salutation", "memberSalutation") + .addSelect("member.firstname", "memberFirstname") + .addSelect("member.lastname", "memberLastname") + .addSelect("member.nameaffix", "memberNameaffix") + .addSelect("member.birthdate", "memberBirthdate") + .addSelect( + "SUM(TIMESTAMPDIFF(DAY, memberExecutivePositions.start, COALESCE(memberExecutivePositions.end, CURRENT_DATE)))", + "durationInDays" + ) + .leftJoin("memberExecutivePositions.executivePosition", "executivePosition") + .leftJoin("memberExecutivePositions.member", "member") + .groupBy("executivePosition.id"), +}) +export class memberExecutivePositionsView { + @ViewColumn() + durationInDays: number; + + @ViewColumn() + position: string; + + @ViewColumn() + positionId: number; + + @ViewColumn() + memberId: number; + + @ViewColumn() + memberSalutation: Salutation; + + @ViewColumn() + memberFirstname: string; + + @ViewColumn() + memberLastname: string; + + @ViewColumn() + memberNameaffix: string; + + @ViewColumn() + memberBirthdate: Date; +} diff --git a/src/views/memberQualificationsView.ts b/src/views/memberQualificationsView.ts new file mode 100644 index 0000000..9cd8bd6 --- /dev/null +++ b/src/views/memberQualificationsView.ts @@ -0,0 +1,53 @@ +import { DataSource, ViewColumn, ViewEntity } from "typeorm"; +import { memberQualifications } from "../entity/memberQualifications"; +import { Salutation } from "../enums/salutation"; + +@ViewEntity({ + expression: (datasource: DataSource) => + datasource + .getRepository(memberQualifications) + .createQueryBuilder("memberQualifications") + .select("qualification.id", "qualificationId") + .addSelect("qualification.qualification", "qualification") + .addSelect("member.id", "memberId") + .addSelect("member.salutation", "memberSalutation") + .addSelect("member.firstname", "memberFirstname") + .addSelect("member.lastname", "memberLastname") + .addSelect("member.nameaffix", "memberNameaffix") + .addSelect("member.birthdate", "memberBirthdate") + .addSelect( + "SUM(TIMESTAMPDIFF(DAY, memberQualifications.start, COALESCE(memberQualifications.end, CURRENT_DATE)))", + "durationInDays" + ) + .leftJoin("memberQualifications.qualification", "qualification") + .leftJoin("memberQualifications.member", "member") + .groupBy("qualification.id"), +}) +export class memberQualificationsView { + @ViewColumn() + durationInDays: number; + + @ViewColumn() + qualification: string; + + @ViewColumn() + qualificationId: number; + + @ViewColumn() + memberId: number; + + @ViewColumn() + memberSalutation: Salutation; + + @ViewColumn() + memberFirstname: string; + + @ViewColumn() + memberLastname: string; + + @ViewColumn() + memberNameaffix: string; + + @ViewColumn() + memberBirthdate: Date; +} diff --git a/src/views/memberView.ts b/src/views/memberView.ts new file mode 100644 index 0000000..d28c944 --- /dev/null +++ b/src/views/memberView.ts @@ -0,0 +1,37 @@ +import { DataSource, ViewColumn, ViewEntity } from "typeorm"; +import { member } from "../entity/member"; +import { Salutation } from "../enums/salutation"; + +@ViewEntity({ + expression: (datasource: DataSource) => + datasource + .getRepository(member) + .createQueryBuilder("member") + .addSelect("TIMESTAMPDIFF(YEAR, member.birthdate, CURDATE())", "todayAge") + .addSelect("YEAR(CURDATE()) - YEAR(member.birthdate)", "ageThisYear"), +}) +export class memberView { + @ViewColumn() + id: number; + + @ViewColumn() + salutation: Salutation; + + @ViewColumn() + firstname: string; + + @ViewColumn() + lastname: string; + + @ViewColumn() + nameaffix: string; + + @ViewColumn() + birthdate: Date; + + @ViewColumn() + todayAge: number; + + @ViewColumn() + ageThisYear: number; +} diff --git a/src/views/membershipsView.ts b/src/views/membershipsView.ts new file mode 100644 index 0000000..6d72bcc --- /dev/null +++ b/src/views/membershipsView.ts @@ -0,0 +1,50 @@ +import { DataSource, ViewColumn, ViewEntity } from "typeorm"; +import { membership } from "../entity/membership"; +import { Salutation } from "../enums/salutation"; + +@ViewEntity({ + expression: (datasource: DataSource) => + datasource + .getRepository(membership) + .createQueryBuilder("membership") + .select("status.id", "statusId") + .addSelect("status.status", "status") + .addSelect("member.id", "memberId") + .addSelect("member.salutation", "memberSalutation") + .addSelect("member.firstname", "memberFirstname") + .addSelect("member.lastname", "memberLastname") + .addSelect("member.nameaffix", "memberNameaffix") + .addSelect("member.birthdate", "memberBirthdate") + .addSelect("SUM(TIMESTAMPDIFF(DAY, membership.start, COALESCE(membership.end, CURRENT_DATE)))", "durationInDays") + .leftJoin("membership.status", "status") + .leftJoin("membership.member", "member") + .groupBy("status.id"), +}) +export class membershipView { + @ViewColumn() + durationInDays: number; + + @ViewColumn() + status: string; + + @ViewColumn() + statusId: number; + + @ViewColumn() + memberId: number; + + @ViewColumn() + memberSalutation: Salutation; + + @ViewColumn() + memberFirstname: string; + + @ViewColumn() + memberLastname: string; + + @ViewColumn() + memberNameaffix: string; + + @ViewColumn() + memberBirthdate: Date; +}