icalcli 1.0.4__tar.gz → 1.0.6__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: icalcli
3
- Version: 1.0.4
3
+ Version: 1.0.6
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
 
@@ -66,6 +71,7 @@ python setup.py install
66
71
  edit (e) edit calendar events
67
72
  agenda (g) get an agenda for a time period
68
73
  calw (w) get a week-based agenda in calendar format
74
+ cal5w (5w) get this week / 2 past / 2 future weeks agenda in calendar format
69
75
  calm (m) get a month agenda in calendar format
70
76
  add (a) add a detailed event to the calendar
71
77
  search (s) (regex) search for events
@@ -87,6 +93,15 @@ backend_interface = ICSInterface("/path/to/ics-file")
87
93
 
88
94
  ```
89
95
 
96
+ #### Example configuration for multiple readonly file_backend
97
+
98
+ ```
99
+ from icalcli import ICSInterface
100
+
101
+ backend_interface = ICSInterface(["/path/to/ics-file-1", "/path/to/ics-file-2"])
102
+
103
+ ```
104
+
90
105
  #### Example configuration for etesync_backend
91
106
 
92
107
  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.
@@ -106,6 +121,16 @@ backend_interface = EtesyncInterface(
106
121
  ```
107
122
  See the [Example code](https://github.com/jrvarma/icalcli/issues/1#issuecomment-979851222) for getting the `uid` and `authToken` for the `etesync` calendar.
108
123
 
124
+ ## Recurring events and default search period
125
+
126
+ `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.
127
+
128
+ In most views, the instances of the recurring event are displayed. Since a recurring event can have an unlimited number of instances, searches with no start or end date can produce an unending series of events. By default therefore searches with no start or end date are limited to the previous five years and following five years. These defaults can be changed using the options ` --default_past_years` and `--default_future_years`
129
+
130
+ ## Raw ICS
131
+
132
+ The `icalendar` specification is quite large and complex, and `icalcli` implements only the most common parts of this specification. It is possible to use the `--raw_ics` option to create/edit event using raw ICS text.
133
+
109
134
  ## Screenshots
110
135
 
111
136
  Some screenshots are available at Github:
@@ -1,20 +1,3 @@
1
- Metadata-Version: 2.1
2
- Name: icalcli
3
- Version: 1.0.4
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
@@ -66,6 +49,7 @@ python setup.py install
66
49
  edit (e) edit calendar events
67
50
  agenda (g) get an agenda for a time period
68
51
  calw (w) get a week-based agenda in calendar format
52
+ cal5w (5w) get this week / 2 past / 2 future weeks agenda in calendar format
69
53
  calm (m) get a month agenda in calendar format
70
54
  add (a) add a detailed event to the calendar
71
55
  search (s) (regex) search for events
@@ -87,6 +71,15 @@ backend_interface = ICSInterface("/path/to/ics-file")
87
71
 
88
72
  ```
89
73
 
74
+ #### Example configuration for multiple readonly file_backend
75
+
76
+ ```
77
+ from icalcli import ICSInterface
78
+
79
+ backend_interface = ICSInterface(["/path/to/ics-file-1", "/path/to/ics-file-2"])
80
+
81
+ ```
82
+
90
83
  #### Example configuration for etesync_backend
91
84
 
92
85
  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.
@@ -106,6 +99,16 @@ backend_interface = EtesyncInterface(
106
99
  ```
107
100
  See the [Example code](https://github.com/jrvarma/icalcli/issues/1#issuecomment-979851222) for getting the `uid` and `authToken` for the `etesync` calendar.
108
101
 
102
+ ## Recurring events and default search period
103
+
104
+ `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.
105
+
106
+ In most views, the instances of the recurring event are displayed. Since a recurring event can have an unlimited number of instances, searches with no start or end date can produce an unending series of events. By default therefore searches with no start or end date are limited to the previous five years and following five years. These defaults can be changed using the options ` --default_past_years` and `--default_future_years`
107
+
108
+ ## Raw ICS
109
+
110
+ The `icalendar` specification is quite large and complex, and `icalcli` implements only the most common parts of this specification. It is possible to use the `--raw_ics` option to create/edit event using raw ICS text.
111
+
109
112
  ## Screenshots
110
113
 
111
114
  Some screenshots are available at Github:
@@ -263,6 +263,11 @@ def get_argument_parser(initial=True):
263
263
  parents=[outputs_parser, output_parser, cal_query_parser])
264
264
  calw.add_argument('-n', "--weeks", type=int, default=2, nargs="?")
265
265
 
266
+ cal5w = sub.add_parser(
267
+ "cal5w", aliases=['5w'],
268
+ parents=[outputs_parser, output_parser, cal_query_parser])
269
+ cal5w.add_argument('-n', "--weeks", type=int, default=2, nargs="?")
270
+
266
271
  sub.add_parser(
267
272
  "calm", aliases=['m'],
268
273
  parents=[outputs_parser, output_parser, cal_query_parser])
@@ -12,10 +12,14 @@ class ICSInterface:
12
12
  filename : path to ics file
13
13
  backup: boolean whether to back up old file before overwriting
14
14
  """
15
- self.filepath = Path(filename).resolve()
15
+ if isinstance(filename, list):
16
+ self.filepaths = [Path(f).resolve() for f in filename]
17
+ self.readonly = True
18
+ else:
19
+ self.filepaths = [Path(filename).resolve()]
20
+ self.readonly = False
16
21
  self.backup = backup
17
- with open(self.filepath, 'r') as fp:
18
- self.ics = fp.read()
22
+ self.ics = []
19
23
  self.all_events()
20
24
 
21
25
  def all_events(self):
@@ -26,13 +30,20 @@ class ICSInterface:
26
30
  return False
27
31
  else:
28
32
  return True
29
- self.calendar = Calendar.from_ical(self.ics)
30
- self.events = [ev for ev in self.calendar.walk('VEVENT')
31
- if check_event(ev)]
33
+ self.events = []
32
34
  self.cache_events = {}
33
- for ev in self.events:
34
- uid = ev.decoded('uid').decode()
35
- self.cache_events[uid] = ev
35
+ filecount = 0
36
+ for path in self.filepaths:
37
+ with open(path, 'r') as fp:
38
+ cal = Calendar.from_ical(fp.read())
39
+ events = [ev for ev in cal.walk('VEVENT') if check_event(ev)]
40
+ self.events += events
41
+ for ev in events:
42
+ uid = ev.decoded('uid').decode()
43
+ if len(self.filepaths) > 1:
44
+ uid = f"File{filecount}:{uid}"
45
+ self.cache_events[uid] = ev
46
+ filecount += 1
36
47
 
37
48
  def create_event(self, event, vtimezone=None):
38
49
  uid = event.decoded('uid').decode()
@@ -46,6 +57,8 @@ class ICSInterface:
46
57
  del self.cache_events[uid]
47
58
 
48
59
  def sync(self, vtimezone=None):
60
+ if self.readonly:
61
+ return
49
62
  if self.backup:
50
63
  with NamedTemporaryFile(mode='w',
51
64
  suffix=self.filepath.suffix,
@@ -95,6 +95,7 @@ class IcalendarInterface:
95
95
  UNIWIDTH = {'W': 2, 'F': 2, 'N': 1, 'Na': 1, 'H': 1, 'A': 1}
96
96
  backend_cache_dirty = False
97
97
  default_outputs = ['end', 'alarms', 'freebusy', 'location']
98
+ default_graphical_outputs = ['end', 'alarms']
98
99
  no_past_events = 5
99
100
  no_future_events = 10
100
101
 
@@ -104,6 +105,7 @@ class IcalendarInterface:
104
105
  self.cals = []
105
106
  self.printer = printer
106
107
  self.initial_options = options
108
+ self.initial_options['stack_trace'] = True
107
109
  self.set_options(options)
108
110
  self.add_parser = add_parser
109
111
  self.backend_interface = backend_interface
@@ -128,6 +130,10 @@ class IcalendarInterface:
128
130
  for key in self.default_outputs:
129
131
  if key not in self.outputs:
130
132
  self.outputs[key] = True
133
+ self.graphical_outputs = options.get('outputs', {})
134
+ for key in self.default_graphical_outputs:
135
+ if key not in self.outputs:
136
+ self.graphical_outputs[key] = True
131
137
 
132
138
  def set_now(self):
133
139
  # This command is run at initiaization, but it should also be
@@ -157,6 +163,12 @@ class IcalendarInterface:
157
163
  self.readonly = True
158
164
  self.printer.err_msg('Duplicate UIDs found. '
159
165
  'Calendar deduplicated and set to readonly\n')
166
+ elif (
167
+ hasattr(self.backend_interface, 'readonly')
168
+ and self.backend_interface.readonly
169
+ ):
170
+ self.readonly = True
171
+ self.printer.err_msg('Calendar backend interface is readonly\n')
160
172
  else:
161
173
  self.readonly = False
162
174
 
@@ -323,7 +335,7 @@ class IcalendarInterface:
323
335
  """
324
336
  return e_start < time_point and e_end >= time_point
325
337
 
326
- def format_title(self, event, allday=False):
338
+ def event_summary(self, event, allday=False):
327
339
  r"""Return event title (summary and start time)
328
340
 
329
341
  Parameters
@@ -335,19 +347,52 @@ class IcalendarInterface:
335
347
  -------
336
348
  string
337
349
  """
350
+ def fmt_time(field):
351
+ if self.options['military']:
352
+ return self.decode_dtm(event, field).strftime("%H:%M")
353
+ else:
354
+ return (
355
+ self.decode_dtm(event, field).strftime("%I:%M")
356
+ .lstrip('0') +
357
+ self.decode_dtm(event, field).strftime('%p') .lower())
338
358
  titlestr = self.valid_title(event)
339
359
  if allday:
340
- return titlestr
341
- elif self.options['military']:
342
- return ' '.join(
343
- [self.decode_dtm(event, 'dtstart').strftime("%H:%M"),
344
- titlestr])
360
+ summary = titlestr
345
361
  else:
346
- return ' '.join([
347
- self.decode_dtm(event, 'dtstart').strftime("%I:%M")
348
- .lstrip('0') +
349
- self.decode_dtm(event, 'dtstart').strftime('%p')
350
- .lower(), titlestr])
362
+ tm = fmt_time('dtstart')
363
+ if self.graphical_outputs.get('end'):
364
+ tm += " - " + fmt_time('dtend')
365
+ summary = tm + " " + titlestr
366
+ if self.graphical_outputs.get('alarms'):
367
+ alarms = event.walk('valarm')
368
+ if alarms:
369
+ if isinstance(alarms[0].Decoded('trigger'), timedelta):
370
+ minutes = -(alarms[0].Decoded('trigger')
371
+ .total_seconds()/60)
372
+ summary += ' AL:%.0fm' % minutes
373
+ else:
374
+ summary += ' AL: ??'
375
+ if self.graphical_outputs.get('freebusy'):
376
+ free = ('transp' in event and
377
+ event.Decoded('transp').decode() == 'TRANSPARENT')
378
+ summary += (' free ' if free else ' busy ')
379
+ if (
380
+ self.graphical_outputs.get('location')
381
+ and 'location' in event
382
+ and event.Decoded('location').decode().strip()
383
+ ):
384
+ summary += " [%s]" % (event.Decoded('location').
385
+ decode().strip())
386
+ if self.graphical_outputs.get('uid'):
387
+ summary += " <%s>" % (event.Decoded('uid').decode().strip())
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
+ return summary
351
396
 
352
397
  def get_week_events(self, start_dt, end_dt, event_list):
353
398
  r"""Returns all events during a week (start_dt to end_dt)
@@ -401,9 +446,11 @@ class IcalendarInterface:
401
446
  force_now_marker = False
402
447
 
403
448
  if to_show_now:
404
- if(self.now >= event_start_date
405
- and self.now <= event_end_date
406
- and not event_allday):
449
+ if (
450
+ self.now >= event_start_date
451
+ and self.now <= event_end_date
452
+ and not event_allday
453
+ ):
407
454
  # line marker is during event (recolor event)
408
455
  force_now_marker = True
409
456
  to_show_now = False
@@ -429,7 +476,7 @@ class IcalendarInterface:
429
476
  event_color = 'default'
430
477
  # NOTE(slawqo): for all day events it's necessary to
431
478
  # add event to more than one day in week_events
432
- titlestr = self.format_title(event, allday=event_allday)
479
+ summary = self.event_summary(event, allday=event_allday)
433
480
  if event_allday and event_start_date < event_end_date:
434
481
  if event_end_date >= end_dt:
435
482
  end_daynum = 6
@@ -444,12 +491,12 @@ class IcalendarInterface:
444
491
  # event_daynum = 0
445
492
  for day in range(start_daynum, end_daynum + 1):
446
493
  week_events[day].append(
447
- EventTitle('\n' + titlestr, event_color))
494
+ EventTitle('\n' + summary, event_color))
448
495
  else:
449
496
  # newline and empty string are the keys to turn off
450
497
  # coloring
451
498
  week_events[event_daynum].append(
452
- EventTitle('\n' + titlestr, event_color))
499
+ EventTitle('\n' + summary, event_color))
453
500
  return week_events
454
501
 
455
502
  def printed_len(self, string):
@@ -504,20 +551,23 @@ class IcalendarInterface:
504
551
  -------
505
552
  int: where to cut the word
506
553
  """
507
- print_len = 0
554
+ # print_len = 0
508
555
 
509
556
  words = _u(string).split()
510
557
  for i, word in enumerate(words):
558
+ print_len = self.printed_len(' '.join(words[:i]))
511
559
  word_len = self.printed_len(word)
512
- if ((cur_print_len + word_len + print_len
513
- ) >= self.options['cal_width']):
560
+ if (
561
+ (cur_print_len + word_len + print_len
562
+ ) >= self.options['cal_width']
563
+ ):
514
564
  cut_idx = len(' '.join(words[:i]))
515
565
  # if the word is too long,
516
566
  # we cannot cut between words
517
567
  if cut_idx == 0:
518
568
  return self.word_cut(word)
519
569
  return (print_len, cut_idx)
520
- print_len += word_len + i # +i for the space between words
570
+ # print_len += word_len + i # +i for the space between words
521
571
  return (print_len, len(' '.join(words[:i])))
522
572
 
523
573
  def get_cut_index(self, event_string):
@@ -553,7 +603,7 @@ class IcalendarInterface:
553
603
 
554
604
  Parameters
555
605
  ----------
556
- cmd : Command ('calw' or 'calm' for week and month)
606
+ cmd : Command ('calw', 'cal5w' or 'calm' for week, 5weeks and month)
557
607
  startDateTime : datetime (start date)
558
608
  count : int (number of weeks or months)
559
609
  eventList : list of icalendar events
@@ -637,7 +687,7 @@ class IcalendarInterface:
637
687
  for i in range(count):
638
688
  # create and print the date line for a week
639
689
  for j in range(days):
640
- if cmd == 'calw':
690
+ if cmd in ('calw', 'cal5w'):
641
691
  d = (startWeekDateTime +
642
692
  timedelta(days=j)).strftime("%d %b")
643
693
  else: # (cmd == 'calm'):
@@ -648,9 +698,11 @@ class IcalendarInterface:
648
698
  d = ''
649
699
  tmpDateColor = self.options['color_date']
650
700
 
651
- if (self.now.strftime("%d%b%Y") == (
652
- (startWeekDateTime + timedelta(days=j))
653
- .strftime("%d%b%Y"))):
701
+ if (
702
+ self.now.strftime("%d%b%Y") == (
703
+ (startWeekDateTime + timedelta(days=j))
704
+ .strftime("%d%b%Y"))
705
+ ):
654
706
  tmpDateColor = self.options['color_now_marker']
655
707
  d += " **"
656
708
 
@@ -690,7 +742,6 @@ class IcalendarInterface:
690
742
  curr_event.title)
691
743
  padding = ' ' * (self.options['cal_width']
692
744
  - print_len)
693
-
694
745
  self.printer.msg(
695
746
  curr_event.title[:cut_idx] + padding,
696
747
  curr_event.color)
@@ -834,9 +885,11 @@ class IcalendarInterface:
834
885
  self.printer.msg(' %s' % self.valid_title(event).strip(),
835
886
  eventColor)
836
887
 
837
- if(self.outputs.get('location')
838
- and 'location' in event
839
- and event.Decoded('location').decode().strip()):
888
+ if (
889
+ self.outputs.get('location')
890
+ and 'location' in event
891
+ and event.Decoded('location').decode().strip()
892
+ ):
840
893
  xstr = " [%s]" % (event.Decoded('location').
841
894
  decode().strip())
842
895
  self.printer.msg(xstr, 'default')
@@ -847,8 +900,10 @@ class IcalendarInterface:
847
900
 
848
901
  self.printer.msg('\n')
849
902
 
850
- if(self.outputs.get('description') and 'description' in event
851
- and event.Decoded('description').decode().strip()):
903
+ if (
904
+ self.outputs.get('description') and 'description' in event
905
+ and event.Decoded('description').decode().strip()
906
+ ):
852
907
  descrIndent = outputsIndent + ' '
