Skip to content

Commit 42b6154

Browse files
authored
Merge pull request #28 from Sachintechjoomla/Issue#5636
Issue#5646 Feat: Added Events table data in centralised report
2 parents 0861fef + 12a1040 commit 42b6154

File tree

11 files changed

+688
-109
lines changed

11 files changed

+688
-109
lines changed

database_schema.sql

Lines changed: 28 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -132,21 +132,37 @@ CREATE TABLE "EventDetails" (
132132
"attendees" text[]
133133
);
134134

135-
-- Events Table
136135
CREATE TABLE "Events" (
137-
"eventId" uuid PRIMARY KEY DEFAULT gen_random_uuid(),
138-
"isRecurring" boolean DEFAULT false,
139-
"recurrenceEndDate" timestamptz,
140-
"recurrencePattern" jsonb NOT NULL,
141-
"createdAt" timestamp NOT NULL DEFAULT now(),
142-
"updatedAt" timestamp NOT NULL DEFAULT now(),
143-
"autoEnroll" boolean DEFAULT false,
136+
id SERIAL PRIMARY KEY,
137+
"eventDetailId" VARCHAR(100) NOT NULL,
138+
title VARCHAR(255),
139+
"shortDescription" TEXT,
140+
"eventType" VARCHAR(100),
141+
"isRestricted" BOOLEAN DEFAULT FALSE,
142+
location VARCHAR(255),
143+
longitude DECIMAL(10, 7),
144+
latitude DECIMAL(10, 7),
145+
"onlineProvider" VARCHAR(100),
146+
"maxAttendees" INT,
147+
recordings JSONB,
148+
status VARCHAR(50),
149+
description TEXT,
150+
"createdAt" TIMESTAMP DEFAULT NOW(),
151+
"updatedAt" TIMESTAMP DEFAULT NOW(),
152+
"meetingDetails" JSONB,
153+
"createdBy" VARCHAR(100),
154+
"updatedBy" VARCHAR(100),
155+
"idealTime" VARCHAR(50),
156+
metadata JSONB,
157+
attendees JSONB,
158+
"eventId" VARCHAR(100),
159+
"startDateTime" TIMESTAMP,
160+
"endDateTime" TIMESTAMP,
161+
"onlineDetails" JSONB,
162+
"autoEnroll" BOOLEAN DEFAULT FALSE,
144163
"registrationStartDate" timestamptz,
145164
"registrationEndDate" timestamptz,
146-
"createdBy" uuid,
147-
"updatedBy" uuid,
148-
"eventDetailId" uuid,
149-
CONSTRAINT "FK_Events_EventDetails" FOREIGN KEY ("eventDetailId") REFERENCES "EventDetails"("eventDetailId")
165+
extra JSONB
150166
);
151167

152168
-- Event Repetition Table

