icalcli 1.0.6__tar.gz → 1.1.2__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.
Files changed (23) hide show
  1. {icalcli-1.0.6 → icalcli-1.1.2}/PKG-INFO +44 -3
  2. {icalcli-1.0.6 → icalcli-1.1.2}/README.md +43 -2
  3. {icalcli-1.0.6 → icalcli-1.1.2}/icalcli/__init__.py +3 -1
  4. icalcli-1.1.2/icalcli/etesync_backend/etebase_crud.py +185 -0
  5. icalcli-1.1.2/icalcli/etesync_backend/etebase_interface.py +98 -0
  6. {icalcli-1.0.6 → icalcli-1.1.2}/icalcli/etesync_backend/etesync_crud.py +3 -0
  7. {icalcli-1.0.6 → icalcli-1.1.2}/icalcli/etesync_backend/etesync_interface.py +5 -0
  8. {icalcli-1.0.6 → icalcli-1.1.2}/icalcli/file_backend/ics_interface.py +4 -4
  9. {icalcli-1.0.6 → icalcli-1.1.2}/icalcli/icalcli.py +12 -2
  10. {icalcli-1.0.6 → icalcli-1.1.2}/icalcli.egg-info/PKG-INFO +44 -3
  11. {icalcli-1.0.6 → icalcli-1.1.2}/icalcli.egg-info/SOURCES.txt +2 -0
  12. {icalcli-1.0.6 → icalcli-1.1.2}/setup.py +1 -1
  13. {icalcli-1.0.6 → icalcli-1.1.2}/LICENSE +0 -0
  14. {icalcli-1.0.6 → icalcli-1.1.2}/icalcli/argparsers.py +0 -0
  15. {icalcli-1.0.6 → icalcli-1.1.2}/icalcli/etesync_backend/__init__.py +0 -0
  16. {icalcli-1.0.6 → icalcli-1.1.2}/icalcli/file_backend/__init__.py +0 -0
  17. {icalcli-1.0.6 → icalcli-1.1.2}/icalcli/printer.py +0 -0
  18. {icalcli-1.0.6 → icalcli-1.1.2}/icalcli/utils.py +0 -0
  19. {icalcli-1.0.6 → icalcli-1.1.2}/icalcli.egg-info/dependency_links.txt +0 -0
  20. {icalcli-1.0.6 → icalcli-1.1.2}/icalcli.egg-info/entry_points.txt +0 -0
  21. {icalcli-1.0.6 → icalcli-1.1.2}/icalcli.egg-info/requires.txt +0 -0
  22. {icalcli-1.0.6 → icalcli-1.1.2}/icalcli.egg-info/top_level.txt +0 -0
  23. {icalcli-1.0.6 → icalcli-1.1.2}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: icalcli
3
- Version: 1.0.6
3
+ Version: 1.1.2
4
4
  Summary: Icalendar Calendar Command Line Interface
5
5
  Home-page: https://github.com/jrvarma/icalcli
6
6
  Maintainer: Jayanth R. Varma
@@ -28,7 +28,7 @@ Requires-Dist: parsedatetime; extra == "parsedatetime"
28
28
 
29
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:
30
30
 
31
- * 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.
32
32
 
33
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.
34
34
 
@@ -55,6 +55,12 @@ The two included backends would also be useful to those who wish to write their
55
55
  pip install icalcli
56
56
  ```
57
57
 
58
+ or via `pipx`:
59
+
60
+ ```sh
61
+ pipx install icalcli
62
+ ```
63
+
58
64
  ### Install from source
59
65
 
60
66
  ```sh
@@ -62,6 +68,13 @@ git clone https://github.com/jrvarma/icalcli.git
62
68
  cd icalcli
63
69
  python setup.py install
64
70
  ```
71
+
72
+ or via `pipx`:
73
+
74
+ ```sh
75
+ pipx install . --editable
76
+ ```
77
+
65
78
  ## Usage
66
79
 
67
80
  ### Command line arguments
@@ -102,7 +115,7 @@ backend_interface = ICSInterface(["/path/to/ics-file-1", "/path/to/ics-file-2"])
102
115
 
103
116
  ```
104
117
 
