weboob-devel/weboob/applications/boobcoming/boobcoming.py
Bezleputh 23c228280d [boobcoming] code review corrections
- split attends command in two (attends / unattends)
- use formatter in export command
2013-12-15 16:10:10 +01:00

388 lines
13 KiB
Python

# -*- coding: utf-8 -*-
# Copyright(C) 2013 Bezleputh
#
# This file is part of weboob.
#
# weboob is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# weboob is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with weboob. If not, see <http://www.gnu.org/licenses/>.
import re
import sys
from datetime import date, timedelta, time, datetime
from weboob.tools.application.formatters.iformatter import IFormatter, PrettyFormatter
from weboob.capabilities.base import empty
from weboob.capabilities.calendar import ICapCalendarEvent, Query, CATEGORIES
from weboob.tools.application.repl import ReplApplication, defaultcount
__all__ = ['Boobcoming']
class UpcomingSimpleFormatter(IFormatter):
MANDATORY_FIELDS = ('id', 'start_date', 'category', 'summary')
def format_obj(self, obj, alias):
return u'%s - %s - %s - %s' % (obj.backend, obj.category, obj.start_date.strftime('%H:%M'), obj.summary)
class ICalFormatter(IFormatter):
MANDATORY_FIELDS = ('id', 'start_date', 'end_date', 'summary', 'status')
def start_format(self, **kwargs):
result = u'BEGIN:VCALENDAR\n'
result += u'VERSION:2.0\n'
result += u'PRODID:-//hacksw/handcal//NONSGML v1.0//EN\n'
self.output(result)
def format_obj(self, obj, alias):
result = u'BEGIN:VEVENT\n'
result += u'DTSTART:%s\n' % obj.start_date.strftime("%Y%m%dT%H%M%SZ")
result += u'DTEND:%s\n' % obj.end_date.strftime("%Y%m%dT%H%M%SZ")
result += u'SUMMARY:%s\n' % obj.summary
result += u'UID:%s\n' % obj.id
result += u'STATUS:%s\n' % obj.status
location = ''
if hasattr(obj, 'location') and not empty(obj.location):
location += obj.location + ' '
if hasattr(obj, 'city') and not empty(obj.city):
location += obj.city + ' '
if not empty(location):
result += u'LOCATION:%s\n' % location
if hasattr(obj, 'categories') and not empty(obj.categories):
result += u'CATEGORIES:%s\n' % obj.categories
if hasattr(obj, 'description') and not empty(obj.description):
result += u'DESCRIPTION:%s\n' % obj.description.replace('\r\n', '\\n') \
.replace(',', '\,')
if hasattr(obj, 'transp') and not empty(obj.transp):
result += u'TRANSP:%s\n' % obj.transp
if hasattr(obj, 'sequence') and not empty(obj.sequence):
result += u'SEQUENCE:%s\n' % obj.sequence
if hasattr(obj, 'url') and not empty(obj.url):
result += u'URL:%s\n' % obj.url
result += u'END:VEVENT\n'
return result
def flush(self, **kwargs):
self.output(u'END:VCALENDAR')
class UpcomingListFormatter(PrettyFormatter):
MANDATORY_FIELDS = ('id', 'start_date', 'end_date', 'summary', 'category')
def get_title(self, obj):
return ' %s - %s ' % (obj.category, obj.summary)
def get_description(self, obj):
result = u''
result += u'\tDate: %s\n' % obj.start_date.strftime('%A %d %B %Y')
result += u'\tHour: %s - %s \n' % (obj.start_date.strftime('%H:%M'), obj.end_date.strftime('%H:%M'))
return result.strip('\n\t')
class UpcomingFormatter(IFormatter):
MANDATORY_FIELDS = ('id', 'start_date', 'end_date', 'summary', 'category')
def format_obj(self, obj, alias):
result = u'%s%s - %s%s\n' % (self.BOLD, obj.category, obj.summary, self.NC)
result += u'Date: %s\n' % obj.start_date.strftime('%A %d %B %Y')
result += u'Hour: %s - %s\n' % (obj.start_date.strftime('%H:%M'), obj.end_date.strftime('%H:%M'))
if hasattr(obj, 'location') and not empty(obj.location):
result += u'Location: %s\n' % obj.location
if hasattr(obj, 'city') and not empty(obj.city):
result += u'City: %s\n' % obj.city
if hasattr(obj, 'event_planner') and not empty(obj.event_planner):
result += u'Event planner: %s/%s \n' % (obj.event_planner)
if hasattr(obj, 'booked_entries') and not empty(obj.booked_entries) and \
hasattr(obj, 'max_entries') and not empty(obj.max_entries):
result += u'Entry: %s/%s \n' % (obj.booked_entries, obj.max_entries)
elif hasattr(obj, 'booked_entries') and not empty(obj.booked_entries):
result += u'Entry: %s \n' % (obj.booked_entries)
elif hasattr(obj, 'max_entries') and not empty(obj.max_entries):
result += u'Max entries: %s \n' % (obj.max_entries)
if hasattr(obj, 'description') and not empty(obj.description):
result += u'Description:\n %s\n\n' % obj.description
if hasattr(obj, 'price') and not empty(obj.price):
result += u'Price: %i\n' % obj.price
if hasattr(obj, 'url') and not empty(obj.url):
result += u'url: %s\n' % obj.url
return result
class Boobcoming(ReplApplication):
APPNAME = 'boobcoming'
VERSION = '0.h'
COPYRIGHT = 'Copyright(C) 2012 Bezleputh'
DESCRIPTION = "Console application to see upcoming events."
SHORT_DESCRIPTION = "see upcoming events"
CAPS = ICapCalendarEvent
EXTRA_FORMATTERS = {'upcoming_list': UpcomingListFormatter,
'upcoming': UpcomingFormatter,
'simple_upcoming': UpcomingSimpleFormatter,
'ical_formatter': ICalFormatter,
}
COMMANDS_FORMATTERS = {'list': 'upcoming_list',
'search': 'upcoming_list',
'info': 'upcoming',
'export': 'ical_formatter'
}
WEEK = {'MONDAY': 0,
'TUESDAY': 1,
'WEDNESDAY': 2,
'THURSDAY': 3,
'FRIDAY': 4,
'SATURDAY': 5,
'SUNDAY': 6,
'LUNDI': 0,
'MARDI': 1,
'MERCREDI': 2,
'JEUDI': 3,
'VENDREDI': 4,
'SAMEDI': 5,
'DIMANCHE': 6,
}
@defaultcount(10)
def do_search(self, line):
"""
search
search for an event. Parameters interactively asked
"""
query = Query()
r = 'notempty'
while r != '':
for category in CATEGORIES.values:
print ' %s%2d)%s [%s] %s' % (self.BOLD,
CATEGORIES.index[category] + 1,
self.NC,
'x' if category in query.categories else ' ', category)
r = self.ask(' Select category (or empty to stop)', regexp='(\d+|)', default='')
if not r.isdigit():
continue
r = int(r)
if r <= 0 or r > len(CATEGORIES.values):
continue
value = CATEGORIES.values[r - 1]
if value in query.categories:
query.categories.remove(value)
else:
query.categories.append(value)
if query.categories and len(query.categories) > 0:
query.city = self.ask('Enter a city', default='')
start_date = self.ask_date('Enter a start date', default='today')
end_date = self.ask_date('Enter a end date', default='')
if end_date:
if end_date == start_date:
end_date = datetime.combine(start_date, time.max)
else:
end_date = datetime.combine(end_date, time.max)
query.start_date = datetime.combine(start_date, time.min)
query.end_date = end_date
self.change_path([u'events'])
self.start_format()
for backend, event in self.do('search_events', query):
if event:
self.cached_format(event)
def ask_date(self, txt, default=''):
r = self.ask(txt, default=default)
return self.parse_date(r)
@defaultcount(10)
def do_list(self, line):
"""
list [PATTERN]
List upcoming events, pattern can be an english or french week day, 'today' or a date
"""
self.change_path([u'events'])
if line:
_date = self.parse_date(line)
if not _date:
print >>sys.stderr, 'Invalid argument: %s' % self.get_command_help('list', short=True)
return 2
date_from = datetime.combine(_date, time.min)
date_to = datetime.combine(_date, time.max)
else:
date_from = datetime.now()
date_to = None
for backend, event in self.do('list_events', date_from, date_to):
self.cached_format(event)
def complete_info(self, text, line, *ignored):
args = line.split(' ')
if len(args) == 2:
return self._complete_object()
def do_info(self, _id):
"""
info ID
Get information about an event.
"""
if not _id:
print >>sys.stderr, 'This command takes an argument: %s' % self.get_command_help('info', short=True)
return 2
event = self.get_object(_id, 'get_event')
if not event:
print >>sys.stderr, 'Upcoming event not found: %s' % _id
return 3
self.start_format()
self.format(event)
def do_export(self, line):
"""
export FILENAME [ID1 ID2 ID3 ...]
ID is the identifier of the event. If no ID every events are exported
FILENAME is where to write the file. If FILENAME is '-', the file is written to stdout.
Export event in ICALENDAR format
"""
if not line:
print >>sys.stderr, 'This command takes at leat one argument: %s' % self.get_command_help('export')
return 2
_file, args = self.parse_command_args(line, 2, req_n=1)
if not _file == "-":
dest = self.check_file_ext(_file)
self.formatter.outfile = dest
l = self.retrieve_events(args)
self.formatter.start_format()
for item in l:
self.format(item)
def retrieve_events(self, args):
l = []
if not args:
_ids = []
for backend, event in self.do('list_events', datetime.now(), None):
_ids.append(event.id)
else:
_ids = args.strip().split(' ')
for _id in _ids:
event = self.get_object(_id, 'get_event')
if not event:
print >>sys.stderr, 'Upcoming event not found: %s' % _id
return 3
l.append(event)
return l
def check_file_ext(self, _file):
splitted_file = _file.split('.')
if splitted_file[-1] != 'ics':
return "%s.ics" % _file
else:
return _file
def get_date_from_day(self, day):
today = date.today()
today_day_number = today.weekday()
requested_day_number = self.WEEK[day.upper()]
if today_day_number < requested_day_number:
day_to_go = requested_day_number - today_day_number
else:
day_to_go = 7 - today_day_number + requested_day_number
requested_date = today + timedelta(day_to_go)
return date(requested_date.year, requested_date.month, requested_date.day)
def parse_date(self, string):
matches = re.search('\s*([012]?[0-9]|3[01])\s*/\s*(0?[1-9]|1[012])\s*/?(\d{2}|\d{4})?$', string)
if matches:
year = matches.group(3)
if not year:
year = date.today().year
elif len(year) == 2:
year = 2000 + int(year)
return date(int(year), int(matches.group(2)), int(matches.group(1)))
elif string.upper() in self.WEEK.keys():
return self.get_date_from_day(string)
elif string.upper() == "TODAY":
return date.today()
def do_attends(self, line):
"""
attends ID1 [ID2 ID3 ...]
ID is the identifier of the event.
"""
if not line:
print >>sys.stderr, 'This command takes at leat one argument: %s' % self.get_command_help('attends')
return 2
args = self.parse_command_args(line, 1, req_n=1)
l = self.retrieve_events(args[0])
for event in l:
self.do('attends_event', event, True)
def do_unattends(self, line):
"""
unattends ID1 [ID2 ID3 ...]
ID is the identifier of the event.
"""
if not line:
print >>sys.stderr, 'This command takes at leat one argument: %s' % self.get_command_help('unattends')
return 2
args = self.parse_command_args(line, 1, req_n=1)
l = self.retrieve_events(args[0])
for event in l:
self.do('attends_event', event, False)