From 93b466df50d94d4c15715f5eb4d88a0be7d61c11 Mon Sep 17 00:00:00 2001 From: Tim Schiewe Date: Mon, 30 Apr 2018 18:15:52 +0200 Subject: Create better structure --- GoogleCalendar.ts | 90 ---- ICal.ts | 30 -- SplusParser.ts | 160 ------- docs/.gitkeep | 0 docs/informatik1.ics | 97 ---- etc/.gitkeep | 0 etc/informatik1.ics | 97 ++++ etc/sample.htm | 1247 +++++++++++++++++++++++++++++++++++++++++++++++++ index.ts | 69 --- package.json | 2 +- sample.htm | 1247 ------------------------------------------------- src/GoogleCalendar.ts | 90 ++++ src/ICal.ts | 30 ++ src/SplusParser.ts | 160 +++++++ src/index.ts | 69 +++ 15 files changed, 1694 insertions(+), 1694 deletions(-) delete mode 100644 GoogleCalendar.ts delete mode 100644 ICal.ts delete mode 100644 SplusParser.ts delete mode 100644 docs/.gitkeep delete mode 100644 docs/informatik1.ics create mode 100644 etc/.gitkeep create mode 100644 etc/informatik1.ics create mode 100644 etc/sample.htm delete mode 100644 index.ts delete mode 100644 sample.htm create mode 100644 src/GoogleCalendar.ts create mode 100644 src/ICal.ts create mode 100644 src/SplusParser.ts create mode 100644 src/index.ts diff --git a/GoogleCalendar.ts b/GoogleCalendar.ts deleted file mode 100644 index 2376c6b..0000000 --- a/GoogleCalendar.ts +++ /dev/null @@ -1,90 +0,0 @@ -import * as fs from 'fs'; -import * as readline from 'readline'; -import {google} from "googleapis"; - -const OAuth2Client = google.auth.OAuth2; -const scopes = ['https://www.googleapis.com/auth/calendar']; - -const credentialsPath = 'client_secret.json'; -const tokenPath = 'credentials.json'; - -export const clientPromise = new Promise((resolve, reject) => { - fs.readFile(credentialsPath, (err, data) => { - if (err) return reject(err); - - const credentials = JSON.parse(data.toString()); - const {client_secret, client_id, redirect_uris} = credentials.installed; - - const client = new OAuth2Client(client_id, client_secret, redirect_uris[0]); - - fs.readFile(tokenPath, (err, data) => { - if (err) { - const authUrl = client.generateAuthUrl({ - access_type: 'offline', - scope: scopes, - }); - - console.log('Authorize this app by visiting this url:', authUrl); - - const rl = readline.createInterface({ - input: process.stdin, - output: process.stdout, - }); - - rl.question('Enter the code from that page here: ', code => { - rl.close(); - client.getToken(code, (err, token) => { - if (err) return reject(err); - fs.writeFile(tokenPath, JSON.stringify(token), err => err ? console.error(err) : 0); - client.setCredentials(token); - resolve(client); - }); - }); - } - else { - client.setCredentials(JSON.parse(data.toString())); - resolve(client); - } - }); - }); -}); - -export const calendarPromise = clientPromise.then(auth => google.calendar({version: 'v3', auth})); - - -export async function listEvents() { - const calendar = await calendarPromise; - calendar.events.list({ - calendarId: 'primary', - timeMin: (new Date()).toISOString(), - maxResults: 10, - singleEvents: true, - orderBy: 'startTime', - }, (err, data) => { - if (err) return console.log('The API returned an error: ' + err); - const events = data.data.items; - if (events.length) { - console.log('Upcoming 10 events:'); - events.map((event, i) => { - const start = event.start.dateTime || event.start.date; - console.log(`${start} - ${event.summary}`); - }); - } else { - console.log('No upcoming events found.'); - } - }); -} - -export function createEvent(event, calendarId = 'primary') { - return calendarPromise.then(calendar => { - return new Promise((resolve, reject) => { - calendar.events.insert({ - calendarId: calendarId, - resource: event, - }, function (err, event) { - if (err) return reject(err); - resolve(event); - }); - }) - }); -} diff --git a/ICal.ts b/ICal.ts deleted file mode 100644 index fe74dab..0000000 --- a/ICal.ts +++ /dev/null @@ -1,30 +0,0 @@ -// ref. https://www.npmjs.com/package/ical-generator -import * as ical from 'ical-generator'; -import * as crypto from 'crypto'; -import * as fs from 'fs'; - -const TARGET_FILE = 'docs/informatik1.ics'; -const DOMAIN = 'schneefux.github.io'; // TODO config management - -const sha256 = (x) => crypto.createHash('sha256').update(x, 'utf8').digest('hex'); - -const cal = ical(); - -// TODO declare an interface -export function createEvent(event) { - // TODO better duplicate detection and resolve conflicts lol - const eventId = sha256(JSON.stringify(event)).substr(0, 16); - - const icalEvent = cal.createEvent({ - uid: eventId, - start: event.start.dateTime, - end: event.end.dateTime, - timestamp: new Date(), - summary: event.summary, - description: event.description, - }); -} - -export function commit() { - fs.writeFile(TARGET_FILE, cal.toString(), (err) => {}); -} diff --git a/SplusParser.ts b/SplusParser.ts deleted file mode 100644 index be693fd..0000000 --- a/SplusParser.ts +++ /dev/null @@ -1,160 +0,0 @@ -import {load} from 'cheerio'; - -interface IBlock { - title: string; - durationHours: number; - durationSlots: number; - info: string; - room: string; - lecturer: string -} - -export interface ILecture { - title: string; - day: number; - begin: number; - end: number; - info: string; - room: string, - lecturer: string; -} - -export class SplusParser { - private readonly startHour = 7; - - private readonly $: CheerioStatic; - private readonly $table: Cheerio; - private readonly $rows: Cheerio; - - private colWidths: number[] = []; - private colBlocked: number[] = []; - - private blocks: IBlock[][][] = []; - - constructor(data: string) { - this.$ = load(data); - - this.$table = this.$(this.$('table.grid-border-args')[0]); - this.$rows = this.$table.find('> tbody > tr'); - - this.parseColWidths(); - this.parseTable(); - } - - getLectures(filter?: (lecture: ILecture) => boolean): ILecture[] { - let lectures: ILecture[] = []; - - for (let i = 0; i < this.blocks.length; i++) { - const begin = this.startHour + i * 0.25; - - const slot = this.blocks[i]; - for (let j = 0; j < slot.length; j++) { - const day = slot[j]; - for (let k = 0; k < day.length; k++) { - const block = day[k]; - - const lecture = { - title: block.title, - day: j, - begin: begin, - end: begin + block.durationHours, - info: block.info, - room: block.room, - lecturer: block.lecturer - }; - - if(!filter || filter(lecture)) { - lectures.push(lecture); - } - } - } - } - - return lectures; - } - - private getDay(col: number) { - return this.colWidths.findIndex(width => (col -= width) < 0); - } - - private parseColWidths() { - const $cols = this.$(this.$rows[0]).find('td:not(:first-child)'); - - for (let i = 0; i < $cols.length; i++) { - const $col = this.$($cols[i]); - this.colWidths[i] = parseInt($col.attr('colspan')); - } - - let index = 0; - for (let i = 0; i < this.colWidths.length; i++) { - for (let j = 0; j < this.colWidths[i]; i++) { - this.colBlocked[index++] = 0; - } - } - } - - private parseTable() { - for (let i = 0; i < this.$rows.length - 1; i++) { - // i + 1 to skip the columns row - const $row = this.$(this.$rows[i + 1]); - - this.colBlocked = this.colBlocked.map(v => v > 0 ? v - 1 : 0); - - this.blocks.push(this.parseRow($row)); - } - } - - private parseRow($row: Cheerio) { - const $cols = $row.find('> td:not(:first-child)'); - const blocks: IBlock[][] = []; - - let offset = 0; - for (let i = 0; i < $cols.length; i++) { - while (this.colBlocked[i + offset] > 0) { - const day = this.getDay(i + offset); - if (blocks[day] === undefined) { - blocks[day] = []; - } - - offset++; - } - - const day = this.getDay(i + offset); - if (blocks[day] === undefined) { - blocks[day] = []; - } - - const $c = this.$($cols[i]); - if ($c.hasClass('object-cell-border')) { - const block = this.parseBlock($c); - blocks[day].push(block); - - this.colBlocked[i + offset] = block.durationSlots; - } - } - - return blocks; - } - - private parseBlock($block: Cheerio): IBlock { - const $tblTitle = $block.find('table:nth-child(1)'); - const $tblInfo = $block.find('table:nth-child(2)'); - const $tblLocation = $block.find('table:nth-child(3)'); - - const $tdRoom = $tblLocation.find('td:nth-child(1)'); - const $tdLecturer = $tblLocation.find('td:nth-child(2)'); - - // Remove the Modulhandbuch "mhb" link - $tblTitle.find('br + strong').remove(); - - const slots = parseInt($block.attr('rowspan')); - return { - title: $tblTitle.text().trim(), - durationHours: slots / 4, - durationSlots: slots, - info: $tblInfo.text().trim(), - room: $tdRoom.text().trim(), - lecturer: $tdLecturer.text().trim() - }; - } -} diff --git a/docs/.gitkeep b/docs/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/docs/informatik1.ics b/docs/informatik1.ics deleted file mode 100644 index 482e313..0000000 --- a/docs/informatik1.ics +++ /dev/null @@ -1,97 +0,0 @@ -BEGIN:VCALENDAR -VERSION:2.0 -PRODID:-//sebbo.net//ical-generator//EN -BEGIN:VEVENT -UID:8cb5463bc6ac09ee@localhost -SEQUENCE:0 -DTSTAMP:20180429T105331Z -DTSTART:20180425T061500Z -DTEND:20180425T074500Z -SUMMARY:Grundlagen des Programmierens - VL -DESCRIPTION:Prof. Dr. J. Weimar -END:VEVENT -BEGIN:VEVENT -UID:3d6d97e8ed0b5008@localhost -SEQUENCE:0 -DTSTAMP:20180429T105331Z -DTSTART:20180426T080000Z -DTEND:20180426T093000Z -SUMMARY:Diskrete Strukturen -DESCRIPTION:Prof. Dr. P. Riegler -END:VEVENT -BEGIN:VEVENT -UID:1926bb7df8411c3f@localhost -SEQUENCE:0 -DTSTAMP:20180429T105331Z -DTSTART:20180424T100000Z -DTEND:20180424T113000Z -SUMMARY:Grundlagen des Programmierens - Labor -DESCRIPTION:Prof. Dr. J. Weimar - Findet ab KW11 (13.3.) statt\, also nich - t am 6.3. -END:VEVENT -BEGIN:VEVENT -UID:b35b7f60b9f78f21@localhost -SEQUENCE:0 -DTSTAMP:20180429T105331Z -DTSTART:20180423T121500Z -DTEND:20180423T134500Z -SUMMARY:Technische Grundlagen der Informatik - VL -DESCRIPTION:Dipl.-Ing. K. Dammann -END:VEVENT -BEGIN:VEVENT -UID:1cbd4999b193f6d7@localhost -SEQUENCE:0 -DTSTAMP:20180429T105331Z -DTSTART:20180424T121500Z -DTEND:20180424T134500Z -SUMMARY:Grundlagen des Programmierens - Labor -DESCRIPTION:Prof. Dr. J. Weimar - Findet ab KW11 (13.3.) statt\, also nich - t am 6.3. -END:VEVENT -BEGIN:VEVENT -UID:b6231494e1ea1f5a@localhost -SEQUENCE:0 -DTSTAMP:20180429T105331Z -DTSTART:20180425T121500Z -DTEND:20180425T134500Z -SUMMARY:Technische Grundlagen der Informatik - VL -DESCRIPTION:Dipl.-Ing. K. Dammann - Achtung\, Veranstaltung findet unregel - mässig statt. Wöchentlich Stundenplan beachten! -END:VEVENT -BEGIN:VEVENT -UID:1a744959b316cabf@localhost -SEQUENCE:0 -DTSTAMP:20180429T105331Z -DTSTART:20180426T121500Z -DTEND:20180426T134500Z -SUMMARY:Grundlagen des Programmierens - VL -DESCRIPTION:Prof. Dr. J. Weimar -END:VEVENT -BEGIN:VEVENT -UID:cdccad28daf287b8@localhost -SEQUENCE:0 -DTSTAMP:20180429T105331Z -DTSTART:20180423T140000Z -DTEND:20180423T153000Z -SUMMARY:Einführung in die Informatik -DESCRIPTION:Prof. Dr. W. Pekrun -END:VEVENT -BEGIN:VEVENT -UID:827650a4f581e5d1@localhost -SEQUENCE:0 -DTSTAMP:20180429T105331Z -DTSTART:20180424T140000Z -DTEND:20180424T153000Z -SUMMARY:Diskrete Strukturen -DESCRIPTION:Prof. Dr. P. Riegler -END:VEVENT -BEGIN:VEVENT -UID:9052b16c3fc97fce@localhost -SEQUENCE:0 -DTSTAMP:20180429T105331Z -DTSTART:20180423T154500Z -DTEND:20180423T171500Z -SUMMARY:Einführung in die Informatik -DESCRIPTION:Prof. Dr. W. Pekrun -END:VEVENT -END:VCALENDAR \ No newline at end of file diff --git a/etc/.gitkeep b/etc/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/etc/informatik1.ics b/etc/informatik1.ics new file mode 100644 index 0000000..482e313 --- /dev/null +++ b/etc/informatik1.ics @@ -0,0 +1,97 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//sebbo.net//ical-generator//EN +BEGIN:VEVENT +UID:8cb5463bc6ac09ee@localhost +SEQUENCE:0 +DTSTAMP:20180429T105331Z +DTSTART:20180425T061500Z +DTEND:20180425T074500Z +SUMMARY:Grundlagen des Programmierens - VL +DESCRIPTION:Prof. Dr. J. Weimar +END:VEVENT +BEGIN:VEVENT +UID:3d6d97e8ed0b5008@localhost +SEQUENCE:0 +DTSTAMP:20180429T105331Z +DTSTART:20180426T080000Z +DTEND:20180426T093000Z +SUMMARY:Diskrete Strukturen +DESCRIPTION:Prof. Dr. P. Riegler +END:VEVENT +BEGIN:VEVENT +UID:1926bb7df8411c3f@localhost +SEQUENCE:0 +DTSTAMP:20180429T105331Z +DTSTART:20180424T100000Z +DTEND:20180424T113000Z +SUMMARY:Grundlagen des Programmierens - Labor +DESCRIPTION:Prof. Dr. J. Weimar - Findet ab KW11 (13.3.) statt\, also nich + t am 6.3. +END:VEVENT +BEGIN:VEVENT +UID:b35b7f60b9f78f21@localhost +SEQUENCE:0 +DTSTAMP:20180429T105331Z +DTSTART:20180423T121500Z +DTEND:20180423T134500Z +SUMMARY:Technische Grundlagen der Informatik - VL +DESCRIPTION:Dipl.-Ing. K. Dammann +END:VEVENT +BEGIN:VEVENT +UID:1cbd4999b193f6d7@localhost +SEQUENCE:0 +DTSTAMP:20180429T105331Z +DTSTART:20180424T121500Z +DTEND:20180424T134500Z +SUMMARY:Grundlagen des Programmierens - Labor +DESCRIPTION:Prof. Dr. J. Weimar - Findet ab KW11 (13.3.) statt\, also nich + t am 6.3. +END:VEVENT +BEGIN:VEVENT +UID:b6231494e1ea1f5a@localhost +SEQUENCE:0 +DTSTAMP:20180429T105331Z +DTSTART:20180425T121500Z +DTEND:20180425T134500Z +SUMMARY:Technische Grundlagen der Informatik - VL +DESCRIPTION:Dipl.-Ing. K. Dammann - Achtung\, Veranstaltung findet unregel + mässig statt. Wöchentlich Stundenplan beachten! +END:VEVENT +BEGIN:VEVENT +UID:1a744959b316cabf@localhost +SEQUENCE:0 +DTSTAMP:20180429T105331Z +DTSTART:20180426T121500Z +DTEND:20180426T134500Z +SUMMARY:Grundlagen des Programmierens - VL +DESCRIPTION:Prof. Dr. J. Weimar +END:VEVENT +BEGIN:VEVENT +UID:cdccad28daf287b8@localhost +SEQUENCE:0 +DTSTAMP:20180429T105331Z +DTSTART:20180423T140000Z +DTEND:20180423T153000Z +SUMMARY:Einführung in die Informatik +DESCRIPTION:Prof. Dr. W. Pekrun +END:VEVENT +BEGIN:VEVENT +UID:827650a4f581e5d1@localhost +SEQUENCE:0 +DTSTAMP:20180429T105331Z +DTSTART:20180424T140000Z +DTEND:20180424T153000Z +SUMMARY:Diskrete Strukturen +DESCRIPTION:Prof. Dr. P. Riegler +END:VEVENT +BEGIN:VEVENT +UID:9052b16c3fc97fce@localhost +SEQUENCE:0 +DTSTAMP:20180429T105331Z +DTSTART:20180423T154500Z +DTEND:20180423T171500Z +SUMMARY:Einführung in die Informatik +DESCRIPTION:Prof. Dr. W. Pekrun +END:VEVENT +END:VCALENDAR \ No newline at end of file diff --git a/etc/sample.htm b/etc/sample.htm new file mode 100644 index 0000000..6e4f0d9 --- /dev/null +++ b/etc/sample.htm @@ -0,0 +1,1247 @@ + + + + Ostfalia Hochschule für angewandte Wissenschaften + + + + + + + + + + + + + + + + + +
+
+ + +
+ + +
+ + + + + + +
AuswahlKalenderwoche AnfangEndeWochentage
+
+
+
    