105
- #### Example configuration for etesync_backend
118
+ #### Example configuration for etesync_backend (etesync 1.0)
106
119
 
107
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.
108
121
 
@@ -121,6 +134,34 @@ backend_interface = EtesyncInterface(
121
134
  ```
122
135
  See the [Example code](https://github.com/jrvarma/icalcli/issues/1#issuecomment-979851222) for getting the `uid` and `authToken` for the `etesync` calendar.
123
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
+
124
165
  ## Recurring events and default search period
125
166
 
126
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.
@@ -6,7 +6,7 @@
6
6
 
7
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:
8
8
 
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.
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.
10
10
 
11
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.
12
12
 
@@ -33,6 +33,12 @@ The two included backends would also be useful to those who wish to write their
33
33
  pip install icalcli
34
34
  ```
35
35
 
36
+ or via `pipx`:
37
+
38
+ ```sh
39
+ pipx install icalcli
40
+ ```
41
+
36
42
  ### Install from source
37
43
 
38
44
  ```sh
@@ -40,6 +46,13 @@ git clone https://github.com/jrvarma/icalcli.git
40
46
  cd icalcli
41
47
  python setup.py install
42
48
  ```
49
+
50
+ or via `pipx`:
51
+
52
+ ```sh
53
+ pipx install . --editable
54
+ ```
55
+
43
56
  ## Usage
44
57
 
45
58
  ### Command line arguments
@@ -80,7 +93,7 @@ backend_interface = ICSInterface(["/path/to/ics-file-1", "/path/to/ics-file-2"])
80
93
 
81
94
  ```
82
95
 
83
- #### Example configuration for etesync_backend
96
+ #### Example configuration for etesync_backend (etesync 1.0)
84
97
 
85
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.
86
99
 
@@ -99,6 +112,34 @@ backend_interface = EtesyncInterface(
99
112
  ```
100
113
  See the [Example code](https://github.com/jrvarma/icalcli/issues/1#issuecomment-979851222) for getting the `uid` and `authToken` for the `etesync` calendar.
101
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
+
102
143
  ## Recurring events and default search period
103
144
 
104
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 F401
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,185 @@
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, Base64Url
7
+ from time import sleep, time
8
+ import json
9
+
10
+ # The EtesyncCRUD class exposes methods for each of the CRUD operations
11
+ # (Create, Retrieve, Update and Delete) and for sync with the server.
12
+ # It handles only one calendar
13
+
14
+ # The class is initialized with user details, authToken and
15
+ # EITHER the encryption password OR the cipher key.
16
+
17
+ # Intended usage is that a calling program (a CLI) obtains user credentials
18
+ # from terminal input or some secure storage (like a key ring)
19
+ # and then creates an instance of EtesyncCRUD as follows:
20
+
21
+ # crud = EtebaseCRUD(user, server_url, password, calendar_uid)
22
+
23
+ # The CLI program can then perform CRUD operations by calling
24
+ # crud.create_event, crud.retrieve_event,
25
+ # crud.update_event and crud.delete_event
26
+
27
+ # The CLI must explicitly call crud.sync when needed. For example:
28
+ # (a) if the server has been updated from another device
29
+ # (b) after any CRUD operation other than Retrieve
30
+
31
+ # No exception handling is done. That is left to the CLI.
32
+
33
+
34
+ class EtebaseCRUD:
35
+
36
+ def __init__(self, user, server_url, password, calendar_uid,
37
+ cache_file=None, silent=True):
38
+ """Initialize
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
+ cache_file: path to the cache file or None
47
+ """
48
+ client = Client(user, server_url)
49
+ etebase = Account.login(client, user, password)
50
+ col_mgr = etebase.get_collection_manager()
51
+ collection = col_mgr.fetch(calendar_uid)
52
+ self.item_mgr = col_mgr.get_item_manager(collection)
53
+ self.items = {}
54
+ self.stoken = None
55
+ self.raw_events = []
56
+ self.cache_file = cache_file
57
+ self.load_cache()
58
+ self.sync(silent)
59
+ print("Reading all events")
60
+ self.get_all_events()
61
+
62
+ def create_event(self, event, event_uid):
63
+ """Create event
64
+
65
+ Parameters
66
+ ----------
67
+ event : iCalendar file as bytes
68
+ (calendar containing one event to be added)
69
+ event_uid : uid of event to be updated
70
+ """
71
+ item = self.item_mgr.create(
72
+ {
73
+ "name": event_uid,
74
+ "mtime": int(round(time() * 1000))
75
+ },
76
+ event
77
+ )
78
+ self.item_mgr.batch([item])
79
+
80
+ def update_event(self, event, event_uid):
81
+ """Edit event
82
+
83
+ Parameters
84
+ ----------
85
+ event : iCalendar file as bytes
86
+ (calendar containing one event to be updated)
87
+ event_uid : uid of event to be updated
88
+ """
89
+ item = self.item_mgr.fetch(self.event_uid_to_item_uid[event_uid])
90
+ assert item.meta['name'] == event_uid
91
+ item.content = event
92
+ # item.meta["mtime"] = int(round(time() * 1000))
93
+ self.item_mgr.batch([item])
94
+
95
+ def retrieve_event(self, event_uid):
96
+ r"""Retrieve event by uid
97
+
98
+ Parameters
99
+ ----------
100
+ event_uid : uid of event to be retrieved
101
+
102
+ Returns
103
+ -------
104
+ iCalendar file (as a string)
105
+ """
106
+ item = self.item_mgr.fetch(self.event_uid_to_item_uid[event_uid])
107
+ assert item.meta['name'] == event_uid
108
+ return item.content.decode()
109
+
110
+ def delete_event(self, event_uid):
111
+ """Delete event and sync calendar
112
+
113
+ Parameters
114
+ ----------
115
+ uid : uid of event to be deleted
116
+ """
117
+ item = self.item_mgr.fetch(self.event_uid_to_item_uid[event_uid])
118
+ assert item.meta['name'] == event_uid
119
+ item.meta["mtime"] = int(round(time() * 1000))
120
+ item.delete()
121
+ self.item_mgr.batch([item])
122
+
123
+ def get_all_events(self):
124
+ """Retrieve all events in calendar
125
+
126
+ Returns
127
+ -------
128
+ List of iCalendar files (as strings)
129
+ """
130
+ self.event_uid_to_item_uid = {
131
+ e.meta['name']: e.uid for e in self.items.values()}
132
+ self.raw_events = [e.content.decode()
133
+ for e in self.items.values() if not e.deleted]
134
+
135
+ def sync(self, silent=False):
136
+ """Initialize
137
+
138
+ Parameters
139
+ ----------
140
+ silent : boolean whether to report sync attempt and success
141
+ """
142
+ silent or print("Syncing with server. Please wait")
143
+ msg = "etebase fetch attempt {:} failed. Will retry after {:} seconds"
144
+ delay = 5
145
+ done = False
146
+ chunk = 100
147
+ for i in range(5):
148
+ try:
149
+ while not done:
150
+ items = self.item_mgr.list(
151
+ FetchOptions().stoken(self.stoken).limit(chunk))
152
+ self.items.update(
153
+ {item.uid: item for item in items.data})
154
+ self.stoken = items.stoken
155
+ done = items.done
156
+ # silent or print(".", end='')
157
+ break
158
+ except Exception:
159
+ silent or print(msg.format(i+1, delay))
160
+ sleep(delay)
161
+ if done:
162
+ silent or print("Syncing completed.")
163
+ self.save_cache()
164
+ else:
165
+ print("Syncing with server failed after 5 attempts")
166
+ return
167
+
168
+ def load_cache(self):
169
+ if self.cache_file:
170
+ d = json.load(open(self.cache_file))
171
+ if 'stoken' in d:
172
+ self.stoken = d['stoken']
173
+ if 'blobs' in d:
174
+ for cache_blob in d['blobs']:
175
+ item = self.item_mgr.cache_load(
176
+ Base64Url.from_base64(cache_blob))
177
+ self.items[item.uid] = item
178
+
179
+ def save_cache(self):
180
+ if self.cache_file:
181
+ cache = dict(
182
+ stoken=self.stoken,
183
+ blobs=[Base64Url.to_base64(self.item_mgr.cache_save(item))
184
+ for item in self.items.values()])
185
+ json.dump(cache, open(self.cache_file, 'w'))
@@ -0,0 +1,98 @@
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 sync() when the server
30
+ # has been updated from another device
31
+ # sync() is not needed after any CRUD operation
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,
38
+ cache_file=None, silent=True):
39
+ r"""Initialize EtebaseInterface
40
+
41
+ Parameters
42
+ ----------
43
+ user : etebase username
44
+ password : etebase password
45
+ server_url : url of etebase server
46
+ calendar_uid : uid of calendar
47
+ """
48
+ super(EtebaseInterface, self).__init__(
49
+ user=user, server_url=server_url, password=password,
50
+ calendar_uid=calendar_uid, cache_file=cache_file, silent=silent)
51
+ print("Parsing all events")
52
+ self.all_events()
53
+
54
+ def all_events(self):
55
+ self.events = [Calendar.from_ical(ev).walk('VEVENT')[0]
56
+ for ev in self.raw_events]
57
+
58
+ def create_event(self, event, vtimezone=None):
59
+ r"""Create event
60
+
61
+ Parameters
62
+ ----------
63
+ event : event to be added (iCalendar object)
64
+ """
65
+ ics = self.event_to_ics(event, vtimezone)
66
+ uid = event.decoded('uid').decode()
67
+ EtebaseCRUD.create_event(self, ics, uid)
68
+
69
+ def update_event(self, event, vtimezone=None):
70
+ r"""Update event
71
+
72
+ Parameters
73
+ ----------
74
+ event : event to be added (iCalendar object)
75
+ """
76
+ uid = event.decoded('uid').decode()
77
+ ics = self.event_to_ics(event, vtimezone)
78
+ EtebaseCRUD.update_event(self, ics, uid)
79
+
80
+ def event_to_ics(self, event, vtimezone=None):
81
+ r"""Make calendar byte array (ics) from event
82
+
83
+ Parameters
84
+ ----------
85
+ event : event to be added (iCalendar object)
86
+ """
87
+ cal = Calendar()
88
+ cal.add_component(event)
89
+ if vtimezone:
90
+ cal.add_component(vtimezone)
91
+ ics = cal.to_ical()
92
+ return ics
93
+
94
+ def sync(self, vtimezone=None):
95
+ r"""Sync with server and rebuild vevent list
96
+ """
97
+ EtebaseCRUD.sync(self)
98
+ self.all_events()
@@ -1,5 +1,8 @@
1
1
  #!/usr/bin/env python
2
2
 
3
+ # this is part of the etesync 1.0 backend
4
+ # for etesync 2.0 backend see etebase_crud.py
5
+
3
6
  import etesync as api
4
7
  from time import sleep
5
8
 
@@ -1,3 +1,8 @@
1
+ #!/usr/bin/env python
2
+
3
+ # this is part of the etesync 1.0 backend
4
+ # for etesync 2.0 backend see etebase_interface.py
5
+
1
6
  from icalcli.etesync_backend.etesync_crud import EtesyncCRUD
2
7
  from icalendar import Calendar
3
8
 
@@ -61,9 +61,9 @@ class ICSInterface:
61
61
  return
62
62
  if self.backup:
63
63
  with NamedTemporaryFile(mode='w',
64
- suffix=self.filepath.suffix,
65
- prefix=self.filepath.name,
66
- dir=self.filepath.parent,
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.filepath, 'w') as fp:
75
+ with open(self.filepaths[0], 'w') as fp:
76
76
  fp.write(self.ics)
77
77
  self.all_events()
@@ -1317,7 +1317,8 @@ class IcalendarInterface:
1317
1317
  continue
1318
1318
  self.backend_interface.delete_event(self.uid(event))
1319
1319
  self.printer.msg("Event deleted\n")
1320
- self.backend_cache_dirty = True
1320
+ if self.backend_interface.sync_after_edit:
1321
+ self.backend_cache_dirty = True
1321
1322
  deleted += 1
1322
1323
  self.printer.msg(f'{deleted} events deleted\n')
1323
1324
 
@@ -1606,7 +1607,8 @@ class IcalendarInterface:
1606
1607
  self.printer.msg("Event %s\n" % ("updated" if old
1607
1608
  else "added"))
1608
1609
  self.iterate_events(None, [event], print_count=False)
1609
- self.backend_cache_dirty = True
1610
+ if self.backend_interface.sync_after_edit:
1611
+ self.backend_cache_dirty = True
1610
1612
  return True
1611
1613
 
1612
1614
  def raw_ics(self, original=None):
@@ -1721,6 +1723,14 @@ def repl(ecal=None):
1721
1723
  add_parser=get_add_parser(),
1722
1724
  backend_interface=config.backend_interface,
1723
1725
  printer=printer, **vars(FLAGS))
