diff --git a/CHANGES.rst b/CHANGES.rst index ea167c6..1bff14e 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,6 +4,9 @@ Changelog 4.0.1 (unreleased) ------------------ +- Added rudimentary command line interface. + [jfjlaros] + - Readme, setup and travis updates. [jdufresne, PabloCastellano] diff --git a/docs/cli.rst b/docs/cli.rst new file mode 100644 index 0000000..f86b7f9 --- /dev/null +++ b/docs/cli.rst @@ -0,0 +1,36 @@ +iCalendar utility +================= + +To get more information about the command line interface, use the ``-h`` +option:: + + $ icalendar -h + +view +---- + +Use the ``view`` subcommand for a human readable summary of an event:: + + $ icalendar view myfile.ics + +The output will look something like this:: + + Organiser: Secretary + Attendees: + John Doe + Randy + Summary: Yearly evaluation. + When: Tue 14 Mar 2017 11:00-12:00 + Location: Randy's office + Comment: Reminder. + Description: + + Your yearly evaluation is scheduled for next Tuesday. Please be on time. + +To use this in terminal based e-mail clients like mutt, add a new mime type (as +root):: + + # cat << EOF > /usr/lib/mime/packages/icalendar + text/calendar; icalendar view '%s'; copiousoutput; description=iCalendar text; priority=2 + EOF + # update-mime diff --git a/docs/credits.rst b/docs/credits.rst index 834a730..bf39a7a 100644 --- a/docs/credits.rst +++ b/docs/credits.rst @@ -15,6 +15,7 @@ icalendar contributors - Erik Simmler - George V. Reilly - Jannis Leidel +- Jeroen F.J. Laros - Jeroen van Meeuwen (Kolab Systems) - Jordan Kiang - Klaus Klein diff --git a/docs/index.rst b/docs/index.rst index 10a7296..5a7e8ae 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -10,5 +10,6 @@ Contents install usage api + cli credits license diff --git a/setup.py b/setup.py index 50ac92f..f28096f 100644 --- a/setup.py +++ b/setup.py @@ -57,6 +57,7 @@ setuptools.setup( zip_safe=False, python_requires=">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*", install_requires=install_requires, + entry_points = {'console_scripts': ['icalendar = icalendar.cli:main']}, extras_require={ 'test': tests_require }, diff --git a/src/icalendar/cli.py b/src/icalendar/cli.py new file mode 100644 index 0000000..5d7b72f --- /dev/null +++ b/src/icalendar/cli.py @@ -0,0 +1,111 @@ +# -*- coding: utf-8 -*- +"""iCalendar utility""" +from __future__ import unicode_literals + +import argparse +import sys +from datetime import datetime + +from . import Calendar, __version__ + + +_template = """Organiser: {organiser} +Attendees: + {attendees} +Summary: {summary} +When: {time_from}-{time_to} +Location: {location} +Comment: {comment} +Description: + +{description} + +""" + + +def _format_name(address): + """Retrieve the e-mail and optionally the name from an address. + + :arg vCalAddress address: An address object. + + :returns str: The name and optionally the e-mail address. + """ + if not address: + return '' + + email = address.title().split(':')[1] + if 'cn' in address.params: + return '{} <{}>'.format(address.params['cn'], email) + + return email + + +def _format_attendees(attendees): + """Format the list of attendees. + + :arg any attendees: Either a list, a string or a vCalAddress object. + + :returns str: Formatted list of attendees. + """ + if type(attendees) == list: + return '\n '.join(map(_format_name, attendees)) + return _format_name(attendees) + + +def view(input_handle, output_handle): + """Make a human readable summary of an iCalendar file. + + :arg stream handle: Open readable handle to an iCalendar file. + + :returns str: Human readable summary. + """ + cal = Calendar.from_ical(input_handle.read()) + + for event in cal.walk('vevent'): + output_handle.write(_template.format( + organiser=_format_name(event.get('organizer', '')), + attendees=_format_attendees(event.get('attendee')), + summary=event.get('summary', ''), + time_from=datetime.strftime( + event.get('dtstart').dt, '%a %d %b %Y %H:%M'), + time_to=datetime.strftime(event.get('dtend').dt, '%H:%M'), + location=event.get('location', ''), + comment=event.get('comment', ''), + description=event.get('description', '')).encode('utf-8')) + + +def main(): + """Main entry point.""" + parser = argparse.ArgumentParser( + formatter_class=argparse.RawDescriptionHelpFormatter, + description=__doc__) + parser.add_argument( + '-v', '--version', action='version', + version='{} version {}'.format(parser.prog, __version__)) + + # This seems a bit of an overkill now, but we will probably add more + # functionality later, e.g., iCalendar to JSON / YAML and vice versa. + subparsers = parser.add_subparsers(dest='subcommand') + + subparser = subparsers.add_parser( + 'view', description=view.__doc__.split('\n\n')[0]) + subparser.add_argument( + 'input_handle', metavar='INPUT', type=argparse.FileType('r'), + help='iCalendar file') + subparser.add_argument( + '-o', dest='output_handle', metavar='OUTPUT', + type=argparse.FileType('w'), default=sys.stdout, + help='output file (default=)') + subparser.set_defaults(func=view) + + args = parser.parse_args() + + try: + args.func(**{k: v for k, v in vars(args).items() + if k not in ('func', 'subcommand')}) + except ValueError as error: + parser.error(error) + + +if __name__ == '__main__': + main()