853
908
  box = True # leave old non-box code for option later
854
909
  if box:
@@ -915,8 +970,10 @@ class IcalendarInterface:
915
970
  day = ''
916
971
 
917
972
  for event in eventList:
918
- if(self.options['ignore_started'] and
919
- (self.decode_dtm(event, 'dtstart') < self.now)):
973
+ if (
974
+ self.options['ignore_started'] and
975
+ (self.decode_dtm(event, 'dtstart') < self.now)
976
+ ):
920
977
  continue
921
978
  if self.options['ignore_declined'] and self._DeclinedEvent(
922
979
  event):
@@ -1158,8 +1215,14 @@ class IcalendarInterface:
1158
1215
  # convert start date to the beginning of the week or month
1159
1216
  if cmd == 'calw':
1160
1217
  dayNum = self.cal_monday(int(start.strftime("%w")))
1161
- start = (start - timedelta(days=dayNum))
1162
- end = (start + timedelta(days=(count * 7)))
1218
+ start -= timedelta(days=dayNum)
1219
+ weeks = count
1220
+ end = (start + timedelta(days=(weeks * 7)))
1221
+ elif cmd == 'cal5w':
1222
+ dayNum = self.cal_monday(int(start.strftime("%w")))
1223
+ start -= timedelta(days=(dayNum + count * 7))
1224
+ weeks = 2 * count + 1
1225
+ end = (start + timedelta(days=(weeks * 7)))
1163
1226
  else: # cmd == 'calm':
