Skip to content

Commit

Permalink
Lots of updates
Browse files Browse the repository at this point in the history
  • Loading branch information
edwardhorsford committed Nov 13, 2024
1 parent 7d1317f commit 39c1e6c
Show file tree
Hide file tree
Showing 27 changed files with 826 additions and 233 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
/dist
/tmp
/public
/app/data/generated/
app/data/generated/
reference

# Runtime data
pids
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ The prototype will generate example data on first run.
- Delete the generated folder to regenerate fresh example data
- You can also run the generator directly with `node app/lib/generate-seed-data.js`
- Uses NHS.UK design system components and patterns
- Use `tree app` to generate a tree diagram of the project

## Security

Expand Down
6 changes: 6 additions & 0 deletions app.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ const express = require('express');
const nunjucks = require('nunjucks');
const sessionInCookie = require('client-sessions');
const sessionInMemory = require('express-session');
const flash = require('connect-flash');

// Run before other code to make sure variables from .env are available
dotenv.config();
Expand Down Expand Up @@ -111,6 +112,9 @@ if (useAutoStoreData === 'true') {
utils.addCheckedFunction(nunjucksAppEnv);
}

// Flash messages
app.use(flash());

// Warn if node_modules folder doesn't exist
function checkFiles() {
const nodeModulesExists = fs.existsSync(path.join(__dirname, '/node_modules'));
Expand Down Expand Up @@ -148,6 +152,8 @@ if (!sessionDataDefaultsFileExists) {
// Local variables
app.use(locals(config));



// View engine
app.set('view engine', 'html');
documentationApp.set('view engine', 'html');
Expand Down
67 changes: 57 additions & 10 deletions app/data/breast-screening-units.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,61 @@
// app/data/breast-screening-units.js
const generateId = require('../lib/utils/id-generator');

module.exports = [
{
id: "f66f2a7d-99a8-4793-8371-3d075e1a7c54",
id: "m5ekcxvu", // Must be hardcoded so it matches generated data
name: "Oxford Breast Imaging Centre",
address: `Surgery and Diagnostics Centre
Churchill Hospital
Old Road,
Headington
Oxford
OX3 7LE`,
phoneNumber: "01865 235621",
abbreviation: "OXF"
address: {
line1: "Surgery and Diagnostics Centre",
line2: "Churchill Hospital",
line3: "Old Road",
line4: "Headington",
city: "Oxford",
postcode: "OX3 7LE"
},
phoneNumber: "01865235621",
abbreviation: "OXF",
locations: [
{
id: generateId(),
name: "Churchill Hospital breast unit",
type: "hospital",
isMainSite: true,
address: {
line1: "Surgery and Diagnostics Centre",
line2: "Churchill Hospital",
line3: "Old Road",
line4: "Headington",
city: "Oxford",
postcode: "OX3 7LE"
}
},
// {
// id: generateId(),
// name: "Horton Hospital breast unit",
// type: "hospital",
// isMainSite: false,
// address: {
// line1: "Horton General Hospital",
// line2: "Oxford Road",
// city: "Banbury",
// postcode: "OX16 9AL"
// }
// },
{
id: generateId(),
name: "Mobile Unit WX71 HCP",
type: "mobile_unit",
isMainSite: false,
registration: "WX71 HCP"
},
{
id: generateId(),
name: "Mobile Unit WX71 HCR",
type: "mobile_unit",
isMainSite: false,
registration: "WX71 HCR"
}
]
}
]
];
41 changes: 36 additions & 5 deletions app/data/session-data-defaults.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,48 @@ const breastScreeningUnits = require("./breast-screening-units");
const ethnicities = require("./ethnicities");
const path = require('path');
const fs = require('fs');
const dayjs = require('dayjs');

// Check if generated data folder exists
// Check if generated data folder exists and create if needed
const generatedDataPath = path.join(__dirname, 'generated');
if (!fs.existsSync(generatedDataPath)) {
fs.mkdirSync(generatedDataPath);
}

let participants = [];
let clinics = [];
let events = [];
let generationInfo = {
generatedAt: 'Never',
stats: { participants: 0, clinics: 0, events: 0 }
};

// Generate data if folder doesn't exist
if (!fs.existsSync(generatedDataPath)) {
console.log('Generating seed data...');
// Check if we need to regenerate data
const generationInfoPath = path.join(generatedDataPath, 'generation-info.json');
let needsRegeneration = true;

if (fs.existsSync(generationInfoPath)) {
try {
generationInfo = JSON.parse(fs.readFileSync(generationInfoPath));
const generatedDate = dayjs(generationInfo.generatedAt).startOf('day');
const today = dayjs().startOf('day');
needsRegeneration = !generatedDate.isSame(today, 'day');
} catch (err) {
console.warn('Error reading generation info:', err);
needsRegeneration = true;
}
}

// Generate or load data
if (needsRegeneration) {
console.log('Generating new seed data...');
require('../lib/generate-seed-data.js');

// Save generation info
fs.writeFileSync(
generationInfoPath,
JSON.stringify({ generatedAt: new Date().toISOString() })
);
}

// Load generated data
Expand All @@ -34,5 +64,6 @@ module.exports = {
breastScreeningUnits,
participants,
clinics,
events
events,
generationInfo
};
17 changes: 15 additions & 2 deletions app/lib/generate-seed-data.js
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,14 @@ const generateData = async () => {
writeData('participants.json', { participants });
writeData('clinics.json', { clinics });
writeData('events.json', { events });
writeData('generation-info.json', {
generatedAt: new Date().toISOString(),
stats: {
participants: participants.length,
clinics: clinics.length,
events: events.length
}
});

console.log('\nData generation complete!');
console.log(`Generated:`);
Expand All @@ -102,5 +110,10 @@ const generateData = async () => {
console.log(`- ${events.length} events`);
};

// Run the generator
generateData().catch(console.error);
// Export the function instead of running it immediately
module.exports = generateData;

// Only run if this file is being run directly
if (require.main === module) {
generateData().catch(console.error);
}
90 changes: 51 additions & 39 deletions app/lib/generators/clinic-generator.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,7 @@

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 dayjs = require('dayjs');

const generateTimeSlots = (date, config) => {
const slots = [];
Expand All @@ -29,29 +24,66 @@ const generateTimeSlots = (date, config) => {
return slots;
};

const determineClinicStatus = (date) => {
const now = dayjs();
const clinicDate = dayjs(date);
const clinicStart = clinicDate.hour(8); // Assume clinic starts at 8am
const clinicEnd = clinicDate.hour(17); // Assume clinic ends at 5pm

if (clinicDate.isBefore(now, 'day')) {
return 'closed';
} else if (clinicDate.isAfter(now, 'day')) {
return 'scheduled';
} else {
// Today - check time
if (now.isBefore(clinicStart)) {
return 'scheduled';
} else if (now.isAfter(clinicEnd)) {
return 'closed';
} else {
return 'in_progress';
}
}
};

const generateMobileSiteName = () => {
const sites = [
"Tesco Extra Banbury",
"Witney Community Hospital",
"Thame Community Hospital",
"Bicester Community Hospital",
"Sainsbury's Kidlington",
"Carterton Health Centre",
"Wantage Community Hospital",
"Tesco Faringdon",
"Didcot Civic Hall",
"Chipping Norton Health Centre"
];

return faker.helpers.arrayElement(sites);
};

// 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)
);

// Randomly select locations from available ones
const selectedLocations = faker.helpers.arrayElements(
breastScreeningUnit.locations,
{ min: numberOfClinics, max: numberOfClinics }
);

return selectedLocations.map(location => {
return {
id: generateId(),
date: date.toISOString().split('T')[0],
breastScreeningUnitId: breastScreeningUnit.id,
clinicType,
location: clinicType === 'hospital' ?
breastScreeningUnit.address :
generateMobileLocation(breastScreeningUnit),
clinicType: location.type,
locationId: location.id,
siteName: location.type === 'mobile_unit' ? generateMobileSiteName() : null,
slots: generateTimeSlots(date, config),
status: date < new Date() ? 'completed' : 'scheduled',
status: determineClinicStatus(date),
staffing: {
mamographers: [],
radiologists: [],
Expand All @@ -64,26 +96,6 @@ const generateClinicsForBSU = ({ date, breastScreeningUnit, config }) => {
});
};

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
};
Loading

0 comments on commit 39c1e6c

Please sign in to comment.