timetree-exporter 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- timetree-exporter-0.1.0/.github/dependabot.yml +10 -0
- timetree-exporter-0.1.0/.github/workflows/pylint.yml +24 -0
- timetree-exporter-0.1.0/.github/workflows/python-publish.yml +39 -0
- timetree-exporter-0.1.0/.github/workflows/release-please.yml +19 -0
- timetree-exporter-0.1.0/.gitignore +7 -0
- timetree-exporter-0.1.0/.vscode/settings.json +4 -0
- timetree-exporter-0.1.0/CHANGELOG.md +32 -0
- timetree-exporter-0.1.0/LICENSE +21 -0
- timetree-exporter-0.1.0/PKG-INFO +110 -0
- timetree-exporter-0.1.0/README.md +92 -0
- timetree-exporter-0.1.0/assets/images/copy-response.png +0 -0
- timetree-exporter-0.1.0/assets/images/prepare-for-signin.png +0 -0
- timetree-exporter-0.1.0/pyproject.toml +36 -0
- timetree-exporter-0.1.0/requirements.txt +1 -0
- timetree-exporter-0.1.0/responses/README.md +49 -0
- timetree-exporter-0.1.0/setup.cfg +4 -0
- timetree-exporter-0.1.0/timetree_exporter/__init__.py +4 -0
- timetree-exporter-0.1.0/timetree_exporter/__main__.py +57 -0
- timetree-exporter-0.1.0/timetree_exporter/event.py +70 -0
- timetree-exporter-0.1.0/timetree_exporter/formatter.py +164 -0
- timetree-exporter-0.1.0/timetree_exporter/utils.py +28 -0
- timetree-exporter-0.1.0/timetree_exporter.egg-info/PKG-INFO +110 -0
- timetree-exporter-0.1.0/timetree_exporter.egg-info/SOURCES.txt +25 -0
- timetree-exporter-0.1.0/timetree_exporter.egg-info/dependency_links.txt +1 -0
- timetree-exporter-0.1.0/timetree_exporter.egg-info/entry_points.txt +2 -0
- timetree-exporter-0.1.0/timetree_exporter.egg-info/requires.txt +1 -0
- timetree-exporter-0.1.0/timetree_exporter.egg-info/top_level.txt +1 -0
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
name: Pylint
|
|
2
|
+
|
|
3
|
+
on: [push]
|
|
4
|
+
|
|
5
|
+
jobs:
|
|
6
|
+
build:
|
|
7
|
+
runs-on: ubuntu-latest
|
|
8
|
+
strategy:
|
|
9
|
+
matrix:
|
|
10
|
+
python-version: ["3.8", "3.9", "3.10", "3.11"]
|
|
11
|
+
steps:
|
|
12
|
+
- uses: actions/checkout@v4
|
|
13
|
+
- name: Set up Python ${{ matrix.python-version }}
|
|
14
|
+
uses: actions/setup-python@v5
|
|
15
|
+
with:
|
|
16
|
+
python-version: ${{ matrix.python-version }}
|
|
17
|
+
- name: Install dependencies
|
|
18
|
+
run: |
|
|
19
|
+
python -m pip install --upgrade pip
|
|
20
|
+
pip install pylint
|
|
21
|
+
python -m pip install -r requirements.txt
|
|
22
|
+
- name: Analysing the code with pylint
|
|
23
|
+
run: |
|
|
24
|
+
pylint $(git ls-files '*.py')
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# This workflow will upload a Python Package using Twine when a release is created
|
|
2
|
+
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python#publishing-to-package-registries
|
|
3
|
+
|
|
4
|
+
# This workflow uses actions that are not certified by GitHub.
|
|
5
|
+
# They are provided by a third-party and are governed by
|
|
6
|
+
# separate terms of service, privacy policy, and support
|
|
7
|
+
# documentation.
|
|
8
|
+
|
|
9
|
+
name: Upload Python Package
|
|
10
|
+
|
|
11
|
+
on:
|
|
12
|
+
release:
|
|
13
|
+
types: [published]
|
|
14
|
+
|
|
15
|
+
permissions:
|
|
16
|
+
contents: read
|
|
17
|
+
|
|
18
|
+
jobs:
|
|
19
|
+
deploy:
|
|
20
|
+
|
|
21
|
+
runs-on: ubuntu-latest
|
|
22
|
+
|
|
23
|
+
steps:
|
|
24
|
+
- uses: actions/checkout@v3
|
|
25
|
+
- name: Set up Python
|
|
26
|
+
uses: actions/setup-python@v3
|
|
27
|
+
with:
|
|
28
|
+
python-version: '3.x'
|
|
29
|
+
- name: Install dependencies
|
|
30
|
+
run: |
|
|
31
|
+
python -m pip install --upgrade pip
|
|
32
|
+
pip install build
|
|
33
|
+
- name: Build package
|
|
34
|
+
run: python -m build
|
|
35
|
+
- name: Publish package
|
|
36
|
+
uses: pypa/gh-action-pypi-publish@27b31702a0e7fc50959f5ad993c78deac1bdfc29
|
|
37
|
+
with:
|
|
38
|
+
user: __token__
|
|
39
|
+
password: ${{ secrets.PYPI_API_TOKEN }}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
on:
|
|
2
|
+
push:
|
|
3
|
+
branches:
|
|
4
|
+
- main
|
|
5
|
+
|
|
6
|
+
permissions:
|
|
7
|
+
contents: write
|
|
8
|
+
pull-requests: write
|
|
9
|
+
|
|
10
|
+
name: release-please
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
release-please:
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
steps:
|
|
16
|
+
- uses: google-github-actions/release-please-action@v4
|
|
17
|
+
with:
|
|
18
|
+
token: ${{ secrets.MY_RELEASE_PLEASE_TOKEN }}
|
|
19
|
+
release-type: python
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.1.0 (2024-04-06)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Features
|
|
7
|
+
|
|
8
|
+
* argparse ([#12](https://github.com/eoleedi/TimeTree-exporter/issues/12)) ([1eae588](https://github.com/eoleedi/TimeTree-exporter/commit/1eae588f96e462dc12f9c5998c88b5582c25e0d5))
|
|
9
|
+
* created & last-modify ([fab8d76](https://github.com/eoleedi/TimeTree-exporter/commit/fab8d76c380c175cc4b7e8cba6fbc740bafe31f6))
|
|
10
|
+
* first commit - basic title, time, and alert function ([bac0eca](https://github.com/eoleedi/TimeTree-exporter/commit/bac0ecab5f9d778f9e5113c988cbbcf024367600))
|
|
11
|
+
* geo (location lat & lon) ([3547100](https://github.com/eoleedi/TimeTree-exporter/commit/3547100430ab817aea98937e6e8ab4e3cc33fea3))
|
|
12
|
+
* note and url ([61adb88](https://github.com/eoleedi/TimeTree-exporter/commit/61adb887f35d1d456b610a6ac19bcf35b5b96438))
|
|
13
|
+
* recurrences ([5c37911](https://github.com/eoleedi/TimeTree-exporter/commit/5c37911b584ba022f2114340612ee5572d8ec265))
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
### Bug Fixes
|
|
17
|
+
|
|
18
|
+
* full day event & timezone issue ([41467ab](https://github.com/eoleedi/TimeTree-exporter/commit/41467ab0942c8a5ded425bbe73ca44de62481d56))
|
|
19
|
+
* write to file after parsing all the files ([bc0fd7f](https://github.com/eoleedi/TimeTree-exporter/commit/bc0fd7f20c12410cf2e548b4c419f89a775a5845))
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
### Documentation
|
|
23
|
+
|
|
24
|
+
* add detailed description & remove the API limitation ([edeea3a](https://github.com/eoleedi/TimeTree-exporter/commit/edeea3aacfa64acaf5479912350c219941845702))
|
|
25
|
+
* add detailed instructions and info ([70c1e89](https://github.com/eoleedi/TimeTree-exporter/commit/70c1e89ec8a6b7172919f02c37ca54964953f911))
|
|
26
|
+
* add newline ([cc5275a](https://github.com/eoleedi/TimeTree-exporter/commit/cc5275a33a01bf1c67db22ed01b5e7402fcf17c2))
|
|
27
|
+
* add recommendation and remove in-development warning ([776d5f2](https://github.com/eoleedi/TimeTree-exporter/commit/776d5f271b8127c724f8d3be03e54e1ab41e52b1))
|
|
28
|
+
* bump icalendar from 5.0.11 to 5.0.12 ([a934c7b](https://github.com/eoleedi/TimeTree-exporter/commit/a934c7bdc53b8206ef7e37af7af3a0585a5d0abc))
|
|
29
|
+
* finish all day ([e23021e](https://github.com/eoleedi/TimeTree-exporter/commit/e23021e24cc9f038bdc070eea530f331bb3e1fde))
|
|
30
|
+
* fix typo ([248b669](https://github.com/eoleedi/TimeTree-exporter/commit/248b669c7027f37035778385d38902ec569ddf70))
|
|
31
|
+
* update document for pip installation method ([83cfaea](https://github.com/eoleedi/TimeTree-exporter/commit/83cfaea4ec55ad38836e9cd7c11896343b1915f9))
|
|
32
|
+
* use absolute path for images ([0bf9e33](https://github.com/eoleedi/TimeTree-exporter/commit/0bf9e33da0e2afe8ae84b085e07357b47ade1080))
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2023 蔡鳳駿
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: timetree-exporter
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A Tool for Exporting TimeTree Calendar and Convert to iCal format(.ics)
|
|
5
|
+
Author-email: Fong-Chun Tsai <eoleedimin@gmail.com>
|
|
6
|
+
Project-URL: Homepage, https://github.com/eoleedi/TimeTree-Exporter
|
|
7
|
+
Project-URL: Repository, https://github.com/eoleedi/TimeTree-Exporter
|
|
8
|
+
Project-URL: Issues, https://github.com/eoleedi/TimeTree-Exporter/issues
|
|
9
|
+
Project-URL: Changelog, https://github.com/eoleedi/TimeTree-Exporter/CHANGELOG.md
|
|
10
|
+
Keywords: timetree,exporter,icalendar,ics
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
+
Classifier: Operating System :: OS Independent
|
|
14
|
+
Requires-Python: >=3.8
|
|
15
|
+
Description-Content-Type: text/markdown
|
|
16
|
+
License-File: LICENSE
|
|
17
|
+
Requires-Dist: icalendar
|
|
18
|
+
|
|
19
|
+
# TimeTree Exporter
|
|
20
|
+
A Tool for Exporting TimeTree Calendar and Convert to iCal format(.ics) \
|
|
21
|
+
(The .ics file can then be imported into other calendar apps such as Google Calendar, Apple Calendar, Outlook Calendar, etc.)
|
|
22
|
+
|
|
23
|
+
# Usage
|
|
24
|
+
|
|
25
|
+
First, download the timetree data manually and put it in the responses folder(see below)
|
|
26
|
+
|
|
27
|
+
## Method 1: Pip
|
|
28
|
+
1. Install the package `pip3 install timetree-exporter`
|
|
29
|
+
2. Run `timetree-exporter <path-to-responses-folder>`
|
|
30
|
+
3. A timetree.ics file will be generated in the same directory, then you can import it to your calendar app.
|
|
31
|
+
|
|
32
|
+
## Method 2: Cloning the repository
|
|
33
|
+
1. Clone the repository `git clone https://github.com/eoleedi/TimeTree-exporter.git`
|
|
34
|
+
2. Change the directory to the project folder `cd TimeTree-exporter`
|
|
35
|
+
3. Install the requirements `pip3 install -r requirements.txt`
|
|
36
|
+
4. Run `python3 -m timetree_exporter responses`
|
|
37
|
+
5. A timetree.ics file will be generated in the same directory, then you can import it to your calendar app.
|
|
38
|
+
|
|
39
|
+
## How to download timetree data
|
|
40
|
+
1. Go to [https://timetreeapp.com/signin](https://timetreeapp.com/signin)
|
|
41
|
+
2. Open the developer tools before logging in
|
|
42
|
+
> Can be opened by pressing F12
|
|
43
|
+
3. Go to the network tab
|
|
44
|
+
> Remember to Press Ctrl + R to start recording
|
|
45
|
+
4. Turn on preserve log
|
|
46
|
+
5. Type in "sync" in the filter box
|
|
47
|
+
6. Log in
|
|
48
|
+
7. Click on the calendar you want to export
|
|
49
|
+
8. You will see couple of requests with the name "sync"
|
|
50
|
+
> There seems to be a maximum of 300 events in single requests. \
|
|
51
|
+
> If you have more than 300 events, you will see multiple requests with the name "sync"
|
|
52
|
+
9. Right click on the request and select "Copy Response"
|
|
53
|
+
10. Paste it into a json file under the response folder under this project (etc. sync.json)
|
|
54
|
+
> The file name should end with .json
|
|
55
|
+
11. Do the same thing for all the requests with steps 7~10
|
|
56
|
+
|
|
57
|
+

|
|
58
|
+
*Prepare for signin: Step 1~5*
|
|
59
|
+

|
|
60
|
+
*Copy response: Step 8~9*
|
|
61
|
+
|
|
62
|
+
# Advanced Usage
|
|
63
|
+
You can also specify the output file name by running `python3 -m timetree_exporter -o <output_file_name>.ics` or `timetree-exporter -o <output_file_name>.ics`
|
|
64
|
+
|
|
65
|
+
# Recommendation
|
|
66
|
+
You are recommended to import the ics file into a separate calendar (take google calendar as an example) as if anything goes wrong, you can just delete the calendar and reimport it.
|
|
67
|
+
|
|
68
|
+
# Requirements
|
|
69
|
+
icalendar==5.0.12
|
|
70
|
+
|
|
71
|
+
# Limitations
|
|
72
|
+
Currently, TimeTree data can only be downloaded manually through a web browser.
|
|
73
|
+
|
|
74
|
+
# Roadmap of the properties mapping to iCal
|
|
75
|
+
- [ ] **ID**
|
|
76
|
+
- [ ] **Primary ID**
|
|
77
|
+
- [ ] **Calendar ID**
|
|
78
|
+
- [x] **UUID**
|
|
79
|
+
- [ ] **Category**
|
|
80
|
+
- [ ] **Type**
|
|
81
|
+
- [ ] **Author ID**
|
|
82
|
+
- [ ] **Author Type**
|
|
83
|
+
- [x] **Title**
|
|
84
|
+
- [x] **All Day**
|
|
85
|
+
- [x] **Start At**
|
|
86
|
+
- [x] **Start Timezone**
|
|
87
|
+
- [x] **End At**
|
|
88
|
+
- [x] **End Timezone**
|
|
89
|
+
- [ ] **Label ID**
|
|
90
|
+
- [x] **Location**
|
|
91
|
+
- [x] **Location Latitude**
|
|
92
|
+
- [x] **Location Longitude**
|
|
93
|
+
- [x] **URL**
|
|
94
|
+
- [x] **Note**
|
|
95
|
+
- [ ] **Lunar**
|
|
96
|
+
- [ ] **Attendees**
|
|
97
|
+
- [x] **Recurrences**
|
|
98
|
+
- [ ] **Recurring UUID**
|
|
99
|
+
- [x] **Alerts**
|
|
100
|
+
- [ ] **Parent ID**
|
|
101
|
+
- [ ] **Link Object ID**
|
|
102
|
+
- [ ] **Link Object ID String**
|
|
103
|
+
- [ ] **Row Order**
|
|
104
|
+
- [ ] **Attachment**
|
|
105
|
+
- [ ] **Like Count**
|
|
106
|
+
- [ ] **Files**
|
|
107
|
+
- [ ] **Deactivated At**
|
|
108
|
+
- [ ] **Pinned At**
|
|
109
|
+
- [x] **Updated At**
|
|
110
|
+
- [x] **Created At**
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# TimeTree Exporter
|
|
2
|
+
A Tool for Exporting TimeTree Calendar and Convert to iCal format(.ics) \
|
|
3
|
+
(The .ics file can then be imported into other calendar apps such as Google Calendar, Apple Calendar, Outlook Calendar, etc.)
|
|
4
|
+
|
|
5
|
+
# Usage
|
|
6
|
+
|
|
7
|
+
First, download the timetree data manually and put it in the responses folder(see below)
|
|
8
|
+
|
|
9
|
+
## Method 1: Pip
|
|
10
|
+
1. Install the package `pip3 install timetree-exporter`
|
|
11
|
+
2. Run `timetree-exporter <path-to-responses-folder>`
|
|
12
|
+
3. A timetree.ics file will be generated in the same directory, then you can import it to your calendar app.
|
|
13
|
+
|
|
14
|
+
## Method 2: Cloning the repository
|
|
15
|
+
1. Clone the repository `git clone https://github.com/eoleedi/TimeTree-exporter.git`
|
|
16
|
+
2. Change the directory to the project folder `cd TimeTree-exporter`
|
|
17
|
+
3. Install the requirements `pip3 install -r requirements.txt`
|
|
18
|
+
4. Run `python3 -m timetree_exporter responses`
|
|
19
|
+
5. A timetree.ics file will be generated in the same directory, then you can import it to your calendar app.
|
|
20
|
+
|
|
21
|
+
## How to download timetree data
|
|
22
|
+
1. Go to [https://timetreeapp.com/signin](https://timetreeapp.com/signin)
|
|
23
|
+
2. Open the developer tools before logging in
|
|
24
|
+
> Can be opened by pressing F12
|
|
25
|
+
3. Go to the network tab
|
|
26
|
+
> Remember to Press Ctrl + R to start recording
|
|
27
|
+
4. Turn on preserve log
|
|
28
|
+
5. Type in "sync" in the filter box
|
|
29
|
+
6. Log in
|
|
30
|
+
7. Click on the calendar you want to export
|
|
31
|
+
8. You will see couple of requests with the name "sync"
|
|
32
|
+
> There seems to be a maximum of 300 events in single requests. \
|
|
33
|
+
> If you have more than 300 events, you will see multiple requests with the name "sync"
|
|
34
|
+
9. Right click on the request and select "Copy Response"
|
|
35
|
+
10. Paste it into a json file under the response folder under this project (etc. sync.json)
|
|
36
|
+
> The file name should end with .json
|
|
37
|
+
11. Do the same thing for all the requests with steps 7~10
|
|
38
|
+
|
|
39
|
+

|
|
40
|
+
*Prepare for signin: Step 1~5*
|
|
41
|
+

|
|
42
|
+
*Copy response: Step 8~9*
|
|
43
|
+
|
|
44
|
+
# Advanced Usage
|
|
45
|
+
You can also specify the output file name by running `python3 -m timetree_exporter -o <output_file_name>.ics` or `timetree-exporter -o <output_file_name>.ics`
|
|
46
|
+
|
|
47
|
+
# Recommendation
|
|
48
|
+
You are recommended to import the ics file into a separate calendar (take google calendar as an example) as if anything goes wrong, you can just delete the calendar and reimport it.
|
|
49
|
+
|
|
50
|
+
# Requirements
|
|
51
|
+
icalendar==5.0.12
|
|
52
|
+
|
|
53
|
+
# Limitations
|
|
54
|
+
Currently, TimeTree data can only be downloaded manually through a web browser.
|
|
55
|
+
|
|
56
|
+
# Roadmap of the properties mapping to iCal
|
|
57
|
+
- [ ] **ID**
|
|
58
|
+
- [ ] **Primary ID**
|
|
59
|
+
- [ ] **Calendar ID**
|
|
60
|
+
- [x] **UUID**
|
|
61
|
+
- [ ] **Category**
|
|
62
|
+
- [ ] **Type**
|
|
63
|
+
- [ ] **Author ID**
|
|
64
|
+
- [ ] **Author Type**
|
|
65
|
+
- [x] **Title**
|
|
66
|
+
- [x] **All Day**
|
|
67
|
+
- [x] **Start At**
|
|
68
|
+
- [x] **Start Timezone**
|
|
69
|
+
- [x] **End At**
|
|
70
|
+
- [x] **End Timezone**
|
|
71
|
+
- [ ] **Label ID**
|
|
72
|
+
- [x] **Location**
|
|
73
|
+
- [x] **Location Latitude**
|
|
74
|
+
- [x] **Location Longitude**
|
|
75
|
+
- [x] **URL**
|
|
76
|
+
- [x] **Note**
|
|
77
|
+
- [ ] **Lunar**
|
|
78
|
+
- [ ] **Attendees**
|
|
79
|
+
- [x] **Recurrences**
|
|
80
|
+
- [ ] **Recurring UUID**
|
|
81
|
+
- [x] **Alerts**
|
|
82
|
+
- [ ] **Parent ID**
|
|
83
|
+
- [ ] **Link Object ID**
|
|
84
|
+
- [ ] **Link Object ID String**
|
|
85
|
+
- [ ] **Row Order**
|
|
86
|
+
- [ ] **Attachment**
|
|
87
|
+
- [ ] **Like Count**
|
|
88
|
+
- [ ] **Files**
|
|
89
|
+
- [ ] **Deactivated At**
|
|
90
|
+
- [ ] **Pinned At**
|
|
91
|
+
- [x] **Updated At**
|
|
92
|
+
- [x] **Created At**
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools", "setuptools-scm"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[tool.setuptools.packages.find]
|
|
6
|
+
include = ["timetree_exporter"]
|
|
7
|
+
|
|
8
|
+
[project]
|
|
9
|
+
name = "timetree-exporter"
|
|
10
|
+
dynamic = ["version"]
|
|
11
|
+
authors = [
|
|
12
|
+
{ name="Fong-Chun Tsai", email="eoleedimin@gmail.com" },
|
|
13
|
+
]
|
|
14
|
+
description = "A Tool for Exporting TimeTree Calendar and Convert to iCal format(.ics)"
|
|
15
|
+
keywords = ["timetree", "exporter", "icalendar", "ics"]
|
|
16
|
+
readme = "README.md"
|
|
17
|
+
requires-python = ">=3.8"
|
|
18
|
+
classifiers = [
|
|
19
|
+
"Programming Language :: Python :: 3",
|
|
20
|
+
"License :: OSI Approved :: MIT License",
|
|
21
|
+
"Operating System :: OS Independent",
|
|
22
|
+
]
|
|
23
|
+
dependencies = [
|
|
24
|
+
"icalendar",
|
|
25
|
+
]
|
|
26
|
+
|
|
27
|
+
[project.urls]
|
|
28
|
+
Homepage = "https://github.com/eoleedi/TimeTree-Exporter"
|
|
29
|
+
Repository = "https://github.com/eoleedi/TimeTree-Exporter"
|
|
30
|
+
Issues = "https://github.com/eoleedi/TimeTree-Exporter/issues"
|
|
31
|
+
Changelog = "https://github.com/eoleedi/TimeTree-Exporter/CHANGELOG.md"
|
|
32
|
+
|
|
33
|
+
[project.scripts]
|
|
34
|
+
timetree-exporter = "timetree_exporter.__main__:main"
|
|
35
|
+
|
|
36
|
+
[tool.setuptools_scm]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
icalendar==5.0.12
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# This is the folder to put the json responses from timetree
|
|
2
|
+
## Here is an exmaple format of the response
|
|
3
|
+
```json
|
|
4
|
+
{
|
|
5
|
+
"since": null,
|
|
6
|
+
"chunk": null,
|
|
7
|
+
"events": [
|
|
8
|
+
{
|
|
9
|
+
"id": null,
|
|
10
|
+
"primary_id": null,
|
|
11
|
+
"calendar_id": null,
|
|
12
|
+
"uuid": null,
|
|
13
|
+
"category": null,
|
|
14
|
+
"type": null,
|
|
15
|
+
"author_id": null,
|
|
16
|
+
"author_type": null,
|
|
17
|
+
"title": null,
|
|
18
|
+
"all_day": null,
|
|
19
|
+
"start_at": null,
|
|
20
|
+
"start_timezone": null,
|
|
21
|
+
"end_at": null,
|
|
22
|
+
"end_timezone": null,
|
|
23
|
+
"label_id": null,
|
|
24
|
+
"location": null,
|
|
25
|
+
"location_lat": null,
|
|
26
|
+
"location_lon": null,
|
|
27
|
+
"url": null,
|
|
28
|
+
"note": null,
|
|
29
|
+
"lunar": null,
|
|
30
|
+
"attendees": [],
|
|
31
|
+
"recurrences": [],
|
|
32
|
+
"recurring_uuid": null,
|
|
33
|
+
"alerts": [],
|
|
34
|
+
"parent_id": null,
|
|
35
|
+
"link_object_id": null,
|
|
36
|
+
"link_object_id_string": null,
|
|
37
|
+
"row_order": null,
|
|
38
|
+
"attachment": {},
|
|
39
|
+
"like_count": null,
|
|
40
|
+
"files": [],
|
|
41
|
+
"deactivated_at": null,
|
|
42
|
+
"pinned_at": null,
|
|
43
|
+
"updated_at": null,
|
|
44
|
+
"created_at": null
|
|
45
|
+
},
|
|
46
|
+
... more events
|
|
47
|
+
]
|
|
48
|
+
}
|
|
49
|
+
```
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"""
|
|
2
|
+
This module converts Timetree events to iCal format.
|
|
3
|
+
It is intended to be used with the Timetree API response files.
|
|
4
|
+
https://timetreeapp.com/api/v1/calendar/{calendar_id}/events/sync
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import argparse
|
|
8
|
+
from icalendar import Calendar
|
|
9
|
+
from timetree_exporter import TimeTreeEvent, ICalEventFormatter
|
|
10
|
+
from timetree_exporter.utils import get_events_from_file, paths_to_filelist
|
|
11
|
+
|
|
12
|
+
# Parse arguments
|
|
13
|
+
parser = argparse.ArgumentParser(
|
|
14
|
+
description="Convert Timetree events to iCal format",
|
|
15
|
+
prog="timetree_exporter",
|
|
16
|
+
)
|
|
17
|
+
parser.add_argument(
|
|
18
|
+
"input",
|
|
19
|
+
type=str,
|
|
20
|
+
help="Path to the Timetree response file(s)/folder",
|
|
21
|
+
nargs="+",
|
|
22
|
+
)
|
|
23
|
+
parser.add_argument(
|
|
24
|
+
"-o",
|
|
25
|
+
"--output",
|
|
26
|
+
type=str,
|
|
27
|
+
help="Path to the output iCal file",
|
|
28
|
+
default="timetree.ics",
|
|
29
|
+
)
|
|
30
|
+
args = parser.parse_args()
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
cal = Calendar()
|
|
34
|
+
filenames = paths_to_filelist(args.input)
|
|
35
|
+
|
|
36
|
+
for filename in filenames:
|
|
37
|
+
# Skip non-JSON files
|
|
38
|
+
if not filename.endswith(".json"):
|
|
39
|
+
print(f"Skipping {filename} (Invalid file type, should be .json)")
|
|
40
|
+
continue
|
|
41
|
+
print(f"Parsing {filename}")
|
|
42
|
+
|
|
43
|
+
# Get events from file
|
|
44
|
+
events = get_events_from_file(filename)
|
|
45
|
+
if events is None:
|
|
46
|
+
continue
|
|
47
|
+
|
|
48
|
+
# Add events to calendar
|
|
49
|
+
for event in events:
|
|
50
|
+
time_tree_event = TimeTreeEvent.from_dict(event)
|
|
51
|
+
formatter = ICalEventFormatter(time_tree_event)
|
|
52
|
+
iCalEvent = formatter.to_ical()
|
|
53
|
+
cal.add_component(iCalEvent)
|
|
54
|
+
|
|
55
|
+
# Write calendar to file
|
|
56
|
+
with open(args.output, "wb") as f: # Path Traversal Vulnerability if on a server
|
|
57
|
+
f.write(cal.to_ical())
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"""This module provides the TimeTreeEvent class for representing TimeTree events."""
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class TimeTreeEvent:
|
|
5
|
+
"""TimeTree event class"""
|
|
6
|
+
|
|
7
|
+
# pylint: disable=too-many-instance-attributes
|
|
8
|
+
|
|
9
|
+
def __init__(
|
|
10
|
+
self,
|
|
11
|
+
uuid: str,
|
|
12
|
+
title: str,
|
|
13
|
+
created_at: int,
|
|
14
|
+
updated_at: int,
|
|
15
|
+
recurrences: list,
|
|
16
|
+
alerts: list,
|
|
17
|
+
url: str,
|
|
18
|
+
note: str,
|
|
19
|
+
start_at: int,
|
|
20
|
+
end_at: int,
|
|
21
|
+
all_day: bool,
|
|
22
|
+
start_timezone: str,
|
|
23
|
+
end_timezone: str,
|
|
24
|
+
location_lat: str,
|
|
25
|
+
location_lon: str,
|
|
26
|
+
location: str,
|
|
27
|
+
):
|
|
28
|
+
# pylint: disable=too-many-arguments
|
|
29
|
+
# pylint: disable=too-many-locals
|
|
30
|
+
self.uuid = uuid
|
|
31
|
+
self.title = title
|
|
32
|
+
self.created_at = created_at
|
|
33
|
+
self.updated_at = updated_at
|
|
34
|
+
self.note = note
|
|
35
|
+
self.location = location
|
|
36
|
+
self.location_lat = location_lat
|
|
37
|
+
self.location_lon = location_lon
|
|
38
|
+
self.url = url
|
|
39
|
+
self.start_at = start_at
|
|
40
|
+
self.start_timezone = start_timezone
|
|
41
|
+
self.end_at = end_at
|
|
42
|
+
self.end_timezone = end_timezone
|
|
43
|
+
self.all_day = all_day
|
|
44
|
+
self.alerts = alerts
|
|
45
|
+
self.recurrences = recurrences
|
|
46
|
+
|
|
47
|
+
@classmethod
|
|
48
|
+
def from_dict(cls, event_data: dict):
|
|
49
|
+
"""Create TimeTreeEvent object from JSON data"""
|
|
50
|
+
return cls(
|
|
51
|
+
uuid=event_data.get("uuid"),
|
|
52
|
+
title=event_data.get("title"),
|
|
53
|
+
created_at=event_data.get("created_at"),
|
|
54
|
+
updated_at=event_data.get("updated_at"),
|
|
55
|
+
note=event_data.get("note"),
|
|
56
|
+
location=event_data.get("location"),
|
|
57
|
+
location_lat=event_data.get("location_lat"),
|
|
58
|
+
location_lon=event_data.get("location_lon"),
|
|
59
|
+
url=event_data.get("url"),
|
|
60
|
+
start_at=event_data.get("start_at"),
|
|
61
|
+
start_timezone=event_data.get("start_timezone"),
|
|
62
|
+
end_at=event_data.get("end_at"),
|
|
63
|
+
end_timezone=event_data.get("end_timezone"),
|
|
64
|
+
all_day=event_data.get("all_day"),
|
|
65
|
+
alerts=event_data.get("alerts"),
|
|
66
|
+
recurrences=event_data.get("recurrences"),
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
def __str__(self):
|
|
70
|
+
return self.title
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
"""
|
|
2
|
+
This module provides the ICalEventFormatter class
|
|
3
|
+
for formatting TimeTree events into iCalendar format.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from datetime import datetime, timedelta
|
|
7
|
+
from icalendar import Event, vRecur, vDate, vDatetime, vGeo
|
|
8
|
+
from icalendar.prop import vDDDLists
|
|
9
|
+
from icalendar.parser import Contentline
|
|
10
|
+
from dateutil import tz
|
|
11
|
+
from timetree_exporter.event import TimeTreeEvent
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class ICalEventFormatter:
|
|
15
|
+
"""
|
|
16
|
+
Class for formatting TimeTree events into iCalendar format.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
def __init__(self, time_tree_event: TimeTreeEvent):
|
|
20
|
+
self.time_tree_event = time_tree_event
|
|
21
|
+
|
|
22
|
+
@property
|
|
23
|
+
def uid(self):
|
|
24
|
+
"""Return the UUID of the event."""
|
|
25
|
+
return self.time_tree_event.uuid
|
|
26
|
+
|
|
27
|
+
@property
|
|
28
|
+
def summary(self):
|
|
29
|
+
"""Return the title of the event."""
|
|
30
|
+
return self.time_tree_event.title
|
|
31
|
+
|
|
32
|
+
@property
|
|
33
|
+
def created(self):
|
|
34
|
+
"""Return the creation time of the event."""
|
|
35
|
+
return vDatetime(
|
|
36
|
+
datetime.fromtimestamp(
|
|
37
|
+
self.time_tree_event.created_at / 1000, tz.gettz("UTC")
|
|
38
|
+
)
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
@property
|
|
42
|
+
def last_modify(self):
|
|
43
|
+
"""Return the last modification time of the event."""
|
|
44
|
+
return vDatetime(
|
|
45
|
+
datetime.fromtimestamp(
|
|
46
|
+
self.time_tree_event.updated_at / 1000, tz.gettz("UTC")
|
|
47
|
+
)
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
@property
|
|
51
|
+
def description(self):
|
|
52
|
+
"""Return the note of the event."""
|
|
53
|
+
return self.time_tree_event.note if self.time_tree_event.note != "" else None
|
|
54
|
+
|
|
55
|
+
@property
|
|
56
|
+
def location(self):
|
|
57
|
+
"""Return the location of the event."""
|
|
58
|
+
return (
|
|
59
|
+
self.time_tree_event.location
|
|
60
|
+
if self.time_tree_event.location != ""
|
|
61
|
+
else None
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
@property
|
|
65
|
+
def geo(self):
|
|
66
|
+
"""Return the geolocation of the event."""
|
|
67
|
+
if (
|
|
68
|
+
self.time_tree_event.location_lat is None
|
|
69
|
+
or self.time_tree_event.location_lon is None
|
|
70
|
+
):
|
|
71
|
+
return None
|
|
72
|
+
return vGeo(
|
|
73
|
+
(self.time_tree_event.location_lat, self.time_tree_event.location_lon)
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
@property
|
|
77
|
+
def url(self):
|
|
78
|
+
"""Return the URL of the event."""
|
|
79
|
+
return self.time_tree_event.url if self.time_tree_event.url != "" else None
|
|
80
|
+
|
|
81
|
+
def get_datetime(self, is_start_time):
|
|
82
|
+
"""Return the start or end time of the event."""
|
|
83
|
+
if is_start_time:
|
|
84
|
+
time = self.time_tree_event.start_at
|
|
85
|
+
timezone = self.time_tree_event.start_timezone
|
|
86
|
+
else:
|
|
87
|
+
time = self.time_tree_event.end_at
|
|
88
|
+
timezone = self.time_tree_event.end_timezone
|
|
89
|
+
|
|
90
|
+
if self.time_tree_event.all_day:
|
|
91
|
+
return vDate(
|
|
92
|
+
datetime.fromtimestamp(
|
|
93
|
+
time / 1000,
|
|
94
|
+
tz.gettz(timezone),
|
|
95
|
+
)
|
|
96
|
+
)
|
|
97
|
+
return vDatetime(
|
|
98
|
+
datetime.fromtimestamp(
|
|
99
|
+
time / 1000,
|
|
100
|
+
tz.gettz(timezone),
|
|
101
|
+
)
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
@property
|
|
105
|
+
def dtstart(self):
|
|
106
|
+
"""Return the start time of the event."""
|
|
107
|
+
return self.get_datetime(is_start_time=True)
|
|
108
|
+
|
|
109
|
+
@property
|
|
110
|
+
def dtend(self):
|
|
111
|
+
"""Return the end time of the event."""
|
|
112
|
+
return self.get_datetime(is_start_time=False)
|
|
113
|
+
|
|
114
|
+
@property
|
|
115
|
+
def alerts(self):
|
|
116
|
+
"""Return the alerts of the event."""
|
|
117
|
+
alerts = []
|
|
118
|
+
for alert in self.time_tree_event.alerts:
|
|
119
|
+
alarm = Event()
|
|
120
|
+
alarm.add("action", "DISPLAY")
|
|
121
|
+
alarm.add("description", "Reminder")
|
|
122
|
+
alarm.add("trigger", timedelta(minutes=-alert))
|
|
123
|
+
alerts.append(alarm)
|
|
124
|
+
return alerts
|
|
125
|
+
|
|
126
|
+
def add_recurrences(self, event):
|
|
127
|
+
"""Add recurrences to iCal event"""
|
|
128
|
+
for recurrence in self.time_tree_event.recurrences:
|
|
129
|
+
contentline = Contentline(recurrence)
|
|
130
|
+
name, parameters, value = contentline.parts()
|
|
131
|
+
if name.lower() == "rrule":
|
|
132
|
+
event.add(name, vRecur.from_ical(value), parameters)
|
|
133
|
+
elif name.lower() == "exdate" or name.lower() == "rdate":
|
|
134
|
+
event.add(name, vDDDLists.from_ical(value), parameters)
|
|
135
|
+
else:
|
|
136
|
+
raise ValueError(f"Unknown recurrence type: {name}")
|
|
137
|
+
|
|
138
|
+
def to_ical(self) -> Event:
|
|
139
|
+
"""Return the iCal event."""
|
|
140
|
+
event = Event()
|
|
141
|
+
|
|
142
|
+
event.add("uid", self.uid)
|
|
143
|
+
event.add("summary", self.summary)
|
|
144
|
+
event.add("dtstamp", datetime.now(tz.tzutc()))
|
|
145
|
+
event.add("created", self.created)
|
|
146
|
+
event.add("last-modify", self.last_modify)
|
|
147
|
+
event.add("dtstart", self.dtstart)
|
|
148
|
+
event.add("dtend", self.dtend)
|
|
149
|
+
|
|
150
|
+
if self.location:
|
|
151
|
+
event.add("location", self.location)
|
|
152
|
+
if self.geo:
|
|
153
|
+
event.add("geo", self.geo)
|
|
154
|
+
if self.url:
|
|
155
|
+
event.add("url", self.url)
|
|
156
|
+
if self.description:
|
|
157
|
+
event.add("description", self.description)
|
|
158
|
+
|
|
159
|
+
for alert in self.alerts:
|
|
160
|
+
event.add_component(alert)
|
|
161
|
+
|
|
162
|
+
self.add_recurrences(event)
|
|
163
|
+
|
|
164
|
+
return event
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"""Utility functions for Timetree Exporter"""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import os
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def get_events_from_file(file_path) -> list:
|
|
8
|
+
"""Fetch events from Timetree response file"""
|
|
9
|
+
try:
|
|
10
|
+
with open(file_path, "r", encoding="UTF-8") as response_file:
|
|
11
|
+
response_data = json.load(response_file)
|
|
12
|
+
return response_data["events"]
|
|
13
|
+
except FileNotFoundError:
|
|
14
|
+
print(f"File not found: {file_path}")
|
|
15
|
+
return None
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def paths_to_filelist(paths: list) -> list:
|
|
19
|
+
"""Converts a list of paths to a list of files"""
|
|
20
|
+
filenames = []
|
|
21
|
+
for path in paths:
|
|
22
|
+
if os.path.isdir(path):
|
|
23
|
+
filenames += [os.path.join(path, file) for file in os.listdir(path)]
|
|
24
|
+
elif os.path.isfile(path):
|
|
25
|
+
filenames.append(path)
|
|
26
|
+
else:
|
|
27
|
+
print(f"Invalid path: {path}")
|
|
28
|
+
return filenames
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: timetree-exporter
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A Tool for Exporting TimeTree Calendar and Convert to iCal format(.ics)
|
|
5
|
+
Author-email: Fong-Chun Tsai <eoleedimin@gmail.com>
|
|
6
|
+
Project-URL: Homepage, https://github.com/eoleedi/TimeTree-Exporter
|
|
7
|
+
Project-URL: Repository, https://github.com/eoleedi/TimeTree-Exporter
|
|
8
|
+
Project-URL: Issues, https://github.com/eoleedi/TimeTree-Exporter/issues
|
|
9
|
+
Project-URL: Changelog, https://github.com/eoleedi/TimeTree-Exporter/CHANGELOG.md
|
|
10
|
+
Keywords: timetree,exporter,icalendar,ics
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
+
Classifier: Operating System :: OS Independent
|
|
14
|
+
Requires-Python: >=3.8
|
|
15
|
+
Description-Content-Type: text/markdown
|
|
16
|
+
License-File: LICENSE
|
|
17
|
+
Requires-Dist: icalendar
|
|
18
|
+
|
|
19
|
+
# TimeTree Exporter
|
|
20
|
+
A Tool for Exporting TimeTree Calendar and Convert to iCal format(.ics) \
|
|
21
|
+
(The .ics file can then be imported into other calendar apps such as Google Calendar, Apple Calendar, Outlook Calendar, etc.)
|
|
22
|
+
|
|
23
|
+
# Usage
|
|
24
|
+
|
|
25
|
+
First, download the timetree data manually and put it in the responses folder(see below)
|
|
26
|
+
|
|
27
|
+
## Method 1: Pip
|
|
28
|
+
1. Install the package `pip3 install timetree-exporter`
|
|
29
|
+
2. Run `timetree-exporter <path-to-responses-folder>`
|
|
30
|
+
3. A timetree.ics file will be generated in the same directory, then you can import it to your calendar app.
|
|
31
|
+
|
|
32
|
+
## Method 2: Cloning the repository
|
|
33
|
+
1. Clone the repository `git clone https://github.com/eoleedi/TimeTree-exporter.git`
|
|
34
|
+
2. Change the directory to the project folder `cd TimeTree-exporter`
|
|
35
|
+
3. Install the requirements `pip3 install -r requirements.txt`
|
|
36
|
+
4. Run `python3 -m timetree_exporter responses`
|
|
37
|
+
5. A timetree.ics file will be generated in the same directory, then you can import it to your calendar app.
|
|
38
|
+
|
|
39
|
+
## How to download timetree data
|
|
40
|
+
1. Go to [https://timetreeapp.com/signin](https://timetreeapp.com/signin)
|
|
41
|
+
2. Open the developer tools before logging in
|
|
42
|
+
> Can be opened by pressing F12
|
|
43
|
+
3. Go to the network tab
|
|
44
|
+
> Remember to Press Ctrl + R to start recording
|
|
45
|
+
4. Turn on preserve log
|
|
46
|
+
5. Type in "sync" in the filter box
|
|
47
|
+
6. Log in
|
|
48
|
+
7. Click on the calendar you want to export
|
|
49
|
+
8. You will see couple of requests with the name "sync"
|
|
50
|
+
> There seems to be a maximum of 300 events in single requests. \
|
|
51
|
+
> If you have more than 300 events, you will see multiple requests with the name "sync"
|
|
52
|
+
9. Right click on the request and select "Copy Response"
|
|
53
|
+
10. Paste it into a json file under the response folder under this project (etc. sync.json)
|
|
54
|
+
> The file name should end with .json
|
|
55
|
+
11. Do the same thing for all the requests with steps 7~10
|
|
56
|
+
|
|
57
|
+

|
|
58
|
+
*Prepare for signin: Step 1~5*
|
|
59
|
+

|
|
60
|
+
*Copy response: Step 8~9*
|
|
61
|
+
|
|
62
|
+
# Advanced Usage
|
|
63
|
+
You can also specify the output file name by running `python3 -m timetree_exporter -o <output_file_name>.ics` or `timetree-exporter -o <output_file_name>.ics`
|
|
64
|
+
|
|
65
|
+
# Recommendation
|
|
66
|
+
You are recommended to import the ics file into a separate calendar (take google calendar as an example) as if anything goes wrong, you can just delete the calendar and reimport it.
|
|
67
|
+
|
|
68
|
+
# Requirements
|
|
69
|
+
icalendar==5.0.12
|
|
70
|
+
|
|
71
|
+
# Limitations
|
|
72
|
+
Currently, TimeTree data can only be downloaded manually through a web browser.
|
|
73
|
+
|
|
74
|
+
# Roadmap of the properties mapping to iCal
|
|
75
|
+
- [ ] **ID**
|
|
76
|
+
- [ ] **Primary ID**
|
|
77
|
+
- [ ] **Calendar ID**
|
|
78
|
+
- [x] **UUID**
|
|
79
|
+
- [ ] **Category**
|
|
80
|
+
- [ ] **Type**
|
|
81
|
+
- [ ] **Author ID**
|
|
82
|
+
- [ ] **Author Type**
|
|
83
|
+
- [x] **Title**
|
|
84
|
+
- [x] **All Day**
|
|
85
|
+
- [x] **Start At**
|
|
86
|
+
- [x] **Start Timezone**
|
|
87
|
+
- [x] **End At**
|
|
88
|
+
- [x] **End Timezone**
|
|
89
|
+
- [ ] **Label ID**
|
|
90
|
+
- [x] **Location**
|
|
91
|
+
- [x] **Location Latitude**
|
|
92
|
+
- [x] **Location Longitude**
|
|
93
|
+
- [x] **URL**
|
|
94
|
+
- [x] **Note**
|
|
95
|
+
- [ ] **Lunar**
|
|
96
|
+
- [ ] **Attendees**
|
|
97
|
+
- [x] **Recurrences**
|
|
98
|
+
- [ ] **Recurring UUID**
|
|
99
|
+
- [x] **Alerts**
|
|
100
|
+
- [ ] **Parent ID**
|
|
101
|
+
- [ ] **Link Object ID**
|
|
102
|
+
- [ ] **Link Object ID String**
|
|
103
|
+
- [ ] **Row Order**
|
|
104
|
+
- [ ] **Attachment**
|
|
105
|
+
- [ ] **Like Count**
|
|
106
|
+
- [ ] **Files**
|
|
107
|
+
- [ ] **Deactivated At**
|
|
108
|
+
- [ ] **Pinned At**
|
|
109
|
+
- [x] **Updated At**
|
|
110
|
+
- [x] **Created At**
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
.gitignore
|
|
2
|
+
CHANGELOG.md
|
|
3
|
+
LICENSE
|
|
4
|
+
README.md
|
|
5
|
+
pyproject.toml
|
|
6
|
+
requirements.txt
|
|
7
|
+
.github/dependabot.yml
|
|
8
|
+
.github/workflows/pylint.yml
|
|
9
|
+
.github/workflows/python-publish.yml
|
|
10
|
+
.github/workflows/release-please.yml
|
|
11
|
+
.vscode/settings.json
|
|
12
|
+
assets/images/copy-response.png
|
|
13
|
+
assets/images/prepare-for-signin.png
|
|
14
|
+
responses/README.md
|
|
15
|
+
timetree_exporter/__init__.py
|
|
16
|
+
timetree_exporter/__main__.py
|
|
17
|
+
timetree_exporter/event.py
|
|
18
|
+
timetree_exporter/formatter.py
|
|
19
|
+
timetree_exporter/utils.py
|
|
20
|
+
timetree_exporter.egg-info/PKG-INFO
|
|
21
|
+
timetree_exporter.egg-info/SOURCES.txt
|
|
22
|
+
timetree_exporter.egg-info/dependency_links.txt
|
|
23
|
+
timetree_exporter.egg-info/entry_points.txt
|
|
24
|
+
timetree_exporter.egg-info/requires.txt
|
|
25
|
+
timetree_exporter.egg-info/top_level.txt
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
icalendar
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
timetree_exporter
|