1164
1227
  start = (start - timedelta(days=(start.day - 1)))
1165
1228
  endMonth = (start.month + 1)
@@ -1175,13 +1238,13 @@ class IcalendarInterface:
1175
1238
  if offsetDays < 0:
1176
1239
  offsetDays = 6
1177
1240
  totalDays = (daysInMonth + offsetDays)
1178
- count = int(totalDays / 7)
1241
+ weeks = int(totalDays / 7)
1179
1242
  if totalDays % 7:
1180
- count += 1
1243
+ weeks += 1
1181
1244
 
1182
1245
  eventList = self.search_for_events(start, end, None)
1183
1246
 
1184
- self.GraphEvents(cmd, start, count, eventList)
1247
+ self.GraphEvents(cmd, start, weeks, eventList)
1185
1248
 
1186
1249
  def sync(self):
1187
1250
  r"""Sync calendar
@@ -1401,8 +1464,10 @@ class IcalendarInterface:
1401
1464
  (old.Decoded('dtend') - old.Decoded('dtstart')))
1402
1465
  if not args.summary:
1403
1466
  args.summary = old.Decoded('summary').decode()
1404
- if(not args.time and not (args.start and 'T' in args.start)
1405
- and self.isallday(old)):
1467
+ if (
1468
+ not args.time and not (args.start and 'T' in args.start)
1469
+ and self.isallday(old)
1470
+ ):
1406
1471
  args.allday = True
1407
1472
  day = args.day or old_start.day
1408
1473
  month = args.month or old_start.month
@@ -1690,6 +1755,10 @@ def repl(ecal=None):
1690
1755
  ecal.CalQuery(
1691
1756
  'calw', count=FLAGS.weeks, startText=FLAGS.start)
1692
1757
 
1758
+ elif FLAGS.command in ['5w', 'cal5w']:
1759
+ ecal.CalQuery(
1760
+ 'cal5w', count=FLAGS.weeks, startText=FLAGS.start)
1761
+
1693
1762
  elif FLAGS.command in ['m', 'calm']:
1694
1763
  ecal.CalQuery('calm', startText=FLAGS.start)
1695
1764
 
@@ -1716,9 +1785,11 @@ def repl(ecal=None):
1716
1785
  field=FLAGS.property)
1717
1786
 
1718
1787
  elif FLAGS.command in ['q', 'quit']:
1719
- if(ecal.backend_cache_dirty and ecal.no_auto_sync
1720
- and not IcalendarInterface.confirm(
1721
- "Changes made in calendar not yet synced. Quit y/n? ")):
1788
+ if (
1789
+ ecal.backend_cache_dirty and ecal.no_auto_sync
1790
+ and not IcalendarInterface.confirm(
1791
+ "Changes made in calendar not yet synced. Quit y/n? ")
1792
+ ):
1722
1793
  pass
1723
1794
  else:
1724
1795
  ecal.interactive = False
@@ -1,3 +1,25 @@
1
+ Metadata-Version: 2.1
2
+ Name: icalcli
3
+ Version: 1.0.6
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
@@ -49,6 +71,7 @@ python setup.py install
49
71
  edit (e) edit calendar events
50
72
  agenda (g) get an agenda for a time period
51
73
  calw (w) get a week-based agenda in calendar format
74
+ cal5w (5w) get this week / 2 past / 2 future weeks agenda in calendar format
52
75
  calm (m) get a month agenda in calendar format
53
76
  add (a) add a detailed event to the calendar
54
77
  search (s) (regex) search for events
@@ -70,6 +93,15 @@ backend_interface = ICSInterface("/path/to/ics-file")
70
93
 
71
94
  ```
72
95
 
96
+ #### Example configuration for multiple readonly file_backend
97
+
98
+ ```
99
+ from icalcli import ICSInterface
100
+
101
+ backend_interface = ICSInterface(["/path/to/ics-file-1", "/path/to/ics-file-2"])
102
+
103
+ ```
104
+
73
105
  #### Example configuration for etesync_backend
74
106
 
75
107
  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.
@@ -89,6 +121,16 @@ backend_interface = EtesyncInterface(
89
121
  ```
90
122
  See the [Example code](https://github.com/jrvarma/icalcli/issues/1#issuecomment-979851222) for getting the `uid` and `authToken` for the `etesync` calendar.
91
123
 
124
+ ## Recurring events and default search period
125
+
126
+ `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.
127
+
128
+ In most views, the instances of the recurring event are displayed. Since a recurring event can have an unlimited number of instances, searches with no start or end date can produce an unending series of events. By default therefore searches with no start or end date are limited to the previous five years and following five years. These defaults can be changed using the options ` --default_past_years` and `--default_future_years`
129
+
130
+ ## Raw ICS
131
+
132
+ The `icalendar` specification is quite large and complex, and `icalcli` implements only the most common parts of this specification. It is possible to use the `--raw_ics` option to create/edit event using raw ICS text.
133
+
92
134
  ## Screenshots
93
135
 
94
136
  Some screenshots are available at Github:
@@ -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.4',
8
+ version='1.0.6',
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