Skip to content

Commit 457b75a

Browse files
committed
Integration updates
1 parent e3d2e6c commit 457b75a

File tree

8 files changed

+96
-1145
lines changed

8 files changed

+96
-1145
lines changed

.gitignore

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,4 +63,6 @@ typings/
6363
# next.js build output
6464
.next
6565

66-
migrator/data
66+
migrator/data
67+
68+
src/scripts/seed/*.json
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
-- AlterTable
2+
ALTER TABLE "resources"."Resource" ADD COLUMN "legacyId" INTEGER;
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
# Please do not edit this file manually
22
# It should be added in your version-control system (e.g., Git)
3-
provider = "postgresql"
3+
provider = "postgresql"

src/scripts/seed-tables.js

Lines changed: 89 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22
* Seed table data in database
33
*/
44
const _ = require('lodash')
5+
const fs = require('fs')
6+
const path = require('path')
7+
const { v4: uuid } = require('uuid')
58
const logger = require('../common/logger')
69
const prisma = require('../common/prisma').getClient()
710

@@ -40,36 +43,97 @@ async function importResourceRolePhaseDependency () {
4043
}
4144

4245
async function importResource () {
43-
let data = require('./seed/Resource.json')
44-
let memberData = {}
45-
data = _.map(data, d => {
46-
const r = _.omit(d, ['created', 'rating'])
47-
r.createdAt = d.created
48-
// collect member data
49-
const memberId = _.parseInt(d.memberId)
50-
if (!_.keys(memberData).includes(memberId)) {
51-
memberData[memberId] = {
52-
userId: memberId,
53-
handle: d.memberHandle,
54-
handleLower: d.memberHandle.toLowerCase(),
55-
maxRating: _.get(d, 'rating', null),
56-
createdBy
46+
const logPath = path.join(__dirname, 'seed', 'resource-import-skipped.log')
47+
// truncate existing log
48+
try { fs.writeFileSync(logPath, '') } catch (e) { /* ignore */ }
49+
50+
const writeSkip = (reason, rec) => {
51+
const line = JSON.stringify({ reason, record: rec }) + '\n'
52+
fs.appendFileSync(logPath, line)
53+
}
54+
55+
const data = require('./seed/Resource.json')
56+
const total = data.length
57+
58+
const memberDataMap = {}
59+
let success = 0
60+
let skipped = 0
61+
const start = Date.now()
62+
const progressTimer = setInterval(() => {
63+
const elapsed = (Date.now() - start) / 1000
64+
const processed = success + skipped
65+
const rate = processed > 0 ? (processed / elapsed) : 0
66+
const pct = total > 0 ? ((processed / total) * 100) : 0
67+
const remaining = Math.max(total - processed, 0)
68+
const eta = rate > 0 ? (remaining / rate) : Infinity
69+
logger.info(`Resource import progress: ${processed}/${total} (${pct.toFixed(1)}%), success=${success}, skipped=${skipped}, rate=${rate.toFixed(2)} rec/s, elapsed=${elapsed.toFixed(1)}s, ETA=${Number.isFinite(eta) ? eta.toFixed(1) : '∞'}s`)
70+
}, 5000)
71+
72+
for (const d of data) {
73+
// Validate required fields
74+
const missing = []
75+
for (const f of ['challengeId', 'memberId', 'memberHandle', 'roleId']) {
76+
if (_.isNil(d[f]) || _.toString(d[f]).trim() === '') missing.push(f)
77+
}
78+
if (missing.length) {
79+
skipped += 1
80+
writeSkip(`Missing required field(s): ${missing.join(', ')}`, d)
81+
continue
82+
}
83+
84+
// Build record for Prisma. Remove fields not in schema or that conflict.
85+
const r = _.omit(d, ['created', 'updated', 'rating', 'legacyId'])
86+
// Ensure id exists; generate if absent
87+
if (!r.id) r.id = uuid()
88+
// Normalize timestamps
89+
if (d.created) r.createdAt = new Date(d.created)
90+
if (d.updated) r.updatedAt = new Date(d.updated)
91+
// Ensure strings for string columns
92+
r.challengeId = _.toString(r.challengeId)
93+
r.memberId = _.toString(r.memberId)
94+
r.memberHandle = _.toString(r.memberHandle)
95+
r.roleId = _.toString(r.roleId)
96+
if (!r.createdBy) r.createdBy = createdBy
97+
98+
try {
99+
await prisma.resource.create({ data: r })
100+
success += 1
101+
// collect member data only for successfully inserted resources
102+
const mid = _.parseInt(d.memberId)
103+
if (!_.has(memberDataMap, mid)) {
104+
memberDataMap[mid] = {
105+
userId: mid,
106+
handle: d.memberHandle,
107+
handleLower: _.toString(d.memberHandle).toLowerCase(),
108+
maxRating: _.get(d, 'rating', null),
109+
createdBy
110+
}
57111
}
112+
} catch (err) {
113+
skipped += 1
114+
writeSkip(`Insert failed: ${err.message}`, d)
58115
}
59-
return r
60-
})
61-
await prisma.resource.createMany({ data })
62-
logger.info('Imported ResourceRole data')
63-
// import memberProfile and memberStats
64-
memberData = _.values(memberData)
65-
await prisma.memberStats.createMany({ data: memberData })
66-
await prisma.memberProfile.createMany({ data: _.map(memberData, d => _.omit(d, 'maxRating')) })
67-
logger.info('Imported Member data')
116+
}
117+
118+
clearInterval(progressTimer)
119+
const elapsed = (Date.now() - start) / 1000
120+
const rate = (success + skipped) > 0 ? ((success + skipped) / elapsed).toFixed(2) : '0.00'
121+
logger.info(`Imported Resource data: success=${success}, skipped=${skipped}, total=${total}, elapsed=${elapsed.toFixed(1)}s, rate=${rate} rec/s`)
122+
123+
// import memberProfile and memberStats for members associated with successfully inserted resources
124+
const memberData = _.values(memberDataMap)
125+
if (memberData.length) {
126+
await prisma.memberStats.createMany({ data: memberData })
127+
await prisma.memberProfile.createMany({ data: _.map(memberData, (m) => _.omit(m, 'maxRating')) })
128+
logger.info(`Imported Member data: count=${memberData.length}`)
129+
} else {
130+
logger.info('No member data to import')
131+
}
68132
}
69133

70134
async function main () {
71-
await importResourceRole()
72-
await importResourceRolePhaseDependency()
135+
//await importResourceRole()
136+
//await importResourceRolePhaseDependency()
73137
await importResource()
74138
}
75139

0 commit comments

Comments
 (0)