Import doctor reviews through Payload so production seed no longer depends on local psql.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1,4 +1,6 @@
|
|||||||
import 'dotenv/config'
|
import 'dotenv/config'
|
||||||
|
import { getPayload } from 'payload'
|
||||||
|
import config from '../payload.config'
|
||||||
|
|
||||||
type ReviewSeed = { authorName: string; rating: 4 | 5; comment: string; daysAgo: number }
|
type ReviewSeed = { authorName: string; rating: 4 | 5; comment: string; daysAgo: number }
|
||||||
|
|
||||||
@@ -347,30 +349,13 @@ const reviewsBySlug: Record<string, ReviewSeed[]> = {
|
|||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
function sqlString(value: string) {
|
export async function seedReviews(payload: Awaited<ReturnType<typeof getPayload>>) {
|
||||||
return `'${value.replace(/'/g, "''")}'`
|
const doctors = await payload.find({
|
||||||
}
|
collection: 'doctors',
|
||||||
|
limit: 500,
|
||||||
async function main() {
|
depth: 0,
|
||||||
const { execFileSync } = await import('node:child_process')
|
|
||||||
const rows = execFileSync('psql', ['-d', 'moniki', '-t', '-A', '-c', "SELECT slug || E'\\t' || id FROM doctors"], {
|
|
||||||
encoding: 'utf8',
|
|
||||||
})
|
})
|
||||||
.trim()
|
const bySlug = Object.fromEntries(doctors.docs.map((doc) => [String(doc.slug), doc.id]))
|
||||||
.split('\n')
|
|
||||||
.filter(Boolean)
|
|
||||||
const bySlug = Object.fromEntries(rows.map((line) => line.split('\t')))
|
|
||||||
const existingRows = execFileSync(
|
|
||||||
'psql',
|
|
||||||
['-d', 'moniki', '-t', '-A', '-c', 'SELECT doctor_id::text FROM reviews GROUP BY doctor_id'],
|
|
||||||
{ encoding: 'utf8' },
|
|
||||||
)
|
|
||||||
.trim()
|
|
||||||
.split('\n')
|
|
||||||
.filter(Boolean)
|
|
||||||
const doctorsWithReviews = new Set(existingRows)
|
|
||||||
|
|
||||||
const statements: string[] = ['BEGIN;']
|
|
||||||
let created = 0
|
let created = 0
|
||||||
|
|
||||||
for (const [slug, items] of Object.entries(reviewsBySlug)) {
|
for (const [slug, items] of Object.entries(reviewsBySlug)) {
|
||||||
@@ -379,52 +364,50 @@ async function main() {
|
|||||||
console.log('skip missing doctor', slug)
|
console.log('skip missing doctor', slug)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if (doctorsWithReviews.has(doctorId)) {
|
|
||||||
|
const existing = await payload.find({
|
||||||
|
collection: 'reviews',
|
||||||
|
where: { doctor: { equals: doctorId } },
|
||||||
|
limit: 1,
|
||||||
|
})
|
||||||
|
if (existing.docs.length) {
|
||||||
console.log('already has reviews', slug)
|
console.log('already has reviews', slug)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const item of items) {
|
for (const item of items) {
|
||||||
const createdAt = new Date()
|
const createdAt = new Date()
|
||||||
createdAt.setDate(createdAt.getDate() - item.daysAgo)
|
createdAt.setDate(createdAt.getDate() - item.daysAgo)
|
||||||
statements.push(`
|
await payload.create({
|
||||||
WITH inserted AS (
|
collection: 'reviews',
|
||||||
INSERT INTO reviews (doctor_id, author_name, rating, is_published, created_at, updated_at)
|
locale: 'ru',
|
||||||
VALUES (${doctorId}, ${sqlString(item.authorName)}, ${item.rating}, true, ${sqlString(createdAt.toISOString())}, now())
|
data: {
|
||||||
RETURNING id
|
doctor: doctorId,
|
||||||
)
|
authorName: item.authorName,
|
||||||
INSERT INTO reviews_locales (comment, _locale, _parent_id)
|
rating: item.rating,
|
||||||
SELECT ${sqlString(item.comment)}, 'ru', id FROM inserted;
|
comment: item.comment,
|
||||||
`)
|
isPublished: true,
|
||||||
|
createdAt: createdAt.toISOString(),
|
||||||
|
updatedAt: createdAt.toISOString(),
|
||||||
|
},
|
||||||
|
})
|
||||||
created += 1
|
created += 1
|
||||||
}
|
}
|
||||||
console.log(slug, items.length)
|
console.log(slug, items.length)
|
||||||
}
|
}
|
||||||
|
|
||||||
statements.push(`
|
|
||||||
UPDATE doctors d SET
|
|
||||||
reviews_count = stats.cnt,
|
|
||||||
rating = stats.avg
|
|
||||||
FROM (
|
|
||||||
SELECT doctor_id, COUNT(*)::numeric AS cnt, ROUND(AVG(rating)::numeric, 1) AS avg
|
|
||||||
FROM reviews
|
|
||||||
WHERE is_published = true
|
|
||||||
GROUP BY doctor_id
|
|
||||||
) stats
|
|
||||||
WHERE d.id = stats.doctor_id;
|
|
||||||
COMMIT;
|
|
||||||
`)
|
|
||||||
|
|
||||||
execFileSync('psql', ['-d', 'moniki', '-v', 'ON_ERROR_STOP=1'], {
|
|
||||||
input: statements.join('\n'),
|
|
||||||
encoding: 'utf8',
|
|
||||||
stdio: ['pipe', 'inherit', 'inherit'],
|
|
||||||
})
|
|
||||||
|
|
||||||
console.log('reviews created', created)
|
console.log('reviews created', created)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
const payload = await getPayload({ config })
|
||||||
|
await seedReviews(payload)
|
||||||
process.exit(0)
|
process.exit(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (process.argv[1]?.includes('import-reviews')) {
|
||||||
main().catch((err) => {
|
main().catch((err) => {
|
||||||
console.error(err)
|
console.error(err)
|
||||||
process.exit(1)
|
process.exit(1)
|
||||||
})
|
})
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import 'dotenv/config'
|
|||||||
import { getPayload } from 'payload'
|
import { getPayload } from 'payload'
|
||||||
import { paidDoctors } from './paid-doctors'
|
import { paidDoctors } from './paid-doctors'
|
||||||
import { seedPromotions } from './import-promotions'
|
import { seedPromotions } from './import-promotions'
|
||||||
|
import { seedReviews } from './import-reviews'
|
||||||
import config from '../payload.config'
|
import config from '../payload.config'
|
||||||
|
|
||||||
const translations = {
|
const translations = {
|
||||||
@@ -359,7 +360,7 @@ async function seed() {
|
|||||||
|
|
||||||
const existingReviews = await payload.find({ collection: 'reviews', limit: 1 })
|
const existingReviews = await payload.find({ collection: 'reviews', limit: 1 })
|
||||||
if (!existingReviews.docs.length) {
|
if (!existingReviews.docs.length) {
|
||||||
console.log('Doctor reviews skipped')
|
await seedReviews(payload)
|
||||||
}
|
}
|
||||||
|
|
||||||
await seedPromotions(payload)
|
await seedPromotions(payload)
|
||||||
|
|||||||
@@ -700,6 +700,7 @@ export async function getReviewsPage(params?: {
|
|||||||
page: String(page),
|
page: String(page),
|
||||||
depth: "1",
|
depth: "1",
|
||||||
sort: "-createdAt",
|
sort: "-createdAt",
|
||||||
|
"where[isPublished][equals]": "true",
|
||||||
}),
|
}),
|
||||||
params?.locale,
|
params?.locale,
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user