shiksha-migration/db.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,20 @@ const dbConfig = {
5757
host: process.env.USER_DESTINATION_DB_HOST,
5858
port: parseInt(process.env.USER_DESTINATION_DB_PORT || '5432'),
5959
database: process.env.USER_DESTINATION_DB_NAME
60+
},
61+
event_source: {
62+
user: process.env.EVENT_SOURCE_DB_USER,
63+
password: process.env.EVENT_SOURCE_DB_PASSWORD,
64+
host: process.env.EVENT_SOURCE_DB_HOST,
65+
port: parseInt(process.env.EVENT_SOURCE_DB_PORT || '5432'),
66+
database: process.env.EVENT_SOURCE_DB_NAME
67+
},
68+
event_destination: {
69+
user: process.env.EVENT_DESTINATION_DB_USER,
70+
password: process.env.EVENT_DESTINATION_DB_PASSWORD,
71+
host: process.env.EVENT_DESTINATION_DB_HOST,
72+
port: parseInt(process.env.EVENT_DESTINATION_DB_PORT || '5432'),
73+
database: process.env.EVENT_DESTINATION_DB_NAME
6074
}
6175
};
6276

Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
/*
2+
Events Migration Script
3+
- Reads from legacy event tables in source DB: "Events", "EventDetails", "EventRepetition"
4+
- Flattens and writes to reporting DB table: "Events" (new schema)
5+
- One row per repetition in destination
6+
*/
7+
8+
const { Client } = require('pg');
9+
const dbConfig = require('./db');
10+
11+
console.log('=== Loading events-migration.js ===');
12+
13+
function coerceString(v) {
14+
return v === undefined || v === null ? null : String(v);
15+
}
16+
function coerceDecimal(v) {
17+
return v === undefined || v === null || v === '' ? null : String(v);
18+
}
19+
function coerceDate(v) {
20+
return v ? new Date(v) : null;
21+
}
22+
// Always return a JSON TEXT representation (or null) for binding with ::jsonb
23+
function coerceJson(v) {
24+
if (v === undefined || v === null) return null;
25+
// Buffer -> try parse, else stringify text
26+
if (Buffer.isBuffer(v)) {
27+
const s = v.toString('utf8').trim();
28+
if (!s || s.toLowerCase() === 'null') return null;
29+
try {
30+
JSON.parse(s);
31+
return s; // already JSON text
32+
} catch {
33+
return JSON.stringify(s);
34+
}
35+
}
36+
// Numbers/booleans
37+
if (typeof v === 'number' || typeof v === 'boolean') {
38+
return JSON.stringify(v);
39+
}
40+
// Objects/arrays
41+
if (typeof v === 'object') {
42+
try {
43+
return JSON.stringify(v);
44+
} catch {
45+
return JSON.stringify(String(v));
46+
}
47+
}
48+
// Strings
49+
if (typeof v === 'string') {
50+
const s = v.trim();
51+
if (!s || s.toLowerCase() === 'null') return null;
52+
try {
53+
JSON.parse(s);
54+
return s; // valid JSON text
55+
} catch {
56+
// not JSON => store as JSON string value
57+
return JSON.stringify(s);
58+
}
59+
}
60+
// Fallback
61+
return JSON.stringify(String(v));
62+
}
63+
64+
async function migrateEvents({ limit = 10000, offset = 0 } = {}) {
65+
console.log('=== STARTING EVENTS MIGRATION ===');
66+
67+
const sourceClient = new Client(dbConfig.event_source);
68+
const destClient = new Client(dbConfig.event_destination);
69+
70+
try {
71+
await sourceClient.connect();
72+
console.log('[EVENTS] Connected to source database');
73+
await destClient.connect();
74+
console.log('[EVENTS] Connected to destination database');
75+
76+
// Join legacy tables; one row per repetition via LEFT JOIN
77+
const sql = `
78+
SELECT
79+
e."eventId" AS event_id,
80+
e."isRecurring" AS is_recurring,
81+
e."recurrenceEndDate" AS recurrence_end_date,
82+
e."recurrencePattern" AS recurrence_pattern,
83+
e."autoEnroll" AS auto_enroll,
84+
e."registrationStartDate" AS reg_start,
85+
e."registrationEndDate" AS reg_end,
86+
e."createdBy" AS created_by,
87+
e."updatedBy" AS updated_by,
88+
ed."eventDetailId" AS event_detail_id,
89+
ed."title" AS title,
90+
ed."shortDescription" AS short_description,
91+
ed."eventType" AS event_type,
92+
ed."isRestricted" AS is_restricted,
93+
ed."location" AS location,
94+
ed."longitude" AS longitude,
95+
ed."latitude" AS latitude,
96+
ed."onlineProvider" AS online_provider,
97+
ed."maxAttendees" AS max_attendees,
98+
ed."recordings" AS recordings,
99+
ed."status" AS status,
100+
ed."description" AS description,
101+
ed."meetingDetails" AS meeting_details,
102+
ed."idealTime" AS ideal_time,
103+
ed."metadata" AS metadata,
104+
ed."attendees" AS attendees,
105+
er."startDateTime" AS start_dt,
106+
er."endDateTime" AS end_dt,
107+
er."onlineDetails" AS online_details
108+
FROM public."Events" e
109+
JOIN public."EventDetails" ed ON ed."eventDetailId" = e."eventDetailId"
110+
LEFT JOIN public."EventRepetition" er ON er."eventId" = e."eventId"
111+
ORDER BY e."eventId"
112+
LIMIT $1 OFFSET $2
113+
`;
114+
115+
const res = await sourceClient.query(sql, [limit, offset]);
116+
console.log(`[EVENTS] Fetched ${res.rows.length} rows to migrate`);
117+
118+
const insertSql = `
119+
INSERT INTO public."Events" (
120+
"eventDetailId", title, "shortDescription", "eventType", "isRestricted",
121+
location, longitude, latitude, "onlineProvider", "maxAttendees",
122+
recordings, status, description, "meetingDetails", "createdBy", "updatedBy",
123+
"idealTime", metadata, attendees, "eventId",
124+
"startDateTime", "endDateTime", "onlineDetails",
125+
"autoEnroll", "registrationStartDate", "registrationEndDate", extra
126+
) VALUES (
127+
$1,$2,$3,$4,$5,
128+
$6,$7,$8,$9,$10,
129+
$11::jsonb,$12,$13,$14::jsonb,$15,$16,
130+
$17,$18::jsonb,$19::jsonb,$20,
131+
$21,$22,$23::jsonb,
132+
$24,$25,$26,$27::jsonb
133+
)
134+
`;
135+
136+
for (const r of res.rows) {
137+
const params = [
138+
coerceString(r.event_detail_id),
139+
coerceString(r.title),
140+
coerceString(r.short_description),
141+
coerceString(r.event_type),
142+
!!r.is_restricted,
143+
coerceString(r.location),
144+
coerceDecimal(r.longitude),
145+
coerceDecimal(r.latitude),
146+
coerceString(r.online_provider),
147+
r.max_attendees ?? null,
148+
coerceJson(r.recordings),
149+
coerceString(r.status),
150+
coerceString(r.description),
151+
coerceJson(r.meeting_details),
152+
coerceString(r.created_by),
153+
coerceString(r.updated_by),
154+
coerceString(r.ideal_time),
155+
coerceJson(r.metadata),
156+
coerceJson(r.attendees),
157+
coerceString(r.event_id),
158+
coerceDate(r.start_dt),
159+
coerceDate(r.end_dt),
160+
coerceJson(r.online_details),
161+
!!r.auto_enroll,
162+
coerceDate(r.reg_start),
163+
coerceDate(r.reg_end),
164+
coerceJson(null),
165+
];
166+
167+
try {
168+
await destClient.query(insertSql, params);
169+
} catch (e) {
170+
console.error('[EVENTS] Insert error for eventId:', r.event_id, e.message);
171+
}
172+
}
173+
174+
console.log('[EVENTS] Migration batch completed');
175+
} catch (err) {
176+
console.error('[EVENTS] Critical error:', err);
177+
} finally {
178+
await sourceClient.end();
179+
await destClient.end();
180+
console.log('[EVENTS] Disconnected from databases');
181+
}
182+
}
183+
184+
if (require.main === module) {
185+
console.log('Running events-migration.js directly');
186+
migrateEvents().catch((err) => {
187+
console.error('Events migration failed with unhandled error:', err);
188+
process.exit(1);
189+
});
190+
}
191+
192+
module.exports = { migrateEvents };
193+