+
+ +
+
+
+
+
+
+ +
+
+Mo +Di +Mi +Do +Fr +Sa +So +
+
+ +
+
+
+ + +
+ +
+ + +
+ +
+
+
 drucken ... + + + +Individual_Semester + + + + + + + + + + + + + + + + + +
+ ++ + + +
Semester B.Sc. - 1. Sem.. Informatik
+
+ ++ + + +
+
+ ++ + + +
+
+ ++ + + +
Plan ist gültig von 23.04.2018 bis 29.04.2018
+
+ ++ + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
MoDiMiDoFrSa
7:00       
7:15       
7:30       
7:45       
8:00       
8:15    + + + + + + +
Grundlagen des Programmierens - VL
mhb
+ + + + + +
+ + + + + + + +
Hörsaal 252Prof. Dr. J. Weimar
+ +
+ + + + + + +
Mathe-Cafe 1. Sem.
+ + + + + +
+ + + + + + + +
Hörsaal 223Dr. S. Bellmer
+ +
  
8:30     
8:45     
9:00     
9:15     
9:30  + + + + + + +
CS-Kompetenzen für die Informatik
mhb
+ + + + + +
+ + + + + + + +
Seminarraum 136Fr. J. Schwiebert
+ +
   
9:45 + + + + + + +
CS-Kompetenzen für die Informatik
mhb
+ + + + + +
+ + + + + + + +
11/5 ExProf. Dr. D. Justen
+ +
     
