Commit 4af10e03 authored by Unit 193's avatar Unit 193

New upstream version 4.1.1

parent f0c73cc2
sudo: false
language: python
python:
- "2.7"
- "3.5"
- "3.6"
install: pip install tox-travis
script: tox
v4.1.1
* Fixed regression on now marking
* Fixed version string management
v4.1.0
* Removed url shortening due to Google deprecation #440
v4.0.4
* Minor bugfixes: conky colors, issues with setup.py
......
......@@ -29,9 +29,6 @@ Requirements
* [vobject](http://vobject.skyhouseconsulting.com) Python module
Used for ics/vcal importing.
* [parsedatetime](http://github.com/bear/parsedatetime) Python module
Used for fuzzy dates/times like "now", "today", "eod tomorrow", etc.
Installation
------------
......@@ -64,10 +61,10 @@ cd gcalcli
python setup.py install
```
### Install optional packages
### Install optional package
```sh
pip install vobject parsedatetime
pip install vobject
```
Features
......@@ -83,7 +80,6 @@ Features
* "delete" event(s) from a calendar(s) (interactively or automatically)
* "edit" event(s) interactively
* import events from ICS/vCal files to a specified calendar
* support for URL shortening via goo.gl
* easy integration with your favorite mail client (attachment handler)
* run as a cron job and execute a command for reminders
* work against specific calendars (by calendar name w/ regex)
......@@ -169,19 +165,22 @@ Note that these environment variables must be lowercase.
#### Flag File
gcalcli is able to read default configuration information from a flag file.
`gcalcli` is able to read default configuration information from a flag file.
This file is located, by default, at '~/.gcalclirc'. The flag file takes one
command line parameter per line.
In the current version, the flag file only supports the global options (options
against the `gcalcli` program itself). The plan, longer term, is to support a
a configuration formation (probably toml or ini), which will allow for
configuration of subcommands (such as `add`, `agenda`, `calw`, etc.)
Example:
```
--military
--duration=55
--details=calendar
--details=location
--details=length
-w 10
--nocache
--nocolor
--default-calendar=CALENDAR_NAME
--client-secret=API_KEY
```
Note that long options require an equal sign if specifying a parameter. With
......@@ -257,6 +256,10 @@ that supports libnotify so it should automagically just work. If you're like
me and use nothing that is common I highly recommend the
[dunst](https://github.com/knopwob/dunst) dmenu'ish notification daemon.
Note that each time you run this you will get a reminder if you're still inside
the event duration. Also note that due to time slip between machines, gcalcli
will give you a ~5 minute margin of error. Plan your cron jobs accordingly.
#### Agenda On Your Root Desktop
Put your agenda on your desktop using
......
__program__ = 'gcalcli'
__version__ = 'v4.0.4'
__version__ = 'v4.1.1'
__author__ = 'Eric Davis, Brian Hartvigsen, Joshua Crowgey'
__API_CLIENT_ID__ = '232867676714.apps.googleusercontent.com'
__API_CLIENT_SECRET__ = '3tZSxItw6_VnZMezQwC8lUqy'
......@@ -7,11 +7,9 @@ from gcalcli.printer import valid_color_name
from oauth2client import tools
import copy as _copy
DETAILS = ['all', 'calendar', 'location', 'length', 'reminders', 'description',
'longurl', 'shorturl', 'url', 'attendees', 'email', 'attachments']
DETAILS = ['calendar', 'location', 'length', 'reminders', 'description',
'url', 'attendees', 'email', 'attachments']
BOOL_DETAILS = ['calendar', 'location', 'length', 'reminders', 'description',
'attendees', 'email', 'attachments']
PROGRAM_OPTIONS = {
'--client-id': {'default': gcalcli.__API_CLIENT_ID__,
......@@ -60,13 +58,9 @@ class DetailsAction(argparse._AppendAction):
details = _copy.copy(getattr(namespace, self.dest, {}))
if value == 'all':
details = {d: True for d in BOOL_DETAILS}
elif value in BOOL_DETAILS:
details.update({d: True for d in DETAILS})
else:
details[value] = True
elif value in ['shorturl', 'url']:
details['url'] = 'short'
elif value == 'longurl':
details['url'] = 'long'
setattr(namespace, self.dest, details)
......@@ -90,7 +84,7 @@ def get_details_parser():
details_parser = argparse.ArgumentParser(add_help=False)
details_parser.add_argument(
'--details', default={}, action=DetailsAction,
choices=DETAILS,
choices=DETAILS + ['all'],
help='Which parts to display, can be: ' + ', '.join(DETAILS))
return details_parser
......
......@@ -47,7 +47,6 @@ class GoogleCalendarInterface:
max_retries = 5
auth_http = None
cal_service = None
url_service = None
ACCESS_OWNER = 'owner'
ACCESS_WRITER = 'writer'
......@@ -140,8 +139,7 @@ class GoogleCalendarInterface:
OAuth2WebServerFlow(
client_id=self.options['client_id'],
client_secret=self.options['client_secret'],
scope=['https://www.googleapis.com/auth/calendar',
'https://www.googleapis.com/auth/urlshortener'],
scope=['https://www.googleapis.com/auth/calendar'],
user_agent=__program__ + '/' + __version__
),
storage,
......@@ -160,15 +158,6 @@ class GoogleCalendarInterface:
return self.cal_service
def get_url_service(self):
if not self.url_service:
self._google_auth()
self.url_service = build(serviceName='urlshortener',
version='v1',
http=self._google_auth())
return self.url_service
def _get_cached(self):
if self.options['config_folder']:
cache_file = os.path.expanduser(
......@@ -224,16 +213,6 @@ class GoogleCalendarInterface:
with open(cache_file, 'wb') as _cache_:
pickle.dump(self.cache, _cache_)
def _shorten_url(self, url):
if self.details.get('url', False) != 'short':
return url
# Note that when authenticated to a google account different shortUrls
# can be returned for the same longUrl. See: http://goo.gl/Ya0A9
shortUrl = self._retry_with_backoff(
self.get_url_service().url().insert(body={'longUrl': url})
)
return shortUrl['id']
def _calendar_color(self, event, override_color=False):
ansi_codes = {
'1': 'brightblue',
......@@ -317,6 +296,7 @@ class GoogleCalendarInterface:
week_events = [[] for _ in range(7)]
now_in_week = True
now_marker_printed = False
if self.now < start_dt or self.now > end_dt:
now_in_week = False
......@@ -344,28 +324,18 @@ class GoogleCalendarInterface:
# events which was started before current period of time and are
# still continue in current period of time
if event_is_today or (event_allday and event_continues_today):
force_now_marker = False
color_as_now_marker = False
if now_in_week:
if now_in_week and not now_marker_printed:
if (days_since_epoch(self.now) <
days_since_epoch(event['s'])):
force_now_marker = False
week_events[event_daynum - 1].append(
EventTitle(
'\n' + self.options['cal_width'] * '-',
self.options['color_now_marker']
)
)
elif self.now <= event['s']:
# add a line marker before next event
force_now_marker = False
week_events[event_daynum].append(
EventTitle(
'\n' + self.options['cal_width'] * '-',
self.options['color_now_marker']
)
)
now_marker_printed = True
# We don't want to recolor all day events, but ignoring
# them leads to issues where the 'now' marker misprints
......@@ -376,9 +346,10 @@ class GoogleCalendarInterface:
self.now <= event_end_date and \
not event_allday:
# line marker is during the event (recolor event)
force_now_marker = True
color_as_now_marker = True
now_marker_printed = True
if force_now_marker:
if color_as_now_marker:
event_color = self.options['color_now_marker']
else:
if self.options['override_color'] and event.get('colorId'):
......@@ -429,20 +400,25 @@ class GoogleCalendarInterface:
if stop >= self.options['cal_width']:
return stop, i + 1
def _next_cut(self, string, cur_print_len):
def _next_cut(self, string):
print_len = 0
words = _u(string).split()
word_lens = []
for i, word in enumerate(words):
word_len = self._printed_len(word)
if (cur_print_len + word_len + print_len) >= \
self.options['cal_width']:
word_lens.append(self._printed_len(word))
if (word_lens[-1] + print_len) >= self.options['cal_width']:
# this many words is too many, try to cut at the prev word
cut_idx = len(' '.join(words[:i]))
# if the first word is too long, we cannot cut between words
# first word is too long, we must cut inside it
if cut_idx == 0:
return self._word_cut(word)
return (print_len, cut_idx)
print_len += word_len + i # +i for the space between words
print_len = sum(word_lens) + i # +i for the space between words
return (print_len, len(' '.join(words[:i])))
def _get_cut_index(self, event_string):
......@@ -459,7 +435,7 @@ class GoogleCalendarInterface:
else:
# we must cut: _next_cut will loop until we find the right spot
return self._next_cut(event_string, 0)
return self._next_cut(event_string)
def _GraphEvents(self, cmd, start_datetime, count, event_list):
# ignore started events (i.e. events that start previous day and end
......@@ -621,9 +597,9 @@ class GoogleCalendarInterface:
_u(event['e'].strftime('%H:%M')))
if self.details.get('url'):
output += '\t%s' % (self._shorten_url(event['htmlLink'])
output += '\t%s' % (event['htmlLink']
if 'htmlLink' in event else '')
output += '\t%s' % (self._shorten_url(event['hangoutLink'])
output += '\t%s' % (event['hangoutLink']
if 'hangoutLink' in event else '')
output += '\t%s' % _u(self._valid_title(event).strip())
......@@ -724,12 +700,12 @@ class GoogleCalendarInterface:
self.printer.msg(xstr, 'default')
if self.details.get('url') and 'htmlLink' in event:
hlink = self._shorten_url(event['htmlLink'])
hlink = event['htmlLink']
xstr = '%s Link: %s\n' % (details_indent, hlink)
self.printer.msg(xstr, 'default')
if self.details.get('url') and 'hangoutLink' in event:
hlink = self._shorten_url(event['hangoutLink'])
hlink = event['hangoutLink']
xstr = '%s Hangout Link: %s\n' % (details_indent, hlink)
self.printer.msg(xstr, 'default')
......@@ -1282,7 +1258,7 @@ class GoogleCalendarInterface:
)
if self.details.get('url'):
hlink = self._shorten_url(new_event['htmlLink'])
hlink = new_event['htmlLink']
self.printer.msg('New event added: %s\n' % hlink, 'green')
return new_event
......@@ -1323,7 +1299,7 @@ class GoogleCalendarInterface:
new_event = self._retry_with_backoff(request)
if self.details.get('url'):
hlink = self._shorten_url(new_event['htmlLink'])
hlink = new_event['htmlLink']
self.printer.msg('New event added: %s\n' % hlink, 'green')
return new_event
......@@ -1557,7 +1533,7 @@ class GoogleCalendarInterface:
body=event
)
)
hlink = self._shorten_url(new_event.get('htmlLink'))
hlink = new_event.get('htmlLink')
self.printer.msg(
'New event added: %s\n' % hlink, 'green'
)
......@@ -1576,7 +1552,7 @@ class GoogleCalendarInterface:
body=event
)
)
hlink = self._shorten_url(new_event.get('htmlLink'))
hlink = new_event.get('htmlLink')
self.printer.msg('New event added: %s\n' % hlink, 'green')
elif val.lower() == 'q':
sys.exit(0)
......
#!/usr/bin/env python
from __future__ import print_function
from setuptools import setup
from gcalcli import __version__
try:
import pypandoc
......@@ -17,7 +18,7 @@ author_emails = ['edavis@insanum.com',
'jcrowgey@uw.edu']
setup(name='gcalcli',
version='4.0.4',
version=__version__,
author='Eric Davis, Brian Hartvigsen, Joshua Crowgey',
author_email=', '.join(author_emails),
maintainer='Joshua Crowgey',
......@@ -38,7 +39,6 @@ setup(name='gcalcli',
],
extras_require={
'vobject': ["vobject"],
'parsedatetime': ["parsedatetime"],
},
entry_points={
'console_scripts':
......
......@@ -42,16 +42,11 @@ def test_details_parser():
parsed_details = details_parser.parse_args(argv).details
assert parsed_details['attendees']
assert parsed_details['location']
assert parsed_details['url'] == 'short'
assert parsed_details['url']
argv = shlex.split('--details all')
parsed_details = details_parser.parse_args(argv).details
assert all(parsed_details[d] for d in argparsers.BOOL_DETAILS)
# ensure we can specify url type even with details=all
argv = shlex.split('--details all --details longurl')
parsed_details = details_parser.parse_args(argv).details
assert parsed_details['url'] == 'long'
assert all(parsed_details[d] for d in argparsers.DETAILS)
def test_handle_unparsed():
......
......@@ -201,3 +201,24 @@ def test_iterate_events(capsys, PatchedGCalI):
assert gcal._iterate_events(gcal.now, []) == 0
# TODO: add some events to a list and assert their selection
def test_next_cut(PatchedGCalI):
gcal = PatchedGCalI()
# default width is 10
test_cal_width = 10
gcal.options['cal_width'] = test_cal_width
event_title = "first looooong"
assert gcal._next_cut(event_title) == (5, 5)
event_title = "tooooooloooong"
assert gcal._next_cut(event_title) == (test_cal_width, test_cal_width)
event_title = "one two three four"
assert gcal._next_cut(event_title) == (7, 7)
# event_title = "& G NSW VIM Project"
# assert gcal._next_cut(event_title) == (7, 7)
event_title = "樹貞 fun fun fun"
assert gcal._next_cut(event_title) == (8, 6)
[tox]
envlist = py35
py27
envlist = py35,py36
[testenv]
usedevelop=true
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment