Created
April 13, 2024 09:37
-
-
Save cristiklein/0790362d08536837a73316600aab671e to your computer and use it in GitHub Desktop.
Determine if the school is open on a given Date in Lomma, SE. Works as an App Script in Google Sheets.
This file contains hidden or 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
/** | |
* Copy-pasted from here: | |
* https://lomma.se/utbildning-och-barnomsorg/grundskola/terminer-och-lov.html | |
*/ | |
const text_from_lomma_se = `; | |
Vårterminen 2024 | |
2024-01-10 - 2024-06-13 | |
Studiedagar* | |
2024-01-08 (även fritidshemmet stängt) | |
2024-01-09 | |
2024-03-13 | |
2024-05-22 | |
2024-06-14 (även fritidshemmet stängt) | |
Lovdagar | |
2025-02-19 - 2024-02-23 | |
2024-03-25 - 2024-03-28 | |
2024-05-10 | |
2024-06-07 | |
Höstterminen 2024 | |
2024-08-14 - 2024-12-20 | |
Studiedagar* | |
2024-08-12 - 2024-08-13 (även fritidshemmet stängt) | |
2024-10-07 (även fritidshemmet stängt) | |
2024-10-08 | |
2024-11-26 | |
Lovdagar | |
2024-10-28 - 2024-11-01 | |
Vårterminen 2025 | |
2025-01-09 - 2025-06-12 | |
Studiedagar* | |
2025-01-07 (även fritidshemmet stängt) | |
2025-03-12 | |
2025-05-21 | |
2025-06-13 (även fritidshemmet stängt) | |
Lovdagar | |
2025-02-17 - 2025-02-21 | |
2025-04-14 - 2025-04-21 | |
2025-05-29 - 2025-05-30 | |
2025-06-06` | |
const useCache = true; // set to false during development | |
/** | |
* Represents days between (and including) a start and and end date, potentially a single day. | |
*/ | |
class DateRange { | |
constructor(start,end=null) { | |
this.start = new Date(start); | |
if (end == null) { | |
end = start; | |
} | |
this.end = new Date(end); | |
} | |
static parse(jsonDateRange) { | |
return new DateRange(jsonDateRange.start, jsonDateRange.end); | |
} | |
isDateInRange(inputDate) { | |
if (typeof inputDate == "string") | |
inputDate = new Date(inputDate); | |
return (this.start <= inputDate && inputDate <= this.end); | |
} | |
} | |
/** | |
* Represents a set of DateRange-s | |
*/ | |
class DateRangeSet { | |
constructor() { | |
this.rangeList = []; | |
} | |
/** | |
* Parse a JSON string into a DateRangeSet. | |
*/ | |
static parse(jsonRangeSet) { | |
const rangeSet = new DateRangeSet(); | |
for (const jsonDateRange of jsonRangeSet.rangeList) { | |
rangeSet.add(DateRange.parse(jsonDateRange)); | |
} | |
return rangeSet; | |
} | |
/** | |
* Add a DateRange to the DateRangeSet | |
*/ | |
add(dateRange) { | |
this.rangeList.push(dateRange); | |
} | |
/** | |
* Checks if a date is in a rangeList. | |
* @param {inputDate} The date to check. | |
* @returns true if date is in rangeList, otherwise false. | |
*/ | |
isDateInRange(inputDate) { | |
for (const range of this.rangeList) { | |
if (range.isDateInRange(inputDate)) { | |
return true; | |
} | |
} | |
return false; | |
} | |
} | |
/** | |
* Convert the text on lomma.se about school holidays into something workable. | |
* @param {text} Copy-paste text from lomma.se | |
* @returns A structure containing holidays, days when school is closed and holidays. | |
*/ | |
function textToSchoolData(text) { | |
terminer = new DateRangeSet(); | |
stangt = new DateRangeSet(); | |
lovdagar = new DateRangeSet(); | |
let mode = '' | |
for (const line of text.split('\n')) { | |
switch (line[0]) { | |
case 'H': | |
case 'V': | |
mode = 'T'; | |
continue; | |
case 'S': | |
case 'L': | |
mode = line[0]; | |
continue; | |
} | |
switch (mode) { | |
case 'T': { | |
const [ start, end ] = line.split(' - '); | |
terminer.add(new DateRange(start, end)); | |
break; | |
} | |
case 'L': { | |
const [ start, end ] = line.split(' - '); | |
lovdagar.add(new DateRange(start, end)); | |
break; | |
} | |
case 'S': { | |
const [ startEnd, comment ] = line.split(' ('); | |
if (comment && comment.includes('stängt')) { | |
const [ start, end ] = startEnd.split(' - '); | |
stangt.add(new DateRange(start, end)); | |
} | |
break; | |
} | |
} | |
} | |
return { terminer, stangt, lovdagar } | |
} | |
/** | |
* Get school data, if possible from cache | |
* @returns school data | |
*/ | |
function getSchoolData() { | |
const cache = CacheService.getDocumentCache(); | |
let schoolData; | |
try { | |
if (useCache) { | |
/* Parse JSON string */ | |
schoolData = {} | |
const jsonSchoolData = JSON.parse(cache.get("schoolData")); | |
for (const rangeName in jsonSchoolData) { | |
schoolData[rangeName] = DateRangeSet.parse(jsonSchoolData[rangeName]); | |
} | |
} | |
} | |
catch (e) { | |
console.log(`Cache error: ${e}`); | |
} | |
if (!schoolData || !schoolData.stangt || !schoolData.lovdagar || !schoolData.terminer) { | |
schoolData = textToSchoolData(text_from_lomma_se); | |
cache.put("schoolData", JSON.stringify(schoolData)); | |
} | |
return schoolData; | |
} | |
/** | |
* Checks if parents can work. | |
* @param {inputDate} The date to check. | |
* @returns 's' is school is closed (stängt), 'l' is school holiday (lov). | |
*/ | |
function isSchoolFreakingOpen(inputDate) { | |
const { terminer, stangt, lovdagar } = getSchoolData(); | |
if (typeof inputDate != "string") { | |
/* Google Sheets sends us time at midnight **local time**. | |
* We only really care about the date. */ | |
inputDate.setHours(12); | |
inputDate = inputDate.toJSON().split('T')[0]; | |
} | |
if (stangt.isDateInRange(inputDate)) { | |
return 's'; | |
} | |
if (!terminer.isDateInRange(inputDate)) { | |
return 'l'; | |
} | |
if (lovdagar.isDateInRange(inputDate)) { | |
return 'l'; | |
} | |
return ''; | |
} | |
const schoolData = textToSchoolData(text_from_lomma_se); | |
function test_isDateInRangeList() { | |
const testSet = [ | |
[ "2024-06-14", schoolData.stangt, true ], | |
[ "2024-06-15", schoolData.stangt, false ], | |
[ "2024-05-10", schoolData.lovdagar, true ], | |
[ "2024-05-11", schoolData.lovdagar, false ], | |
]; | |
for (const [ inputDate, rangeList, expected ] of testSet) { | |
const actual = rangeList.isDateInRange(inputDate); | |
if (actual != expected) { | |
throw new Error(`\ | |
AssertionError: | |
Input: ${inputDate} | |
Actual: ${actual}; | |
Expected: ${expected}`); | |
} | |
console.log(`isDateInRange ${inputDate} ... PASS`); | |
} | |
} | |
function test_isSchoolFreakingOpen() { | |
const testSet = [ | |
[ "2024-01-08", "s" ], | |
[ "2024-01-09", "l" ], | |
[ "2024-01-10", "" ], | |
[ "2024-05-10", "l" ], | |
[ "2024-05-11", "" ], | |
[ new Date("2024-05-10"), "l" ], | |
[ new Date("2024-05-11"), "" ], | |
[ new Date("Fri May 10 2024 00:00:00 GMT+0200 (GMT+02:00)"), "l" ], | |
[ new Date("Sat May 11 2024 00:00:00 GMT+0200 (GMT+02:00)"), "" ], | |
]; | |
for (const [ inputDate, expected ] of testSet) { | |
const actual = isSchoolFreakingOpen(inputDate); | |
if (actual != expected) { | |
throw new Error(`\ | |
AssertionError: | |
Input: ${inputDate}; | |
Actual: ${actual}; | |
Expected: ${expected}`); | |
} | |
console.log(`isSchoolFreakingOpen ${inputDate} ... PASS`); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment