How to copy lecture information from opas.peppi.utu.fi to Google Calendar

Hello,

If you found this page you probably have also missing feature from peppi.utu.fi i.e. way to get the lecture dates to your calendar. I wrote small program to do the job. The code is a little hack, not very nice one so I instruct you how to do your own..

I chose Python 3 as programming language. I wrote the app in Windows environment but should work also in Linux without problem. ActivePython can be found from https://www.activestate.com/activepython/downloads. Select the Python 3 version.

In Python it is easy to do user interface using PyQt3. Once you have the ActivePython installed, install PyQt3 from command line:

pip3 install PyQt5

I used Google Calendar API as I am using Gmail. The API instructions for Python can be found from

https://developers.google.com/calendar/quickstart/python

In order you to use Calendar API follow the instructions on that page.. find the ENABLE THE GOOGLE CALENDAR API button. You’ll figure out the details (I hope).

Behind that button you should get Client ID and Client Secret, you need this in your Python code.


Before going to the UI part, let’s check the lecture information here:

https://opas.peppi.utu.fi/en/course/DTEK8103/9933

You’ll see the header “DTEK8103 Software Design and Architecture”, that is the one you probably want to see in the calendar (atleast I did). Also I would like to see the lecture counter current/max ..meaning this format.. first lecture out of 4.

(1/4) DTEK8103 Software Design and Architecture

Then under Implementations header there is lecture times under “Teaching” header (on some other pages there can be more lecture times under some button).

02.10.2018 14:00-16:00 XIV, Natura luentosali XIV
09.10.2018 14:00-16:00 XIV, Natura luentosali XIV
16.10.2018 14:00-16:00 XIV, Natura luentosali XIV
23.10.2018 14:00-16:00 XIV, Natura luentosali XIV
so.. Our program should input the header of the course and the schdule. It should be able to parse those rows, to get the times out. For parsing we use regular expression. You can try regular expressions in online services like this: https://regex101.com/
The format of the row is
dd.mm.yyyy hh:mm-hh:mm text
where dd can be between 01-31, mm between 01-12, year >=2018, hh between 00-23, mm between 00-59 and text is just free text. To parse the line using regex we can use this:
# day month year start hh start mm end hh end mm course location
# 1 2 3 4 5 6 7 8 9 
lecture = re.compile('([0123][0-9])\.([01][0-9])\.([0-9]{4})\s+([012][0-9]):([012][0-9])-([012][0-9]):([012][0-9])\s+(\S+),\s+(.*)')

so.. what we have done this far..

  • installed Python 3
  • installed PyQt3
  • activacted Google Calendar API
  • figured how to parse the teaching schedule

Let’s create UI. For UI we need textbox for the lecture header and multiline textbox for the teaching schedule. We take copy from the opas.peppi.utu.fi page (the header) to header field and copy teaching schedule to teaching schedule box. Also some button for sending the calendar items to Google Calendar.

from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import * 
import datetime
import re
import sys
import datetime
from googleapiclient.discovery import build
from httplib2 import Http
from oauth2client import file, client, tools

# copy your calendar id to next variable..
# format is <some crypting stuff>@group.calendar.google.com
CALENDAR_ID = '<copy here your calendar id>'


class Form(QDialog):
    def __init__(self):
        super(Form, self).__init__()
        self.setWindowTitle("Lecture to Google Calendar")
        self.move(50,50)
        self.resize(1024,500)
        self.createFormGroupBox()
 
        self.mainLayout = QVBoxLayout()
        self.mainLayout.addWidget(self.formGroupBox)
        self.setLayout(self.mainLayout)

    def createFormGroupBox(self):
        self.formGroupBox = QGroupBox("Lecture Information")
        layout = QFormLayout(self)
        self.courseName = QLineEdit()
        layout.addRow(QLabel("Lecture Name (Paste Here):"), self.courseName)
        self.lecturesBox = QTextEdit()
        layout.addRow(QLabel("Lecture Schedule (Paste Here)"), self.lecturesBox)

