Compare commits
17 Commits
1985a196b7
..
main
| Author | SHA1 | Date | |
|---|---|---|---|
| a5e622b950 | |||
| 9cd7505517 | |||
| c0ed56d9e0 | |||
| 75e0fd8a47 | |||
| f41c0b5806 | |||
| da8a7afb56 | |||
| 647c20955c | |||
| 2faabee6c4 | |||
| efbd8f5122 | |||
| 4f9c4716ef | |||
| a5ea31fa6f | |||
| 8a9fa7a954 | |||
| 2573093694 | |||
| 0de41e3522 | |||
| c897eb8265 | |||
| 1821d86a1e | |||
| 1dfaac75d2 |
@@ -57,7 +57,9 @@ jobs:
|
||||
- name: Install package
|
||||
run: poetry install --no-interaction
|
||||
- name: Generate new calendar Files
|
||||
run: poetry run generate-ics
|
||||
run: mkdir output && poetry run generate-ics | tee output/run_logs.txt
|
||||
env:
|
||||
GOOGLE_SERVICE_ACCOUNT_CREDS: ${{ secrets.GOOGLE_SERVICE_ACCOUNT_CREDS }}
|
||||
#----------------------------------------------
|
||||
# load cached venv if cache exists
|
||||
#----------------------------------------------
|
||||
@@ -66,7 +68,7 @@ jobs:
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: output
|
||||
key: calendar-files-${{ hashFiles('output/**') }}
|
||||
key: calendar-files-${{ hashFiles('output/**/*.ics') }}
|
||||
#----------------------------------------------
|
||||
# publish artifacts and release
|
||||
#----------------------------------------------
|
||||
@@ -90,5 +92,6 @@ jobs:
|
||||
with:
|
||||
name: ${{ steps.time.outputs.time }}
|
||||
tag_name: ${{ steps.time_tag.outputs.tag }}
|
||||
body_path: output/run_logs.txt
|
||||
files: |-
|
||||
calendars.zip
|
||||
@@ -29,10 +29,10 @@ poetry run generate-ics
|
||||
```
|
||||
|
||||
## TODO
|
||||
* sync events to gcal by icaluid on every release via gcal api
|
||||
* ~~sync events to gcal by icaluid on every release via gcal api~~
|
||||
* ~~compare event ids from last sync to current and highlight changes to previously selected events in release~~
|
||||
* modify runner scripts to stop running on events that have already elapsed
|
||||
* refactor event scrapers to use a config containing start/stop and other common metadata
|
||||
* create multi-select webui
|
||||
* save selected event uids in cookie
|
||||
* compare event ids from last sync to current and highlight changes to previously selected events
|
||||
* refactor event scrapers to use a config containing start/stop and other common metadata
|
||||
* modify runner scripts to stop running on events that have already elapsed
|
||||
* add support for other events like lbx
|
||||
|
||||
Executable
+18
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"start": 1744948800,
|
||||
"end": 1745207999,
|
||||
"calendars": {
|
||||
"Sakura-Con Presents": "f2802717d1727828bf1bfe9f6cc35844a36ffff6daad7c3fa293ab35bf51c495@group.calendar.google.com",
|
||||
"Fan Panel": "8062c73edfb8db11c385b255531d97ba7a30672a5e6ded4e24cd589b25282d65@group.calendar.google.com",
|
||||
"Gaming": "46c95e247f9e449f2968b44edfe659560a232efbf57576efc9f15ba8668ee0ff@group.calendar.google.com",
|
||||
"Main Stage": "ae20459e6d4c41b46496663b04ec87279fb1a0ed858351ea796d82c217e20579@group.calendar.google.com",
|
||||
"Cultural Panel": "633adb5a3a96b984092c8e641dda4011b80a8a314d03fc0c6e941895caabf2d2@group.calendar.google.com",
|
||||
"Guest of Honor": "da7b9a6845eb7868457147046aa388101ef2601cffbaf4d29f1a1c8d00a8bea9@group.calendar.google.com",
|
||||
"Charity Auction": "a51250c24fd2c4fd2d4bbefc94c055eb1f392ccf89d93799d4a92ff2c319f436@group.calendar.google.com",
|
||||
"Industry Panel": "fe3a9a7e1e839dd41ac934bb0b79287d4ff705f577f726d06b0f4249bae1ac40@group.calendar.google.com",
|
||||
"Autographs": "1959b9a550cbdf73d7997c070009ea836c1af93d8330353bbfdbbc2f82aff27a@group.calendar.google.com",
|
||||
"Summit Stage": "106d7620bbe3a8e14ee593758e7db9c71a311615e3a17ada938f50974b0b9ccf@group.calendar.google.com"
|
||||
},
|
||||
"tzSuffix": "-07:00",
|
||||
"uidPrefix": "sakuracon2025-"
|
||||
}
|
||||
Generated
+392
-1
@@ -164,6 +164,18 @@ docs = ["cogapp", "furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphi
|
||||
tests = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"]
|
||||
tests-mypy = ["mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\""]
|
||||
|
||||
[[package]]
|
||||
name = "cachetools"
|
||||
version = "5.5.2"
|
||||
description = "Extensible memoizing collections and decorators"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "cachetools-5.5.2-py3-none-any.whl", hash = "sha256:d26a22bcc62eb95c3beabd9f1ee5e820d3d2704fe2967cbe350e20c8ffcd3f0a"},
|
||||
{file = "cachetools-5.5.2.tar.gz", hash = "sha256:1a661caa9175d26759571b2e19580f9d6393969e5dfca11fdb1f947a23e640d4"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "certifi"
|
||||
version = "2025.1.31"
|
||||
@@ -396,6 +408,146 @@ files = [
|
||||
{file = "frozenlist-1.5.0.tar.gz", hash = "sha256:81d5af29e61b9c8348e876d442253723928dce6433e0e76cd925cd83f1b4b817"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "google-api-core"
|
||||
version = "2.24.2"
|
||||
description = "Google API client core library"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "google_api_core-2.24.2-py3-none-any.whl", hash = "sha256:810a63ac95f3c441b7c0e43d344e372887f62ce9071ba972eacf32672e072de9"},
|
||||
{file = "google_api_core-2.24.2.tar.gz", hash = "sha256:81718493daf06d96d6bc76a91c23874dbf2fac0adbbf542831b805ee6e974696"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
google-auth = ">=2.14.1,<3.0.0"
|
||||
googleapis-common-protos = ">=1.56.2,<2.0.0"
|
||||
proto-plus = [
|
||||
{version = ">=1.25.0,<2.0.0", markers = "python_version >= \"3.13\""},
|
||||
{version = ">=1.22.3,<2.0.0", markers = "python_version < \"3.13\""},
|
||||
]
|
||||
protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<7.0.0"
|
||||
requests = ">=2.18.0,<3.0.0"
|
||||
|
||||
[package.extras]
|
||||
async-rest = ["google-auth[aiohttp] (>=2.35.0,<3.0.dev0)"]
|
||||
grpc = ["grpcio (>=1.33.2,<2.0dev)", "grpcio (>=1.49.1,<2.0dev) ; python_version >= \"3.11\"", "grpcio-status (>=1.33.2,<2.0.dev0)", "grpcio-status (>=1.49.1,<2.0.dev0) ; python_version >= \"3.11\""]
|
||||
grpcgcp = ["grpcio-gcp (>=0.2.2,<1.0.dev0)"]
|
||||
grpcio-gcp = ["grpcio-gcp (>=0.2.2,<1.0.dev0)"]
|
||||
|
||||
[[package]]
|
||||
name = "google-api-python-client"
|
||||
version = "2.166.0"
|
||||
description = "Google API Client Library for Python"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "google_api_python_client-2.166.0-py2.py3-none-any.whl", hash = "sha256:dd8cc74d9fc18538ab05cbd2e93cb4f82382f910c5f6945db06c91f1deae6e45"},
|
||||
{file = "google_api_python_client-2.166.0.tar.gz", hash = "sha256:b8cf843bd9d736c134aef76cf1dc7a47c9283a2ef24267b97207b9dd43b30ef7"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
google-api-core = ">=1.31.5,<2.0.dev0 || >2.3.0,<3.0.0"
|
||||
google-auth = ">=1.32.0,<2.24.0 || >2.24.0,<2.25.0 || >2.25.0,<3.0.0"
|
||||
google-auth-httplib2 = ">=0.2.0,<1.0.0"
|
||||
httplib2 = ">=0.19.0,<1.0.0"
|
||||
uritemplate = ">=3.0.1,<5"
|
||||
|
||||
[[package]]
|
||||
name = "google-auth"
|
||||
version = "2.38.0"
|
||||
description = "Google Authentication Library"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "google_auth-2.38.0-py2.py3-none-any.whl", hash = "sha256:e7dae6694313f434a2727bf2906f27ad259bae090d7aa896590d86feec3d9d4a"},
|
||||
{file = "google_auth-2.38.0.tar.gz", hash = "sha256:8285113607d3b80a3f1543b75962447ba8a09fe85783432a784fdeef6ac094c4"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
cachetools = ">=2.0.0,<6.0"
|
||||
pyasn1-modules = ">=0.2.1"
|
||||
rsa = ">=3.1.4,<5"
|
||||
|
||||
[package.extras]
|
||||
aiohttp = ["aiohttp (>=3.6.2,<4.0.0.dev0)", "requests (>=2.20.0,<3.0.0.dev0)"]
|
||||
enterprise-cert = ["cryptography", "pyopenssl"]
|
||||
pyjwt = ["cryptography (>=38.0.3)", "pyjwt (>=2.0)"]
|
||||
pyopenssl = ["cryptography (>=38.0.3)", "pyopenssl (>=20.0.0)"]
|
||||
reauth = ["pyu2f (>=0.1.5)"]
|
||||
requests = ["requests (>=2.20.0,<3.0.0.dev0)"]
|
||||
|
||||
[[package]]
|
||||
name = "google-auth-httplib2"
|
||||
version = "0.2.0"
|
||||
description = "Google Authentication Library: httplib2 transport"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "google-auth-httplib2-0.2.0.tar.gz", hash = "sha256:38aa7badf48f974f1eb9861794e9c0cb2a0511a4ec0679b1f886d108f5640e05"},
|
||||
{file = "google_auth_httplib2-0.2.0-py2.py3-none-any.whl", hash = "sha256:b65a0a2123300dd71281a7bf6e64d65a0759287df52729bdd1ae2e47dc311a3d"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
google-auth = "*"
|
||||
httplib2 = ">=0.19.0"
|
||||
|
||||
[[package]]
|
||||
name = "google-auth-oauthlib"
|
||||
version = "1.2.1"
|
||||
description = "Google Authentication Library"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "google_auth_oauthlib-1.2.1-py2.py3-none-any.whl", hash = "sha256:2d58a27262d55aa1b87678c3ba7142a080098cbc2024f903c62355deb235d91f"},
|
||||
{file = "google_auth_oauthlib-1.2.1.tar.gz", hash = "sha256:afd0cad092a2eaa53cd8e8298557d6de1034c6cb4a740500b5357b648af97263"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
google-auth = ">=2.15.0"
|
||||
requests-oauthlib = ">=0.7.0"
|
||||
|
||||
[package.extras]
|
||||
tool = ["click (>=6.0.0)"]
|
||||
|
||||
[[package]]
|
||||
name = "googleapis-common-protos"
|
||||
version = "1.69.2"
|
||||
description = "Common protobufs used in Google APIs"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "googleapis_common_protos-1.69.2-py3-none-any.whl", hash = "sha256:0b30452ff9c7a27d80bfc5718954063e8ab53dd3697093d3bc99581f5fd24212"},
|
||||
{file = "googleapis_common_protos-1.69.2.tar.gz", hash = "sha256:3e1b904a27a33c821b4b749fd31d334c0c9c30e6113023d495e48979a3dc9c5f"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
protobuf = ">=3.20.2,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<7.0.0"
|
||||
|
||||
[package.extras]
|
||||
grpc = ["grpcio (>=1.44.0,<2.0.0)"]
|
||||
|
||||
[[package]]
|
||||
name = "httplib2"
|
||||
version = "0.22.0"
|
||||
description = "A comprehensive HTTP client library."
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "httplib2-0.22.0-py3-none-any.whl", hash = "sha256:14ae0a53c1ba8f3d37e9e27cf37eabb0fb9980f435ba405d546948b009dd64dc"},
|
||||
{file = "httplib2-0.22.0.tar.gz", hash = "sha256:d7a10bc5ef5ab08322488bde8c726eeee5c8618723fdb399597ec58f3d82df81"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
pyparsing = {version = ">=2.4.2,<3.0.0 || >3.0.0,<3.0.1 || >3.0.1,<3.0.2 || >3.0.2,<3.0.3 || >3.0.3,<4", markers = "python_version > \"3.0\""}
|
||||
|
||||
[[package]]
|
||||
name = "icalendar"
|
||||
version = "6.1.3"
|
||||
@@ -430,6 +582,24 @@ files = [
|
||||
[package.extras]
|
||||
all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2)"]
|
||||
|
||||
[[package]]
|
||||
name = "jsondiff"
|
||||
version = "2.2.1"
|
||||
description = "Diff JSON and JSON-like structures in Python"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "jsondiff-2.2.1-py3-none-any.whl", hash = "sha256:b1f0f7e2421881848b1d556d541ac01a91680cfcc14f51a9b62cdf4da0e56722"},
|
||||
{file = "jsondiff-2.2.1.tar.gz", hash = "sha256:658d162c8a86ba86de26303cd86a7b37e1b2c1ec98b569a60e2ca6180545f7fe"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
pyyaml = "*"
|
||||
|
||||
[package.extras]
|
||||
dev = ["build", "hypothesis", "pytest", "setuptools-scm"]
|
||||
|
||||
[[package]]
|
||||
name = "multidict"
|
||||
version = "6.3.2"
|
||||
@@ -535,6 +705,23 @@ files = [
|
||||
[package.dependencies]
|
||||
typing-extensions = {version = ">=4.1.0", markers = "python_version < \"3.11\""}
|
||||
|
||||
[[package]]
|
||||
name = "oauthlib"
|
||||
version = "3.2.2"
|
||||
description = "A generic, spec-compliant, thorough implementation of the OAuth request-signing logic"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "oauthlib-3.2.2-py3-none-any.whl", hash = "sha256:8139f29aac13e25d502680e9e19963e83f16838d48a0d71c287fe40e7067fbca"},
|
||||
{file = "oauthlib-3.2.2.tar.gz", hash = "sha256:9859c40929662bec5d64f34d01c99e093149682a3f38915dc0655d5a633dd918"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
rsa = ["cryptography (>=3.0.0)"]
|
||||
signals = ["blinker (>=1.4.0)"]
|
||||
signedtoken = ["cryptography (>=3.0.0)", "pyjwt (>=2.0.0,<3)"]
|
||||
|
||||
[[package]]
|
||||
name = "propcache"
|
||||
version = "0.3.1"
|
||||
@@ -643,6 +830,85 @@ files = [
|
||||
{file = "propcache-0.3.1.tar.gz", hash = "sha256:40d980c33765359098837527e18eddefc9a24cea5b45e078a7f3bb5b032c6ecf"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proto-plus"
|
||||
version = "1.26.1"
|
||||
description = "Beautiful, Pythonic protocol buffers"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "proto_plus-1.26.1-py3-none-any.whl", hash = "sha256:13285478c2dcf2abb829db158e1047e2f1e8d63a077d94263c2b88b043c75a66"},
|
||||
{file = "proto_plus-1.26.1.tar.gz", hash = "sha256:21a515a4c4c0088a773899e23c7bbade3d18f9c66c73edd4c7ee3816bc96a012"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
protobuf = ">=3.19.0,<7.0.0"
|
||||
|
||||
[package.extras]
|
||||
testing = ["google-api-core (>=1.31.5)"]
|
||||
|
||||
[[package]]
|
||||
name = "protobuf"
|
||||
version = "6.30.2"
|
||||
description = ""
|
||||
optional = false
|
||||
python-versions = ">=3.9"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "protobuf-6.30.2-cp310-abi3-win32.whl", hash = "sha256:b12ef7df7b9329886e66404bef5e9ce6a26b54069d7f7436a0853ccdeb91c103"},
|
||||
{file = "protobuf-6.30.2-cp310-abi3-win_amd64.whl", hash = "sha256:7653c99774f73fe6b9301b87da52af0e69783a2e371e8b599b3e9cb4da4b12b9"},
|
||||
{file = "protobuf-6.30.2-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:0eb523c550a66a09a0c20f86dd554afbf4d32b02af34ae53d93268c1f73bc65b"},
|
||||
{file = "protobuf-6.30.2-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:50f32cc9fd9cb09c783ebc275611b4f19dfdfb68d1ee55d2f0c7fa040df96815"},
|
||||
{file = "protobuf-6.30.2-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:4f6c687ae8efae6cf6093389a596548214467778146b7245e886f35e1485315d"},
|
||||
{file = "protobuf-6.30.2-cp39-cp39-win32.whl", hash = "sha256:524afedc03b31b15586ca7f64d877a98b184f007180ce25183d1a5cb230ee72b"},
|
||||
{file = "protobuf-6.30.2-cp39-cp39-win_amd64.whl", hash = "sha256:acec579c39c88bd8fbbacab1b8052c793efe83a0a5bd99db4a31423a25c0a0e2"},
|
||||
{file = "protobuf-6.30.2-py3-none-any.whl", hash = "sha256:ae86b030e69a98e08c77beab574cbcb9fff6d031d57209f574a5aea1445f4b51"},
|
||||
{file = "protobuf-6.30.2.tar.gz", hash = "sha256:35c859ae076d8c56054c25b59e5e59638d86545ed6e2b6efac6be0b6ea3ba048"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pyasn1"
|
||||
version = "0.6.1"
|
||||
description = "Pure-Python implementation of ASN.1 types and DER/BER/CER codecs (X.208)"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "pyasn1-0.6.1-py3-none-any.whl", hash = "sha256:0d632f46f2ba09143da3a8afe9e33fb6f92fa2320ab7e886e2d0f7672af84629"},
|
||||
{file = "pyasn1-0.6.1.tar.gz", hash = "sha256:6f580d2bdd84365380830acf45550f2511469f673cb4a5ae3857a3170128b034"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pyasn1-modules"
|
||||
version = "0.4.2"
|
||||
description = "A collection of ASN.1-based protocols modules"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "pyasn1_modules-0.4.2-py3-none-any.whl", hash = "sha256:29253a9207ce32b64c3ac6600edc75368f98473906e8fd1043bd6b5b1de2c14a"},
|
||||
{file = "pyasn1_modules-0.4.2.tar.gz", hash = "sha256:677091de870a80aae844b1ca6134f54652fa2c8c5a52aa396440ac3106e941e6"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
pyasn1 = ">=0.6.1,<0.7.0"
|
||||
|
||||
[[package]]
|
||||
name = "pyparsing"
|
||||
version = "3.2.3"
|
||||
description = "pyparsing module - Classes and methods to define and execute parsing grammars"
|
||||
optional = false
|
||||
python-versions = ">=3.9"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "pyparsing-3.2.3-py3-none-any.whl", hash = "sha256:a749938e02d6fd0b59b356ca504a24982314bb090c383e3cf201c95ef7e2bfcf"},
|
||||
{file = "pyparsing-3.2.3.tar.gz", hash = "sha256:b9c13f1ab8b3b542f72e28f634bad4de758ab3ce4546e4301970ad6fa77c38be"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
diagrams = ["jinja2", "railroad-diagrams"]
|
||||
|
||||
[[package]]
|
||||
name = "python-dateutil"
|
||||
version = "2.9.0.post0"
|
||||
@@ -670,6 +936,69 @@ files = [
|
||||
{file = "pytz-2025.2.tar.gz", hash = "sha256:360b9e3dbb49a209c21ad61809c7fb453643e048b38924c765813546746e81c3"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pyyaml"
|
||||
version = "6.0.2"
|
||||
description = "YAML parser and emitter for Python"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086"},
|
||||
{file = "PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf"},
|
||||
{file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237"},
|
||||
{file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b"},
|
||||
{file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed"},
|
||||
{file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180"},
|
||||
{file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68"},
|
||||
{file = "PyYAML-6.0.2-cp310-cp310-win32.whl", hash = "sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99"},
|
||||
{file = "PyYAML-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e"},
|
||||
{file = "PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774"},
|
||||
{file = "PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee"},
|
||||
{file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c"},
|
||||
{file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317"},
|
||||
{file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85"},
|
||||
{file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4"},
|
||||
{file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e"},
|
||||
{file = "PyYAML-6.0.2-cp311-cp311-win32.whl", hash = "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5"},
|
||||
{file = "PyYAML-6.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44"},
|
||||
{file = "PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab"},
|
||||
{file = "PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725"},
|
||||
{file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5"},
|
||||
{file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425"},
|
||||
{file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476"},
|
||||
{file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48"},
|
||||
{file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b"},
|
||||
{file = "PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4"},
|
||||
{file = "PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8"},
|
||||
{file = "PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba"},
|
||||
{file = "PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1"},
|
||||
{file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133"},
|
||||
{file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484"},
|
||||
{file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5"},
|
||||
{file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc"},
|
||||
{file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652"},
|
||||
{file = "PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183"},
|
||||
{file = "PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563"},
|
||||
{file = "PyYAML-6.0.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:24471b829b3bf607e04e88d79542a9d48bb037c2267d7927a874e6c205ca7e9a"},
|
||||
{file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7fded462629cfa4b685c5416b949ebad6cec74af5e2d42905d41e257e0869f5"},
|
||||
{file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d84a1718ee396f54f3a086ea0a66d8e552b2ab2017ef8b420e92edbc841c352d"},
|
||||
{file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9056c1ecd25795207ad294bcf39f2db3d845767be0ea6e6a34d856f006006083"},
|
||||
{file = "PyYAML-6.0.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:82d09873e40955485746739bcb8b4586983670466c23382c19cffecbf1fd8706"},
|
||||
{file = "PyYAML-6.0.2-cp38-cp38-win32.whl", hash = "sha256:43fa96a3ca0d6b1812e01ced1044a003533c47f6ee8aca31724f78e93ccc089a"},
|
||||
{file = "PyYAML-6.0.2-cp38-cp38-win_amd64.whl", hash = "sha256:01179a4a8559ab5de078078f37e5c1a30d76bb88519906844fd7bdea1b7729ff"},
|
||||
{file = "PyYAML-6.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:688ba32a1cffef67fd2e9398a2efebaea461578b0923624778664cc1c914db5d"},
|
||||
{file = "PyYAML-6.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a8786accb172bd8afb8be14490a16625cbc387036876ab6ba70912730faf8e1f"},
|
||||
{file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8e03406cac8513435335dbab54c0d385e4a49e4945d2909a581c83647ca0290"},
|
||||
{file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12"},
|
||||
{file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b1fdb9dc17f5a7677423d508ab4f243a726dea51fa5e70992e59a7411c89d19"},
|
||||
{file = "PyYAML-6.0.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0b69e4ce7a131fe56b7e4d770c67429700908fc0752af059838b1cfb41960e4e"},
|
||||
{file = "PyYAML-6.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a9f8c2e67970f13b16084e04f134610fd1d374bf477b17ec1599185cf611d725"},
|
||||
{file = "PyYAML-6.0.2-cp39-cp39-win32.whl", hash = "sha256:6395c297d42274772abc367baaa79683958044e5d3835486c16da75d2a694631"},
|
||||
{file = "PyYAML-6.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:39693e1f8320ae4f43943590b49779ffb98acb81f788220ea932a6b6c51004d8"},
|
||||
{file = "pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "requests"
|
||||
version = "2.32.3"
|
||||
@@ -692,6 +1021,56 @@ urllib3 = ">=1.21.1,<3"
|
||||
socks = ["PySocks (>=1.5.6,!=1.5.7)"]
|
||||
use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"]
|
||||
|
||||
[[package]]
|
||||
name = "requests-oauthlib"
|
||||
version = "2.0.0"
|
||||
description = "OAuthlib authentication support for Requests."
|
||||
optional = false
|
||||
python-versions = ">=3.4"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "requests-oauthlib-2.0.0.tar.gz", hash = "sha256:b3dffaebd884d8cd778494369603a9e7b58d29111bf6b41bdc2dcd87203af4e9"},
|
||||
{file = "requests_oauthlib-2.0.0-py2.py3-none-any.whl", hash = "sha256:7dd8a5c40426b779b0868c404bdef9768deccf22749cde15852df527e6269b36"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
oauthlib = ">=3.0.0"
|
||||
requests = ">=2.0.0"
|
||||
|
||||
[package.extras]
|
||||
rsa = ["oauthlib[signedtoken] (>=3.0.0)"]
|
||||
|
||||
[[package]]
|
||||
name = "rsa"
|
||||
version = "4.2"
|
||||
description = "Pure-Python RSA implementation"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
groups = ["main"]
|
||||
markers = "python_version >= \"3.13\""
|
||||
files = [
|
||||
{file = "rsa-4.2.tar.gz", hash = "sha256:aaefa4b84752e3e99bd8333a2e1e3e7a7da64614042bd66f775573424370108a"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
pyasn1 = ">=0.1.3"
|
||||
|
||||
[[package]]
|
||||
name = "rsa"
|
||||
version = "4.9"
|
||||
description = "Pure-Python RSA implementation"
|
||||
optional = false
|
||||
python-versions = ">=3.6,<4"
|
||||
groups = ["main"]
|
||||
markers = "python_version < \"3.13\""
|
||||
files = [
|
||||
{file = "rsa-4.9-py3-none-any.whl", hash = "sha256:90260d9058e514786967344d0ef75fa8727eed8a7d2e43ce9f4bcf1b536174f7"},
|
||||
{file = "rsa-4.9.tar.gz", hash = "sha256:e38464a49c6c85d7f1351b0126661487a7e0a14a50f1675ec50eb34d4f20ef21"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
pyasn1 = ">=0.1.3"
|
||||
|
||||
[[package]]
|
||||
name = "setuptools"
|
||||
version = "75.3.2"
|
||||
@@ -750,6 +1129,18 @@ files = [
|
||||
{file = "tzdata-2025.2.tar.gz", hash = "sha256:b60a638fcc0daffadf82fe0f57e53d06bdec2f36c4df66280ae79bce6bd6f2b9"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "uritemplate"
|
||||
version = "4.1.1"
|
||||
description = "Implementation of RFC 6570 URI Templates"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "uritemplate-4.1.1-py2.py3-none-any.whl", hash = "sha256:830c08b8d99bdd312ea4ead05994a38e8936266f84b9a7878232db50b044e02e"},
|
||||
{file = "uritemplate-4.1.1.tar.gz", hash = "sha256:4346edfc5c3b79f694bccd6d6099a322bbeb628dbf2cd86eea55a456ce5124f0"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "urllib3"
|
||||
version = "2.2.3"
|
||||
@@ -928,4 +1319,4 @@ testing = ["coverage[toml]", "zope.event", "zope.testing"]
|
||||
[metadata]
|
||||
lock-version = "2.1"
|
||||
python-versions = ">=3.9"
|
||||
content-hash = "b5f9acef18a15409718a71521e2a39a7cae20793f55425d28e46f6b37ad3b3cd"
|
||||
content-hash = "06976e001b4080474473ef0cb78ee3ab086eea319c391f759d74d72206e2bfa3"
|
||||
|
||||
+6
-1
@@ -11,11 +11,16 @@ dependencies = [
|
||||
"icalendar (>=6.1.3,<7.0.0)",
|
||||
"datetime (>=5.5,<6.0)",
|
||||
"requests (>=2.32.3,<3.0.0)",
|
||||
"aiohttp (>=3.11.16,<4.0.0)"
|
||||
"aiohttp (>=3.11.16,<4.0.0)",
|
||||
"google-api-python-client (>=2.166.0,<3.0.0)",
|
||||
"google-auth-httplib2 (>=0.2.0,<0.3.0)",
|
||||
"google-auth-oauthlib (>=1.2.1,<2.0.0)",
|
||||
"jsondiff (>=2.2.1,<3.0.0)"
|
||||
]
|
||||
|
||||
[project.scripts]
|
||||
generate-ics = 'events_plus.console:generate_all'
|
||||
clear-gcals = 'events_plus.console:clear_all'
|
||||
|
||||
|
||||
[build-system]
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
from .scrapers.sakuracon import collect_sakuracon_events
|
||||
from .scrapers.sakuracon import collect_sakuracon_events, clear_sakuracon_events
|
||||
import asyncio
|
||||
|
||||
def generate_all():
|
||||
loop = asyncio.new_event_loop()
|
||||
loop.run_until_complete(collect_sakuracon_events())
|
||||
|
||||
|
||||
def clear_all():
|
||||
loop = asyncio.new_event_loop()
|
||||
loop.run_until_complete(clear_sakuracon_events())
|
||||
@@ -1,20 +1,41 @@
|
||||
import requests
|
||||
from icalendar import Calendar, Event
|
||||
from datetime import datetime
|
||||
from datetime import datetime, timezone
|
||||
from collections import defaultdict
|
||||
import asyncio
|
||||
import aiohttp
|
||||
import json
|
||||
import os
|
||||
import base64
|
||||
import jsondiff as jd
|
||||
import pprint
|
||||
|
||||
from google.oauth2 import service_account
|
||||
from googleapiclient.discovery import build
|
||||
from googleapiclient.errors import HttpError
|
||||
|
||||
|
||||
EVENTNY_ENDPOINT = "https://www.eventeny.com/funcs/event/event-page-elements-2022-03-06.php"
|
||||
SAK_BIZ_ID = "233997"
|
||||
SAK_EVENT_ID = "13462"
|
||||
BATCH_LIMIT = 190
|
||||
|
||||
async def collect_sakuracon_events():
|
||||
async def clear_sakuracon_events(config_path="config/sakuracon.json"):
|
||||
with open(config_path) as f:
|
||||
cfg = json.load(f)
|
||||
print("using config:")
|
||||
pprint.pp(cfg, indent=2)
|
||||
clear_gcals(cfg)
|
||||
|
||||
async def collect_sakuracon_events(config_path="config/sakuracon.json"):
|
||||
with open(config_path) as f:
|
||||
cfg = json.load(f)
|
||||
#print("using config:")
|
||||
#pprint.pp(cfg, indent=2)
|
||||
events, tracks = await get_event_data()
|
||||
cals = convert_events_to_icals(events, tracks)
|
||||
cals = convert_events_to_icals(events, tracks, cfg)
|
||||
write_ics(cals)
|
||||
update_gcal(events, tracks, cfg)
|
||||
|
||||
|
||||
async def get_event_data():
|
||||
@@ -48,7 +69,7 @@ async def get_event_data():
|
||||
all_events.extend(data['list'])
|
||||
all_tracks.update(data['track'])
|
||||
|
||||
all_events = await insert_descriptions(all_events)
|
||||
all_events = await insert_fields(all_events)
|
||||
|
||||
return all_events, all_tracks
|
||||
|
||||
@@ -67,7 +88,7 @@ async def get_description(eventid):
|
||||
results = json.loads(data.decode())
|
||||
return results["schedule"]["overview"]["description"] or ""
|
||||
|
||||
async def insert_descriptions(events):
|
||||
async def insert_fields(events):
|
||||
tasks = []
|
||||
for event in events:
|
||||
t = asyncio.create_task(
|
||||
@@ -79,11 +100,27 @@ async def insert_descriptions(events):
|
||||
|
||||
for desc, event in zip(descs, events):
|
||||
event["raw_description"] = desc
|
||||
uid_str = f"UID: {event['id']}"
|
||||
tags_str = ""
|
||||
if 'hashtag_title' in event and event['hashtag_title']:
|
||||
tags_str = f"Tags: {','.join(event['hashtag_title'])}\n"
|
||||
event["description"] = f"Track: {event['tag_title']}\n{tags_str}\n{uid_str}\n{desc}"
|
||||
return events
|
||||
|
||||
def batch_create_handler(_, resp, exception):
|
||||
if exception is not None:
|
||||
print(exception)
|
||||
return
|
||||
print(f"Event created: g {resp['id']}")
|
||||
|
||||
def convert_events_to_icals(all_events, all_tracks) -> dict[str, Calendar]:
|
||||
def batch_exception_handler(_, resp, exception):
|
||||
if exception is not None:
|
||||
pprint.pp(resp)
|
||||
pprint.pp(exception)
|
||||
|
||||
def convert_events_to_icals(all_events, all_tracks, cfg) -> dict[str, Calendar]:
|
||||
# Group events by track_title
|
||||
uid_prefix = cfg["uidPrefix"]
|
||||
calendars = defaultdict(Calendar)
|
||||
|
||||
for event in all_events:
|
||||
@@ -100,6 +137,7 @@ def convert_events_to_icals(all_events, all_tracks) -> dict[str, Calendar]:
|
||||
|
||||
# Create event
|
||||
ical_event = Event()
|
||||
ical_event.add("uid", uid_prefix + event["id"]) # we are assuming the ids generated by eventny are unique
|
||||
ical_event.add("summary", event['title'])
|
||||
ical_event.add("dtstart", datetime.fromisoformat(event['start_calendar']))
|
||||
ical_event.add("dtend", datetime.fromisoformat(event['end_calendar']))
|
||||
@@ -109,9 +147,8 @@ def convert_events_to_icals(all_events, all_tracks) -> dict[str, Calendar]:
|
||||
# Add hashtags as categories if available
|
||||
if 'hashtag_title' in event and event['hashtag_title']:
|
||||
ical_event.add('categories', event['hashtag_title'])
|
||||
tags_str = f"Tags: {','.join(event['hashtag_title'])}\n"
|
||||
|
||||
ical_event.add("description", f"Track: {track_title}\n{tags_str}\n{event['raw_description']}")
|
||||
ical_event.add("description", event['description'])
|
||||
|
||||
cal.add_component(ical_event)
|
||||
return calendars
|
||||
@@ -123,4 +160,205 @@ def write_ics(calendars, output_dir="output/sakuracon"):
|
||||
os.makedirs(os.path.dirname(filename), exist_ok=True)
|
||||
with open(filename, 'wb') as f:
|
||||
f.write(cal.to_ical())
|
||||
print(f"Wrote: {filename}")
|
||||
#print(f"Wrote: {filename}")
|
||||
|
||||
def update_gcal(scraped_events, tracks, cfg):
|
||||
SCOPES = ["https://www.googleapis.com/auth/calendar.events"]
|
||||
creds_b64 = os.getenv("GOOGLE_SERVICE_ACCOUNT_CREDS")
|
||||
creds_body = json.loads(base64.b64decode(creds_b64))
|
||||
creds = service_account.Credentials.from_service_account_info(creds_body, scopes=SCOPES)
|
||||
tz_suffix = cfg["tzSuffix"]
|
||||
gcal_map = cfg["calendars"]
|
||||
uid_prefix = cfg["uidPrefix"]
|
||||
|
||||
uids = set([uid_prefix + e["id"] for e in scraped_events])
|
||||
print(f"There seem to be {len(uids)} unique events by ical uids")
|
||||
|
||||
try:
|
||||
service = build("calendar", "v3", credentials=creds)
|
||||
# list all events in the gcals
|
||||
gcal_events = []
|
||||
gcalevent_gcal_map = {}
|
||||
uncancelled_count = 0
|
||||
batch = service.new_batch_http_request()
|
||||
batchlen = 0
|
||||
for _, gcal in gcal_map.items():
|
||||
page_token = None
|
||||
while True:
|
||||
resp = service.events().list(calendarId=gcal, maxResults=BATCH_LIMIT, pageToken=page_token, showDeleted=True).execute()
|
||||
for e in resp["items"]:
|
||||
#print(e.get("iCalUID"))
|
||||
gcalevent_gcal_map[e["id"]] = gcal
|
||||
if e["status"] != "cancelled":
|
||||
uncancelled_count += 1
|
||||
|
||||
gcal_events.extend(resp["items"])
|
||||
page_token = resp.get("nextPageToken")
|
||||
if not page_token:
|
||||
break
|
||||
|
||||
print(f"Retrieved {uncancelled_count} active events out of {len(gcal_events)} total gcal events")
|
||||
|
||||
# associate icaluid
|
||||
uid_gcalevent_map = {}
|
||||
for event in gcal_events:
|
||||
uid = event.get("iCalUID")
|
||||
if uid and uid.startswith(uid_prefix):
|
||||
uid_gcalevent_map[uid] = event
|
||||
|
||||
# go through scraped events and note changes from gcal (correlated by icaluid), if any
|
||||
events_by_track = defaultdict(list)
|
||||
for e in scraped_events:
|
||||
events_by_track[e["tag_title"]].append(e)
|
||||
|
||||
|
||||
action_taken_map = {
|
||||
"added": [],
|
||||
"updated": [],
|
||||
"deleted": [],
|
||||
"unchanged": [],
|
||||
"other": [],
|
||||
}
|
||||
for track_name, events in events_by_track.items():
|
||||
batch = service.new_batch_http_request()
|
||||
track_gcal_id = gcal_map[track_name]
|
||||
for e in events:
|
||||
ical_uid = uid_prefix + e["id"]
|
||||
try:
|
||||
start = datetime.fromisoformat(e["start_calendar"])
|
||||
end = datetime.fromisoformat(e["end_calendar"])
|
||||
|
||||
# sometimes events have weird start/end times that fail gcal's validation, but don't let that kill our vibe
|
||||
if start >= end:
|
||||
action_taken_map["other"].append(e)
|
||||
raise Exception((f"Event {e['title']} (uid: {ical_uid}) has an invalid duration, {start} -> {end}"))
|
||||
|
||||
e_content = {
|
||||
"summary": e["title"],
|
||||
"start": e["start_calendar"] + tz_suffix,
|
||||
"end": e["end_calendar"] + tz_suffix,
|
||||
"location": e['location'].replace('&', '&'),
|
||||
"description": e["description"],
|
||||
"status": "confirmed"
|
||||
}
|
||||
gcal_body = {
|
||||
"summary": e["title"],
|
||||
"iCalUID": ical_uid,
|
||||
"start": {
|
||||
"dateTime": e["start_calendar"] + tz_suffix,
|
||||
},
|
||||
"end": {
|
||||
"dateTime": e["end_calendar"] + tz_suffix,
|
||||
},
|
||||
"location": e['location'].replace('&', '&'),
|
||||
"description": e["description"],
|
||||
"status": "confirmed"
|
||||
}
|
||||
|
||||
if ical_uid in uid_gcalevent_map:
|
||||
g_e = uid_gcalevent_map[ical_uid]
|
||||
#print(g_e)
|
||||
g_content = {
|
||||
"summary": g_e.get("summary"),
|
||||
"start": g_e["start"]["dateTime"],
|
||||
"end": g_e["end"]["dateTime"],
|
||||
"location": g_e.get("location"),
|
||||
"description": g_e.get("description"),
|
||||
"status": g_e["status"] # this will likely either be confirmed or cancelled, and because we are using icaluids, we have to fish the cancelled/trashed event out of the bin using this field
|
||||
}
|
||||
|
||||
changed = e_content != g_content
|
||||
|
||||
# if changes exist, update existing event
|
||||
if not changed:
|
||||
action_taken_map["unchanged"].append(e)
|
||||
#print(f"Event {e['title']} (uid: {ical_uid}) seems to be the same, leaving as is")
|
||||
else:
|
||||
print(f"Updating event {e['title']} (ical: {ical_uid}/g: {g_e['id']})")
|
||||
pprint.pp(jd.diff(g_content, e_content, syntax="symmetric"), depth=3)
|
||||
action_taken_map["updated"].append(e)
|
||||
# print(e_content)
|
||||
# print(g_content)
|
||||
g_e.update(gcal_body)
|
||||
batch.add(service.events().update(calendarId=track_gcal_id, eventId=g_e["id"], body=g_e), callback=batch_exception_handler)
|
||||
batchlen += 1
|
||||
else: # if event is new, insert
|
||||
print(f"Event {e['title']} (uid: {ical_uid}) seems to be new, adding...")
|
||||
action_taken_map["added"].append(e)
|
||||
#service.events().insert(calendarId=track_gcal_id, body=gcal_body).execute()
|
||||
batch.add(service.events().insert(calendarId=track_gcal_id, body=gcal_body), callback=batch_create_handler)
|
||||
batchlen += 1
|
||||
|
||||
if batchlen == BATCH_LIMIT:
|
||||
batch.execute()
|
||||
batchlen = 0
|
||||
batch = service.new_batch_http_request()
|
||||
except HttpError as error:
|
||||
print(f"An error occurred while processing the event: {error}")
|
||||
print(gcal_body)
|
||||
except Exception as error:
|
||||
print(f"An error occurred while processing the event: {error}")
|
||||
batch.execute()
|
||||
batchlen = 0
|
||||
|
||||
# delete all gcal_events which are not in the events array
|
||||
# we have the list of gcal events
|
||||
# we have a set of event uids
|
||||
# if the gcal event does not have a uid or its uid is not in the uids set, then delete
|
||||
# but we need the cal of the gcal event in order to delete it
|
||||
|
||||
#print(gcalevent_gcal_map)
|
||||
|
||||
# dont bother batching the deletes b/c cal has to be same in batch, lazy...
|
||||
for event in gcal_events:
|
||||
if "iCalUID" not in event or event["iCalUID"] not in uids and event["status"] != "cancelled":
|
||||
cal = gcalevent_gcal_map[event["id"]]
|
||||
action_taken_map["unchanged"].append(event)
|
||||
print(f"gcal event {event['summary']} g {event['id']} (uid: {event.get('iCalUID') or 'unknown'}) not in latest scrape, deleting...")
|
||||
|
||||
service.events().delete(calendarId=cal, eventId=event["id"]).execute()
|
||||
|
||||
except HttpError as error:
|
||||
print(f"An error occurred: {error}")
|
||||
print(gcal_body)
|
||||
|
||||
print("-------------------------")
|
||||
print("Summary:")
|
||||
summary_map = {}
|
||||
for k,v in action_taken_map.items():
|
||||
summary_map[k] = len(v)
|
||||
pprint.pp(summary_map, indent=2)
|
||||
|
||||
|
||||
def clear_gcals(cfg):
|
||||
SCOPES = ["https://www.googleapis.com/auth/calendar.events"]
|
||||
creds_b64 = os.getenv("GOOGLE_SERVICE_ACCOUNT_CREDS")
|
||||
creds_body = json.loads(base64.b64decode(creds_b64))
|
||||
creds = service_account.Credentials.from_service_account_info(creds_body, scopes=SCOPES)
|
||||
gcal_map = cfg["calendars"]
|
||||
try:
|
||||
service = build("calendar", "v3", credentials=creds)
|
||||
# list all events in the gcals
|
||||
count = 0
|
||||
|
||||
for _, gcal in gcal_map.items():
|
||||
page_token = None
|
||||
while True:
|
||||
batch = service.new_batch_http_request()
|
||||
resp = service.events().list(calendarId=gcal, maxResults=BATCH_LIMIT, pageToken=page_token).execute()
|
||||
for e in resp["items"]:
|
||||
print(e.get("iCalUID"))
|
||||
|
||||
events = resp["items"]
|
||||
print(events)
|
||||
for e in events:
|
||||
batch.add(service.events().delete(calendarId=gcal, eventId=e["id"]), callback=batch_exception_handler)
|
||||
count += len(events)
|
||||
batch.execute()
|
||||
page_token = resp.get("nextPageToken")
|
||||
if not page_token:
|
||||
break
|
||||
|
||||
print(f"Removed {count} gcal events")
|
||||
except HttpError as error:
|
||||
print(f"An error occurred: {error}")
|
||||
Reference in New Issue
Block a user