10:00 + + + + + + +
Mathe-Repetitorium
+ + + + + +
+ + + + + + + +
Hörsaal 223Dr. S. Bellmer
+ +
+ + + + + + +
Reservierung Prof . Riegler
+ + + + + +
+ + + + + + + +
Scale Up - Raum 201
+ +
+ + + + + + +
Diskrete Strukturen
mhb
+ + + + + +
+ + + + + + + +
Hörsaal 252Prof. Dr. P. Riegler
+ +
  
10:15  
10:30  
10:45  
11:00  
11:15  
11:30      
11:45      
12:00 + + + + + + +
CS-Kompetenzen für die Informatik
mhb
+ + + + + +
+ + + + + + + +
Seminarraum 136Fr. J. Schwiebert
+ +
+ + + + + + +
CS-Kompetenzen für die Informatik
mhb
+ + + + + +
+ + + + + + + +
11/5 ExProf. Dr. D. Justen
+ +
+ + + + + + +
Grundlagen des Programmierens - Labor
mhb
+ + + + + +
Findet ab KW11 (13.3.) statt, also nicht am 6.3.
+ + + + + + + +
P4, P3, P2, P1Prof. Dr. J. Weimar
+ +
  + + + + + + +
Informatik-Lounge 1. Sem
+ + + + + +
+ + + + + + + +
Poolraum 132Dipl.-Inf. D.Dick-Anwander
+ +
  
