Skip to content

Commit d199478

Browse files
authored
Update data fetching method for the features tracker (#684)
1 parent 828350f commit d199478

1 file changed

Lines changed: 50 additions & 19 deletions

File tree

overrides/assets/javascript/gtfs-tracker.js

Lines changed: 50 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,18 @@
11
(function () {
22
const CSV_URL = 'https://docs.google.com/spreadsheets/d/e/2PACX-1vSu9_3lyF9caXrDdlGCtO1Bg17Uhkh_L9l-REYkYVUINvrEEaVwrx1mSZ--_iKAGcJ2x8bFBzYHVU74/pub?output=csv';
33
const FIELDS_URL = 'https://docs.google.com/spreadsheets/d/e/2PACX-1vSu9_3lyF9caXrDdlGCtO1Bg17Uhkh_L9l-REYkYVUINvrEEaVwrx1mSZ--_iKAGcJ2x8bFBzYHVU74/pub?gid=1583091937&single=true&output=csv';
4+
const CONSUMERS_URL = 'https://docs.google.com/spreadsheets/d/e/2PACX-1vSu9_3lyF9caXrDdlGCtO1Bg17Uhkh_L9l-REYkYVUINvrEEaVwrx1mSZ--_iKAGcJ2x8bFBzYHVU74/pub?gid=1998786437&single=true&output=csv';
45

5-
const CONSUMERS = [
6-
{ id: 'google', label: 'Google' },
7-
{ id: 'transitapp', label: 'Transit' },
8-
{ id: 'motis', label: 'MOTIS' },
9-
{ id: 'OpenTripPlanner', label: 'OTP' },
10-
{ id: 'aubin', label: 'Aubin' },
11-
];
12-
6+
let CONSUMERS = [];
137
let knownFields = [];
148

159
const ICONS = {
1610
check: `<svg class="gft-icon icon-success" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"/></svg>`,
1711
x: `<svg class="gft-icon icon-danger" viewBox="0 0 24 24"><path d="M19,6.41L17.59,5L12,10.59L6.41,5L5,6.41L10.59,12L5,17.59L6.41,19L12,13.41L17.59,19L19,17.59L13.41,12L19,6.41Z"/></svg>`,
18-
clock: `<svg class="gft-icon icon-warning" viewBox="0 0 24 24"><path d="M12,20A8,8 0 0,0 20,12A8,8 0 0,0 12,4A8,8 0 0,0 12,20M12,2A10,10 0 0,1 22,12A10,10 0 0,1 12,22C6.47,22 2,17.53 2,12A10,10 0 0,1 12,2M12.5,7V12.25L17,14.92L16.25,16.15L11,13V7H12.5Z"/></svg>`,
12+
clock: `<svg class="gft-icon icon-warning" viewBox="0 0 24 24"><path d="M12,20A8,8 0 0,0 20,12A8,8 0 0,0 12,4A8,8 0 0,0 12,20M12,2A10,10 0 0,1 22,12A10,10 0 0,1 12,22C6.47,22 2,17.53 2,12A10,10 0 0,1 12,2Z"/></svg>`,
1913
partial: `<svg class="gft-icon" viewBox="0 0 24 24" fill="#FFE296"><path d="M12,2A10,10 0 0,0 12,22A10,10 0 0,0 12,2M12,4V20A8,8 0 0,1 12,4Z"/></svg>`,
2014
planned: `<svg class="gft-icon icon-info" viewBox="0 0 24 24"><path d="M4,15V9H12V4.16L19.84,12L12,19.84V15H4Z"/></svg>`,
21-
na: `<svg class="gft-icon" viewBox="0 0 24 24" fill="#9aa0a6"><path d="M12 2C6.47 2 2 6.47 2 12s4.47 10 10 10 10-4.47 10-10S17.53 2 12 2zm0 18c-4.41 0-8-3.59-8-8 0-1.86.64-3.57 1.71-4.93L16.93 18.29C15.57 19.36 13.86 20 12 20zm6.29-4.71L7.71 5.71C9.07 4.64 10.78 4 12 4c4.41 0 8 3.59 8 8 0 1.86-.64 3.57-1.71 4.93z"/></svg>`
15+
na: `<svg class="gft-icon" viewBox="0 0 24 24" fill="#9aa0a6"><path d="M12 2C6.47 2 2 6.47 2 12s4.47 10 10 10 10-4.47 10-10S17.53 2 12 2zm0 18c-4.41 0-8-3.59-8-8 0-1.86.64-3.57 1.71-4.93L16.93 13.5l-1.41 1.41L4.5 3.5l1.41-1.41 12.02 12.02z"/></svg>`,
2216
};
2317

2418
function getStatusMeta(raw) {
@@ -77,20 +71,22 @@
7771
const tableRows = catRows.map(f => {
7872
const cells = CONSUMERS.map(c => {
7973
const s = f.support[c.id];
80-
const m = getStatusMeta(s.rawStatus);
81-
const hasDet = s.details && s.details.trim().length > 0;
74+
const rawStatus = s ? s.rawStatus : '';
75+
const details = s ? s.details : '';
76+
const m = getStatusMeta(rawStatus);
77+
const hasDet = details && details.trim().length > 0;
8278
return `<td class="gft-cell">
8379
<div class="gft-badge ${m.cls}">
8480
<span class="gft-icon-anchor">${m.icon}</span>
8581
<span class="gft-label">${m.label}</span>
86-
${hasDet ? `<i class="gft-info-btn" onclick="showGftDetails('${f.name.replace(/'/g, "\\'")}${c.label}', \`${s.details.replace(/`/g, '\\`').replace(/\\/g, '\\\\')}\`)">i</i>` : ''}
82+
${hasDet ? `<i class="gft-info-btn" onclick="showGftDetails('${f.name.replace(/'/g, "\\'")}${c.label}', \`${details.replace(/`/g, '\\`').replace(/\\/g, '\\\\')}\`)">i</i>` : ''}
8783
</div>
8884
</td>`;
8985
}).join('');
9086
return `<tr class="gft-row"><td class="gft-feature-name" title="${f.name}">${f.name}</td>${cells}</tr>`;
9187
}).join('');
9288

93-
el.innerHTML = `
89+
el.innerHTML = `
9490
<div class="gft-wrapper">
9591
<div class="gft-table-wrap">
9692
<table class="gft-table" style="width:100%; border-collapse:collapse; font-family:sans-serif;">
@@ -100,7 +96,7 @@ el.innerHTML = `
10096
${CONSUMERS.map(c => `
10197
<th style="border:1px solid #eee; padding:8px;">
10298
<div class="gft-consumer-header" style="display:flex; flex-direction:column; align-items:center; gap:4px;">
103-
<img src="/assets/${c.id}.png" alt="" style="height:16px; width:auto;" onerror="this.style.display='none'">
99+
${c.logo ? `<img src="${c.logo}" alt="${c.label} logo" style="height:16px; width:auto;" onerror="this.style.display='none'">` : ''}
104100
<span style="font-size:11px;">${c.label}</span>
105101
</div>
106102
</th>`).join('')}
@@ -129,19 +125,54 @@ el.innerHTML = `
129125

130126
Promise.all([
131127
fetch(FIELDS_URL).then(r => r.text()),
132-
fetch(CSV_URL).then(r => r.text())
133-
]).then(([fieldsText, dataText]) => {
128+
fetch(CSV_URL).then(r => r.text()),
129+
fetch(CONSUMERS_URL).then(r => r.text())
130+
]).then(([fieldsText, dataText, consumersText]) => {
134131
const fieldRows = parseCSV(fieldsText);
135132
knownFields = fieldRows.slice(1).map(r => r[0]).filter(name => name && name.length > 2);
136133

137134
const rows = parseCSV(dataText);
138135
const headers = rows[0];
136+
137+
const discoveredConsumers = [];
138+
headers.forEach(header => {
139+
if (header && header.endsWith('_use')) {
140+
const id = header.replace('_use', '');
141+
const defaultLabel = id.charAt(0).toUpperCase() + id.slice(1);
142+
discoveredConsumers.push({ id, label: defaultLabel, logo: '' });
143+
}
144+
});
145+
146+
if (!discoveredConsumers || discoveredConsumers.length === 0) {
147+
console.warn("No consumers discovered from spreadsheet headers");
148+
return;
149+
}
150+
151+
const consumerRows = parseCSV(consumersText);
152+
const cHeaders = consumerRows[0];
153+
const consumerIdIdx = cHeaders.indexOf('consumer');
154+
const logoUrlIdx = cHeaders.indexOf('logo_url');
155+
const labelIdx = cHeaders.indexOf('consumer_public_label');
156+
157+
consumerRows.slice(1).forEach(r => {
158+
const rawId = (r[consumerIdIdx] || '').trim().toLowerCase();
159+
const target = discoveredConsumers.find(c => c.id.toLowerCase() === rawId);
160+
if (!target) return;
161+
if (logoUrlIdx !== -1 && r[logoUrlIdx]) target.logo = r[logoUrlIdx].trim();
162+
if (labelIdx !== -1 && r[labelIdx]) target.label = r[labelIdx].trim();
163+
});
164+
165+
CONSUMERS = discoveredConsumers;
166+
139167
const data = rows.slice(1).filter(r => r[0]).map(r => {
140168
const support = {};
141169
CONSUMERS.forEach(c => {
142170
const useIdx = headers.indexOf(c.id + '_use');
143171
const detIdx = headers.indexOf(c.id + '_details');
144-
support[c.id] = { rawStatus: r[useIdx] || '', details: r[detIdx] || '' };
172+
support[c.id] = {
173+
rawStatus: useIdx !== -1 ? r[useIdx] || '' : '',
174+
details: detIdx !== -1 ? r[detIdx] || '' : ''
175+
};
145176
});
146177
return { name: r[0], category: r[1], support };
147178
});
@@ -151,4 +182,4 @@ el.innerHTML = `
151182
}
152183

153184
if (typeof document$ !== 'undefined') document$.subscribe(boot); else document.addEventListener('DOMContentLoaded', boot);
154-
})();
185+
})();

0 commit comments

Comments
 (0)