Make sure all files have encoding set to utf-8 unless they already have an encoding Added COPYING with contense of GPL-2 Added plugin name prefix to all log output. This should take the form of self.log.severity("Plugin: message"), when not logging from plugin.py use: "Plugin/file_without_dot_py: message" Bantracker: Made the confgure() function do something, it also creates an initial database if it doesn't exist Encyclopedia: Made the configure() function do something, it also creates an initial database if it doesn't exist PackageInfo: Improve the configure() function, it also now creates some initial .list files and prompts to run update_apt and update_apt_file This goes some way to getting an "ubottu" package together, all we need to do is patch supybot-wizard to download the plugins from bzr and put them somewhere supybot will see them, then the wizard will do all the initial setup
297 lines
10 KiB
Python
297 lines
10 KiB
Python
#!/usr/bin/env python
|
|
# -*- Encoding: utf-8 -*-
|
|
###
|
|
# Copyright (c) 2008-2010 Terence Simpson
|
|
#
|
|
# This program is free software; you can redistribute it and/or modify
|
|
# it under the terms of version 2 of the GNU General Public License as
|
|
# published by the Free Software Foundation.
|
|
#
|
|
# This program 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 General Public License for more details.
|
|
#
|
|
###
|
|
|
|
import sys, os
|
|
sys.path.append(os.path.dirname(__file__))
|
|
from icalendar import Calendar, cal, prop
|
|
from dateutil import tz as tzmod
|
|
from cStringIO import StringIO
|
|
import pytz
|
|
import urllib2
|
|
import datetime
|
|
import rruler
|
|
|
|
DEB_OBJ = None
|
|
|
|
SECONDS_PER_DAY=24*60*60
|
|
def seconds(timediff):
|
|
return SECONDS_PER_DAY * timediff.days + timediff.seconds
|
|
|
|
def toTz(date, tz):
|
|
assert isinstance(tz, datetime.tzinfo), "tz must be a tzinfo type"
|
|
if isinstance(date, datetime.datetime):
|
|
try:
|
|
return date.astimezone(tz)
|
|
except:
|
|
return datetime.datetime.combine(date.date(), datetime.time(date.time().hour, date.time().minute, date.time().second, tzinfo=tz))
|
|
elif isinstance(datetime.date):
|
|
return datetime.datetime.combine(date, datetime.time(0, 0, 0, tzinfo=tz))
|
|
|
|
class ICalReader:
|
|
def __init__(self, data):
|
|
self.events = []
|
|
self.timezones = {}
|
|
self.raw_data = data
|
|
self.readEvents()
|
|
|
|
def readEvents(self):
|
|
self.events = []
|
|
self.timezones = {}
|
|
parser = Calendar.from_string(self.raw_data)
|
|
tzs = parser.walk("vtimezone")
|
|
self.parseTzs(tzs)
|
|
events = parser.walk("vevent")
|
|
for event in events:
|
|
res = self.parseEvent(event)
|
|
if res:
|
|
self.events.append(res)
|
|
|
|
def parseTzs(self, tzs):
|
|
if not tzs:
|
|
return
|
|
for tz in tzs:
|
|
if 'X-LIC-LOCATION' in tz:
|
|
del tz['X-LIC-LOCATION']
|
|
data = ''.join([str(i) for i in tzs])
|
|
data = '\r\n'.join([i for i in data.splitlines() if i.strip()])
|
|
fd = StringIO(data)
|
|
times = tzmod.tzical(fd)
|
|
for tz in times.keys():
|
|
self.timezones[tz] = times.get(tz)
|
|
|
|
def parseEvent(self, e):
|
|
for k in ["dtstart", "dtend", "summary"]:
|
|
if not k in e:
|
|
return
|
|
if not isinstance(e['dtstart'].dt, datetime.datetime):
|
|
return
|
|
return ICalEvent.from_event(e, self)
|
|
startDate = endDate = rule = summary = None
|
|
startDate = self.parseDate(e.get("dtstart"))
|
|
endDate = self.parseDate(e.get("dtend"))
|
|
rule = e.get("RRULE")
|
|
summary = e.get("summary")
|
|
if e.get("exdate"):
|
|
event.addExceptionDate(e['EXDATE'].ical()[7:])
|
|
if not startDate or not endDate or not summary: # Bad event
|
|
return
|
|
|
|
event = ICalEvent()
|
|
event.raw_data = str(e)
|
|
event.summary = summary
|
|
event.startDate = startDate
|
|
event.endDate = endDate
|
|
if rule:
|
|
event.addRecurrenceRule(rule)
|
|
return event
|
|
|
|
def parseDate(self, date):
|
|
if not date:
|
|
return
|
|
tz = pytz.UTC
|
|
if 'tzid' in date.params:
|
|
tz = self.timezones[date.params['tzid']]
|
|
for attr in ['hour', 'minute', 'second']:
|
|
if not hasattr(date.dt, attr):
|
|
return
|
|
return toTz(date.dt, tz)
|
|
# return datetime.datetime(date.dt.year, date.dt.month, date.dt.day, date.dt.hour, date.dt.minute, date.dt.second, tzinfo=tz)
|
|
|
|
def selectEvents(self, selectFunction):
|
|
self.events.sort()
|
|
events = filter(selectFunction, self.events)
|
|
return events
|
|
|
|
def todaysEvents(self, event):
|
|
return event.startsToday()
|
|
|
|
def tomorrowsEvents(self, event):
|
|
return event.startsTomorrow()
|
|
|
|
def eventsFor(self, date):
|
|
self.events.sort()
|
|
ret = []
|
|
for event in self.events:
|
|
if event.startsOn(date):
|
|
ret.append(event)
|
|
return re
|
|
|
|
|
|
#class ICalEvent:
|
|
# def __init__(self):
|
|
# self.exceptionDates = []
|
|
# self.dateSet = None
|
|
#
|
|
# def __str__(self):
|
|
# return "%s (%s - %s)" % (self.summary, self.startDate, self.endDate)
|
|
|
|
class ICalEvent(cal.Event):
|
|
def __init__(self, *args, **kwargs):
|
|
self.exceptionDates = []
|
|
self.dateSet = None
|
|
self.__parent = super(ICalEvent, self)
|
|
self.__parent.__init__(self, *args, **kwargs)
|
|
|
|
@classmethod
|
|
def from_event(cls, event, parent):
|
|
global DEB_OBJ
|
|
x = cls(**dict(event))
|
|
x.__dict__ = event.__dict__
|
|
x.exceptionDates = []
|
|
x.dateSet = None
|
|
x.summary = x['summary']
|
|
x.timezone = x['dtstart'].dt.tzinfo
|
|
x.startDate = parent.parseDate(x['dtstart'])
|
|
x.endDate = parent.parseDate(x['dtend'])
|
|
if not x.timezone:
|
|
x.timezone = pytz.UTC
|
|
x.startDate = parent.parseDate(x['dtstart'])
|
|
x.endDate = parent.parseDate(x['dtend'])
|
|
x.raw_data = str(x)
|
|
if 'rrule' in event:
|
|
x.addRecurrenceRule(event['rrule'])
|
|
if x.summary == "Server Team Meeting":
|
|
DEB_OBJ = x
|
|
return x
|
|
|
|
def __str__(self):
|
|
return "%s (%s - %s)" % (self.summary, self.startDate, self.endDate)
|
|
|
|
def __eq__(self, otherEvent):
|
|
return self.startTime() == otherEvent.startTime()
|
|
|
|
def __lt__(self, otherEvent):
|
|
return self.startTime() < otherEvent.startTime()
|
|
|
|
def __gt__(self, otherEvent):
|
|
return self.startTime() > otherEvent.startTime()
|
|
|
|
def __ge__(self, otherEvent):
|
|
return self.startTime() >= otherEvent.startTime()
|
|
|
|
def __le__(self, otherEvent):
|
|
return self.startTime() <= otherEvent.startTime()
|
|
|
|
def addExceptionDate(self, date):
|
|
self.exceptionDates.append(date)
|
|
|
|
def addRecurrenceRule(self, rule):
|
|
self.dateSet = DateSet(self.startDate, self.endDate, rule)
|
|
|
|
def startsToday(self):
|
|
return self.startsOn(datetime.datetime.today())
|
|
|
|
def startsTomorrow(self):
|
|
tomorrow = datetime.datetime.today() + datetime.timedelta(1)
|
|
# tomorrow = datetime.datetime.fromtimestamp(time.time() + SECONDS_PER_DAY)
|
|
return self.startsOn(tomorrow)
|
|
|
|
def startsOn(self, date):
|
|
return (self.startDate.year == date.year and
|
|
self.startDate.month == date.month and
|
|
self.startDate.day == date.day or
|
|
(self.dateSet and self.dateSet.includes(date)))
|
|
|
|
def startTime(self):
|
|
now = datetime.datetime.now(pytz.UTC)
|
|
if self.dateSet and self.startDate < now:
|
|
dates = self.dateSet.getRecurrence()
|
|
for date in dates:
|
|
if date.date() >= now.date():
|
|
if date.date() > now.date() or (date.date() == now.date and date.astimezone(pytz.UTC).time() >= now.time()):
|
|
return toTz(datetime.datetime.combine(date,self.startDate.time()), self.startDate.tzinfo)
|
|
return self.startDate
|
|
|
|
def endTime(self):
|
|
now = datetime.datetime.now(pytz.UTC).date()
|
|
if self.dateSet and self.endDate.date() < now:
|
|
return toTz(datetime.datetime.combine(self.startTime().date(), self.endDate.time()), self.startDate.tzinfo)
|
|
return self.endDate
|
|
|
|
def schedule(self, timezone=None):
|
|
if not timezone:
|
|
return "%s UTC: %s" % (self.startTime().astimezone(pytz.UTC).strftime("%d %b %H:%M"), self.summary.replace('Meeting','').strip())
|
|
if isinstance(timezone, basestring):
|
|
return "%s: %s" % (self.startTime().astimezone(pytz.timezone(timezone)).strftime("%d %b %H:%M"), self.summary.replace('Meeting','').strip())
|
|
return "%s: %s" % (self.startTime().astimezone(timezone).strftime("%d %b %H:%M"), self.summary.replace('Meeting','').strip())
|
|
|
|
def is_on(self):
|
|
now = datetime.datetime.now(pytz.UTC)
|
|
return self.startTime() >= now and self.endTime() < now
|
|
|
|
def has_passed(self):
|
|
if self.dateSet:
|
|
return toTz(datetime.datetime.combine(self.startTime().date(), self.endDate.time()), self.startDate.tzinfo) < datetime.datetime.now(pytz.UTC)
|
|
return self.endDate < datetime.datetime.now(pytz.UTC)
|
|
|
|
def seconds_to_go(self):
|
|
return seconds(self.startTime() - datetime.datetime.now(pytz.UTC))
|
|
|
|
def seconds_ago(self):
|
|
return seconds(datetime.datetime.now(pytz.UTC) - self.endTime())
|
|
|
|
def time_to_go(self):
|
|
if self.endTime() < datetime.datetime.now(pytz.UTC):
|
|
return False
|
|
delta = self.startTime() - datetime.datetime.now(pytz.UTC)
|
|
s = ''
|
|
if delta.days:
|
|
if delta.days != 1:
|
|
s = 's'
|
|
return '%d day%s' % (delta.days, s)
|
|
h = ''
|
|
if delta.seconds > 7200:
|
|
s = 's'
|
|
if delta.seconds > 3600:
|
|
h = '%d hour%s ' % (int(delta.seconds/3600),s)
|
|
s = ''
|
|
minutes = (delta.seconds % 3600) / 60
|
|
if minutes != 1:
|
|
s = 's'
|
|
return '%s%d minute%s' % (h,minutes,s)
|
|
|
|
class DateSet:
|
|
def __init__(self, startDate, endDate, rule):
|
|
self.startDate = startDate
|
|
self.endDate = endDate
|
|
self.frequency = None
|
|
self.count = None
|
|
self.untilDate = None
|
|
self.byMonth = None
|
|
self.byDate = None
|
|
self.dates = None
|
|
self.parseRecurrenceRule(rule)
|
|
|
|
def parseRecurrenceRule(self, rule):
|
|
freq = rruler.rrule_map[rule.pop('freq')[0]]
|
|
now = datetime.datetime.now(self.startDate.tzinfo)
|
|
rule['dtstart'] = now
|
|
rule['until'] = now + datetime.timedelta(60)
|
|
self.recurrence = rruler.rrule_wrapper(freq, **rule)
|
|
|
|
def getRecurrence(self):
|
|
if not self.dates:
|
|
self.dates = []
|
|
for x in list(self.recurrence):
|
|
self.dates.append(toTz(x, self.startDate.tzinfo))
|
|
self.dates.append(self.startDate)
|
|
return self.dates
|
|
|
|
def includes(self, date):
|
|
if isinstance(date, datetime.datetime):
|
|
date = date.date()
|
|
return date in [x.date() for x in self.getRecurrence()]
|