Skip to content

Commit 75fb635

Browse files
Update automatic images (#196)
* Start refactoring automatic images * Update automatic image display * Default view counts for common case * Show placeholder if view is missing
1 parent edb2834 commit 75fb635

File tree

13 files changed

+723
-678
lines changed

13 files changed

+723
-678
lines changed

app/assets/sass/_misc.scss

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44

55
.app-mammogram-image--placeholder {
66
background-color: black;
7-
width: 75px;
8-
height: 100px;
7+
width: 125px;
8+
// height: 150px;
99

1010
outline: 1px solid rgba(255, 255, 255, 0.2);
1111

@@ -15,6 +15,21 @@
1515
}
1616
}
1717

18+
.app-mammogram-image--missing {
19+
background-color: $color_nhsuk-grey-5;
20+
display: flex;
21+
align-items: center;
22+
justify-content: center;
23+
width: 125px;
24+
height: 187.5px;
25+
border: 2px dashed $color_nhsuk-grey-2;
26+
}
27+
28+
.app-mammogram-image--missing-content {
29+
text-align: center;
30+
font-size: 16px;
31+
}
32+
1833
// Progressive enhancement helpers
1934
.app-js-only {
2035
display: none;

app/lib/generators/event-generator.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ const {
1515
} = require('./special-appointment-generator')
1616
const { generateAppointmentNote } = require('./appointment-note-generator')
1717
const users = require('../../data/users')
18+
const screeningRooms = require('../../data/screening-rooms')
1819

1920
const NOT_SCREENED_REASONS = [
2021
'Recent mammogram at different facility',
@@ -248,6 +249,17 @@ const generateEvent = ({
248249
config: participant.config
249250
})
250251

252+
// Add machine room for hospital locations
253+
if (clinic.location?.id) {
254+
const availableRooms = screeningRooms.filter(
255+
(room) => room.locationId === clinic.location.id
256+
)
257+
if (availableRooms.length > 0) {
258+
const randomRoom = faker.helpers.arrayElement(availableRooms)
259+
event.mammogramData.machineRoom = randomRoom.displayName
260+
}
261+
}
262+
251263
// Pretend some events have previous images requested
252264
event.hasRequestedImages = weighted.select({ true: 0.3, false: 0.7 })
253265

app/lib/utils/strings.js

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -369,6 +369,34 @@ const pluralise = (word, ...args) => {
369369
return pluralizeLib(word, ...args)
370370
}
371371

372+
/**
373+
* Format mammogram view code for display
374+
* Converts "REklund" or "LEklund" to "Right Eklund" or "Left Eklund"
375+
* Leaves standard codes like "RCC", "RMLO", "LCC", "LMLO" unchanged
376+
*
377+
* @param {string} code - View code to format
378+
* @returns {string} Formatted view code
379+
* @example
380+
* formatMammogramViewCode('RCC') // returns 'RCC'
381+
* formatMammogramViewCode('REklund') // returns 'Right Eklund'
382+
* formatMammogramViewCode('Left Eklund') // returns 'Left Eklund' (already formatted)
383+
*/
384+
const formatMammogramViewCode = (code) => {
385+
if (!code || typeof code !== 'string') return code
386+
387+
// Already in the correct format (has a space)
388+
if (code.includes(' ')) return code
389+
390+
// Handle REklund -> Right Eklund
391+
if (code === 'REklund') return 'Right Eklund'
392+
393+
// Handle LEklund -> Left Eklund
394+
if (code === 'LEklund') return 'Left Eklund'
395+
396+
// Return standard codes unchanged (RCC, RMLO, LCC, LMLO, etc)
397+
return code
398+
}
399+
372400
module.exports = {
373401
addIndefiniteArticle,
374402
formatCurrency,
@@ -393,5 +421,6 @@ module.exports = {
393421
stringIncludes,
394422
stringLiteral,
395423
formatPhoneNumber,
396-
pluralise
424+
pluralise,
425+
formatMammogramViewCode
397426
}

app/routes/events.js

Lines changed: 122 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1445,6 +1445,18 @@ module.exports = (router) => {
14451445
isSeedData: false,
14461446
config: eventData?.participant?.config
14471447
})
1448+
1449+
// Add machine room from current screening room
1450+
if (data.currentScreeningRoom) {
1451+
const screeningRooms = data.screeningRooms || []
1452+
const currentRoom = screeningRooms.find(
1453+
(room) => room.id === data.currentScreeningRoom
1454+
)
1455+
if (currentRoom) {
1456+
mammogramData.machineRoom = currentRoom.displayName
1457+
}
1458+
}
1459+
14481460
data.event.mammogramData = mammogramData
14491461
res.locals.event = data.event
14501462
}
@@ -1504,13 +1516,75 @@ module.exports = (router) => {
15041516
(req, res) => {
15051517
const { clinicId, eventId } = req.params
15061518
const data = req.session.data
1519+
1520+
// Save current screening room if not already set
1521+
if (!data.event.mammogramData.machineRoom && data.currentScreeningRoom) {
1522+
const screeningRooms = data.screeningRooms || []
1523+
const currentRoom = screeningRooms.find(
1524+
(room) => room.id === data.currentScreeningRoom
1525+
)
1526+
if (currentRoom) {
1527+
data.event.mammogramData.machineRoom = currentRoom.displayName
1528+
}
1529+
}
1530+
1531+
// Process repeat data from form submission for automatic flow
1532+
// The form fields are named like "event[mammogramData][repeatNeeded-RCC]"
1533+
// and are already in data.event.mammogramData
1534+
const mammogramData = data.event.mammogramData
1535+
if (mammogramData.views) {
1536+
for (const [viewKey, viewData] of Object.entries(mammogramData.views)) {
1537+
const code = viewData.viewShortWithSide
1538+
const imageCount = viewData.images ? viewData.images.length : 0
1539+
1540+
if (imageCount > 1) {
1541+
const repeatNeeded = mammogramData[`repeatNeeded-${code}`]
1542+
const extraImageCount = imageCount - 1
1543+
let repeatCount = 0
1544+
let repeatReasons = null
1545+
1546+
if (repeatNeeded === 'yes') {
1547+
// Single extra image answered "yes"
1548+
repeatCount = 1
1549+
const reasons = mammogramData[`repeatReasons-${code}`]
1550+
if (reasons && reasons.length > 0) {
1551+
repeatReasons = reasons
1552+
}
1553+
} else if (repeatNeeded === 'all-repeats') {
1554+
// All extra images were repeats
1555+
repeatCount = extraImageCount
1556+
const reasons = mammogramData[`repeatReasons-${code}`]
1557+
if (reasons && reasons.length > 0) {
1558+
repeatReasons = reasons
1559+
}
1560+
} else if (repeatNeeded === 'some-repeats') {
1561+
// Some were repeats, some were additional
1562+
repeatCount = parseInt(mammogramData[`repeatCount-${code}`]) || 0
1563+
const reasons = mammogramData[`repeatReasons-${code}`]
1564+
if (reasons && reasons.length > 0) {
1565+
repeatReasons = reasons
1566+
}
1567+
}
1568+
1569+
// Update view with repeat information
1570+
viewData.repeatCount = repeatCount
1571+
viewData.repeatReasons = repeatReasons
1572+
}
1573+
}
1574+
}
1575+
15071576
const isPartialMammography = data.event.mammogramData.isPartialMammography
15081577

15091578
// Check if array includes 'yes' (checkbox format) or equals 'yes' (string format from manual entry)
15101579
const hasPartialMammography = Array.isArray(isPartialMammography)
15111580
? isPartialMammography.includes('yes')
15121581
: isPartialMammography === 'yes'
15131582

1583+
// Initialize workflowStatus if it doesn't exist
1584+
if (!data.event.workflowStatus) {
1585+
data.event.workflowStatus = {}
1586+
}
1587+
15141588
// Mark the workflow step as completed regardless of partial mammography status
15151589
data.event.workflowStatus['take-images'] = 'completed'
15161590

@@ -1521,9 +1595,41 @@ module.exports = (router) => {
15211595

15221596
// Manual imaging routes
15231597

1524-
// Add this section to events.js after the existing imaging routes
1598+
// Handle take-images route - redirect to appropriate page based on state
1599+
router.get('/clinics/:clinicId/events/:eventId/take-images', (req, res) => {
1600+
const { clinicId, eventId } = req.params
1601+
const data = req.session.data
15251602

1526-
// Manual imaging routes
1603+
const isManualImageCollection =
1604+
data.settings?.screening?.manualImageCollection
1605+
const imagesStageCompleted =
1606+
data.event?.workflowStatus?.['take-images'] === 'completed'
1607+
1608+
// If manual flow and images already completed, redirect to details page for editing
1609+
if (
1610+
isManualImageCollection &&
1611+
imagesStageCompleted &&
1612+
data.event?.mammogramData?.isManualEntry
1613+
) {
1614+
return res.redirect(
1615+
`/clinics/${clinicId}/events/${eventId}/images-manual-details`
1616+
)
1617+
}
1618+
1619+
// If automatic flow and images completed, redirect to automatic page
1620+
if (
1621+
!isManualImageCollection &&
1622+
imagesStageCompleted &&
1623+
data.event?.mammogramData
1624+
) {
1625+
return res.redirect(
1626+
`/clinics/${clinicId}/events/${eventId}/images-automatic`
1627+
)
1628+
}
1629+
1630+
// Otherwise render the take-images template which will determine which flow to show
1631+
res.render('events/take-images')
1632+
})
15271633

15281634
// Initialize or edit manual imaging - clears temp or prepopulates from existing data
15291635
router.get('/clinics/:clinicId/events/:eventId/images-manual', (req, res) => {
@@ -1748,7 +1854,12 @@ module.exports = (router) => {
17481854

17491855
// Only add view if count > 0
17501856
if (count > 0) {
1751-
const code = `${config.sideCode}${config.viewType}`
1857+
// For Eklund views, use full form like "Right Eklund" or "Left Eklund"
1858+
// For standard views, use abbreviated form like "RCC" or "LMLO"
1859+
const code =
1860+
config.viewType === 'Eklund'
1861+
? `${config.side.charAt(0).toUpperCase() + config.side.slice(1)} ${config.viewType}`
1862+
: `${config.sideCode}${config.viewType}`
17521863

17531864
// Get repeat data if this view has count > 1
17541865
let repeatCount = 0
@@ -1936,30 +2047,6 @@ module.exports = (router) => {
19362047
const data = req.session.data
19372048
const formData = data.event?.mammogramDataTemp || {}
19382049

1939-
// Normalize checkbox data into count fields (our single source of truth)
1940-
if (formData.uiVersion === 'checkboxes') {
1941-
const rightViews = formData.viewsRightBreast || []
1942-
const leftViews = formData.viewsLeftBreast || []
1943-
1944-
// Set counts to 0 for unchecked views, preserve existing counts for checked views
1945-
const viewTypes = ['CC', 'MLO', 'Eklund']
1946-
1947-
viewTypes.forEach((viewType) => {
1948-
// Right breast
1949-
if (!rightViews.includes(viewType)) {
1950-
formData[`viewsRightBreast${viewType}Count`] = '0'
1951-
}
1952-
// Left breast
1953-
if (!leftViews.includes(viewType)) {
1954-
formData[`viewsLeftBreast${viewType}Count`] = '0'
1955-
}
1956-
})
1957-
1958-
// Delete the checkbox arrays - we only keep the count fields
1959-
delete formData.viewsRightBreast
1960-
delete formData.viewsLeftBreast
1961-
}
1962-
19632050
// Validate at least one view has a count > 0
19642051
const viewTypes = ['CC', 'MLO', 'Eklund']
19652052
const breasts = ['Right', 'Left']
@@ -2029,7 +2116,14 @@ module.exports = (router) => {
20292116

20302117
// Clean up and normalize repeat reasons based on which option was selected
20312118
const formData = data.event?.mammogramDataTemp || {}
2032-
const viewCodes = ['RCC', 'RMLO', 'REklund', 'LCC', 'LMLO', 'LEklund']
2119+
const viewCodes = [
2120+
'RCC',
2121+
'RMLO',
2122+
'Right Eklund',
2123+
'LCC',
2124+
'LMLO',
2125+
'Left Eklund'
2126+
]
20332127

20342128
viewCodes.forEach((code) => {
20352129
const repeatNeeded = formData[`repeatNeeded-${code}`]

0 commit comments

Comments
 (0)