self.parsedTimes = QTableWidget()
        self.parsedTimes.setRowCount(15)
        self.parsedTimes.setColumnCount(4)
        header = self.parsedTimes.horizontalHeader()
        header.setSectionResizeMode(0, QHeaderView.ResizeToContents)
        header.setSectionResizeMode(1, QHeaderView.ResizeToContents)
        header.setSectionResizeMode(2, QHeaderView.ResizeToContents)
        header.setSectionResizeMode(3, QHeaderView.ResizeToContents)
        self.parsedTimes.setHorizontalHeaderLabels("Start;End;Course;Class room".split(";"))

        layout.addRow(QLabel("Parsed"), self.parsedTimes)
        self.parseSchedule = QPushButton('Parse')
        self.parseSchedule.clicked.connect(self.onParse)
        self.addToCalendar = QPushButton('Add to Google Calendar')
        self.addToCalendar.clicked.connect(self.onAddCalendar)
        hbox = QHBoxLayout()
        hbox.addWidget(self.parseSchedule)
        hbox.addWidget(self.addToCalendar)
        layout.addRow(QLabel(""), hbox)
    def onAddCalendar(self):
        self.Process(True)
        
    def onParse(self):
        self.Process(False)

    def Process(self, sendFlag):
        courseName = self.courseName.text()

        lecture = re.compile('([0123][0-9])\.([01][0-9])\.([0-9]{4})\s+([012][0-9]):([012][0-9])-([012][0-9]):([012][0-9])\s+(\S+),\s+(.*)')
        rows = self.lecturesBox.toPlainText().split("\n")
        lecture_counter = 0
        for r in rows:
            matched = lecture.match(r)
            if matched:
                lecture_counter = lecture_counter + 1

        self.parsedTimes.setRowCount(lecture_counter)
        no_lectures = lecture_counter

        lecture_counter = 0
        for r in rows:
            matched = lecture.match(r)
            if matched:
                lecture_counter = lecture_counter + 1
                self.parsedTimes.setItem(lecture_counter-1, 0, QTableWidgetItem(matched[1] + "." + matched[2] + "." + matched[3] + " " + matched[4] + ":" + matched[5]))
                self.parsedTimes.setItem(lecture_counter-1, 1, QTableWidgetItem(matched[1] + "." + matched[2] + "." + matched[3] + " " + matched[6] + ":" + matched[7]))
                self.parsedTimes.setItem(lecture_counter-1, 2, QTableWidgetItem("(" + str(lecture_counter) + "/" + str(no_lectures) + ") " + courseName))
                self.parsedTimes.setItem(lecture_counter-1, 3, QTableWidgetItem(matched[9]))
                start_datetime_object = datetime.datetime(int(matched[3]), int(matched[2]), int(matched[1]), int(matched[4]), int(matched[5]))
                end_datetime_object = datetime.datetime(int(matched[3]), int(matched[2]), int(matched[1]), int(matched[6]), int(matched[7]))
                location = matched[8] + " " + matched[9]

                if sendFlag:
                    self.addToGoogleCalendar(courseName, start_datetime_object, end_datetime_object, lecture_counter, no_lectures, location, "description")

    def addToGoogleCalendar(self,name,startDateTime,endDateTime,lecture_counter,no_lectures, location,description):
        SCOPES = 'https://www.googleapis.com/auth/calendar'

        date_format = '%Y-%m-%dT%H:%M:%S'
        
        event = {
          'summary': "(" + str(lecture_counter) + "/" + str(no_lectures) + ") " + name,
          'location': location,
          'description': description,
          'start': {
            'dateTime': startDateTime.strftime(date_format), 
            'timeZone': 'EET',
          },
          'end': {
            'dateTime': endDateTime.strftime(date_format), 
            'timeZone': 'EET',
          },
          'attendees': [
            {'email': 'your.email@somewhere.com'},
            {'email': 'your.otheremail@somewhere.com'},
          ],
          'reminders': {
            'useDefault': False,
            'overrides': [
              {'method': 'email', 'minutes': 2 * 60},
              {'method': 'popup', 'minutes': 40},
            ],
          }
        }
        event = service.events().insert(calendarId=CALENDAR_ID, body=event, sendNotifications=True).execute()

if __name__ == '__main__':
    app = QApplication(sys.argv)
    form = Form()
    sys.exit(form.exec_())

 

run from command line using python3:

python3 lecture_to_google_calendar.py

It should look like this:

if it doesn’t or if there is bugs in above code, let me know ijthun@utu.fi

How to use it?

  • copy name of the course/lecture to Lecture name field
  • schedule to Lecture Schedule.
  • Then press Parse button.. lectures should be parsed into the Parsed table.
  • If it seems to be ok, press Add to Google Calendar button and there we go

after this check your google calendar.. if something wrong, fix the bugs.

use saved time for .. something useful…