icalcli 1.0.5__tar.gz → 1.1__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.
- {icalcli-1.0.5 → icalcli-1.1}/PKG-INFO +50 -4
- icalcli-1.0.5/icalcli.egg-info/PKG-INFO → icalcli-1.1/README.md +43 -19
- {icalcli-1.0.5 → icalcli-1.1}/icalcli/__init__.py +3 -1
- icalcli-1.1/icalcli/etesync_backend/etebase_crud.py +137 -0
- icalcli-1.1/icalcli/etesync_backend/etebase_interface.py +94 -0
- {icalcli-1.0.5 → icalcli-1.1}/icalcli/etesync_backend/etesync_crud.py +3 -0
- {icalcli-1.0.5 → icalcli-1.1}/icalcli/etesync_backend/etesync_interface.py +5 -0
- {icalcli-1.0.5 → icalcli-1.1}/icalcli/file_backend/ics_interface.py +4 -4
- {icalcli-1.0.5 → icalcli-1.1}/icalcli/icalcli.py +10 -10
- icalcli-1.0.5/README.md → icalcli-1.1/icalcli.egg-info/PKG-INFO +65 -2
- {icalcli-1.0.5 → icalcli-1.1}/icalcli.egg-info/SOURCES.txt +2 -3
- {icalcli-1.0.5 → icalcli-1.1}/setup.py +1 -1
- icalcli-1.0.5/CHANGELOG.md +0 -45
- icalcli-1.0.5/exclude +0 -7
- icalcli-1.0.5/update-github.md +0 -33
- {icalcli-1.0.5 → icalcli-1.1}/LICENSE +0 -0
- {icalcli-1.0.5 → icalcli-1.1}/icalcli/argparsers.py +0 -0
- {icalcli-1.0.5 → icalcli-1.1}/icalcli/etesync_backend/__init__.py +0 -0
- {icalcli-1.0.5 → icalcli-1.1}/icalcli/file_backend/__init__.py +0 -0
- {icalcli-1.0.5 → icalcli-1.1}/icalcli/printer.py +0 -0
- {icalcli-1.0.5 → icalcli-1.1}/icalcli/utils.py +0 -0
- {icalcli-1.0.5 → icalcli-1.1}/icalcli.egg-info/dependency_links.txt +0 -0
- {icalcli-1.0.5 → icalcli-1.1}/icalcli.egg-info/entry_points.txt +0 -0
- {icalcli-1.0.5 → icalcli-1.1}/icalcli.egg-info/requires.txt +0 -0
- {icalcli-1.0.5 → icalcli-1.1}/icalcli.egg-info/top_level.txt +0 -0
- {icalcli-1.0.5 → icalcli-1.1}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: icalcli
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.1
|
|
4
4
|
Summary: Icalendar Calendar Command Line Interface
|
|
5
5
|
Home-page: https://github.com/jrvarma/icalcli
|
|
6
6
|
Maintainer: Jayanth R. Varma
|
|
@@ -12,8 +12,13 @@ Classifier: License :: OSI Approved :: MIT License
|
|
|
12
12
|
Classifier: Programming Language :: Python :: 3
|
|
13
13
|
Requires-Python: >=3.6
|
|
14
14
|
Description-Content-Type: text/markdown
|
|
15
|
-
Provides-Extra: parsedatetime
|
|
16
15
|
License-File: LICENSE
|
|
16
|
+
Requires-Dist: python-dateutil
|
|
17
|
+
Requires-Dist: parsedatetime
|
|
18
|
+
Requires-Dist: icalendar
|
|
19
|
+
Requires-Dist: recurring_ical_events
|
|
20
|
+
Provides-Extra: parsedatetime
|
|
21
|
+
Requires-Dist: parsedatetime; extra == "parsedatetime"
|
|
17
22
|
|
|
18
23
|
# icalcli
|
|
19
24
|
|
|
@@ -23,7 +28,7 @@ License-File: LICENSE
|
|
|
23
28
|
|
|
24
29
|
Unlike [gcalcli](https://github.com/insanum/gcalcli) which is tied to Google Calendar, `icalcli` is agnostic to (abstracts away from) the actual backend calendar service. It relies on a backend interface which interacts with the backend calendar to perform all the CRUD (Create, Retrieve, Update and Delete) operations on the actual calendar. The package includes two backends:
|
|
25
30
|
|
|
26
|
-
* The `etesync_backend` subpackage provides a backend interface to the [EteSync](https://www.etesync.com/) calendar. In fact, `icalcli` was created primarily to provide a command line interface to my self hosted `EteSync` calendar.
|
|
31
|
+
* The `etesync_backend` subpackage provides a backend interface to the [EteSync](https://www.etesync.com/) calendar (versions 1.0 and 2.0). In fact, `icalcli` was created primarily to provide a command line interface to my self hosted `EteSync` calendar.
|
|
27
32
|
|
|
28
33
|
* The `file_backend` subpackage provides a backend interface to a calendar contained in a local `ics` file. This is useful as a viewer/editor of `ics` files. It is also useful to try out `icalci` without having any other backend configured.
|
|
29
34
|
|
|
@@ -50,6 +55,12 @@ The two included backends would also be useful to those who wish to write their
|
|
|
50
55
|
pip install icalcli
|
|
51
56
|
```
|
|
52
57
|
|
|
58
|
+
or via `pipx`:
|
|
59
|
+
|
|
60
|
+
```sh
|
|
61
|
+
pipx install icalcli
|
|
62
|
+
```
|
|
63
|
+
|
|
53
64
|
### Install from source
|
|
54
65
|
|
|
55
66
|
```sh
|
|
@@ -57,6 +68,13 @@ git clone https://github.com/jrvarma/icalcli.git
|
|
|
57
68
|
cd icalcli
|
|
58
69
|
python setup.py install
|
|
59
70
|
```
|
|
71
|
+
|
|
72
|
+
or via `pipx`:
|
|
73
|
+
|
|
74
|
+
```sh
|
|
75
|
+
pipx install . --editable
|
|
76
|
+
```
|
|
77
|
+
|
|
60
78
|
## Usage
|
|
61
79
|
|
|
62
80
|
### Command line arguments
|
|
@@ -97,7 +115,7 @@ backend_interface = ICSInterface(["/path/to/ics-file-1", "/path/to/ics-file-2"])
|
|
|
97
115
|
|
|
98
116
|
```
|
|
99
117
|
|
|
100
|
-
#### Example configuration for etesync_backend
|
|
118
|
+
#### Example configuration for etesync_backend (etesync 1.0)
|
|
101
119
|
|
|
102
120
|
This configuration assumes that all the credentials are stored in a plain text (`json`) file. In practice, you would use a more secure storage (perhaps, the `Gnome keyring`) or just read it from the terminal.
|
|
103
121
|
|
|
@@ -116,6 +134,34 @@ backend_interface = EtesyncInterface(
|
|
|
116
134
|
```
|
|
117
135
|
See the [Example code](https://github.com/jrvarma/icalcli/issues/1#issuecomment-979851222) for getting the `uid` and `authToken` for the `etesync` calendar.
|
|
118
136
|
|
|
137
|
+
#### Example configuration for etebase_backend (etesync 2.0)
|
|
138
|
+
|
|
139
|
+
This configuration assumes that all the credentials are stored in a plain text (`json`) file. In practice, you would use a more secure storage (perhaps, the `Gnome keyring`) or just read it from the terminal.
|
|
140
|
+
|
|
141
|
+
```
|
|
142
|
+
from icalcli import EtebaseInterface
|
|
143
|
+
import json
|
|
144
|
+
|
|
145
|
+
conf_file = '/path/to/json-file'
|
|
146
|
+
with open(conf_file, 'r') as fp:
|
|
147
|
+
c = json.load(fp)
|
|
148
|
+
|
|
149
|
+
backend_interface = EtebaseInterface(c['user'], c['server_url'], c['password'],
|
|
150
|
+
c['calendar_uid'], silent=False)
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
The `calendar_uid` can be obtained using the following code. This code assumes that the `dict c` has been populated with the credentials from the `json` file as above.
|
|
154
|
+
|
|
155
|
+
```
|
|
156
|
+
from etebase import Client, Account, FetchOptions
|
|
157
|
+
|
|
158
|
+
client = Client(c['user'], c['server_url'])
|
|
159
|
+
etebase = Account.login(client, c['user'], c['password'])
|
|
160
|
+
col_mgr = etebase.get_collection_manager()
|
|
161
|
+
print({col.uid: col.meta
|
|
162
|
+
for col in col_mgr.list("etebase.vevent").data})
|
|
163
|
+
```
|
|
164
|
+
|
|
119
165
|
## Recurring events and default search period
|
|
120
166
|
|
|
121
167
|
`icalcli` understands the `RRULE`, `RDATE`, `EXRULE`, `EXDATE` elements of the `icalendar` specification. These elements can be added while creating or editing events using `--rrule`, `--rdate`, `--exrule` and `--exdate` options.
|
|
@@ -1,20 +1,3 @@
|
|
|
1
|
-
Metadata-Version: 2.1
|
|
2
|
-
Name: icalcli
|
|
3
|
-
Version: 1.0.5
|
|
4
|
-
Summary: Icalendar Calendar Command Line Interface
|
|
5
|
-
Home-page: https://github.com/jrvarma/icalcli
|
|
6
|
-
Maintainer: Jayanth R. Varma
|
|
7
|
-
Maintainer-email: jrvarma@gmail.com
|
|
8
|
-
Classifier: Development Status :: 3 - Alpha
|
|
9
|
-
Classifier: Environment :: Console
|
|
10
|
-
Classifier: Intended Audience :: End Users/Desktop
|
|
11
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
12
|
-
Classifier: Programming Language :: Python :: 3
|
|
13
|
-
Requires-Python: >=3.6
|
|
14
|
-
Description-Content-Type: text/markdown
|
|
15
|
-
Provides-Extra: parsedatetime
|
|
16
|
-
License-File: LICENSE
|
|
17
|
-
|
|
18
1
|
# icalcli
|
|
19
2
|
|
|
20
3
|
### An iCalendar Command Line Interface
|
|
@@ -23,7 +6,7 @@ License-File: LICENSE
|
|
|
23
6
|
|
|
24
7
|
Unlike [gcalcli](https://github.com/insanum/gcalcli) which is tied to Google Calendar, `icalcli` is agnostic to (abstracts away from) the actual backend calendar service. It relies on a backend interface which interacts with the backend calendar to perform all the CRUD (Create, Retrieve, Update and Delete) operations on the actual calendar. The package includes two backends:
|
|
25
8
|
|
|
26
|
-
* The `etesync_backend` subpackage provides a backend interface to the [EteSync](https://www.etesync.com/) calendar. In fact, `icalcli` was created primarily to provide a command line interface to my self hosted `EteSync` calendar.
|
|
9
|
+
* The `etesync_backend` subpackage provides a backend interface to the [EteSync](https://www.etesync.com/) calendar (versions 1.0 and 2.0). In fact, `icalcli` was created primarily to provide a command line interface to my self hosted `EteSync` calendar.
|
|
27
10
|
|
|
28
11
|
* The `file_backend` subpackage provides a backend interface to a calendar contained in a local `ics` file. This is useful as a viewer/editor of `ics` files. It is also useful to try out `icalci` without having any other backend configured.
|
|
29
12
|
|
|
@@ -50,6 +33,12 @@ The two included backends would also be useful to those who wish to write their
|
|
|
50
33
|
pip install icalcli
|
|
51
34
|
```
|
|
52
35
|
|
|
36
|
+
or via `pipx`:
|
|
37
|
+
|
|
38
|
+
```sh
|
|
39
|
+
pipx install icalcli
|
|
40
|
+
```
|
|
41
|
+
|
|
53
42
|
### Install from source
|
|
54
43
|
|
|
55
44
|
```sh
|
|
@@ -57,6 +46,13 @@ git clone https://github.com/jrvarma/icalcli.git
|
|
|
57
46
|
cd icalcli
|
|
58
47
|
python setup.py install
|
|
59
48
|
```
|
|
49
|
+
|
|
50
|
+
or via `pipx`:
|
|
51
|
+
|
|
52
|
+
```sh
|
|
53
|
+
pipx install . --editable
|
|
54
|
+
```
|
|
55
|
+
|
|
60
56
|
## Usage
|
|
61
57
|
|
|
62
58
|
### Command line arguments
|
|
@@ -97,7 +93,7 @@ backend_interface = ICSInterface(["/path/to/ics-file-1", "/path/to/ics-file-2"])
|
|
|
97
93
|
|
|
98
94
|
```
|
|
99
95
|
|
|
100
|
-
#### Example configuration for etesync_backend
|
|
96
|
+
#### Example configuration for etesync_backend (etesync 1.0)
|
|
101
97
|
|
|
102
98
|
This configuration assumes that all the credentials are stored in a plain text (`json`) file. In practice, you would use a more secure storage (perhaps, the `Gnome keyring`) or just read it from the terminal.
|
|
103
99
|
|
|
@@ -116,6 +112,34 @@ backend_interface = EtesyncInterface(
|
|
|
116
112
|
```
|
|
117
113
|
See the [Example code](https://github.com/jrvarma/icalcli/issues/1#issuecomment-979851222) for getting the `uid` and `authToken` for the `etesync` calendar.
|
|
118
114
|
|
|
115
|
+
#### Example configuration for etebase_backend (etesync 2.0)
|
|
116
|
+
|
|
117
|
+
This configuration assumes that all the credentials are stored in a plain text (`json`) file. In practice, you would use a more secure storage (perhaps, the `Gnome keyring`) or just read it from the terminal.
|
|
118
|
+
|
|
119
|
+
```
|
|
120
|
+
from icalcli import EtebaseInterface
|
|
121
|
+
import json
|
|
122
|
+
|
|
123
|
+
conf_file = '/path/to/json-file'
|
|
124
|
+
with open(conf_file, 'r') as fp:
|
|
125
|
+
c = json.load(fp)
|
|
126
|
+
|
|
127
|
+
backend_interface = EtebaseInterface(c['user'], c['server_url'], c['password'],
|
|
128
|
+
c['calendar_uid'], silent=False)
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
The `calendar_uid` can be obtained using the following code. This code assumes that the `dict c` has been populated with the credentials from the `json` file as above.
|
|
132
|
+
|
|
133
|
+
```
|
|
134
|
+
from etebase import Client, Account, FetchOptions
|
|
135
|
+
|
|
136
|
+
client = Client(c['user'], c['server_url'])
|
|
137
|
+
etebase = Account.login(client, c['user'], c['password'])
|
|
138
|
+
col_mgr = etebase.get_collection_manager()
|
|
139
|
+
print({col.uid: col.meta
|
|
140
|
+
for col in col_mgr.list("etebase.vevent").data})
|
|
141
|
+
```
|
|
142
|
+
|
|
119
143
|
## Recurring events and default search period
|
|
120
144
|
|
|
121
145
|
`icalcli` understands the `RRULE`, `RDATE`, `EXRULE`, `EXDATE` elements of the `icalendar` specification. These elements can be added while creating or editing events using `--rrule`, `--rdate`, `--exrule` and `--exdate` options.
|
|
@@ -4,7 +4,9 @@ __author__ = 'Jayanth Varma (modified gcalcli by Eric Davis et al.)'
|
|
|
4
4
|
from icalcli.icalcli import IcalendarInterface # noqa F401
|
|
5
5
|
try:
|
|
6
6
|
from icalcli.etesync_backend.etesync_crud import EtesyncCRUD # noqa F401
|
|
7
|
-
from icalcli.etesync_backend.etesync_interface import EtesyncInterface # noqa
|
|
7
|
+
from icalcli.etesync_backend.etesync_interface import EtesyncInterface # noqa F
|
|
8
|
+
from icalcli.etesync_backend.etebase_crud import EtebaseCRUD # noqa F401
|
|
9
|
+
from icalcli.etesync_backend.etebase_interface import EtebaseInterface # noqa F401
|
|
8
10
|
except ImportError:
|
|
9
11
|
pass
|
|
10
12
|
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
|
|
3
|
+
# this is part of the etesync 2.0 backend
|
|
4
|
+
# for etesync 1.0 backend see etesync_crud.py
|
|
5
|
+
|
|
6
|
+
from etebase import Client, Account, FetchOptions
|
|
7
|
+
from time import sleep, time
|
|
8
|
+
|
|
9
|
+
# The EtesyncCRUD class exposes methods for each of the CRUD operations
|
|
10
|
+
# (Create, Retrieve, Update and Delete) and for sync with the server.
|
|
11
|
+
# It handles only one calendar
|
|
12
|
+
|
|
13
|
+
# The class is initialized with user details, authToken and
|
|
14
|
+
# EITHER the encryption password OR the cipher key.
|
|
15
|
+
|
|
16
|
+
# Intended usage is that a calling program (a CLI) obtains user credentials
|
|
17
|
+
# from terminal input or some secure storage (like a key ring)
|
|
18
|
+
# and then creates an instance of EtesyncCRUD as follows:
|
|
19
|
+
|
|
20
|
+
# crud = EtebaseCRUD(user, server_url, password, calendar_uid)
|
|
21
|
+
|
|
22
|
+
# The CLI program can then perform CRUD operations by calling
|
|
23
|
+
# crud.create_event, crud.retrieve_event,
|
|
24
|
+
# crud.update_event and crud.delete_event
|
|
25
|
+
|
|
26
|
+
# The CLI must explicitly call crud.sync when needed. For example:
|
|
27
|
+
# (a) if the server has been updated from another device
|
|
28
|
+
# (b) after any CRUD operation other than Retrieve
|
|
29
|
+
|
|
30
|
+
# No exception handling is done. That is left to the CLI.
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class EtebaseCRUD:
|
|
34
|
+
def __init__(self, user, server_url, password, calendar_uid, silent=True):
|
|
35
|
+
"""Initialize
|
|
36
|
+
|
|
37
|
+
Parameters
|
|
38
|
+
----------
|
|
39
|
+
user : etebase username
|
|
40
|
+
password : etebase password
|
|
41
|
+
server_url : url of etebase server
|
|
42
|
+
calendar_uid : uid of calendar
|
|
43
|
+
"""
|
|
44
|
+
client = Client(user, server_url)
|
|
45
|
+
etebase = Account.login(client, user, password)
|
|
46
|
+
col_mgr = etebase.get_collection_manager()
|
|
47
|
+
collection = col_mgr.fetch(calendar_uid)
|
|
48
|
+
self.item_mgr = col_mgr.get_item_manager(collection)
|
|
49
|
+
self.sync(silent)
|
|
50
|
+
|
|
51
|
+
def create_event(self, event, uid):
|
|
52
|
+
"""Create event
|
|
53
|
+
|
|
54
|
+
Parameters
|
|
55
|
+
----------
|
|
56
|
+
event : iCalendar file as bytes
|
|
57
|
+
(calendar containing one event to be added)
|
|
58
|
+
"""
|
|
59
|
+
item = self.item_mgr.create(
|
|
60
|
+
{
|
|
61
|
+
"name": uid,
|
|
62
|
+
"mtime": int(round(time() * 1000))
|
|
63
|
+
},
|
|
64
|
+
event
|
|
65
|
+
)
|
|
66
|
+
self.item_mgr.batch([item])
|
|
67
|
+
|
|
68
|
+
def update_event(self, event, uid):
|
|
69
|
+
"""Edit event
|
|
70
|
+
|
|
71
|
+
Parameters
|
|
72
|
+
----------
|
|
73
|
+
event : iCalendar file as bytes
|
|
74
|
+
(calendar containing one event to be updated)
|
|
75
|
+
uid : uid of event to be updated
|
|
76
|
+
"""
|
|
77
|
+
item = self.item_mgr.fetch(uid)
|
|
78
|
+
item.content = event
|
|
79
|
+
self.item_mgr.batch([item])
|
|
80
|
+
|
|
81
|
+
def retrieve_event(self, uid):
|
|
82
|
+
r"""Retrieve event by uid
|
|
83
|
+
|
|
84
|
+
Parameters
|
|
85
|
+
----------
|
|
86
|
+
uid : uid of event to be retrieved
|
|
87
|
+
|
|
88
|
+
Returns
|
|
89
|
+
-------
|
|
90
|
+
iCalendar file (as a string)
|
|
91
|
+
"""
|
|
92
|
+
return self.item_mgr.fetch(uid).content.decode()
|
|
93
|
+
|
|
94
|
+
def all_events(self):
|
|
95
|
+
"""Retrieve all events in calendar
|
|
96
|
+
|
|
97
|
+
Returns
|
|
98
|
+
-------
|
|
99
|
+
List of iCalendar files (as strings)
|
|
100
|
+
"""
|
|
101
|
+
return [e.content.decode() for e in self.items.data if not e.deleted]
|
|
102
|
+
|
|
103
|
+
def delete_event(self, uid):
|
|
104
|
+
"""Delete event and sync calendar
|
|
105
|
+
|
|
106
|
+
Parameters
|
|
107
|
+
----------
|
|
108
|
+
uid : uid of event to be deleted
|
|
109
|
+
"""
|
|
110
|
+
item = self.item_mgr.fetch(uid)
|
|
111
|
+
item.delete()
|
|
112
|
+
self.item_mgr.batch([item])
|
|
113
|
+
|
|
114
|
+
def sync(self, silent=False):
|
|
115
|
+
"""Initialize
|
|
116
|
+
|
|
117
|
+
Parameters
|
|
118
|
+
----------
|
|
119
|
+
silent : boolean whether to report sync attempt and success
|
|
120
|
+
"""
|
|
121
|
+
silent or print("Syncing with server. Please wait")
|
|
122
|
+
msg = "etebase fetch attempt {:} failed. Will retry after {:} seconds"
|
|
123
|
+
delay = 5
|
|
124
|
+
synced = False
|
|
125
|
+
for i in range(5):
|
|
126
|
+
try:
|
|
127
|
+
self.items = self.item_mgr.list(FetchOptions().limit(10**6))
|
|
128
|
+
synced = True
|
|
129
|
+
break
|
|
130
|
+
except Exception:
|
|
131
|
+
silent or print(msg.format(i+1, delay))
|
|
132
|
+
sleep(delay)
|
|
133
|
+
if synced:
|
|
134
|
+
silent or print("Syncing completed.")
|
|
135
|
+
self.all_events()
|
|
136
|
+
else:
|
|
137
|
+
print("Syncing with server failed after 5 attempts")
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
|
|
3
|
+
# this is part of the etesync 2.0 backend
|
|
4
|
+
# for etesync 1.0 backend see etesync_interface.py
|
|
5
|
+
|
|
6
|
+
from icalcli.etesync_backend.etebase_crud import EtebaseCRUD
|
|
7
|
+
from icalendar import Calendar
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
# The EtebaseInterface class is basically a wrapper around the
|
|
11
|
+
# EtebaseCRUD class from which it is derived
|
|
12
|
+
# The CRUD operations (Create, Retrieve, Update and Delete)
|
|
13
|
+
# work with icalendar events (VEVENT). While calling EtebaseCRUD methods,
|
|
14
|
+
# both inputs and return values are converted to/from
|
|
15
|
+
# ics calendar bytes/strings
|
|
16
|
+
|
|
17
|
+
# The class is initialized with user details and credentials.
|
|
18
|
+
|
|
19
|
+
# Intended usage is that a calling program obtains user credentials
|
|
20
|
+
# from terminal input or some secure storage (like a key ring)
|
|
21
|
+
# and then creates an instance of EtebaseCRUD as follows:
|
|
22
|
+
|
|
23
|
+
# etes = EtebaseInterface(user, server_url, password, calendar_uid)
|
|
24
|
+
|
|
25
|
+
# The calling program can then perform CRUD operations by calling
|
|
26
|
+
# etes.create_event, etes.retrieve_event,
|
|
27
|
+
# etes.update_event and etes.delete_event
|
|
28
|
+
|
|
29
|
+
# The calling program must explicitly call etes.sync when needed. For example:
|
|
30
|
+
# (a) if the server has been updated from another device
|
|
31
|
+
# (b) after any CRUD operation other than Retrieve
|
|
32
|
+
|
|
33
|
+
# No exception handling is done. That is left to the calling program.
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class EtebaseInterface (EtebaseCRUD):
|
|
37
|
+
def __init__(self, user, server_url, password, calendar_uid, silent=True):
|
|
38
|
+
r"""Initialize EtebaseInterface
|
|
39
|
+
|
|
40
|
+
Parameters
|
|
41
|
+
----------
|
|
42
|
+
user : etebase username
|
|
43
|
+
password : etebase password
|
|
44
|
+
server_url : url of etebase server
|
|
45
|
+
calendar_uid : uid of calendar
|
|
46
|
+
"""
|
|
47
|
+
super(EtebaseInterface, self).__init__(
|
|
48
|
+
user, server_url, password, calendar_uid, silent)
|
|
49
|
+
self.all_events()
|
|
50
|
+
|
|
51
|
+
def all_events(self):
|
|
52
|
+
self.events = [Calendar.from_ical(ev).walk('VEVENT')[0]
|
|
53
|
+
for ev in EtebaseCRUD.all_events(self)]
|
|
54
|
+
|
|
55
|
+
def create_event(self, event, vtimezone=None):
|
|
56
|
+
r"""Create event
|
|
57
|
+
|
|
58
|
+
Parameters
|
|
59
|
+
----------
|
|
60
|
+
event : event to be added (iCalendar object)
|
|
61
|
+
"""
|
|
62
|
+
ics = self.event_to_ics(event, vtimezone)
|
|
63
|
+
EtebaseCRUD.create_event(self, ics)
|
|
64
|
+
|
|
65
|
+
def update_event(self, event, vtimezone=None):
|
|
66
|
+
r"""Update event
|
|
67
|
+
|
|
68
|
+
Parameters
|
|
69
|
+
----------
|
|
70
|
+
event : event to be added (iCalendar object)
|
|
71
|
+
"""
|
|
72
|
+
uid = event.decoded('uid').decode()
|
|
73
|
+
ics = self.event_to_ics(event, vtimezone)
|
|
74
|
+
EtebaseCRUD.update_event(self, ics, uid)
|
|
75
|
+
|
|
76
|
+
def event_to_ics(self, event, vtimezone=None):
|
|
77
|
+
r"""Make calendar byte array (ics) from event
|
|
78
|
+
|
|
79
|
+
Parameters
|
|
80
|
+
----------
|
|
81
|
+
event : event to be added (iCalendar object)
|
|
82
|
+
"""
|
|
83
|
+
cal = Calendar()
|
|
84
|
+
cal.add_component(event)
|
|
85
|
+
if vtimezone:
|
|
86
|
+
cal.add_component(vtimezone)
|
|
87
|
+
ics = cal.to_ical()
|
|
88
|
+
return ics
|
|
89
|
+
|
|
90
|
+
def sync(self, vtimezone=None):
|
|
91
|
+
r"""Sync with server and rebuild vevent list
|
|
92
|
+
"""
|
|
93
|
+
EtebaseCRUD.sync(self)
|
|
94
|
+
self.all_events()
|
|
@@ -61,9 +61,9 @@ class ICSInterface:
|
|
|
61
61
|
return
|
|
62
62
|
if self.backup:
|
|
63
63
|
with NamedTemporaryFile(mode='w',
|
|
64
|
-
suffix=self.
|
|
65
|
-
prefix=self.
|
|
66
|
-
dir=self.
|
|
64
|
+
suffix=self.filepaths[0].suffix,
|
|
65
|
+
prefix=self.filepaths[0].name,
|
|
66
|
+
dir=self.filepaths[0].parent,
|
|
67
67
|
delete=False) as fp:
|
|
68
68
|
fp.write(self.ics)
|
|
69
69
|
cal = Calendar()
|
|
@@ -72,6 +72,6 @@ class ICSInterface:
|
|
|
72
72
|
if vtimezone:
|
|
73
73
|
cal.add_component(vtimezone)
|
|
74
74
|
self.ics = cal.to_ical().decode()
|
|
75
|
-
with open(self.
|
|
75
|
+
with open(self.filepaths[0], 'w') as fp:
|
|
76
76
|
fp.write(self.ics)
|
|
77
77
|
self.all_events()
|
|
@@ -385,13 +385,13 @@ class IcalendarInterface:
|
|
|
385
385
|
decode().strip())
|
|
386
386
|
if self.graphical_outputs.get('uid'):
|
|
387
387
|
summary += " <%s>" % (event.Decoded('uid').decode().strip())
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
388
|
+
if (
|
|
389
|
+
self.graphical_outputs.get('description')
|
|
390
|
+
and 'description' in event
|
|
391
|
+
and event.Decoded('description').decode().strip()
|
|
392
|
+
and self.outputs.get('description')
|
|
393
|
+
):
|
|
394
|
+
summary += event.Decoded('description'). decode().strip()
|
|
395
395
|
return summary
|
|
396
396
|
|
|
397
397
|
def get_week_events(self, start_dt, end_dt, event_list):
|
|
@@ -551,10 +551,11 @@ class IcalendarInterface:
|
|
|
551
551
|
-------
|
|
552
552
|
int: where to cut the word
|
|
553
553
|
"""
|
|
554
|
-
print_len = 0
|
|
554
|
+
# print_len = 0
|
|
555
555
|
|
|
556
556
|
words = _u(string).split()
|
|
557
557
|
for i, word in enumerate(words):
|
|
558
|
+
print_len = self.printed_len(' '.join(words[:i]))
|
|
558
559
|
word_len = self.printed_len(word)
|
|
559
560
|
if (
|
|
560
561
|
(cur_print_len + word_len + print_len
|
|
@@ -566,7 +567,7 @@ class IcalendarInterface:
|
|
|
566
567
|
if cut_idx == 0:
|
|
567
568
|
return self.word_cut(word)
|
|
568
569
|
return (print_len, cut_idx)
|
|
569
|
-
print_len += word_len + i # +i for the space between words
|
|
570
|
+
# print_len += word_len + i # +i for the space between words
|
|
570
571
|
return (print_len, len(' '.join(words[:i])))
|
|
571
572
|
|
|
572
573
|
def get_cut_index(self, event_string):
|
|
@@ -741,7 +742,6 @@ class IcalendarInterface:
|
|
|
741
742
|
curr_event.title)
|
|
742
743
|
padding = ' ' * (self.options['cal_width']
|
|
743
744
|
- print_len)
|
|
744
|
-
|
|
745
745
|
self.printer.msg(
|
|
746
746
|
curr_event.title[:cut_idx] + padding,
|
|
747
747
|
curr_event.color)
|
|
@@ -1,3 +1,25 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: icalcli
|
|
3
|
+
Version: 1.1
|
|
4
|
+
Summary: Icalendar Calendar Command Line Interface
|
|
5
|
+
Home-page: https://github.com/jrvarma/icalcli
|
|
6
|
+
Maintainer: Jayanth R. Varma
|
|
7
|
+
Maintainer-email: jrvarma@gmail.com
|
|
8
|
+
Classifier: Development Status :: 3 - Alpha
|
|
9
|
+
Classifier: Environment :: Console
|
|
10
|
+
Classifier: Intended Audience :: End Users/Desktop
|
|
11
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Requires-Python: >=3.6
|
|
14
|
+
Description-Content-Type: text/markdown
|
|
15
|
+
License-File: LICENSE
|
|
16
|
+
Requires-Dist: python-dateutil
|
|
17
|
+
Requires-Dist: parsedatetime
|
|
18
|
+
Requires-Dist: icalendar
|
|
19
|
+
Requires-Dist: recurring_ical_events
|
|
20
|
+
Provides-Extra: parsedatetime
|
|
21
|
+
Requires-Dist: parsedatetime; extra == "parsedatetime"
|
|
22
|
+
|
|
1
23
|
# icalcli
|
|
2
24
|
|
|
3
25
|
### An iCalendar Command Line Interface
|
|
@@ -6,7 +28,7 @@
|
|
|
6
28
|
|
|
7
29
|
Unlike [gcalcli](https://github.com/insanum/gcalcli) which is tied to Google Calendar, `icalcli` is agnostic to (abstracts away from) the actual backend calendar service. It relies on a backend interface which interacts with the backend calendar to perform all the CRUD (Create, Retrieve, Update and Delete) operations on the actual calendar. The package includes two backends:
|
|
8
30
|
|
|
9
|
-
* The `etesync_backend` subpackage provides a backend interface to the [EteSync](https://www.etesync.com/) calendar. In fact, `icalcli` was created primarily to provide a command line interface to my self hosted `EteSync` calendar.
|
|
31
|
+
* The `etesync_backend` subpackage provides a backend interface to the [EteSync](https://www.etesync.com/) calendar (versions 1.0 and 2.0). In fact, `icalcli` was created primarily to provide a command line interface to my self hosted `EteSync` calendar.
|
|
10
32
|
|
|
11
33
|
* The `file_backend` subpackage provides a backend interface to a calendar contained in a local `ics` file. This is useful as a viewer/editor of `ics` files. It is also useful to try out `icalci` without having any other backend configured.
|
|
12
34
|
|
|
@@ -33,6 +55,12 @@ The two included backends would also be useful to those who wish to write their
|
|
|
33
55
|
pip install icalcli
|
|
34
56
|
```
|
|
35
57
|
|
|
58
|
+
or via `pipx`:
|
|
59
|
+
|
|
60
|
+
```sh
|
|
61
|
+
pipx install icalcli
|
|
62
|
+
```
|
|
63
|
+
|
|
36
64
|
### Install from source
|
|
37
65
|
|
|
38
66
|
```sh
|
|
@@ -40,6 +68,13 @@ git clone https://github.com/jrvarma/icalcli.git
|
|
|
40
68
|
cd icalcli
|
|
41
69
|
python setup.py install
|
|
42
70
|
```
|
|
71
|
+
|
|
72
|
+
or via `pipx`:
|
|
73
|
+
|
|
74
|
+
```sh
|
|
75
|
+
pipx install . --editable
|
|
76
|
+
```
|
|
77
|
+
|
|
43
78
|
## Usage
|
|
44
79
|
|
|
45
80
|
### Command line arguments
|
|
@@ -80,7 +115,7 @@ backend_interface = ICSInterface(["/path/to/ics-file-1", "/path/to/ics-file-2"])
|
|
|
80
115
|
|
|
81
116
|
```
|
|
82
117
|
|
|
83
|
-
#### Example configuration for etesync_backend
|
|
118
|
+
#### Example configuration for etesync_backend (etesync 1.0)
|
|
84
119
|
|
|
85
120
|
This configuration assumes that all the credentials are stored in a plain text (`json`) file. In practice, you would use a more secure storage (perhaps, the `Gnome keyring`) or just read it from the terminal.
|
|
86
121
|
|
|
@@ -99,6 +134,34 @@ backend_interface = EtesyncInterface(
|
|
|
99
134
|
```
|
|
100
135
|
See the [Example code](https://github.com/jrvarma/icalcli/issues/1#issuecomment-979851222) for getting the `uid` and `authToken` for the `etesync` calendar.
|
|
101
136
|
|
|
137
|
+
#### Example configuration for etebase_backend (etesync 2.0)
|
|
138
|
+
|
|
139
|
+
This configuration assumes that all the credentials are stored in a plain text (`json`) file. In practice, you would use a more secure storage (perhaps, the `Gnome keyring`) or just read it from the terminal.
|
|
140
|
+
|
|
141
|
+
```
|
|
142
|
+
from icalcli import EtebaseInterface
|
|
143
|
+
import json
|
|
144
|
+
|
|
145
|
+
conf_file = '/path/to/json-file'
|
|
146
|
+
with open(conf_file, 'r') as fp:
|
|
147
|
+
c = json.load(fp)
|
|
148
|
+
|
|
149
|
+
backend_interface = EtebaseInterface(c['user'], c['server_url'], c['password'],
|
|
150
|
+
c['calendar_uid'], silent=False)
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
The `calendar_uid` can be obtained using the following code. This code assumes that the `dict c` has been populated with the credentials from the `json` file as above.
|
|
154
|
+
|
|
155
|
+
```
|
|
156
|
+
from etebase import Client, Account, FetchOptions
|
|
157
|
+
|
|
158
|
+
client = Client(c['user'], c['server_url'])
|
|
159
|
+
etebase = Account.login(client, c['user'], c['password'])
|
|
160
|
+
col_mgr = etebase.get_collection_manager()
|
|
161
|
+
print({col.uid: col.meta
|
|
162
|
+
for col in col_mgr.list("etebase.vevent").data})
|
|
163
|
+
```
|
|
164
|
+
|
|
102
165
|
## Recurring events and default search period
|
|
103
166
|
|
|
104
167
|
`icalcli` understands the `RRULE`, `RDATE`, `EXRULE`, `EXDATE` elements of the `icalendar` specification. These elements can be added while creating or editing events using `--rrule`, `--rdate`, `--exrule` and `--exdate` options.
|
|
@@ -1,9 +1,6 @@
|
|
|
1
|
-
CHANGELOG.md
|
|
2
1
|
LICENSE
|
|
3
2
|
README.md
|
|
4
|
-
exclude
|
|
5
3
|
setup.py
|
|
6
|
-
update-github.md
|
|
7
4
|
icalcli/__init__.py
|
|
8
5
|
icalcli/argparsers.py
|
|
9
6
|
icalcli/icalcli.py
|
|
@@ -16,6 +13,8 @@ icalcli.egg-info/entry_points.txt
|
|
|
16
13
|
icalcli.egg-info/requires.txt
|
|
17
14
|
icalcli.egg-info/top_level.txt
|
|
18
15
|
icalcli/etesync_backend/__init__.py
|
|
16
|
+
icalcli/etesync_backend/etebase_crud.py
|
|
17
|
+
icalcli/etesync_backend/etebase_interface.py
|
|
19
18
|
icalcli/etesync_backend/etesync_crud.py
|
|
20
19
|
icalcli/etesync_backend/etesync_interface.py
|
|
21
20
|
icalcli/file_backend/__init__.py
|
icalcli-1.0.5/CHANGELOG.md
DELETED
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
# 1.0.5 released 2024-04-17
|
|
2
|
-
|
|
3
|
-
* Added `cal5w` command to provide current week, two past weeks and two future weeks in a calendar format.
|
|
4
|
-
* Introduced option to get more event details (end time, alarm, location, free/busy) in week and calendar views.
|
|
5
|
-
* Extended the file backend to allow multiple `ics` files in read only mode.
|
|
6
|
-
|
|
7
|
-
# 1.0.4 released 2022-10-11
|
|
8
|
-
Bug fixes
|
|
9
|
-
|
|
10
|
-
# 1.0.3 released 2022-09-19
|
|
11
|
-
Bug fixes
|
|
12
|
-
|
|
13
|
-
# 1.0.2 released 2022-08-17
|
|
14
|
-
Bug fixes
|
|
15
|
-
|
|
16
|
-
# 1.0.1 released 2022-08-16
|
|
17
|
-
Bug fixes
|
|
18
|
-
|
|
19
|
-
# 1.0.0 released 2022-08-16
|
|
20
|
-
|
|
21
|
-
## Added
|
|
22
|
-
|
|
23
|
-
* Added support for recurring events using [python-recurring-ical-events ](https://github.com/niccokunzmann/python-recurring-ical-events).
|
|
24
|
-
* It is also possible to create and edit recurring events The recurrence rule has to be given as a string using the `--rrule` option to the `add` and `edit` commands (for example `--rrule FREQ=DAILY;COUNT=5`). Editing/deleting modifies/deletes all occurrences of the event. To delete a single occurrence, use `--exdate`.
|
|
25
|
-
* Added support for creating/editing events using raw ICS text. This is mainly for properties (like `attendee` and `comment`) that are not available as command line options.
|
|
26
|
-
* If duplicate UIDs are found, they are de-duplicated and the calendar is opened in read only mode. Editing a file with multiple UIDs is dangerous.
|
|
27
|
-
|
|
28
|
-
## Changed
|
|
29
|
-
|
|
30
|
-
* Searching for events without any start or end parameters now returns event occurrences only within the last 5 years and next 5 years (this number can be changed with the option `--default_past_years` and `--default_future_years`). More events can be obtained using explicit start and end parameters. For example, `s home '-10 y' 15y` will go back 10 years and forward 15 years. This change was necessitated because a single recurring event (without any `COUNT` or `UNTIL` restrictions) can generate an infinite number of occurrences.
|
|
31
|
-
* The `-u` or `--uid` option has been removed and replaced by a more general `-p` or `--property` option that allows any property of the event to be searched. For example, `s -p rrule DAILY` will display all events with a recurrence rule that has a daily frequency.
|
|
32
|
-
* Many Bug fixes.
|
|
33
|
-
* Several method names were changed (the leading underscore was dropped from all method names), but these were intended to be used only internally.
|
|
34
|
-
|
|
35
|
-
# 0.9.9 released 2022-07-12
|
|
36
|
-
|
|
37
|
-
## Added
|
|
38
|
-
|
|
39
|
-
* Check for errors while parsing `iCalendar` events
|
|
40
|
-
* Added option to provide stack trace for errors. By default, most errors are caught using try blocks and only a cryptic summary is provided. With this option, the stack trace is printed, but the program continues to run.
|
|
41
|
-
|
|
42
|
-
## Changed
|
|
43
|
-
|
|
44
|
-
* A few bug fixes
|
|
45
|
-
|
icalcli-1.0.5/exclude
DELETED
icalcli-1.0.5/update-github.md
DELETED
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
# After updating local file in /0/bp/
|
|
2
|
-
|
|
3
|
-
* Update local github folder with remote by Pull from origin/master
|
|
4
|
-
* `rsync -avc --dry-run --exclude-from ~/0/bp/icalcli/exclude \
|
|
5
|
-
/home/jrvarma/0/bp/icalcli/ /home/jrvarma/0/src/github/icalcli/`
|
|
6
|
-
* `rsync -avc --exclude-from ~/0/bp/icalcli/exclude \
|
|
7
|
-
/home/jrvarma/0/bp/icalcli/ /home/jrvarma/0/src/github/icalcli/`
|
|
8
|
-
* Commit changes in local github folder
|
|
9
|
-
* Push to remote origin/master
|
|
10
|
-
|
|
11
|
-
# After updating repo
|
|
12
|
-
|
|
13
|
-
* `rsync -avc --dry-run --exclude '.git/' --exclude screenshots\
|
|
14
|
-
~/0/src/github/icalcli/ ~/0/bp/icalcli/ `
|
|
15
|
-
* `rsync -avc --exclude '.git/' \
|
|
16
|
-
~/0/src/github/icalcli/ ~/0/bp/icalcli/ `
|
|
17
|
-
|
|
18
|
-
## Update PyPi
|
|
19
|
-
|
|
20
|
-
* Bump version number (Edit `setup.py`)
|
|
21
|
-
|
|
22
|
-
```
|
|
23
|
-
cd ~/0/bp/icalcli
|
|
24
|
-
rm /0/bp/icalcli/dist/*
|
|
25
|
-
python3 setup.py sdist bdist_wheel
|
|
26
|
-
python3 -m twine upload dist/*
|
|
27
|
-
```
|
|
28
|
-
|
|
29
|
-
## Install new version from PyPi
|
|
30
|
-
|
|
31
|
-
```
|
|
32
|
-
pip install --upgrade icalcli
|
|
33
|
-
```
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|