Browse Source

Initial commit.

main
Harvey Tindall 1 year ago
commit
13cab84fb2
4 changed files with 175 additions and 0 deletions
  1. 4
      .gitignore
  2. 21
      LICENSE.txt
  3. 19
      README.md
  4. 131
      import.py

4
.gitignore

@ -0,0 +1,4 @@
delete.py
*.timetable
token.pickle
credentials.json

21
LICENSE.txt

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2020 Harvey Tindall
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

19
README.md

@ -0,0 +1,19 @@
# classtime-calendar
Script to import a timetable from [ClassTime](https://play.google.com/store/apps/details?id=eu.nohus.classtime) into Google Calendar.
## Usage
```
typeprint.py [-tz|--timezone] <timezone> [-c|--calendar] <calendar id> <timetable>
```
* `--timezone`: Timezone in TZ database. Defaults to `Europe/London`.
* `--calendar`: ID of calendar to apply to. To find: [calendar settings](https://calendar.google.com/calendar/r/settings) > click your calendar under "Settings for my calendar", "Calendar ID" under Integration at bottom of page.
* `<timetable>`: .timetable file can be exported through the share menu in the ClassTime app.
On the first run, a browser window will open where you can login. Credentials are saved to `token.pickle`/`credentials.json`.
## Known problems
* Currently only works with A/B week timetables. Each should be imported separately, at which time the script will ask whether it is for the current week. If you have a single timetable, either import twice as different weeks or change `self.recurrence`'s interval.
* The delete from calendar script was too rough to include, so there's no way to quickly remove classes from the calendar.
## Licenses
Check LICENSE.txt. Code taken from quickstart.py in import.py includes Copyright notice

131
import.py

@ -0,0 +1,131 @@
#!/usr/bin/env python3
# Script to import a timetable from the app ClassTime into a google calendar.
# Calendar ID is found in the calendar's settings, DB file can be exported through the share button in the app.
# Only works with A/B timetables, which are exported separately.
import json, datetime, pickle, os.path, sys, argparse
from googleapiclient.discovery import build
from google_auth_oauthlib.flow import InstalledAppFlow
from google.auth.transport.requests import Request
parser = argparse.ArgumentParser()
parser.add_argument("timetable", help="filename of .timetable db.")
parser.add_argument("-tz", "--timezone", help="Timezone in TZ database format. Defaults to Europe/London.")
parser.add_argument("-c", "--calendar", help="ID of the calendar you are adding to. Found under the settings on calendar.google.com.")
try:
args = parser.parse_args()
calendar_id = args.calendar
dbfile = args.timetable
if not args.timezone:
timeZone = "Europe/London"
else:
timeZone = args.timezone
except FileNotFoundError:
print("No filename supplied.")
sys.exit()
print("This script only works for A/B week timetables, each being loaded seperately.")
choice = input("Is this timetable made for this week? [y/n]: ").lower()
if choice == "y":
today = datetime.datetime.today()
elif choice == "n":
today = datetime.datetime.today() + datetime.timedelta(days=7)
else:
sys.exit()
days = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"]
class Lesson:
def __init__(self, name, startDelta, endDelta, teacher, room, day):
self.summary = name
if teacher != "":
self.description = "Teacher: {}".format(teacher)
self.location = "Room: {}".format(room)
currentDay = days.index(today.strftime("%A"))
lessonDay = days.index(day)
dayDelta = lessonDay - currentDay
applyDate = (today + datetime.timedelta(days=dayDelta)).day
# the .timetable file stores lesson start & end times as minutes from 00:00, so they are converted here
self.start = {
"dateTime": datetime.datetime(today.year, today.month, applyDate, int(startDelta/60), int(startDelta%60)).isoformat(),
"timeZone": timeZone
}
self.end = {
"dateTime": datetime.datetime(today.year, today.month, applyDate, int(endDelta/60), int(endDelta%60)).isoformat(),
"timeZone": timeZone
}
# Change this if you have the same timetable each week
self.recurrence = ["RRULE:FREQ=WEEKLY;INTERVAL=2"]
lessons = []
try:
with open(dbfile, "r") as raw:
db = json.load(raw)
for d in range(1,6):
try:
for l in range(1,6):
name = db["LESSON_{}_{}".format(d,l)]
startDelta = db["LESSON_START_TIME_{}_{}".format(d,l)]
endDelta = db["LESSON_END_TIME_{}_{}".format(d,l)]
teacher = db["TEACHER_{}_{}".format(d,l)]
room = db["ROOM_{}_{}".format(d,l)]
day = days[d-1]
if name != "":
lessons.append(Lesson(name, startDelta, endDelta, teacher, room, day))
except:
pass
except FileNotFoundError:
print("File not found.")
sys.exit()
print("Lessons imported. Do these look right?")
for l in lessons:
try:
d = l.description+", "
except:
d = ""
print("{}: {}, {}{}".format(l.start["dateTime"], l.summary, d, l.location))
if input(":) ? [y/n]: ").lower() != "y":
sys.exit()
# Copyright 2018 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# borrowed from https://github.com/gsuitedevs/python-samples/blob/master/drive/quickstart/quickstart.py
# Removed unnecessary libraries, comments, and changed scope.
SCOPES = ['https://www.googleapis.com/auth/calendar']
creds = None
if os.path.exists('token.pickle'):
with open('token.pickle', 'rb') as token:
creds = pickle.load(token)
if not creds or not creds.valid:
if creds and creds.expired and creds.refresh_token:
creds.refresh(Request())
else:
flow = InstalledAppFlow.from_client_secrets_file(
'credentials.json', SCOPES)
creds = flow.run_local_server(port=0)
# Save the credentials for the next run
with open('token.pickle', 'wb') as token:
pickle.dump(creds, token)
service = build('calendar', 'v3', credentials=creds)
for l in lessons:
event = json.loads(json.dumps(l.__dict__))
event = service.events().insert(calendarId=calendar_id, body=event).execute()
print("Events Added. Refresh your calendar to see classes.")
Loading…
Cancel
Save