12:15   
12:30   
12:45   
13:00   
13:15   
13:30     
13:45     
14:00      
14:15 + + + + + + +
Technische Grundlagen der Informatik - VL
mhb
+ + + + + +
+ + + + + + + +
11/1Dipl.-Ing. K. Dammann
+ +
  + + + + + + +
Grundlagen des Programmierens - Labor
mhb
+ + + + + +
Findet ab KW11 (13.3.) statt, also nicht am 6.3.
+ + + + + + + +
P4, P3, P2, P1Prof. Dr. J. Weimar
+ +
+ + + + + + +
Technische Grundlagen der Informatik - VL
mhb
+ + + + + +
Achtung, Veranstaltung findet unregelmässig statt. Wöchentlich Stundenplan beachten!
+ + + + + + + +
11/1Dipl.-Ing. K. Dammann
+ +
+ + + + + + +
Grundlagen des Programmierens - VL
mhb
+ + + + + +
+ + + + + + + +
Hörsaal 026Prof. Dr. J. Weimar
+ +
  
14:30   
14:45   
15:00   
15:15   
15:30   
15:45       
16:00 + + + + + + +
Einführung in die Informatik
mhb
+ + + + + +
+ + + + + + + +
11/1Prof. Dr. W. Pekrun
+ +
  + + + + + + +