1726
+ if not hasattr(ecal.backend_interface, "sync_after_edit"):
1727
+ ecal.backend_interface.sync_after_edit = True
1728
+ # The file and etesync 1.0 backends operate on the cache and
1729
+ # they sync to server only when sync() is called
1730
+ # Some backends may work directly on server
1731
+ # and may require sync() only if server updated from another device
1732
+ # Such backends can set the attribute sync_after_edit = False
1733
+ # If attribute is missing, we assume it is True
1724
1734
  if hasattr(config, 'timezones') and 'tz' in config.timezones:
1725
1735
  IcalendarInterface.calendar_tz = config.timezones['tz']
1726
1736
  else:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: icalcli
3
- Version: 1.0.6
3
+ Version: 1.1.2
4
4
  Summary: Icalendar Calendar Command Line Interface
5
5
  Home-page: https://github.com/jrvarma/icalcli
6
6
  Maintainer: Jayanth R. Varma
@@ -28,7 +28,7 @@ Requires-Dist: parsedatetime; extra == "parsedatetime"
28
28
 
29
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:
30
30
 
31
- * 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.
32
32
 
33
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.
34
34
 
@@ -55,6 +55,12 @@ The two included backends would also be useful to those who wish to write their
55
55
  pip install icalcli
56
56
  ```
57
57
 
58
+ or via `pipx`:
59
+
60
+ ```sh
61
+ pipx install icalcli
62
+ ```
63
+
58
64
  ### Install from source
59
65
 
60
66
  ```sh