shiksha-migration/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
"scripts": {
77
"start:assessment": "node assessment-migration.js",
88
"start:course": "node course-migration.js",
9+
"start:events": "node events-migration.js",
910
"start:user": "node user-migration.js",
1011
"test": "echo \"Error: no test specified\" && exit 1"
1112
},

src/app.module.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import { CourseTracker } from './entities/course-tracker.entity';
3030
import { ContentTracker } from './entities/content-tracker.entity';
3131
import { ContentHandler } from './handlers/content.handler';
3232
import { CohortHandler } from './handlers/cohort.handler';
33+
import { CohortMemberHandler } from './handlers/cohort-member.handler';
3334
import { RegistrationTracker } from './entities/registration-tracker.entity';
3435

3536
@Module({
@@ -92,6 +93,7 @@ import { RegistrationTracker } from './entities/registration-tracker.entity';
9293
AssessmentHandler,
9394
EventHandler,
9495
CohortHandler,
96+
CohortMemberHandler,
9597
TransformService,
9698
// Cron job services
9799
ExternalApiService,

src/entities/cohort-member.entity.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm';
22

3-
@Entity({ name: 'CohortMember' })
3+
@Entity({ name: 'CohortMember', schema: 'public' })
44
export class CohortMember {
55
@PrimaryGeneratedColumn('uuid')
66
CohortMemberID: string;

0 commit comments

Comments
 (0)