Diskrete Strukturen
mhb
+ + + + + +
+ + + + + + + +
Hörsaal 252Prof. Dr. P. Riegler
+ +
  + + + + + + +
+ + + + + +
+ + + + + + + +
11/5 ExProf. Dr. D. Justen
+ +
  
16:15    
16:30    
16:45    
17:00    
17:15    
17:30      
17:45 + + + + + + +
Einführung in die Informatik
mhb
+ + + + + +
+ + + + + + + +
11/1Prof. Dr. W. Pekrun
+ +
     
18:00     
18:15      
18:30      
18:45      
19:00      
19:15       
19:30       
19:45       
20:00       
+ + + + + + + +
+ +
+ + + \ No newline at end of file diff --git a/index.ts b/index.ts deleted file mode 100644 index f01ce49..0000000 --- a/index.ts +++ /dev/null @@ -1,69 +0,0 @@ -import {readFile} from 'fs'; -import {SplusParser} from './SplusParser'; -let createEvent = (arg) => {}; -let commit = () => {}; -if (process.argv.includes('ics')) { - createEvent = require('./ICal').createEvent; - commit = require('./ICal').commit; -} else { - createEvent = require('./GoogleCalendar').createEvent; -} - - -const splusUrl = 'http://splus.ostfalia.de/semesterplan123.php?id=1362F014835FFFD0F67159E302EC1A3C&identifier=%23SPLUS7A3292'; - -/* -get(splusUrl, (res) => { - let data = ''; - res.on('data', chunk => data += chunk); - res.on('end', () => TODO); -}); -*/ - -const baseDate = new Date(2018, 3, 23); - -readFile('sample.htm', (err, data) => { - const lectures = new SplusParser(data.toString()).getLectures(lecture => { - // Filter some lectures out - if (lecture.title === '') return false; - if (lecture.title.indexOf('Mathe-Cafe') !== -1) return false; - if (lecture.title.indexOf('Mathe-Repetitorium') !== -1) return false; - if (lecture.title.indexOf('Informatik-Lounge') !== -1) return false; - if (lecture.title.indexOf('Reservierung') !== -1) return false; - return lecture.title.indexOf('Kompetenzen') === -1; - }); - - lectures.forEach((lec, index) => { - let beginDate = new Date(baseDate); - - beginDate.setDate(beginDate.getDate() + lec.day); - beginDate.setMinutes(beginDate.getMinutes() + lec.begin * 60); - - let endDate = new Date(baseDate); - endDate.setDate(endDate.getDate() + lec.day); - endDate.setMinutes(endDate.getMinutes() + lec.end * 60); - - console.log(lec.title, beginDate.toISOString(), endDate.toISOString()); - - createEvent({ - summary: lec.title, - description: lec.lecturer + (lec.info !== '' ? ' - ' : '') + lec.info, - location: lec.room, - start: { - dateTime: beginDate.toISOString() - }, - end: { - dateTime: endDate.toISOString() - }, - reminders: { - useDefault: false - } - }); - - // TODO - if (index == lectures.length - 1) { - commit(); - } - }) -}); - diff --git a/package.json b/package.json index a23d5ca..65028ed 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ "main": "index.js", "license": "MIT", "scripts": { - "start": "ts-node index.ts" + "start": "ts-node src/index.ts" }, "dependencies": { "cheerio": "^1.0.0-rc.2", diff --git a/sample.htm b/sample.htm deleted file mode 100644 index 6e4f0d9..0000000 --- a/sample.htm +++ /dev/null @@ -1,1247 +0,0 @@ - - - - Ostfalia Hochschule für angewandte Wissenschaften - - - - - - - - - - - - - - - - - -
-
- - -
- - -
- - - - - - -
AuswahlKalenderwoche AnfangEndeWochentage
-
-
-
    
-
- -
-
-
-
-
-
- -
-
-Mo -Di -Mi -Do -Fr -Sa -So -
-
- -
-
-
- - -
- -
- - -
- -
-
-
 drucken ... - - - -Individual_Semester - - - - - - - - - - - - - - - - - -
- -- - - -
Semester B.Sc. - 1. Sem.. Informatik
-
- -- - - -
-
- -- - - -
-
- -- - - -
Plan ist gültig von 23.04.2018 bis 29.04.2018
-
- -- - - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
MoDiMiDoFrSa
7:00       
7:15       
7:30       
7:45       
8:00       
8:15    - - - - - - -
Grundlagen des Programmierens - VL
mhb
- - - - - -
- - - - - - - -
Hörsaal 252Prof. Dr. J. Weimar
- -
- - - - - - -
Mathe-Cafe 1. Sem.
- - - - - -
- - - - - - - -
Hörsaal 223Dr. S. Bellmer
- -
  
8:30     
8:45     
9:00     
9:15     
9:30  - - - - - - -
CS-Kompetenzen für die Informatik
mhb
- - - - - -
- - - - - - - -
Seminarraum 136Fr. J. Schwiebert
- -
   
9:45 - - - - - - -
CS-Kompetenzen für die Informatik
mhb
- - - - - -
- - - - - - - -
11/5 ExProf. Dr. D. Justen
- -
     
10:00 - - - - - - -
Mathe-Repetitorium
- - - - - -
- - - - - - - -
Hörsaal 223Dr. S. Bellmer
- -
- - - - - - -
Reservierung Prof . Riegler
- - - - - -
- - - - - - - -
Scale Up - Raum 201
- -
- - - - - - -
Diskrete Strukturen
mhb
- - - - - -
- - - - - - - -
Hörsaal 252Prof. Dr. P. Riegler
- -
  
10:15  
10:30  
10:45  
11:00  
11:15  
11:30      
11:45      
12:00 - - - - - - -
CS-Kompetenzen für die Informatik
mhb
- - - - - -
- - - - - - - -
Seminarraum 136Fr. J. Schwiebert
- -
- - - - - - -
CS-Kompetenzen für die Informatik
mhb
- - - - - -
- - - - - - - -
11/5 ExProf. Dr. D. Justen
- -
- - - - - - -
Grundlagen des Programmierens - Labor
mhb
- - - - - -
Findet ab KW11 (13.3.) statt, also nicht am 6.3.
- - - - - - - -
P4, P3, P2, P1Prof. Dr. J. Weimar
- -
  - - - - - - -
Informatik-Lounge 1. Sem
- - - - - -
- - - - - - - -
Poolraum 132Dipl.-Inf. D.Dick-Anwander
- -
  
12:15   
12:30   
12:45   
13:00   
13:15   
13:30     
13:45     
14:00      
14:15 - - - - - - -
Technische Grundlagen der Informatik - VL
mhb
- - - - - -
- - - - - - - -
11/1Dipl.-Ing. K. Dammann
- -
  - - - - - - -
Grundlagen des Programmierens - Labor
mhb
- - - - - -
Findet ab KW11 (13.3.) statt, also nicht am 6.3.
- - - - - - - -
P4, P3, P2, P1Prof. Dr. J. Weimar
- -
- - - - - - -
Technische Grundlagen der Informatik - VL
mhb
- - - - - -
Achtung, Veranstaltung findet unregelmässig statt. Wöchentlich Stundenplan beachten!
- - - - - - - -
11/1Dipl.-Ing. K. Dammann
- -
- - - - - - -
Grundlagen des Programmierens - VL
mhb
- - - - - -
- - - - - - - -
Hörsaal 026Prof. Dr. J. Weimar
- -
  
14:30   
14:45   
15:00   
15:15   
15:30   
15:45       
16:00 - - - - - - -
Einführung in die Informatik
mhb
- - - - - -
- - - - - - - -
11/1Prof. Dr. W. Pekrun
- -
  - - - - - - -
Diskrete Strukturen
mhb
- - - - - -
- - - - - - - -
Hörsaal 252Prof. Dr. P. Riegler
- -
  - - - - - - -
- - - - - -
- - - - - - - -
11/5 ExProf. Dr. D. Justen
- -
  
16:15    
16:30    
16:45    
17:00    
17:15    
17:30      
17:45 - - - - - - -
Einführung in die Informatik
mhb
- - - - - -
- - - - - - - -
11/1Prof. Dr. W. Pekrun
- -
     
18:00     
18:15      
18:30      
18:45      
19:00      
19:15       
19:30       
19:45       
20:00       
- - - - - - - -
- -
- - - \ No newline at end of file diff --git a/src/GoogleCalendar.ts b/src/GoogleCalendar.ts new file mode 100644 index 0000000..2376c6b --- /dev/null +++ b/src/GoogleCalendar.ts @@ -0,0 +1,90 @@ +import * as fs from 'fs'; +import * as readline from 'readline'; +import {google} from "googleapis"; + +const OAuth2Client = google.auth.OAuth2; +const scopes = ['https://www.googleapis.com/auth/calendar']; + +const credentialsPath = 'client_secret.json'; +const tokenPath = 'credentials.json'; + +export const clientPromise = new Promise((resolve, reject) => { + fs.readFile(credentialsPath, (err, data) => { + if (err) return reject(err); + + const credentials = JSON.parse(data.toString()); + const {client_secret, client_id, redirect_uris} = credentials.installed; + + const client = new OAuth2Client(client_id, client_secret, redirect_uris[0]); + + fs.readFile(tokenPath, (err, data) => { + if (err) { + const authUrl = client.generateAuthUrl({ + access_type: 'offline', + scope: scopes, + }); + + console.log('Authorize this app by visiting this url:', authUrl); + + const rl = readline.createInterface({ + input: process.stdin, + output: process.stdout, + }); + + rl.question('Enter the code from that page here: ', code => { + rl.close(); + client.getToken(code, (err, token) => { + if (err) return reject(err); + fs.writeFile(tokenPath, JSON.stringify(token), err => err ? console.error(err) : 0); + client.setCredentials(token); + resolve(client); + }); + }); + } + else { + client.setCredentials(JSON.parse(data.toString())); + resolve(client); + } + }); + }); +}); + +export const calendarPromise = clientPromise.then(auth => google.calendar({version: 'v3', auth})); + + +export async function listEvents() { + const calendar = await calendarPromise; + calendar.events.list({ + calendarId: 'primary', + timeMin: (new Date()).toISOString(), + maxResults: 10, + singleEvents: true, + orderBy: 'startTime', + }, (err, data) => { + if (err) return console.log('The API returned an error: ' + err); + const events = data.data.items; + if (events.length) { + console.log('Upcoming 10 events:'); + events.map((event, i) => { + const start = event.start.dateTime || event.start.date; + console.log(`${start} - ${event.summary}`); + }); + } else { + console.log('No upcoming events found.'); + } + }); +} + +export function createEvent(event, calendarId = 'primary') { + return calendarPromise.then(calendar => { + return new Promise((resolve, reject) => { + calendar.events.insert({ + calendarId: calendarId, + resource: event, + }, function (err, event) { + if (err) return reject(err); + resolve(event); + }); + }) + }); +} diff --git a/src/ICal.ts b/src/ICal.ts new file mode 100644 index 0000000..09bc9bf --- /dev/null +++ b/src/ICal.ts @@ -0,0 +1,30 @@ +// ref. https://www.npmjs.com/package/ical-generator +import * as ical from 'ical-generator'; +import * as crypto from 'crypto'; +import * as fs from 'fs'; + +const TARGET_FILE = 'etc/informatik1.ics'; +const DOMAIN = 'schneefux.github.io'; // TODO config management + +const sha256 = (x) => crypto.createHash('sha256').update(x, 'utf8').digest('hex'); + +const cal = ical(); + +// TODO declare an interface +export function createEvent(event) { + // TODO better duplicate detection and resolve conflicts lol + const eventId = sha256(JSON.stringify(event)).substr(0, 16); + + const icalEvent = cal.createEvent({ + uid: eventId, + start: event.start.dateTime, + end: event.end.dateTime, + timestamp: new Date(), + summary: event.summary, + description: event.description, + }); +} + +export function commit() { + fs.writeFile(TARGET_FILE, cal.toString(), (err) => {}); +} diff --git a/src/SplusParser.ts b/src/SplusParser.ts new file mode 100644 index 0000000..be693fd --- /dev/null +++ b/src/SplusParser.ts @@ -0,0 +1,160 @@ +import {load} from 'cheerio'; + +interface IBlock { + title: string; + durationHours: number; + durationSlots: number; + info: string; + room: string; + lecturer: string +} + +export interface ILecture { + title: string; + day: number; + begin: number; + end: number; + info: string; + room: string, + lecturer: string; +} + +export class SplusParser { + private readonly startHour = 7; + + private readonly $: CheerioStatic; + private readonly $table: Cheerio; + private readonly $rows: Cheerio; + + private colWidths: number[] = []; + private colBlocked: number[] = []; + + private blocks: IBlock[][][] = []; + + constructor(data: string) { + this.$ = load(data); + + this.$table = this.$(this.$('table.grid-border-args')[0]); + this.$rows = this.$table.find('> tbody > tr'); + + this.parseColWidths(); + this.parseTable(); + } + + getLectures(filter?: (lecture: ILecture) => boolean): ILecture[] { + let lectures: ILecture[] = []; + + for (let i = 0; i < this.blocks.length; i++) { + const begin = this.startHour + i * 0.25; + + const slot = this.blocks[i]; + for (let j = 0; j < slot.length; j++) { + const day = slot[j]; + for (let k = 0; k < day.length; k++) { + const block = day[k]; + + const lecture = { + title: block.title, + day: j, + begin: begin, + end: begin + block.durationHours, + info: block.info, + room: block.room, + lecturer: block.lecturer + }; + + if(!filter || filter(lecture)) { + lectures.push(lecture); + } + } + } + } + + return lectures; + } + + private getDay(col: number) { + return this.colWidths.findIndex(width => (col -= width) < 0); + } + + private parseColWidths() { + const $cols = this.$(this.$rows[0]).find('td:not(:first-child)'); + + for (let i = 0; i < $cols.length; i++) { + const $col = this.$($cols[i]); + this.colWidths[i] = parseInt($col.attr('colspan')); + } + + let index = 0; + for (let i = 0; i < this.colWidths.length; i++) { + for (let j = 0; j < this.colWidths[i]; i++) { + this.colBlocked[index++] = 0; + } + } + } + + private parseTable() { + for (let i = 0; i < this.$rows.length - 1; i++) { + // i + 1 to skip the columns row + const $row = this.$(this.$rows[i + 1]); + + this.colBlocked = this.colBlocked.map(v => v > 0 ? v - 1 : 0); + + this.blocks.push(this.parseRow($row)); + } + } + + private parseRow($row: Cheerio) { + const $cols = $row.find('> td:not(:first-child)'); + const blocks: IBlock[][] = []; + + let offset = 0; + for (let i = 0; i < $cols.length; i++) { + while (this.colBlocked[i + offset] > 0) { + const day = this.getDay(i + offset); + if (blocks[day] === undefined) { + blocks[day] = []; + } + + offset++; + } + + const day = this.getDay(i + offset); + if (blocks[day] === undefined) { + blocks[day] = []; + } + + const $c = this.$($cols[i]); + if ($c.hasClass('object-cell-border')) { + const block = this.parseBlock($c); + blocks[day].push(block); + + this.colBlocked[i + offset] = block.durationSlots; + } + } + + return blocks; + } + + private parseBlock($block: Cheerio): IBlock { + const $tblTitle = $block.find('table:nth-child(1)'); + const $tblInfo = $block.find('table:nth-child(2)'); + const $tblLocation = $block.find('table:nth-child(3)'); + + const $tdRoom = $tblLocation.find('td:nth-child(1)'); + const $tdLecturer = $tblLocation.find('td:nth-child(2)'); + + // Remove the Modulhandbuch "mhb" link + $tblTitle.find('br + strong').remove(); + + const slots = parseInt($block.attr('rowspan')); + return { + title: $tblTitle.text().trim(), + durationHours: slots / 4, + durationSlots: slots, + info: $tblInfo.text().trim(), + room: $tdRoom.text().trim(), + lecturer: $tdLecturer.text().trim() + }; + } +} diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 0000000..4791fb2 --- /dev/null +++ b/src/index.ts @@ -0,0 +1,69 @@ +import {readFile} from 'fs'; +import {SplusParser} from './SplusParser'; +let createEvent = (arg) => {}; +let commit = () => {}; +if (process.argv.includes('ics')) { + createEvent = require('./ICal').createEvent; + commit = require('./ICal').commit; +} else { + createEvent = require('./GoogleCalendar').createEvent; +} + + +const splusUrl = 'http://splus.ostfalia.de/semesterplan123.php?id=1362F014835FFFD0F67159E302EC1A3C&identifier=%23SPLUS7A3292'; + +/* +get(splusUrl, (res) => { + let data = ''; + res.on('data', chunk => data += chunk); + res.on('end', () => TODO); +}); +*/ + +const baseDate = new Date(2018, 3, 23); + +readFile('etc/sample.htm', (err, data) => { + const lectures = new SplusParser(data.toString()).getLectures(lecture => { + // Filter some lectures out + if (lecture.title === '') return false; + if (lecture.title.indexOf('Mathe-Cafe') !== -1) return false; + if (lecture.title.indexOf('Mathe-Repetitorium') !== -1) return false; + if (lecture.title.indexOf('Informatik-Lounge') !== -1) return false; + if (lecture.title.indexOf('Reservierung') !== -1) return false; + return lecture.title.indexOf('Kompetenzen') === -1; + }); + + lectures.forEach((lec, index) => { + let beginDate = new Date(baseDate); + + beginDate.setDate(beginDate.getDate() + lec.day); + beginDate.setMinutes(beginDate.getMinutes() + lec.begin * 60); + + let endDate = new Date(baseDate); + endDate.setDate(endDate.getDate() + lec.day); + endDate.setMinutes(endDate.getMinutes() + lec.end * 60); + + console.log(lec.title, beginDate.toISOString(), endDate.toISOString()); + + createEvent({ + summary: lec.title, + description: lec.lecturer + (lec.info !== '' ? ' - ' : '') + lec.info, + location: lec.room, + start: { + dateTime: beginDate.toISOString() + }, + end: { + dateTime: endDate.toISOString() + }, + reminders: { + useDefault: false + } + }); + + // TODO + if (index == lectures.length - 1) { + commit(); + } + }) +}); + -- cgit v1.3.1