@@ -62,6 +68,13 @@ git clone https://github.com/jrvarma/icalcli.git
62
68
  cd icalcli
63
69
  python setup.py install
64
70
  ```
71
+
72
+ or via `pipx`:
73
+
74
+ ```sh
75
+ pipx install . --editable
76
+ ```
77
+
65
78
  ## Usage
66
79
 
67
80
  ### Command line arguments
@@ -102,7 +115,7 @@ backend_interface = ICSInterface(["/path/to/ics-file-1", "/path/to/ics-file-2"])
102
115
 
103
116
  ```
104
117
 
105
- #### Example configuration for etesync_backend
118
+ #### Example configuration for etesync_backend (etesync 1.0)
106
119
 
107
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.
108
121
 
@@ -121,6 +134,34 @@ backend_interface = EtesyncInterface(
121
134
  ```
122
135
  See the [Example code](https://github.com/jrvarma/icalcli/issues/1#issuecomment-979851222) for getting the `uid` and `authToken` for the `etesync` calendar.
123
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
+
124
165
  ## Recurring events and default search period
125
166
 
126
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.
@@ -13,6 +13,8 @@ icalcli.egg-info/entry_points.txt
13
13
  icalcli.egg-info/requires.txt
14
14
  icalcli.egg-info/top_level.txt
15
15
  icalcli/etesync_backend/__init__.py
16
+ icalcli/etesync_backend/etebase_crud.py
17
+ icalcli/etesync_backend/etebase_interface.py
16
18
  icalcli/etesync_backend/etesync_crud.py
17
19
  icalcli/etesync_backend/etesync_interface.py
18
20
  icalcli/file_backend/__init__.py
@@ -5,7 +5,7 @@ with open("README.md", "r") as fh:
5
5
  long_description = fh.read()
6
6
 
7
7
  setup(name='icalcli',
8
- version='1.0.6',
8
+ version='1.1.2',
9
9
  maintainer='Jayanth R. Varma',
10
10
  maintainer_email='jrvarma@gmail.com',
11
11
  description='Icalendar Calendar Command Line Interface',
File without changes
File without changes
File without changes
File without changes
File without changes