From ed90f640769d33f37c6f177d42bc339db355318d Mon Sep 17 00:00:00 2001 From: inportb Date: Sun, 24 Mar 2024 06:32:43 -0500 Subject: [PATCH] Fix unintended `.gitignore` inheritance in `frontend/.gitignore` --- frontend/.gitignore | 3 + frontend/src/lib/Navbar.svelte | 54 +++++++++ frontend/src/lib/backend.js | 36 ++++++ frontend/src/lib/index.js | 1 + frontend/src/lib/stores.js | 18 +++ frontend/src/lib/util.js | 194 +++++++++++++++++++++++++++++++++ 6 files changed, 306 insertions(+) create mode 100644 frontend/src/lib/Navbar.svelte create mode 100644 frontend/src/lib/backend.js create mode 100644 frontend/src/lib/index.js create mode 100644 frontend/src/lib/stores.js create mode 100644 frontend/src/lib/util.js diff --git a/frontend/.gitignore b/frontend/.gitignore index 6635cf5..a91aa0f 100644 --- a/frontend/.gitignore +++ b/frontend/.gitignore @@ -8,3 +8,6 @@ node_modules !.env.example vite.config.js.timestamp-* vite.config.ts.timestamp-* + +# Override +!/src/* diff --git a/frontend/src/lib/Navbar.svelte b/frontend/src/lib/Navbar.svelte new file mode 100644 index 0000000..3386be6 --- /dev/null +++ b/frontend/src/lib/Navbar.svelte @@ -0,0 +1,54 @@ + + + + + diff --git a/frontend/src/lib/backend.js b/frontend/src/lib/backend.js new file mode 100644 index 0000000..3e71356 --- /dev/null +++ b/frontend/src/lib/backend.js @@ -0,0 +1,36 @@ +export async function get_api_appointments({ fetch, clinics = [], date = 'T' } = {}) { + if(clinics.constructor === Array) clinics = clinics.map(x => x.replace(/^\s+|\s+$/g, '').replace(/\s+/, ' ')).filter(x => x).join('^').replace(/\//g, '|'); + else clinics = clinics.replace(/^\s+|\s+$/g, '').replace(/\s+/, ' ').replace(/\//g, '|'); + return await (await (fetch || window.fetch)('/api/appointments/' + clinics + '/' + date)).json(); +} + +export async function get_api_lookup({ fetch, query, ordinal, force = false } = {}) { + if(query) { + if(ordinal === undefined) return await (await (fetch || window.fetch)('/api/lookup/' + encodeURIComponent(query))).json(); + else return (await (fetch || window.fetch)('/api/lookup/' + encodeURIComponent(query) + '/' + (ordinal || '0') + (force ? '/force' : ''))).text() + } +} + +export async function get_api_rcrs_patients({ fetch, alpha = 'T-30', omega = 'N' } = {}) { + return await (await (fetch || window.fetch)('/api/rcrs/patients/' + alpha + '/' + omega)).json(); +} + +export async function get_api_rcrs_tumors({ fetch, alpha = 'T-30', omega = 'N' } = {}) { + return await (await (fetch || window.fetch)('/api/rcrs/tumors/' + alpha + '/' + omega)).json(); +} + +export async function get_api_measurements({ fetch, mrn, alpha = 'T-30', omega = 'N' } = {}) { + return await (await (fetch || window.fetch)('/api/measurements/' + mrn + '/' + alpha + '/' + omega)).json(); +} + +export async function get_api_labs({ fetch, mrn, alpha = 'T-30', omega = 'N' } = {}) { + return await (await (fetch || window.fetch)('/api/labs/' + mrn + '/' + alpha + '/' + omega)).json(); +} + +export async function get_api_notes({ fetch, mrn, alpha = 'T-30', omega = 'N' } = {}) { + return await (await (fetch || window.fetch)('/api/notes/' + mrn + '/' + alpha + '/' + omega)).json(); +} + +export async function get_api_orders({ fetch, mrn, alpha = 'T-30', omega = 'N' } = {}) { + return await (await (fetch || window.fetch)('/api/orders/' + mrn + '/' + alpha + '/' + omega)).json(); +} diff --git a/frontend/src/lib/index.js b/frontend/src/lib/index.js new file mode 100644 index 0000000..856f2b6 --- /dev/null +++ b/frontend/src/lib/index.js @@ -0,0 +1 @@ +// place files you want to import through the `$lib` alias in this folder. diff --git a/frontend/src/lib/stores.js b/frontend/src/lib/stores.js new file mode 100644 index 0000000..a3729d8 --- /dev/null +++ b/frontend/src/lib/stores.js @@ -0,0 +1,18 @@ +import { get, writable } from 'svelte/store'; + +export const navlinks = writable([]); +navlinks.push = function(item) { + navlinks.pop(item); + navlinks.update(value => (value.unshift(item), value)); + return item; +}; +navlinks.pop = function(item) { + navlinks.update(value => { + if(item !== undefined) { + let idx = value.indexOf(item); + if(idx >= 0) value.splice(idx, 1); + } else value.shift(); + return value; + }); + return item; +} diff --git a/frontend/src/lib/util.js b/frontend/src/lib/util.js new file mode 100644 index 0000000..d4fcaf2 --- /dev/null +++ b/frontend/src/lib/util.js @@ -0,0 +1,194 @@ +export const comp = (...fs) => x0 => fs.reduceRight((x, f) => f(x), x0); +export const flow = (...fs) => x0 => fs.reduce((x, f) => f(x), x0); +export const pipe = (x0, ...fs) => fs.reduce((x, f) => f(x), x0); +export const aflow = (f0, ...fs) => async (...args) => fs.reduce((x, f) => f(x), await f0(...args)); + +export function uniq(xs) { + var seen = {}; + return xs.filter(x => seen.hasOwnProperty(x) ? false : (seen[x] = true)); +} + +export function groupBy(xs, key) { + return xs.reduce(function(rv, x) { + var v = key instanceof Function ? key(x) : x[key]; + (rv[v] = rv[v] || []).push(x); + return rv; + }, {}); +} + +export function groupByArray(xs, key) { + var mapping = {}; + return xs.reduce(function(rv, x) { + var v = key instanceof Function ? key(x) : x[key]; + var el = mapping[v]; + if(el) el.values.push(x); + else rv.push(mapping[v] = { key: v, values: [x] }); + return rv; + }, []); +} + +export function pivotByArray(xs, key, reducer) { + var groups = groupByArray(xs, key); + groups.forEach(function(group) { + group.aggregate = group.values.reduce(reducer, {}); + }); + return groups; +} + +export function quantile_sorted(arr_sorted, quantile) { + var pos = (arr_sorted.length - 1) * quantile, base = Math.floor(pos), rest = pos - base; + return arr_sorted[base + 1] !== undefined ? arr_sorted[base] + rest * (arr_sorted[base + 1] - arr_sorted[base]) : arr_sorted[base]; +} + +export function strtr(s, a, b) { + var res = ''; + for(var i = 0; i < s.length; ++i) { + var j = a.indexOf(s.charAt(i)); + res += j >= 0 ? b.charAt(j) : s.charAt(i); + } + return res; +} + +export function strtr_unscramble(name) { + return name.length > 0 ? (name.charAt(0) + strtr(name.substring(1), 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'LKJIHGFEDCBAZYXWVUTSRQPONM')) : name; +} + +export function strHashCode(str) { + var hash = 0; + for(var i = 0; i < str.length; ++i) hash = str.charCodeAt(i) + ((hash << 5) - hash); + return hash & hash; // convert to 32 bit +} + +export function strHashJenkins(str) { + for(var hash = 0, i = str.length; i--;) hash += str.charCodeAt(i), hash += hash << 10, hash ^= hash >> 6; + hash += hash << 3; + hash ^= hash >> 11; + return (hash + (hash << 15) & 4294967295) >>> 0 +} + +export function strHashHex(str) { + var hash = strHashJenkins(str), color = '#'; + for(var i = 0; i < 3; ++i) color += ('00' + ((hash >> (i * 8)) & 0xFF).toString(16)).slice(-2); + return color; +} + +export function strHashHSL(str, lightness='50%') { + var hash = strHashJenkins(str); + return 'hsl(' + (hash%360) + ',' + (hash%100) + '%,' + lightness + ')'; +} + +export function datetime_datestr(dt) { + return dt ? dt.toLocaleDateString('sv-SE') : undefined; +} + +export function datetime_timestr(dt) { + if(dt) { + const res = dt.toLocaleTimeString('en-GB'); + return res.endsWith(':00') ? res.substring(0, 5) : res; + } +} + +export function datetime_dtstr(dt) { + if(dt) { + const res = dt.toLocaleTimeString('en-GB'); + return dt.toLocaleDateString('sv-SE') + ' ' + (res.endsWith(':00') ? res.substring(0, 5) : res); + } +} + +export function strftime_vista(date) { + return 10000*(date.getFullYear() - 1700) + 100*(date.getMonth() + 1) + date.getDate() + date.getHours()/100 + date.getMinutes()/10000 + date.getSeconds()/1000000 + date.getMilliseconds()/1000000000; +} + +export function strfdate_vista(date) { + return 10000*(date.getFullYear() - 1700) + 100*(date.getMonth() + 1) + date.getDate(); +} + +export function strptime_vista(s) { + s = +s; + var date = Math.floor(s), time = s - date; + return new Date(Math.floor(date/10000) + 1700, (Math.floor(date/100) + '').slice(-2) - 1, (date + '').slice(-2), Math.floor(time*100), (Math.floor(time*10000) + '').slice(-2), (Math.floor(time*1000000) + '').slice(-2), (Math.floor(time*1000000000) + '').slice(-3)); +} + +export function escapeHTML(unsafe) { + return unsafe.replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"').replace(/'/g, '''); +} + +export function escapeRegExp(unsafe) { + return unsafe.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); +} + +export function debounce(fn, delay) { + var clock = null; + return function() { + window.clearTimeout(clock); + var self = this, args = arguments; + clock = window.setTimeout(function() { fn.apply(self, args) }, delay); + } +} + +export function isInViewport(el, entirely = false) { + const rect = el.getBoundingClientRect(); + return entirely ? ( + (rect.top >= 0) && + (rect.left >= 0) && + (rect.bottom <= (window.innerHeight || document.documentElement.clientHeight)) && + (rect.right <= (window.innerWidth || document.documentElement.clientWidth)) + ) : ( + (rect.bottom >= 0) && + (rect.right >= 0) && + (rect.top <= (window.innerHeight || document.documentElement.clientHeight)) && + (rect.left <= (window.innerWidth || document.documentElement.clientWidth)) + ); +} + +export function filter_pattern(query, between, flags) { + if((query = query.replace(/^\s+|\s+$/g, '')).length > 0) { + if(query.startsWith('/')) { + if(query.length > 1) { + var m = /^\/(.*)\/([a-z]*)$/.exec(query); + return m ? new RegExp(m[1], m[2]) : new RegExp(query.substring(1), flags || 'gims'); + } + } else { + query = query.split('|').map(x => filter_part(x, between || 5)).filter(x => x); + if(query.length > 0) return new RegExp(query.join('|'), flags || 'gims'); + } + } +} + +function filter_part(query, between) { + if((query = query.replace(/^\s+|\s+$/g, '')).length > 0) { + if(query.startsWith('"')) { + query = query.substring(1, query.length - ((query.length > 1) && (query.endsWith('"')) ? 1 : 0)); + if(query.length > 0) return query.split(/\s+/).map(x => x.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')).join('\\s*'); + } else return '(?:' + query.split(/\s+/).map(x => '(?$&'); +} + +export function filter_snippets(pattern, haystack, mark, before, after) { + var res = [], context = new RegExp('(?:\\S+[ \t\v\xa0\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u200b\u202f\u205f\u3000\ufeff]+){0,' + (+(before || 3)) + '}\\S*(' + pattern.source + ')\\S*(?:[ \t\v\xa0\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u200b\u202f\u205f\u3000\ufeff]+\\S+){0,' + (+(after || before || 3)) + '}', pattern.flags), match; + if(context.global) while((match = context.exec(haystack)) !== null) res.push(match[0].replace(pattern, mark || '$&').replace(/\s+/g, ' ').replace(/([\W_])\1{2,}/g, '$1$1')); + else if((match = context.exec(haystack)) !== null) res.push(match[0].replace(pattern, mark || '$&').replace(/\s+/g, ' ').replace(/([\W_])\1{2,}/g, '$1$1')); + return uniq(res); +} + +export function filter_snippets_lines(pattern, haystack, mark) { + var res = [], context = new RegExp('[^\r\n]*(' + pattern.source + ')[^\r\n]*', pattern.flags), match; + if(context.global) while((match = context.exec(haystack)) !== null) res.push(match[0].replace(pattern, mark || '$&').replace(/[\r\n]/g, ' ')); + else if((match = context.exec(haystack)) !== null) res.push(match[0].replace(pattern, mark || '$&').replace(/[\r\n]/g, ' ')); + return uniq(res); +} + +function Descendant() {} +export function inherit(obj) { + Descendant.prototype = obj; + return new Descendant(); +}