-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
97cd5a4
commit a5049de
Showing
16 changed files
with
991 additions
and
40 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,14 @@ | ||
module.exports = [ | ||
{ | ||
id: "f66f2a7d-99a8-4793-8371-3d075e1a7c54", | ||
name: "Oxford Breast screening unit", | ||
name: "Oxford Breast Imaging Centre", | ||
address: `Surgery and Diagnostics Centre | ||
Churchill Hospital | ||
Old Road, | ||
Headington | ||
Oxford | ||
OX3 7LE`, | ||
phoneNumber: "01865 235621", | ||
abbreviation: "OXF" | ||
} | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
module.exports = { | ||
"Asian or Asian British": [ | ||
"Bangladeshi", | ||
"Chinese", | ||
"Indian", | ||
"Pakistani", | ||
"Another Asian background" | ||
], | ||
"Black, African, Black British or Caribbean": [ | ||
"African", | ||
"Caribbean", | ||
"Another black background" | ||
], | ||
"Mixed or multiple ethnic groups": [ | ||
"Asian and White", | ||
"Black African and White", | ||
"Black Caribbean and White", | ||
"Another mixed background", | ||
], | ||
"White": [ | ||
"British, English, Northern Irish, Scottish, or Welsh", | ||
"Irish", | ||
"Irish Traveller or Gypsy", | ||
"Another White background" | ||
], | ||
"Another ethnic group": [ | ||
"Arab", | ||
"Another ethnic background" | ||
] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,10 +1,27 @@ | ||
const users = require("./users") | ||
const breastScreeningUnits = require("./breast-screening-units.js") | ||
// app/data/session-data-defaults.js | ||
|
||
const users = require("./users"); | ||
const breastScreeningUnits = require("./breast-screening-units"); | ||
const ethnicities = require("./ethnicities"); | ||
|
||
// Load generated data | ||
let participants = []; | ||
let clinics = []; | ||
let events = []; | ||
|
||
try { | ||
participants = require("./generated/participants.json").participants; | ||
clinics = require("./generated/clinics.json").clinics; | ||
events = require("./generated/events.json").events; | ||
} catch (err) { | ||
console.warn('Generated data files not found. Please run the data generator first.'); | ||
} | ||
|
||
module.exports = { | ||
users, | ||
currentUser: users[0], | ||
breastScreeningUnits | ||
} | ||
breastScreeningUnits, | ||
participants, | ||
clinics, | ||
events | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
// app/lib/generate-seed-data.js | ||
|
||
const { faker } = require('@faker-js/faker'); | ||
const weighted = require('weighted'); | ||
const fs = require('fs'); | ||
const path = require('path'); | ||
|
||
const { generateParticipant } = require('./generators/participant-generator'); | ||
const { generateClinicsForBSU } = require('./generators/clinic-generator'); | ||
const { generateEvent } = require('./generators/event-generator'); | ||
|
||
// Load existing data | ||
const breastScreeningUnits = require('../data/breast-screening-units'); | ||
const ethnicities = require('../data/ethnicities'); | ||
|
||
const CONFIG = { | ||
numberOfParticipants: 1000, | ||
outputPath: path.join(__dirname, '../data/generated'), | ||
clinicDefaults: { | ||
slotsPerDay: 32, | ||
daysToGenerate: 5, | ||
startTime: '09:00', | ||
endTime: '17:00', | ||
slotDurationMinutes: 8 | ||
}, | ||
eventOutcomes: { | ||
'clear': 0.95, | ||
'needs_further_tests': 0.04, | ||
'cancer_detected': 0.01 | ||
} | ||
}; | ||
|
||
const generateData = async () => { | ||
// Create output directory if it doesn't exist | ||
if (!fs.existsSync(CONFIG.outputPath)) { | ||
fs.mkdirSync(CONFIG.outputPath, { recursive: true }); | ||
} | ||
|
||
// Generate base data | ||
console.log('Generating participants...'); | ||
const participants = Array.from({ length: CONFIG.numberOfParticipants }, () => | ||
generateParticipant({ ethnicities, breastScreeningUnits }) | ||
); | ||
|
||
console.log('Generating clinics and events...'); | ||
const clinics = []; | ||
const events = []; | ||
|
||
// Calculate date range | ||
const startDate = new Date(); | ||
startDate.setDate(startDate.getDate() - 3); | ||
|
||
for (let i = 0; i < CONFIG.clinicDefaults.daysToGenerate; i++) { | ||
const clinicDate = new Date(startDate); | ||
clinicDate.setDate(clinicDate.getDate() + i); | ||
|
||
// Generate clinics for each BSU (currently just Oxford) | ||
breastScreeningUnits.forEach(unit => { | ||
const newClinics = generateClinicsForBSU({ | ||
date: clinicDate, | ||
breastScreeningUnit: unit, | ||
config: CONFIG.clinicDefaults | ||
}); | ||
|
||
// Generate events for each clinic | ||
newClinics.forEach(clinic => { | ||
const clinicEvents = clinic.slots | ||
.filter(slot => Math.random() > 0.2) | ||
.map(slot => { | ||
const participant = faker.helpers.arrayElement(participants); | ||
return generateEvent({ | ||
slot, | ||
participant, | ||
clinic, | ||
outcomeWeights: CONFIG.eventOutcomes | ||
}); | ||
}); | ||
|
||
events.push(...clinicEvents); | ||
}); | ||
|
||
clinics.push(...newClinics); | ||
}); | ||
} | ||
|
||
// Write generated data to files | ||
const writeData = (filename, data) => { | ||
fs.writeFileSync( | ||
path.join(CONFIG.outputPath, filename), | ||
JSON.stringify(data, null, 2) | ||
); | ||
}; | ||
|
||
writeData('participants.json', { participants }); | ||
writeData('clinics.json', { clinics }); | ||
writeData('events.json', { events }); | ||
|
||
console.log('\nData generation complete!'); | ||
console.log(`Generated:`); | ||
console.log(`- ${participants.length} participants`); | ||
console.log(`- ${clinics.length} clinics`); | ||
console.log(`- ${events.length} events`); | ||
}; | ||
|
||
// Run the generator | ||
generateData().catch(console.error); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
// app/lib/generators/clinic-generator.js | ||
|
||
const { faker } = require('@faker-js/faker'); | ||
const generateId = require('../utils/id-generator'); | ||
const weighted = require('weighted'); | ||
|
||
const CLINIC_TYPES = [ | ||
{ type: 'hospital', weight: 0.7 }, | ||
{ type: 'mobile_unit', weight: 0.3 } | ||
]; | ||
|
||
const generateTimeSlots = (date, config) => { | ||
const slots = []; | ||
const startTime = new Date(`${date.toISOString().split('T')[0]}T${config.startTime}`); | ||
const endTime = new Date(`${date.toISOString().split('T')[0]}T${config.endTime}`); | ||
|
||
let currentTime = new Date(startTime); | ||
while (currentTime < endTime) { | ||
const slotId = generateId(); | ||
slots.push({ | ||
id: slotId, | ||
dateTime: new Date(currentTime).toISOString(), | ||
type: 'screening', | ||
capacity: 2, | ||
bookedCount: 0 | ||
}); | ||
currentTime.setMinutes(currentTime.getMinutes() + config.slotDurationMinutes); | ||
} | ||
return slots; | ||
}; | ||
|
||
// Generate multiple clinics for a BSU on a given day | ||
const generateClinicsForBSU = ({ date, breastScreeningUnit, config }) => { | ||
// Determine number of clinics for this BSU today (1-2) | ||
const numberOfClinics = Math.random() < 0.3 ? 2 : 1; | ||
|
||
return Array.from({ length: numberOfClinics }, () => { | ||
// If this is the second clinic for the day, make it more likely to be a mobile unit | ||
const isSecondClinic = numberOfClinics === 2; | ||
const clinicType = weighted.select( | ||
CLINIC_TYPES.map(t => t.type), | ||
CLINIC_TYPES.map(t => isSecondClinic ? (t.type === 'mobile_unit' ? 0.7 : 0.3) : t.weight) | ||
); | ||
|
||
return { | ||
id: generateId(), | ||
date: date.toISOString().split('T')[0], | ||
breastScreeningUnitId: breastScreeningUnit.id, | ||
clinicType, | ||
location: clinicType === 'hospital' ? | ||
breastScreeningUnit.address : | ||
generateMobileLocation(breastScreeningUnit), | ||
slots: generateTimeSlots(date, config), | ||
status: date < new Date() ? 'completed' : 'scheduled', | ||
staffing: { | ||
mamographers: [], | ||
radiologists: [], | ||
support: [] | ||
}, | ||
targetBookings: 60, | ||
targetAttendance: 40, | ||
notes: null | ||
}; | ||
}); | ||
}; | ||
|
||
const generateMobileLocation = (bsu) => { | ||
const locations = [ | ||
'Community Centre', | ||
'Health Centre', | ||
'Leisure Centre', | ||
'Shopping Centre Car Park', | ||
'Supermarket Car Park' | ||
]; | ||
|
||
const location = faker.helpers.arrayElement(locations); | ||
return { | ||
name: `${faker.location.city()} ${location}`, | ||
address: { | ||
line1: faker.location.streetAddress(), | ||
city: faker.location.city(), | ||
postcode: faker.location.zipCode('??# #??') | ||
} | ||
}; | ||
}; | ||
|
||
module.exports = { | ||
generateClinicsForBSU | ||
}; |
Oops, something went wrong.