From 3948e0b316aac63f8f9885917e09f6dcaf378349 Mon Sep 17 00:00:00 2001 From: Oliver Bell Date: Sat, 22 Sep 2018 14:19:44 +0100 Subject: [PATCH] initial commit --- .gitignore | 117 +++++++++++++++++++++++++++++++++++++ Pipfile | 16 ++++++ Pipfile.lock | 159 +++++++++++++++++++++++++++++++++++++++++++++++++++ main.py | 88 ++++++++++++++++++++++++++++ 4 files changed, 380 insertions(+) create mode 100644 .gitignore create mode 100644 Pipfile create mode 100644 Pipfile.lock create mode 100644 main.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ba17f27 --- /dev/null +++ b/.gitignore @@ -0,0 +1,117 @@ +credentials.json +token.json +timetableurl + +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +.hypothesis/ +.pytest_cache/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +.python-version + +# celery beat schedule file +celerybeat-schedule + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +.vscode \ No newline at end of file diff --git a/Pipfile b/Pipfile new file mode 100644 index 0000000..3b4d6c9 --- /dev/null +++ b/Pipfile @@ -0,0 +1,16 @@ +[[source]] +name = "pypi" +verify_ssl = true +url = "https://pypi.org/simple" + +[packages] +google-api-python-client = "*" +"oauth2client" = "*" +requests = "*" +"bs4" = "*" +pyjsparser = "*" + +[dev-packages] + +[requires] +python_version = "3.5" diff --git a/Pipfile.lock b/Pipfile.lock new file mode 100644 index 0000000..65d558d --- /dev/null +++ b/Pipfile.lock @@ -0,0 +1,159 @@ +{ + "_meta": { + "hash": { + "sha256": "4f51c5cdd18415e19bcc08f7b94be7674d2ed4295d49a0668d611e3e0e284e3d" + }, + "pipfile-spec": 6, + "requires": { + "python_version": "3.5" + }, + "sources": [ + { + "name": "pypi", + "url": "https://pypi.org/simple", + "verify_ssl": true + } + ] + }, + "default": { + "beautifulsoup4": { + "hashes": [ + "sha256:194ec62a25438adcb3fdb06378b26559eda1ea8a747367d34c33cef9c7f48d57", + "sha256:90f8e61121d6ae58362ce3bed8cd997efb00c914eae0ff3d363c32f9a9822d10", + "sha256:f0abd31228055d698bb392a826528ea08ebb9959e6bea17c606fd9c9009db938" + ], + "version": "==4.6.3" + }, + "bs4": { + "hashes": [ + "sha256:36ecea1fd7cc5c0c6e4a1ff075df26d50da647b75376626cc186e2212886dd3a" + ], + "index": "pypi", + "version": "==0.0.1" + }, + "cachetools": { + "hashes": [ + "sha256:90f1d559512fc073483fe573ef5ceb39bf6ad3d39edc98dc55178a2b2b176fa3", + "sha256:d1c398969c478d336f767ba02040fa22617333293fb0b8968e79b16028dfee35" + ], + "version": "==2.1.0" + }, + "certifi": { + "hashes": [ + "sha256:376690d6f16d32f9d1fe8932551d80b23e9d393a8578c5633a2ed39a64861638", + "sha256:456048c7e371c089d0a77a5212fb37a2c2dce1e24146e3b7e0261736aaeaa22a" + ], + "version": "==2018.8.24" + }, + "chardet": { + "hashes": [ + "sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae", + "sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691" + ], + "version": "==3.0.4" + }, + "google-api-python-client": { + "hashes": [ + "sha256:5d5cb02c6f3112c68eed51b74891a49c0e35263380672d662f8bfe85b8114d7c", + "sha256:7cc47cf80b25ecd7f3d917ea247bb6c62587514e40604ae29c47c0e4ebd1174b" + ], + "index": "pypi", + "version": "==1.7.4" + }, + "google-auth": { + "hashes": [ + "sha256:9ca363facbf2622d9ba828017536ccca2e0f58bd15e659b52f312172f8815530", + "sha256:a4cf9e803f2176b5de442763bd339b313d3f1ed3002e3e1eb6eec1d7c9bbc9b4" + ], + "version": "==1.5.1" + }, + "google-auth-httplib2": { + "hashes": [ + "sha256:098fade613c25b4527b2c08fa42d11f3c2037dda8995d86de0745228e965d445", + "sha256:f1c437842155680cf9918df9bc51c1182fda41feef88c34004bd1978c8157e08" + ], + "version": "==0.0.3" + }, + "httplib2": { + "hashes": [ + "sha256:e71daed9a0e6373642db61166fa70beecc9bf04383477f84671348c02a04cbdf" + ], + "version": "==0.11.3" + }, + "idna": { + "hashes": [ + "sha256:156a6814fb5ac1fc6850fb002e0852d56c0c8d2531923a51032d1b70760e186e", + "sha256:684a38a6f903c1d71d6d5fac066b58d7768af4de2b832e426ec79c30daa94a16" + ], + "version": "==2.7" + }, + "oauth2client": { + "hashes": [ + "sha256:bd3062c06f8b10c6ef7a890b22c2740e5f87d61b6e1f4b1c90d069cdfc9dadb5", + "sha256:cf061f52f75e91d489bf5c276498f8af2655fe331b454f10022441513cf445a6" + ], + "index": "pypi", + "version": "==4.1.2" + }, + "pyasn1": { + "hashes": [ + "sha256:b9d3abc5031e61927c82d4d96c1cec1e55676c1a991623cfed28faea73cdd7ca", + "sha256:f58f2a3d12fd754aa123e9fa74fb7345333000a035f3921dbdaa08597aa53137" + ], + "version": "==0.4.4" + }, + "pyasn1-modules": { + "hashes": [ + "sha256:a0cf3e1842e7c60fde97cb22d275eb6f9524f5c5250489e292529de841417547", + "sha256:a38a8811ea784c0136abfdba73963876328f66172db21a05a82f9515909bfb4e" + ], + "version": "==0.2.2" + }, + "pyjsparser": { + "hashes": [ + "sha256:e4a659df3db42a2ff9fbc961eb6d4076a0b945e1aadfc20d48f913ad5dca011d" + ], + "index": "pypi", + "version": "==2.5.2" + }, + "requests": { + "hashes": [ + "sha256:63b52e3c866428a224f97cab011de738c36aec0185aa91cfacd418b5d58911d1", + "sha256:ec22d826a36ed72a7358ff3fe56cbd4ba69dd7a6718ffd450ff0e9df7a47ce6a" + ], + "index": "pypi", + "version": "==2.19.1" + }, + "rsa": { + "hashes": [ + "sha256:25df4e10c263fb88b5ace923dd84bf9aa7f5019687b5e55382ffcdb8bede9db5", + "sha256:43f682fea81c452c98d09fc316aae12de6d30c4b5c84226642cf8f8fd1c93abd" + ], + "version": "==3.4.2" + }, + "six": { + "hashes": [ + "sha256:70e8a77beed4562e7f14fe23a786b54f6296e34344c23bc42f07b15018ff98e9", + "sha256:832dc0e10feb1aa2c68dcc57dbb658f1c7e65b9b61af69048abc87a2db00a0eb" + ], + "version": "==1.11.0" + }, + "uritemplate": { + "hashes": [ + "sha256:01c69f4fe8ed503b2951bef85d996a9d22434d2431584b5b107b2981ff416fbd", + "sha256:1b9c467a940ce9fb9f50df819e8ddd14696f89b9a8cc87ac77952ba416e0a8fd", + "sha256:c02643cebe23fc8adb5e6becffe201185bf06c40bda5c0b4028a93f1527d011d" + ], + "version": "==3.0.0" + }, + "urllib3": { + "hashes": [ + "sha256:a68ac5e15e76e7e5dd2b8f94007233e01effe3e50e8daddf69acfd81cb686baf", + "sha256:b5725a0bd4ba422ab0e66e89e030c806576753ea3ee08554382c14e685d117b5" + ], + "markers": "python_version != '3.0.*' and python_version < '4' and python_version != '3.3.*' and python_version >= '2.6' and python_version != '3.2.*' and python_version != '3.1.*'", + "version": "==1.23" + } + }, + "develop": {} +} diff --git a/main.py b/main.py new file mode 100644 index 0000000..37fc900 --- /dev/null +++ b/main.py @@ -0,0 +1,88 @@ +import requests +from bs4 import BeautifulSoup +from datetime import datetime +from googleapiclient.discovery import build +from httplib2 import Http +from oauth2client import file, client, tools + +URL = open("timetableurl").read().strip() + +def parse_events(page_data): + soup = BeautifulSoup(page_data, features="html.parser") + + source = "" + for script in soup.head.findAll("script", {"type": "text/javascript"}): + if not script.has_attr("src"): + source = script.text + break + + events_data = source.split("events:")[1].split("]")[0] +"]" + + # Replace date objects with tuples, easier to parse + events_data = events_data.replace("new Date", "") + + cleaned_data = "" + + # Remove comments, properties to keys + for line in events_data.split("\n"): + comment_pos = line.find("//") + if comment_pos != -1: + line = line[:comment_pos] + + + if ":" in line: + line_values = line.split(":") + line = "'" + line_values[0] + "': " + line_values[1] + + cleaned_data += line + "\n" + + parsed_data = eval(cleaned_data) + + for event in parsed_data: + if "start" in event: + event["start"] = list(event["start"]) + event["start"][1] += 1 + event["start"] = datetime(*event["start"]) + + if "end" in event: + event["end"] = list(event["end"]) + event["end"][1] += 1 + event["end"] = datetime(*event["end"]) + + return parsed_data + +# If modifying these scopes, delete the file token.json. +SCOPES = 'https://www.googleapis.com/auth/calendar.readonly' + +def main(): + """Shows basic usage of the Google Calendar API. + Prints the start and name of the next 10 events on the user's calendar. + """ + store = file.Storage('token.json') + creds = store.get() + if not creds or creds.invalid: + flow = client.flow_from_clientsecrets('credentials.json', SCOPES) + creds = tools.run_flow(flow, store) + service = build('calendar', 'v3', http=creds.authorize(Http())) + + # Call the Calendar API + now = datetime.utcnow().isoformat() + 'Z' # 'Z' indicates UTC time + print('Getting the upcoming 10 events') + events_result = service.events().list(calendarId='primary', timeMin=now, + maxResults=10, singleEvents=True, + orderBy='startTime').execute() + events = events_result.get('items', []) + + if not events: + print('No upcoming events found.') + for event in events: + start = event['start'].get('dateTime', event['start'].get('date')) + print(start, event['summary']) + + new_event = parse_events(requests.get(URL).text)[0] + new_event["title"] += " - " + new_event["lecturer"] + new_event["description"] = new_event[""] + print(new_event) + service.events().insert(body=new_event).execute() + +main()