SEARCH

Enter your search query in the box above ^, or use the forum search tool.

You are not logged in.

#1 2012-05-09 21:31:47

arclance
#! Die Hard
Registered: 2012-03-29
Posts: 987

display icalendar file events in conky - v2.5.1

Here is the script I wrote to display icalendar events in conky using calcurse.
conky_icalendar_display_scripts_by_arclance-d4z8xtu.png
I wrote this after finding that this script no longer worked correctly.

After looking into making recurring appointments work I found that this was not possible using calcurse.
I switched to using the icalendar python module to directly parse icalendar files instead of having calcurse do it for me.

The new script does everything the other scripts did and is less buggy.
The script now supports DAILY, WEEKLY, and YEARLY recurring appointment types.
The MONTHLY recurring appointment type is more complicated to handle due to months having different numbers of days and is not supported yet.
The script should inform you of any types of recurring appointments I have not discovered yet so I can add functions to handle them.
You will need to install v3.0 or higher of the icalendar Python module.

If your OS has a package for pip you can install the icalendar module with

pip install icalendar

as root. 
Otherwise follow the install instructions from wherever you get it from.

Someone left a debugging print statement in the current version (v3.0.1b2) of the icalendar module so you will need to open

/usr/local/lib/python2.7/dist-packages/icalendar/cal.py

as root and change line 384 from

                        print vals.to_ical()

to

                        #print vals.to_ical()

or it will print a bunch of unecessary stuff before the appointments and calendar are printed by the script.
I have been told this has already been fixed for the next version of the icalendar module.

Here is the new script it outputs both the appointments and calendar by default.
That can be changed if you don't want both.

#!/usr/bin/env python2.7
# -*- coding: utf-8 -*-
"""
Name: conky_icalendar_2.py
Author: arclance
Date_Created:2012-01-10
Date_Updated:2012-05-11
    
Reads the evolution calendar(or another ical calendar) using calcurse and format the output for dispaly in conky.
Does not work with recurring appointments yet (need to write appointment sorting function first).

Requires
    icalendar python package v3.0 or higher | http://pypi.python.org/pypi/icalendar/3.0.1b2#downloads
    time 
    subprocess

Changelog
v2.5.1 2012-05-11
    fix for displaying non-ascii characters in conky (worked in terminal before this makes it work in conky)

v2.5 2012-05-10
    Add support for DAILY, WEEKLY, and YEARLY recurring events

v2.0 2012-05-09
    replace calcurse with icalendar python module
    rewrite appointment display
    fixed to12Hour (12 PM was showed as 12 AM | 12 AM would have been displayed as 00 AM)
    - 2012-05-10 -
    rewrite calendar display
        fixed entry duplication bug in appointmentDates
"""

import time
from subprocess import Popen, PIPE
from icalendar.cal import Calendar, Event
from icalendar.prop import vDatetime, vDDDTypes, vDDDLists

#### user defined variables ####
daysToDisplay = 3 # number of days to display appointments for
icalendarPath = '~/.local/share/evolution/calendar/system/calendar.ics' # location of ics format calendar file, default is the loctaion for evolution but files from other programs should work but are untested
calendarFont = "DejaVu Sans Mono:size=10"
defaultColor = "${color}"
color0 = "${color0}"
usingShading = False # Set to True if using draw_shades yes
defaultShadeColor = "${shadecolor 8B0000}"
secondaryShadeColor = "${shadecolor 000000}"
#### end user defined variables ####

def to12Hour(inputTime):
    Hour = int(inputTime.split(":")[0])
    Minute = int(inputTime.split(":")[1])
    if Hour >= 12: # check for 24 hour format
        if Hour > 12:
            Hour = (Hour - 12) # make 12 hour format
        #endif
        _12HourTime = str(Hour).rjust(2, "0") + ':' + str(Minute).rjust(2, "0") + ' PM' # make sunset time string
    else:
        if Hour == 0:
            Hour = 12
        #endif
        _12HourTime = str(Hour).rjust(2, "0") + ':' + str(Minute).rjust(2, "0") + ' AM' # make sunset time string
    #endif
    return _12HourTime
#enddef    

def timeUntil(inputTime, dayHappens):
    Hour = int(inputTime.split(":")[0])
    Minute = int(inputTime.split(":")[1])
    secondsFromTimeZero = (60*60*Hour) + (60*Minute) # calculate seconds from start of day of event to the event
    currentHour = int(time.strftime('%H', time.localtime(time.time()))) # get current hour
    currentMinute = int(time.strftime('%M', time.localtime(time.time()))) # get current minute
    secondsSinceTimeZero = (60*60*currentHour) + (60*currentMinute)    # calculate seconds since the start of today
    if dayHappens == 0:
        if secondsSinceTimeZero > secondsFromTimeZero: # event has already ocurred
            secondsLeft = - secondsFromTimeZero + secondsSinceTimeZero # calculate seconds until the event
            hoursLeft = secondsLeft / 3600 # get hours left
            minutesLeft = (secondsLeft % 3600) / 60 # get minutes left        
            timeLeft = "-" + str(hoursLeft).rjust(2, "0") + ":" + str(minutesLeft).rjust(2, "0") # make timeLeft string    
        else:
            secondsLeft = secondsFromTimeZero - secondsSinceTimeZero # calculate seconds until the event
            hoursLeft = secondsLeft / 3600 # get hours left
            minutesLeft = (secondsLeft % 3600) / 60 # get minutes left
            if secondsLeft <= (3600*0.5):
                if usingShading == True:
                    timeLeft = " " + secondaryShadeColor + "${color EE0000}" + str(hoursLeft).rjust(2, "0") + ":" + str(minutesLeft).rjust(2, "0") + defaultShadeColor # make timeLeft string
                else:
                    timeLeft = " ${color EE0000}" + str(hoursLeft).rjust(2, "0") + ":" + str(minutesLeft).rjust(2, "0") # make timeLeft string
                #endif
            elif secondsLeft <= (3600*1.5):
                timeLeft = " ${color FF8000}" + str(hoursLeft).rjust(2, "0") + ":" + str(minutesLeft).rjust(2, "0") # make timeLeft string
            elif secondsLeft <= (3600*2.5):
                timeLeft = " ${color EEEE00}" + str(hoursLeft).rjust(2, "0") + ":" + str(minutesLeft).rjust(2, "0") # make timeLeft string
            elif secondsLeft <= (3600*4):
                if usingShading == True:
                    timeLeft = " " + secondaryShadeColor + "${color 006400}" + str(hoursLeft).rjust(2, "0") + ":" + str(minutesLeft).rjust(2, "0") + defaultShadeColor # make timeLeft string
                else:
                    timeLeft = " ${color 006400}" + str(hoursLeft).rjust(2, "0") + ":" + str(minutesLeft).rjust(2, "0") # make timeLeft string                
                #endif
            else:    
                timeLeft = " " + str(hoursLeft).rjust(2, "0") + ":" + str(minutesLeft).rjust(2, "0") # make timeLeft string
            #endif
        #endif    
    else:
        timeLeftInToday = (60*60*24) - (60*60*currentHour) - (60*currentMinute) # calcuate seconds until the end of the current day
        secondsLeft = timeLeftInToday + (3600*24*(dayHappens -1)) + secondsFromTimeZero # calcualate seconds until event
        hoursLeft = secondsLeft / 3600 # get hours left
        minutesLeft = (secondsLeft % 3600) / 60 # get minutes left        
        if secondsLeft <= (3600*0.5):
            if usingShading == True:
                timeLeft = " " + secondaryShadeColor + "${color EE0000}" + str(hoursLeft).rjust(2, "0") + ":" + str(minutesLeft).rjust(2, "0") + defaultShadeColor # make timeLeft string
            else:
                timeLeft = " ${color EE0000}" + str(hoursLeft).rjust(2, "0") + ":" + str(minutesLeft).rjust(2, "0") # make timeLeft string
            #endif
        elif secondsLeft <= (3600*1.5):
            timeLeft = " ${color FF8000}" + str(hoursLeft).rjust(2, "0") + ":" + str(minutesLeft).rjust(2, "0") # make timeLeft string
        elif secondsLeft <= (3600*2.5):
            timeLeft = " ${color EEEE00}" + str(hoursLeft).rjust(2, "0") + ":" + str(minutesLeft).rjust(2, "0") # make timeLeft string
        elif secondsLeft <= (3600*4):
            if usingShading == True:
                timeLeft = " " + secondaryShadeColor + "${color 006400}" + str(hoursLeft).rjust(2, "0") + ":" + str(minutesLeft).rjust(2, "0") + defaultShadeColor # make timeLeft string
            else:
                timeLeft = " ${color 006400}" + str(hoursLeft).rjust(2, "0") + ":" + str(minutesLeft).rjust(2, "0") # make timeLeft string                
            #endif
        else:    
            timeLeft = " " + str(hoursLeft).rjust(2, "0") + ":" + str(minutesLeft).rjust(2, "0") # make timeLeft string
        #endif
    #endif
    return timeLeft
#enddef

##############################

class eventList:
    
    def __init__(self):
        self._listOfEvents = []
        self._daysToDisplay = daysToDisplay
        self._currentTime = time.time()
        self._currentDate = time.strftime('%Y-%m-%d' ,time.localtime(float(self._currentTime)))
        self._currentDateSplit = self._currentDate.split("-")
        self._currentWeekday = time.strftime('%a' ,time.localtime(float(self._currentTime)))
        self._checkDates = [self._currentDate]
        for i in xrange(self._daysToDisplay - 1):
            self._checkDates.append(time.strftime('%Y-%m-%d' ,time.localtime(float(self._currentTime + float((i + 1)*3600*24)))))
        #endfor
        #### Appointments ####
        self._hrOffset = "${voffset -8}"
        self._dayHeaderOffset = "${voffset -5}"
        self._eventOffset = "${voffset -2}"
        #### Calendar ####
        self._tophrOffset = "${voffset -6}"
        self._monthYearAlignment = "${alignc}${voffset -6}"
        self._bodyAlignment = "${alignc}${voffset -2}"
    #enddef
    
    def appendSingleEvent(self, newEvent):
        self._listOfEvents.append(newEvent)
    #enddef
    
    def appendReccuringEvent_Weekly(self, startDate, startTime, endDate, endTime, recurDays, recurEndDate, recurCount, typeWeekly, summary): # eventList.appendReccuringEvent_Weekly(eventStartDate, eventStartTime, eventEndDate, eventEndTime, eventRecurDays, eventRecurEndDate, eventSummary)
        # UNTIL=20120518;BYDAY=SA,SU,MO,TU,WE,TH,FR
        #print str(recurEndDate)
        #print type(recurDays[0])
        for i in xrange(len(recurDays)):
            recurDays[i] = str(recurDays[i]).strip().replace("SA","Sat").replace("SU","Sun").replace("MO","Mon").replace("TU","Tue").replace("WE","Wed").replace("TH","Thu").replace("FR","Fri")
        #endfor
        #print str(recurDays)
        if typeWeekly == 1:
            #print len(str(recurEndDate))
            if len(str(recurEndDate)) < 19:
                recurEndDate_Seconds = time.mktime(time.strptime(str(recurEndDate), "%Y-%m-%d"))
            else:
                recurEndDate_Seconds = time.mktime(time.strptime(str(recurEndDate), "%Y-%m-%d %H:%M:%S"))
            #endif
            if recurEndDate_Seconds > self._currentTime: # only calculate recurring events if the reccurance has not ended yet
                #print str(recurEndDate_Seconds)
                numDays = 0
                timeStartSeconds = dateInSeconds(startDate, startTime)
                timeEndSeconds = dateInSeconds(endDate, endTime)
                checkDateSeconds = float(timeStartSeconds + float(numDays*3600*24))
                dayOfWeek = time.strftime('%a' ,time.localtime(checkDateSeconds))
                while checkDateSeconds < recurEndDate_Seconds:
                    #break
                    #print dayOfWeek
                    if dayOfWeek in recurDays:
                        checkStartDate = time.strftime('%Y-%m-%d',time.localtime(checkDateSeconds))
                        checkEndDate = time.strftime('%Y-%m-%d',time.localtime(float(timeEndSeconds + float(numDays*3600*24))))
                        self._listOfEvents.append([checkDateSeconds, checkStartDate, startTime, checkEndDate, endTime, summary, "WEEKLY"])
                    #endif
                    numDays = numDays + 1
                    checkDateSeconds = float(timeStartSeconds + float(numDays*3600*24))
                    dayOfWeek = time.strftime('%a' ,time.localtime(checkDateSeconds))
                #endwhile
                #pass
            #endif
        elif typeWeekly == 2:
            #print recurCount[0]
            numOccurances = 0
            numDays = 0
            timeStartSeconds = dateInSeconds(startDate, startTime)
            timeEndSeconds = dateInSeconds(endDate, endTime)
            while numOccurances < recurCount[0]:
                #break
                checkDateSeconds = float(timeStartSeconds + float(numDays*3600*24))
                dayOfWeek = time.strftime('%a' ,time.localtime(checkDateSeconds))
                #print dayOfWeek
                if dayOfWeek in recurDays:
                    checkStartDate = time.strftime('%Y-%m-%d',time.localtime(checkDateSeconds))
                    checkEndDate = time.strftime('%Y-%m-%d',time.localtime(float(timeEndSeconds + float(numDays*3600*24))))
                    self._listOfEvents.append([checkDateSeconds, checkStartDate, startTime, checkEndDate, endTime, summary, "WEEKLY"])
                    numOccurances = numOccurances + 1
                #endif
                numDays = numDays + 1
            #endwhile
        #endif
        #pass # for now
    #enddef
    
    def appendReccuringEvent_Daily(self, startDate, startTime, endDate, endTime, recurEndDate, recurCount, recurInterval, typeDaily, summary):
        if typeDaily == 1:
            #break
            timeStartSeconds = dateInSeconds(startDate, startTime)
            timeEndSeconds = dateInSeconds(endDate, endTime)
            #print (recurCount[0]*recurInterval[0])
            for i in xrange(0, int(recurCount[0]*recurInterval[0]), recurInterval[0]):
                #break
                #print i
                checkDateSeconds = float(timeStartSeconds + float(i*3600*24))
                checkStartDate = time.strftime('%Y-%m-%d',time.localtime(checkDateSeconds))
                #print checkStartDate
                checkEndDate = time.strftime('%Y-%m-%d',time.localtime(float(timeEndSeconds + float(i*3600*24))))
                self._listOfEvents.append([checkDateSeconds, checkStartDate, startTime, checkEndDate, endTime, summary, "DAILY"])
            #endfor
        elif typeDaily == 2:
            #break
            timeStartSeconds = dateInSeconds(startDate, startTime)
            timeEndSeconds = dateInSeconds(endDate, endTime)
            for i in xrange(recurCount[0]):
                #break
                checkDateSeconds = float(timeStartSeconds + float(i*3600*24))
                checkStartDate = time.strftime('%Y-%m-%d',time.localtime(checkDateSeconds))
                checkEndDate = time.strftime('%Y-%m-%d',time.localtime(float(timeEndSeconds + float(i*3600*24))))
                self._listOfEvents.append([checkDateSeconds, checkStartDate, startTime, checkEndDate, endTime, summary, "DAILY"])
            #endfor
        #endif
    #enddef
    
    def appendReccuringEvent_Monthly(self):
        pass
    #enddef
    
    def appendReccuringEvent_Yearly(self, startDate, startTime, endDate, endTime, recurCount, typeYearly, summary):
        if typeYearly == 1:
            timeStartSeconds = dateInSeconds(startDate, startTime)
            timeEndSeconds = dateInSeconds(endDate, endTime)
            for i in xrange(recurCount[0]):
                #break
                checkDateSeconds = float(timeStartSeconds + float(i*3600*24*365))
                checkStartDate = time.strftime('%Y-%m-%d',time.localtime(checkDateSeconds))
                checkEndDate = time.strftime('%Y-%m-%d',time.localtime(float(timeEndSeconds + float(i*3600*24*365))))
                self._listOfEvents.append([checkDateSeconds, checkStartDate, startTime, checkEndDate, endTime, summary, "YEARLY"])
            #endfor
        #endif
    #enddef
    
    def sortList(self):
        self._sortedList = sorted(self._listOfEvents, key=lambda epoch: int(epoch[0]))
    #enddef
    
    def returnList(self):
        return self._listOfEvents
    #enddef
    
    def returnSortedList(self):
        return self._sortedList
    #enddef
    
    def conkyApts(self):
        previousDate = ""
        outputString = "" # set outputString to empty String
        ##print "conkyApts Test"
        for l in xrange(len(self._sortedList)):
            for i in xrange(len(self._checkDates)):
                checkDateString = self._checkDates[i]
                if (str(self._sortedList[l][1]).strip() == checkDateString):
                    ##print "date match"
                    if i == 0: # today
                        if previousDate != self._sortedList[l][1]:
                            dayName = time.strftime('%A' ,time.localtime(float(self._currentTime + float(3600*24*i)))) # get the weekday name
                            outputString = outputString + self._hrOffset + color0 + "${hr}\n" + self._dayHeaderOffset + defaultColor + "Today (" + dayName + ") " + color0 + "- " + defaultColor + "" + checkDateString #
                            previousDate = checkDateString
                        #endif
                        startTime = self._sortedList[l][2]
                        endTime = self._sortedList[l][4]
                        eventName = self._sortedList[l][5]
                        outputString = outputString + "\n" + self._eventOffset + "" + color0 + "" + to12Hour(startTime) + " " + defaultColor + "-> " + color0 + "" + to12Hour(endTime) + " |" + defaultColor + "" + timeUntil(startTime, i) + " " + color0 + "| " + defaultColor + "" + eventName.replace("$","$$") #
                    elif i == 1: # tommorow
                        if previousDate != self._sortedList[l][1]:
                            dayName = time.strftime('%A' ,time.localtime(float(self._currentTime + float(3600*24*i)))) # get the weekday name
                            outputString = outputString + "\n" + self._hrOffset + color0 + "${hr}\n" + self._dayHeaderOffset + defaultColor + "Tommorow (" + dayName + ") " + color0 + "- " + defaultColor + "" + checkDateString#
                            previousDate = checkDateString
                        #endif
                        startTime = self._sortedList[l][2]
                        endTime = self._sortedList[l][4]
                        eventName = self._sortedList[l][5]
                        outputString = outputString + "\n" + self._eventOffset + "" + color0 + "" + to12Hour(startTime) + " " + defaultColor + "-> " + color0 + "" + to12Hour(endTime) + " |" + defaultColor + "" + timeUntil(startTime, i) + " " + color0 + "| " + defaultColor + "" + eventName.replace("$","$$") #
                    else:
                        if previousDate != self._sortedList[l][1]:
                            dayName = time.strftime('%A' ,time.localtime(float(self._currentTime + float(3600*24*i)))) # get the weekday name
                            outputString = outputString + "\n" +self._hrOffset + color0 + "${hr}\n" + self._dayHeaderOffset + defaultColor + "" + dayName + " " + color0 + "- " + defaultColor + "" + checkDateString #
                            previousDate = checkDateString
                        #endif
                        startTime = self._sortedList[l][2]
                        endTime = self._sortedList[l][4]
                        eventName = self._sortedList[l][5]
                        outputString = outputString + "\n" + self._eventOffset + "" + color0 + "" + to12Hour(startTime) + " " + defaultColor + "-> " + color0 + "" + to12Hour(endTime) + " |" + defaultColor + "" + timeUntil(startTime, i) + " " + color0 + "| " + defaultColor + "" + eventName.replace("$","$$") #
                    #endif    
                #endif    
            #endfor
        #endfor
        if outputString != "":
            return outputString.strip()
        else:
            return self._hrOffset + color0 + "${hr}\n" + self._dayHeaderOffset + defaultColor + "No Events"
        #endif
    #enddef
    
    def conkyCalendar(self):
        (stdout, stderr) = Popen(["cal"], stdout=PIPE, stderr=PIPE).communicate()
        if stderr != "":
            print "cal error = " + stderr
        #endif
        rawcalendar = stdout.strip().split("\n")
        lastDateMonth = rawcalendar[len(rawcalendar) - 1].strip().split(" ")
        #print lastDateMonth
        lastDateMonth = lastDateMonth[len(lastDateMonth) - 1].replace("_\x08","")
        """
        currentYear = self._currentDateSplit[0]
        currentMonth = self._currentDateSplit[1]
        currentDay = self._currentDateSplit[2]
        """
        #print self._currentDateSplit[2]
        appointmentDates = []
        for l in xrange(len(self._sortedList)):
            for i in xrange(1, (int(lastDateMonth) + 1)):
                checkDateString = self._currentDateSplit[0] + "-" + self._currentDateSplit[1] + "-" + str(i).rjust(2, "0")
                if self._sortedList[l][1] == checkDateString and (str(i) not in appointmentDates):
                    appointmentDates.append(str(i))
                #endif
            #endfor
        #endfor
        #print str(appointmentDates)
        finalcalendar = self._tophrOffset + color0 + "${hr}\n"
        for m in xrange(len(rawcalendar)):
            if m == 0:
                finalcalendar = finalcalendar + self._monthYearAlignment + color0 + rawcalendar[m].strip() + "\n"
            elif m == 1:
                finalcalendar = finalcalendar + self._bodyAlignment + color0 + rawcalendar[m].strip() + "\n"
            elif len(rawcalendar[m].strip()) != 0:
                #print "len test"
                rawcalendar[m] = rawcalendar[m] + " "
                rawLength = len(rawcalendar[m])
                for k in xrange(len(appointmentDates)):
                    if appointmentDates[k] == str(self._currentDateSplit[2]):
                        if usingShading == True:
                            rawcalendar[m] = rawcalendar[m].replace("_\x08","").replace(appointmentDates[k].rjust(2," ").ljust(3," "), ("${font " + calendarFont + ":style=bold}" + secondaryShadeColor + "${color EE0000}" + appointmentDates[k].rjust(2," ").ljust(3," ") + "${font}" + defaultShadeColor + defaultColor))    # make today bold and red if it has an appointment
                        else:
                            rawcalendar[m] = rawcalendar[m].replace("_\x08","").replace(appointmentDates[k].rjust(2," ").ljust(3," "), ("${font " + calendarFont + ":style=bold}${color EE0000}" + appointmentDates[k].rjust(2," ").ljust(3," ") + "${font}" + defaultColor))    # make today bold and red if it has an appointment
                        #endif
                    else:    
                        rawcalendar[m] = rawcalendar[m].replace("_\x08","").replace(appointmentDates[k].rjust(2," ").ljust(3," "), (color0 + appointmentDates[k].rjust(2," ").ljust(3," ") + defaultColor)) # make other day a different color if they have appointments
                    #endif
                #endfor
                if str(self._currentDateSplit[2]) not in appointmentDates:
                    if usingShading == True:
                        rawcalendar[m] = rawcalendar[m].replace("_\x08","").replace(str(currentDate).rjust(2," ").ljust(3," "), ("${font " + calendarFont + ":style=bold}" + secondaryShadeColor + "${color 006400}" + str(currentDate).rjust(2," ").ljust(3," ") + "${font}" + defaultShadeColor + defaultColor)) # make today bold and green if it does not have an appointment
                    else:
                        rawcalendar[m] = rawcalendar[m].replace("_\x08","").replace(str(currentDate).rjust(2," ").ljust(3," "), ("${font " + calendarFont + ":style=bold}${color 006400}" + str(currentDate).rjust(2," ").ljust(3," ") + "${font}" + defaultColor)) # make today bold and green if it does not have an appointment
                    #endif
                #endif
                if i == 2:
                    finalcalendar = finalcalendar + self._bodyAlignment + defaultColor + (" " * (21 - rawLength)) + rawcalendar[m]  + "\n"
                else:
                    finalcalendar = finalcalendar + self._bodyAlignment + defaultColor + rawcalendar[m] + (" " * (21 - rawLength)) + "\n"
                #endif
            #endif
        #endfor
        return str(finalcalendar).strip("\n")
    #enddef
#endclass

def dateInSeconds(startDate, startTime):
    timeStruct = time.strptime((startDate + " " + startTime), "%Y-%m-%d %H:%M:%S")
    return time.mktime(timeStruct)
#enddef

def parseTest():
    rawCalendar = open(icalendarPath, "rb") # open calendar for reading
    evoCalendar = rawCalendar.read()
    rawCalendar.close()
    ####
    #print "importing"
    parsCalendar = Calendar.from_ical(evoCalendar)
    #print "walking"
    eventHolder = eventList()
    for component in parsCalendar.walk('VEVENT'):
        #print component
        #print component.canonical_order
        #print component.name
        eventSummary = component['SUMMARY']
        #print str(compSummary)
        compDTStart = component['DTSTART'].to_ical()
        #print compDTStart
        if compDTStart.find("T") != (-1):
            [eventStartDate, eventStartTime] = str(vDatetime.from_ical(compDTStart)).split(" ")
        else:
            #print "no start time"
            #print str(compDTStart)
            eventStartDate = compDTStart[0:4] + "-" + compDTStart[4:6] + "-" + compDTStart[6:8]
            eventStartTime = "00:00:00"
        #endif
        eventStartSeconds = dateInSeconds(eventStartDate, eventStartTime)
        #print str(eventStartSeconds)
        compDTEnd = component['DTEND'].to_ical()
        if compDTEnd.find("T") != (-1):
            [eventEndDate, eventEndTime] = str(vDatetime.from_ical(compDTEnd)).split(" ")
        else:
            #print "no end time"
            #print str(compDTEnd)
            eventEndDate = compDTEnd[0:4] + "-" + compDTEnd[4:6] + "-" + compDTEnd[6:8]
            eventEndTime = "23:59:59"
        #endif
        try:
            compRRule = component['RRULE']
            #print eventSummary
            #print str(compRRule)
            eventRecurFreq = compRRule['FREQ']
            #print str(eventRecurFreq)
        except Exception, e:
            #print "RRule Error = " + str(e)
            compRRule = "N/A"
        #endtry
        weeklyType = 0
        dailyType = 0
        yearlyType = 0
        if compRRule == "N/A": # Single appointments
            eventHolder.appendSingleEvent([eventStartSeconds, eventStartDate, eventStartTime, eventEndDate, eventEndTime, eventSummary, compRRule])
        elif eventRecurFreq[0] == "WEEKLY": # Weekly reccuring appointments ## RRULE:FREQ=WEEKLY;UNTIL=20111209T040000;BYDAY=MO,WE,FR;COUNT=4
            #print str(eventRecurFreq[0])
            try:
                eventRecurDays = compRRule['BYDAY']
            except Exception, e:
                print "Error Unknown WEEKLY Recurring Appointment Type: " + str(e)
                break
            #endtry
            try: # check for WEEKLY events defined by enddate
                eventRecurEndDate = compRRule['UNTIL'][0]
                weeklyType = 1
                eventRecurCount = "N/A"
            except Exception, e:
                pass
            #endtry
            try: # check for WEEKLY events defined by number of occurances
                eventRecurCount = compRRule['COUNT']
                weeklyType = 2
                eventRecurEndDate = "N/A"
            except Exception, e:
                pass
            #endtry
            if weeklyType != 0:
                eventHolder.appendReccuringEvent_Weekly(eventStartDate, eventStartTime, eventEndDate, eventEndTime, eventRecurDays, eventRecurEndDate, eventRecurCount, weeklyType, eventSummary)
            #endif
        elif eventRecurFreq[0] == 'DAILY': # Daily reccruing appointments ## RRULE;X-EVOLUTION-ENDDATE=20120525T233000Z:FREQ=DAILY;COUNT=8;INTERVAL=2;
            #print eventSummary
            #print str(compRRule)
            try: # check for DAILY events that occur at a interval of every other day or greater
                eventRecurInterval = compRRule['INTERVAL']
                eventRecurCount = compRRule['COUNT']
                eventRecurEndDate = "N/A"
                dailyType = 1
            except Exception, e:
                #print str(e)
                pass
            #endtry
            if dailyType != 1:
                try: # check for DAILY
                    eventRecurCount = compRRule['COUNT']
                    dailyType = 2
                    eventRecurEndDate = "N/A"
                    eventRecurInterval = 1
                except Exception, e:
                    print "Error Unknown DAILY Recurring Appointment Type: " + str(e) 
                    #pass
                #endtry
            #endif
            if dailyType != 0:
                eventHolder.appendReccuringEvent_Daily(eventStartDate, eventStartTime, eventEndDate, eventEndTime, eventRecurEndDate, eventRecurCount, eventRecurInterval, dailyType, eventSummary)
            #endif
            pass # for now
        elif eventRecurFreq[0] == 'MONTHLY':
            pass # this is the most complicated type and I don't use it, it can wait till later
        elif eventRecurFreq[0] == 'YEARLY': #RRULE;X-EVOLUTION-ENDDATE=20140511T000000Z:FREQ=YEARLY;COUNT=3;WKST=SA
            try: # check for YEARLY
                eventRecurCount = compRRule['COUNT']
                yearlyType = 1
            except Exception, e:
                #print str(e)
                print "Error Unknown YEARLY Recurring Appointment Type: " + str(e)
                #pass
            #endtry
            if yearlyType != 0:
                eventHolder.appendReccuringEvent_Yearly(eventStartDate, eventStartTime, eventEndDate, eventEndTime, eventRecurCount, yearlyType, eventSummary)
            #endif
        else:
            print "Error Unknown Recurring Appointment Type: " + str(eventRecurFreq)
            #pass
        #endif
    #endfor
    """
    unsortedList = eventHolder.returnList()
    for i in xrange(len(unsortedList)):
    #    print str(unsortedList[i])
    #endfor
    """
    eventHolder.sortList()
    
    #sortedList = eventHolder.returnSortedList()
    #print ""
    #for i in xrange(len(sortedList)):
    #    print str(sortedList[i])
    #endfor
    print (eventHolder.conkyApts()).encode('utf-8')
    print eventHolder.conkyCalendar().encode('utf-8')
#enddef

#########

#timeStart = time.time()
parseTest()
#print "time end = " + str(time.time() - timeStart) + "s"

Features
The scripts are written in Python and use the icalendar module to process the icalendar file into a simpler format.
The script reads the Evolution calendar by default but it should work with icalendar formated files produced by other programs.
Support for DAILY, WEEKLY, and YEARLY recurring appointment types.

Appointments
The remaing time until an event starts will change colors as it gets closer to starting.
Events that have already started show the time since they started as a negative value.

Calendar
The calendar is centered in the conky window.
Days without events are displayed in conkys configured default_color.
Days with events are displayed in conkys configured color0
The current day is displayed in green if there are no events today.
The current day is displayed in red if there are events today.

Limitations
MONTHLY reccuring appointments are not supported for now.

How To Use
The scripts should be run from conky using ${execp} or ${execpi}.
If you don't want the appointemnts to be output change line 529 to

    #print eventHolder.conkyApts()

If you don't want the calendar to be output change line 530 to

    #print eventHolder.conkyCalendar()

Configuraton
Make sure

calendarLocation = '~/.local/share/evolution/calendar/system/calendar.ics' # location of ics format calendar file, default is the loctaion for evolution but files from other programs should work but are untested

is the path to your icalendar file.
Set

daysToDisplay = 3 # number of days to display appointments for

to the number of days you want to display events for.
Set

calendarFont = "DejaVu Sans Mono:size=10"

to your conkys default font.
If you have draw_shades yes in your .conkyrc change

usingShading = False # Set to True if using draw_shades yes

to

usingShading = True # Set to True if using draw_shades yes

and configure

defaultShadeColor = "${shadecolor 8B0000}"

to match your .conkyrc to improve the appearance of the countdown text.

Changelog
v2.5.1 2012-05-11
    fix for displaying non-ascii characters in conky

v2.5 2012-05-10
    Add support for DAILY, WEEKLY, and YEARLY recurring events

v2.0 2012-05-09
    replace calcurse with icalendar python module
    rewrite appointment display
    fixed to12Hour (12 PM was showed as 12 AM | 12 AM would have been displayed as 00 AM)
    - 2012-05-10 -
    rewrite calendar display
        fixed entry duplication bug in appointmentDates

Last edited by arclance (2012-05-11 19:23:20)

Offline

Help fund CrunchBang, donate to the project!

#2 2012-05-09 23:32:25

TitanMech
Banned
From: Blue Zone 3
Registered: 2011-08-28
Posts: 930

Re: display icalendar file events in conky - v2.5.1

When you say icalender, are you refereing to iCal on mac?
If so i will be giving this a try for sure!

Offline

#3 2012-05-09 23:45:06

arclance
#! Die Hard
Registered: 2012-03-29
Posts: 987

Re: display icalendar file events in conky - v2.5.1

TitanMech wrote:

When you say icalender, are you refereing to iCal on mac?
If so i will be giving this a try for sure!

Yes according to wikipedia iCal uses the icalendar format to store it's calendar so it should work.
Just change Script 1 so it uses the calendar file from iCal instead of the defalult settings.
I have only tested the script on icalendar files produced by Evolution so I can't be absolutely sure it will work.

Offline

#4 2012-05-10 17:05:43

ZlyChleba
Member
Registered: 2012-05-10
Posts: 11

Re: display icalendar file events in conky - v2.5.1

ok, so i tried this script. But something occured.
On my conky on desktop i get:

remove notes error = [Errno 2] No such file or directory: '/home/kerel/.calcurse/notes'
calcurse import error = utils.c: 859: temporary file "/home/kerel/.calcurse/notes/gJNng3"
io.c: 2349: Warning: could not create new note file to store description. Aborting...

" + color0 + "_________________________________________________________________
" + defaultColor + "No Events

Thats all... I setuped everything, from my .conkyrc file i use ${execp  python  ~/evolutionconky.py} command to get script. Do you know what is the problem ?
Thanks for response.... smile

Last edited by ZlyChleba (2012-05-10 17:06:23)

Offline

#5 2012-05-10 18:18:37

arclance
#! Die Hard
Registered: 2012-03-29
Posts: 987

Re: display icalendar file events in conky - v2.5.1

ZlyChleba wrote:

ok, so i tried this script. But something occured.
On my conky on desktop i get:

remove notes error = [Errno 2] No such file or directory: '/home/kerel/.calcurse/notes'

That is normal the first time you run the script the errors happen because calcurse has not written any files yet.

ZlyChleba wrote:
calcurse import error = utils.c: 859: temporary file "/home/kerel/.calcurse/notes/gJNng3"
io.c: 2349: Warning: could not create new note file to store description. Aborting...

That comes from calcurse indicating it was unable to write one of its output files.
You don't need that file so as long as the ones you do need were written first it should not be a problem (other than it appearing on your conky).
Is the partion your /home is on full?
/home/kerel/.calcurse is not read only is it?

ZlyChleba wrote:
" + color0 + "_________________________________________________________________
" + defaultColor + "No Events

Thats all... I setuped everything, from my .conkyrc file i use ${execp  python  ~/evolutionconky.py} command to get script. Do you know what is the problem ?
Thanks for response.... smile

That was caused by a typo left from when I moved the color configuration to the top so it would be easy to find.
I updated the first post with a fixed version.

I looked into making recurring appointments work yesterday and found that calcurse does not import them correctely.
I am currently rewriting the script to use the python icalendar package to directly parse the icalendar file without using calcurse.
I have the appoinments section (Script 1) completely rewritten and working.
The script is collecting the data needed to get reccuring appointments working, I just need to write functions to handle the different types of recurring appointments.
I am going to redo the calendar (Script 2) first though.
If you don't need the calendar on the bottom you could try that since it looks like calcurse may be having problems with your icalendar file.

If you don't want to try the new script please run the fixed Script 1 in a terminal and post the output here.
Also post the configuration section of the script so I can make sure you have everything set up correctly.

Offline

#6 2012-05-10 18:58:22

ZlyChleba
Member
Registered: 2012-05-10
Posts: 11

Re: display icalendar file events in conky - v2.5.1

Thanks for your reply.
I tried fixed script you posted and the think with

" + color0 + "_________________________________________________________________
" + defaultColor + "No Events

was soluted.

but, I still get the other errors.

Configuration section :

daysToDisplay = 3 # number of days to display appointments for
homePath = '/home/kerel/' # full path to your home folder 
calendarLocation = '.evolution/calendar/local/system/calendar.ics' # location of ics format calendar file, default is the loctaion for evolution but files from other programs should work but are untested
defaultColor = "${color}"
color0 = "${color0}"
usingShading = True # Set to True if using draw_shades yes
defaultShadeColor = "${shadecolor 8B0000}"
secondaryShadeColor = "${shadecolor 000000}"

I run the script.py in terminal with command "python script.py"  and i got:

remove notes error = [Errno 2] No such file or directory: '/home/kerel/.calcurse/notes'
calcurse import error = utils.c: 859: temporary file "/home/kerel/.calcurse/notes/QvBnV4" could not be created
io.c: 2349: Warning: could not create new note file to store description. Aborting...


${voffset -8}${color0}${hr}
${voffset -5}${color}No Events

Offline

#7 2012-05-10 19:19:25

arclance
#! Die Hard
Registered: 2012-03-29
Posts: 987

Re: display icalendar file events in conky - v2.5.1

ZlyChleba wrote:

Thanks for your reply.
I tried fixed script you posted and the think with

" + color0 + "_________________________________________________________________
" + defaultColor + "No Events

was soluted.

but, I still get the other errors.

Configuration section :

daysToDisplay = 3 # number of days to display appointments for
homePath = '/home/kerel/' # full path to your home folder 
calendarLocation = '.evolution/calendar/local/system/calendar.ics' # location of ics format calendar file, default is the loctaion for evolution but files from other programs should work but are untested
defaultColor = "${color}"
color0 = "${color0}"
usingShading = True # Set to True if using draw_shades yes
defaultShadeColor = "${shadecolor 8B0000}"
secondaryShadeColor = "${shadecolor 000000}"

I run the script.py in terminal with command "python script.py"  and i got:

remove notes error = [Errno 2] No such file or directory: '/home/kerel/.calcurse/notes'
calcurse import error = utils.c: 859: temporary file "/home/kerel/.calcurse/notes/QvBnV4" could not be created
io.c: 2349: Warning: could not create new note file to store description. Aborting...


${voffset -8}${color0}${hr}
${voffset -5}${color}No Events

Is there a /home/kerel/.calcurse/notes directory? 
Maybe it was not created when you installed calcurse?
If not try making one and then running the script again.

Offline

#8 2012-05-10 19:27:56

ZlyChleba
Member
Registered: 2012-05-10
Posts: 11

Re: display icalendar file events in conky - v2.5.1

Well, i tried to make folder notes in .calcurse and run the script. But the script deletes the folder and keep printing errors...

Offline

#9 2012-05-10 19:37:26

arclance
#! Die Hard
Registered: 2012-03-29
Posts: 987

Re: display icalendar file events in conky - v2.5.1

ZlyChleba wrote:

Well, i tried to make folder notes in .calcurse and run the script. But the script deletes the folder and keep printing errors...

I don't know then the script deletes the folder to prevent calcurse from making millions of temporary files so it has to delete the folder.
It is working here the folder is deleted and recreated every time the script runs.
I should have all the features of the current scripts recreated in the new version that does not depend on calcurse in about an hour.

Since you seem to be having problems with calcurse on your system do you want to wait for me to finish that?

Offline

#10 2012-05-10 19:41:07

ZlyChleba
Member
Registered: 2012-05-10
Posts: 11

Re: display icalendar file events in conky - v2.5.1

Yes, I would like to wait, with calcurse are only problems... I really appreciate your work smile thanks for everything.

Offline

#11 2012-05-10 20:05:56

arclance
#! Die Hard
Registered: 2012-03-29
Posts: 987

Re: display icalendar file events in conky - v2.5.1

ZlyChleba wrote:

Yes, I would like to wait, with calcurse are only problems... I really appreciate your work smile thanks for everything.

Okay here it is.
It does everything the other scripts did and is less buggy.
I have not written the functions for handling recuring appointments yet so they will not be displayed at all.
The script should inform you of any types of recurring appointments I have not discovered yet so I can add functions to handle them.
You will need to install v3.0 or higher of the icalendar Python module.
This python module replaces calcurse which would not have worked with reccuring appointments.

If your OS has a package for pip you can install the icalendar module with

pip install icalendar

as root.
Someone left a debugging print statement in the current version of the icalendar module so you will need to open

/usr/local/lib/python2.7/dist-packages/icalendar/cal.py

as root and change line 384 from

                        print vals.to_ical()

to

                        #print vals.to_ical()

or it will print a buch of unecessary stuff before the appointments and calendar are printed by the script.

Here is the new script it outputs both the appointments and calendar by default.
That can be changed if you don't want both.

#!/usr/bin/env python2.7
# -*- coding: utf-8 -*-
"""
Name:conky_calendar2.py
Author: arclance
Date_Created:2012-01-10
Date_Updated:2012-05-09
    
Reads the evolution calendar(or another ical calendar) using icalendar and format the output for display in conky.
Does not work with recurring appointments yet (need to write appointment handling function first).

Requires
    icalendar python package v3.0 or higher | http://pypi.python.org/pypi/icalendar/3.0.1b2#downloads
    time 
    subprocess

Changelog
v2.0 2012-05-09
    replace calcurse with icalendar python module
    rewrite appointment display
    fixed to12Hour (12 PM was showed as 12 AM | 12 AM would have been displayed as 00 AM)
    - 2012-05-10 -
    rewrite calendar display
        fixed entry duplication bug in appointmentDates
"""

import time
from subprocess import Popen, PIPE
from icalendar.cal import Calendar, Event
from icalendar.prop import vDatetime, vDDDTypes, vDDDLists

#### user defined variables ####
daysToDisplay = 3 # number of days to display appointments for
icalendarPath = '~/.local/share/evolution/calendar/system/calendar.ics' # location of ics format calendar file, default is the loctaion for evolution but files from other programs should work but are untested
calendarFont = "DejaVu Sans Mono:size=10"
defaultColor = "${color}"
color0 = "${color0}"
usingShading = False # Set to True if using draw_shades yes
defaultShadeColor = "${shadecolor 8B0000}"
secondaryShadeColor = "${shadecolor 000000}"
#### end user defined variables ####

def to12Hour(inputTime):
    Hour = int(inputTime.split(":")[0])
    Minute = int(inputTime.split(":")[1])
    if Hour >= 12: # check for 24 hour format
        if Hour > 12:
            Hour = (Hour - 12) # make 12 hour format
        #endif
        _12HourTime = str(Hour).rjust(2, "0") + ':' + str(Minute).rjust(2, "0") + ' PM' # make sunset time string
    else:
        if Hour == 0:
            Hour = 12
        #endif
        _12HourTime = str(Hour).rjust(2, "0") + ':' + str(Minute).rjust(2, "0") + ' AM' # make sunset time string
    #endif
    return _12HourTime
#enddef    

def timeUntil(inputTime, dayHappens):
    Hour = int(inputTime.split(":")[0])
    Minute = int(inputTime.split(":")[1])
    secondsFromTimeZero = (60*60*Hour) + (60*Minute) # calculate seconds from start of day of event to the event
    currentHour = int(time.strftime('%H', time.localtime(time.time()))) # get current hour
    currentMinute = int(time.strftime('%M', time.localtime(time.time()))) # get current minute
    secondsSinceTimeZero = (60*60*currentHour) + (60*currentMinute)    # calculate seconds since the start of today
    if dayHappens == 0:
        if secondsSinceTimeZero > secondsFromTimeZero: # event has already ocurred
            secondsLeft = - secondsFromTimeZero + secondsSinceTimeZero # calculate seconds until the event
            hoursLeft = secondsLeft / 3600 # get hours left
            minutesLeft = (secondsLeft % 3600) / 60 # get minutes left        
            timeLeft = "-" + str(hoursLeft).rjust(2, "0") + ":" + str(minutesLeft).rjust(2, "0") # make timeLeft string    
        else:
            secondsLeft = secondsFromTimeZero - secondsSinceTimeZero # calculate seconds until the event
            hoursLeft = secondsLeft / 3600 # get hours left
            minutesLeft = (secondsLeft % 3600) / 60 # get minutes left
            if secondsLeft <= (3600*0.5):
                if usingShading == True:
                    timeLeft = " " + secondaryShadeColor + "${color EE0000}" + str(hoursLeft).rjust(2, "0") + ":" + str(minutesLeft).rjust(2, "0") + defaultShadeColor # make timeLeft string
                else:
                    timeLeft = " ${color EE0000}" + str(hoursLeft).rjust(2, "0") + ":" + str(minutesLeft).rjust(2, "0") # make timeLeft string
                #endif
            elif secondsLeft <= (3600*1.5):
                timeLeft = " ${color FF8000}" + str(hoursLeft).rjust(2, "0") + ":" + str(minutesLeft).rjust(2, "0") # make timeLeft string
            elif secondsLeft <= (3600*2.5):
                timeLeft = " ${color EEEE00}" + str(hoursLeft).rjust(2, "0") + ":" + str(minutesLeft).rjust(2, "0") # make timeLeft string
            elif secondsLeft <= (3600*4):
                if usingShading == True:
                    timeLeft = " " + secondaryShadeColor + "${color 006400}" + str(hoursLeft).rjust(2, "0") + ":" + str(minutesLeft).rjust(2, "0") + defaultShadeColor # make timeLeft string
                else:
                    timeLeft = " ${color 006400}" + str(hoursLeft).rjust(2, "0") + ":" + str(minutesLeft).rjust(2, "0") # make timeLeft string                
                #endif
            else:    
                timeLeft = " " + str(hoursLeft).rjust(2, "0") + ":" + str(minutesLeft).rjust(2, "0") # make timeLeft string
            #endif
        #endif    
    else:
        timeLeftInToday = (60*60*24) - (60*60*currentHour) - (60*currentMinute) # calcuate seconds until the end of the current day
        secondsLeft = timeLeftInToday + (3600*24*(dayHappens -1)) + secondsFromTimeZero # calcualate seconds until event
        hoursLeft = secondsLeft / 3600 # get hours left
        minutesLeft = (secondsLeft % 3600) / 60 # get minutes left        
        if secondsLeft <= (3600*0.5):
            if usingShading == True:
                timeLeft = " " + secondaryShadeColor + "${color EE0000}" + str(hoursLeft).rjust(2, "0") + ":" + str(minutesLeft).rjust(2, "0") + defaultShadeColor # make timeLeft string
            else:
                timeLeft = " ${color EE0000}" + str(hoursLeft).rjust(2, "0") + ":" + str(minutesLeft).rjust(2, "0") # make timeLeft string
            #endif
        elif secondsLeft <= (3600*1.5):
            timeLeft = " ${color FF8000}" + str(hoursLeft).rjust(2, "0") + ":" + str(minutesLeft).rjust(2, "0") # make timeLeft string
        elif secondsLeft <= (3600*2.5):
            timeLeft = " ${color EEEE00}" + str(hoursLeft).rjust(2, "0") + ":" + str(minutesLeft).rjust(2, "0") # make timeLeft string
        elif secondsLeft <= (3600*4):
            if usingShading == True:
                timeLeft = " " + secondaryShadeColor + "${color 006400}" + str(hoursLeft).rjust(2, "0") + ":" + str(minutesLeft).rjust(2, "0") + defaultShadeColor # make timeLeft string
            else:
                timeLeft = " ${color 006400}" + str(hoursLeft).rjust(2, "0") + ":" + str(minutesLeft).rjust(2, "0") # make timeLeft string                
            #endif
        else:    
            timeLeft = " " + str(hoursLeft).rjust(2, "0") + ":" + str(minutesLeft).rjust(2, "0") # make timeLeft string
        #endif
    #endif
    return timeLeft
#enddef

##############################

class eventList:
    
    def __init__(self):
        self._listOfEvents = []
        self._daysToDisplay = daysToDisplay
        self._currentTime = time.time()
        self._currentDate = time.strftime('%Y-%m-%d' ,time.localtime(float(self._currentTime)))
        self._currentDateSplit = self._currentDate.split("-")
        self._currentWeekday = time.strftime('%a' ,time.localtime(float(self._currentTime))).upper()
        self._checkDates = [self._currentDate]
        for i in xrange(self._daysToDisplay - 1):
            self._checkDates.append(time.strftime('%Y-%m-%d' ,time.localtime(float(self._currentTime + float((i + 1)*3600*24)))))
        #endfor
        #### Appointments ####
        self._hrOffset = "${voffset -8}"
        self._dayHeaderOffset = "${voffset -5}"
        self._eventOffset = "${voffset -2}"
        #### Calendar ####
        self._tophrOffset = "${voffset -6}"
        self._monthYearAlignment = "${alignc}${voffset -6}"
        self._bodyAlignment = "${alignc}${voffset -2}"
    #enddef
    
    def appendSingleEvent(self, newEvent):
        self._listOfEvents.append(newEvent)
    #enddef
    
    def appendReccuringEvent_Weekly(self, newEvent, recurDays, startDate, endDate): # eventList.appendReccuringEvent_Weekly(formatedEvent, eventRecurDays, eventStartDate, eventRecurEndDate)
        pass # for now
    #enddef
    
    def appendReccuringEvent_Daily(self):
        pass # for now
    #enddef
    
    def sortList(self):
        self._sortedList = sorted(self._listOfEvents, key=lambda epoch: int(epoch[0]))
    #enddef
    
    def returnList(self):
        return self._listOfEvents
    #enddef
    
    def returnSortedList(self):
        return self._sortedList
    #enddef
    
    def conkyApts(self):
        previousDate = ""
        outputString = "" # set outputString to empty String
        ##print "conkyApts Test"
        for l in xrange(len(self._sortedList)):
            for i in xrange(len(self._checkDates)):
                checkDateString = self._checkDates[i]
                if (str(self._sortedList[l][1]).strip() == checkDateString):
                    ##print "date match"
                    if i == 0: # today
                        if previousDate != self._sortedList[l][1]:
                            dayName = time.strftime('%A' ,time.localtime(float(self._currentTime + float(3600*24*i)))) # get the weekday name
                            outputString = outputString + self._hrOffset + color0 + "${hr}\n" + self._dayHeaderOffset + defaultColor + "Today (" + dayName + ") " + color0 + "- " + defaultColor + "" + checkDateString #
                            previousDate = checkDateString
                        #endif
                        startTime = self._sortedList[l][2]
                        endTime = self._sortedList[l][4]
                        eventName = self._sortedList[l][5]
                        outputString = outputString + "\n" + self._eventOffset + "" + color0 + "" + to12Hour(startTime) + " " + defaultColor + "-> " + color0 + "" + to12Hour(endTime) + " |" + defaultColor + "" + timeUntil(startTime, i) + " " + color0 + "| " + defaultColor + "" + eventName.replace("$","$$") #
                    elif i == 1: # tommorow
                        if previousDate != self._sortedList[l][1]:
                            dayName = time.strftime('%A' ,time.localtime(float(self._currentTime + float(3600*24*i)))) # get the weekday name
                            outputString = outputString + "\n" + self._hrOffset + color0 + "${hr}\n" + self._dayHeaderOffset + defaultColor + "Tommorow (" + dayName + ") " + color0 + "- " + defaultColor + "" + checkDateString#
                            previousDate = checkDateString
                        #endif
                        startTime = self._sortedList[l][2]
                        endTime = self._sortedList[l][4]
                        eventName = self._sortedList[l][5]
                        outputString = outputString + "\n" + self._eventOffset + "" + color0 + "" + to12Hour(startTime) + " " + defaultColor + "-> " + color0 + "" + to12Hour(endTime) + " |" + defaultColor + "" + timeUntil(startTime, i) + " " + color0 + "| " + defaultColor + "" + eventName.replace("$","$$") #
                    else:
                        if previousDate != self._sortedList[l][1]:
                            dayName = time.strftime('%A' ,time.localtime(float(self._currentTime + float(3600*24*i)))) # get the weekday name
                            outputString = outputString + "\n" +self._hrOffset + color0 + "${hr}\n" + self._dayHeaderOffset + defaultColor + "" + dayName + " " + color0 + "- " + defaultColor + "" + checkDateString #
                            previousDate = checkDateString
                        #endif
                        startTime = self._sortedList[l][2]
                        endTime = self._sortedList[l][4]
                        eventName = self._sortedList[l][5]
                        outputString = outputString + "\n" + self._eventOffset + "" + color0 + "" + to12Hour(startTime) + " " + defaultColor + "-> " + color0 + "" + to12Hour(endTime) + " |" + defaultColor + "" + timeUntil(startTime, i) + " " + color0 + "| " + defaultColor + "" + eventName.replace("$","$$") #
                    #endif    
                #endif    
            #endfor
        #endfor
        if outputString != "":
            return outputString.strip()
        else:
            return self._hrOffset + color0 + "${hr}\n" + self._dayHeaderOffset + defaultColor + "No Events"
        #endif
    #enddef
    
    def conkyCalendar(self):
        (stdout, stderr) = Popen(["cal"], stdout=PIPE, stderr=PIPE).communicate()
        if stderr != "":
            print "cal error = " + stderr
        #endif
        rawcalendar = stdout.strip().split("\n")
        lastDateMonth = rawcalendar[len(rawcalendar) - 1].strip().split(" ")
        #print lastDateMonth
        lastDateMonth = lastDateMonth[len(lastDateMonth) - 1].replace("_\x08","")
        """
        currentYear = self._currentDateSplit[0]
        currentMonth = self._currentDateSplit[1]
        currentDay = self._currentDateSplit[2]
        """
        #print self._currentDateSplit[2]
        appointmentDates = []
        for l in xrange(len(self._sortedList)):
            for i in xrange(1, (int(lastDateMonth) + 1)):
                checkDateString = self._currentDateSplit[0] + "-" + self._currentDateSplit[1] + "-" + str(i).rjust(2, "0")
                if self._sortedList[l][1] == checkDateString and (str(i) not in appointmentDates):
                    appointmentDates.append(str(i))
                #endif
            #endfor
        #endfor
        #print str(appointmentDates)
        finalcalendar = self._tophrOffset + color0 + "${hr}\n"
        for m in xrange(len(rawcalendar)):
            if m == 0:
                finalcalendar = finalcalendar + self._monthYearAlignment + color0 + rawcalendar[m].strip() + "\n"
            elif m == 1:
                finalcalendar = finalcalendar + self._bodyAlignment + color0 + rawcalendar[m].strip() + "\n"
            elif len(rawcalendar[m].strip()) != 0:
                #print "len test"
                rawcalendar[m] = rawcalendar[m] + " "
                rawLength = len(rawcalendar[m])
                for k in xrange(len(appointmentDates)):
                    if appointmentDates[k] == str(self._currentDateSplit[2]):
                        if usingShading == True:
                            rawcalendar[m] = rawcalendar[m].replace("_\x08","").replace(appointmentDates[k].rjust(3," ").ljust(3," "), ("${font " + calendarFont + ":style=bold}" + secondaryShadeColor + "${color EE0000}" + appointmentDates[k].rjust(3," ").ljust(3," ") + "${font}" + defaultShadeColor + defaultColor))    # make today bold and red if it has an appointment
                        else:
                            rawcalendar[m] = rawcalendar[m].replace("_\x08","").replace(appointmentDates[k].rjust(3," ").ljust(3," "), ("${font " + calendarFont + ":style=bold}${color EE0000}" + appointmentDates[k].rjust(3," ").ljust(3," ") + "${font}" + defaultColor))    # make today bold and red if it has an appointment
                        #endif
                    else:    
                        rawcalendar[m] = rawcalendar[m].replace("_\x08","").replace(appointmentDates[k].rjust(3," ").ljust(3," "), (color0 + appointmentDates[k].rjust(3," ").ljust(3," ") + defaultColor)) # make other day a different color if they have appointments
                    #endif
                #endfor
                if str(self._currentDateSplit[2]) not in appointmentDates:
                    if usingShading == True:
                        rawcalendar[m] = rawcalendar[m].replace("_\x08","").replace(str(currentDate).rjust(3," ").ljust(3," "), ("${font " + calendarFont + ":style=bold}" + secondaryShadeColor + "${color 006400}" + str(currentDate).rjust(3," ").ljust(3," ") + "${font}" + defaultShadeColor + defaultColor)) # make today bold and green if it does not have an appointment
                    else:
                        rawcalendar[m] = rawcalendar[m].replace("_\x08","").replace(str(currentDate).rjust(3," ").ljust(3," "), ("${font " + calendarFont + ":style=bold}${color 006400}" + str(currentDate).rjust(3," ").ljust(3," ") + "${font}" + defaultColor)) # make today bold and green if it does not have an appointment
                    #endif
                #endif
                if i == 2:
                    finalcalendar = finalcalendar + self._bodyAlignment + defaultColor + (" " * (21 - rawLength)) + rawcalendar[m]  + "\n"
                else:
                    finalcalendar = finalcalendar + self._bodyAlignment + defaultColor + rawcalendar[m] + (" " * (21 - rawLength)) + "\n"
                #endif
            #endif
        #endfor
        return str(finalcalendar).strip("\n")
        #pass # for now
    #enddef
#endclass

def dateInSeconds(startDate, startTime):
    timeStruct = time.strptime((startDate + " " + startTime), "%Y-%m-%d %H:%M:%S")
    return time.mktime(timeStruct)
#enddef

def parseTest():
    rawCalendar = open(icalendarPath, "rb") # open calendar for reading
    evoCalendar = rawCalendar.read()
    rawCalendar.close()
    ####
    #print "importing"
    parsCalendar = Calendar.from_ical(evoCalendar)
    #print "walking"
    eventHolder = eventList()
    for component in parsCalendar.walk('VEVENT'):
        #print component
        #print component.canonical_order
        #print component.name
        eventSummary = component['SUMMARY']
        #print str(compSummary)
        compDTStart = component['DTSTART'].to_ical()
        #print compDTStart
        if compDTStart.find("T") != (-1):
            [eventStartDate, eventStartTime] = str(vDatetime.from_ical(compDTStart)).split(" ")
        else:
            #print "no start time"
            #print str(compDTStart)
            eventStartDate = compDTStart[0:4] + "-" + compDTStart[4:6] + "-" + compDTStart[6:8]
            eventStartTime = "00:00:00"
        #endif
        eventStartSeconds = dateInSeconds(eventStartDate, eventStartTime)
        #print str(eventStartSeconds)
        compDTEnd = component['DTEND'].to_ical()
        if compDTEnd.find("T") != (-1):
            [eventEndDate, eventEndTime] = str(vDatetime.from_ical(compDTEnd)).split(" ")
        else:
            #print "no end time"
            #print str(compDTEnd)
            eventEndDate = compDTEnd[0:4] + "-" + compDTEnd[4:6] + "-" + compDTEnd[6:8]
            eventEndTime = "24:00:00"
        #endif
        try:
            compRRule = component['RRULE']
            eventRecurFreq = compRRule['FREQ']
            if eventRecurFreq[0] == 'WEEKLY':
                eventRecurDays = compRRule['BYDAY']
                eventRecurEndDate = compRRule['UNTIL'][0]
            elif eventRecurFreq[0] == 'DAILY':
                eventRecurCount = compRRule['COUNT']
                #print str(eventRecurCount)
            #endif
            #print str(eventRecurFreq)
            
        except Exception, e:
            #print "RRule Error = " + str(e)
            compRRule = "N/A"
        #endtry
        
        if compRRule == "N/A": # Single appointments
            eventHolder.appendSingleEvent([eventStartSeconds, eventStartDate, eventStartTime, eventEndDate, eventEndTime, eventSummary, compRRule])
        elif eventRecurFreq[0] == "WEEKLY": # Weekly reccuring appointments ## RRULE:FREQ=WEEKLY;UNTIL=20111209T040000;BYDAY=MO,WE,FR
            #print str(eventRecurFreq[0])
            pass # for now
        elif eventRecurFreq[0] == 'DAILY': # Daily reccruing appointments ## RRULE;X-EVOLUTION-ENDDATE=20120511T190000Z:FREQ=DAILY;COUNT=2;WKST=SA
            pass # for now
        else:
            #print "Error Unknown Reccuring Appointment Type: " + str(eventRecurFreq)
            pass
        #endif
    #endfor
    """
    unsortedList = eventHolder.returnList()
    for i in xrange(len(unsortedList)):
    #    print str(unsortedList[i])
    #endfor
    """
    eventHolder.sortList()
    
    sortedList = eventHolder.returnSortedList()
    #print ""
    #for i in xrange(len(sortedList)):
    #    print str(sortedList[i])
    #endfor
    print eventHolder.conkyApts()
    print eventHolder.conkyCalendar()
#enddef

#########

#timeStart = time.time()
parseTest()
#print "time end = " + str(time.time() - timeStart) + "s"

Last edited by arclance (2012-05-11 15:41:36)

Offline

#12 2012-05-11 04:07:01

arclance
#! Die Hard
Registered: 2012-03-29
Posts: 987

Re: display icalendar file events in conky - v2.5.1

Update v2.5
Added support for DAILY, WEEKLY, and YEARLY recurring appointment types.
MONTHLY recurring appointments are not supported yet.

Offline

#13 2012-05-11 11:41:52

ZlyChleba
Member
Registered: 2012-05-10
Posts: 11

Re: display icalendar file events in conky - v2.5.1

I tried to run it in terminal and it is ok :

kerel@kerel:~$ python evolutionconky.py
${voffset -8}${color0}${hr}
${voffset -5}${color}Today (Friday) ${color0}- ${color}2012-05-11
${voffset -2}${color0}12:30 PM ${color}-> ${color0}01:00 PM |${color}-01:04 ${color0}| ${color}něco
${voffset -6}${color0}${hr}
${alignc}${voffset -6}${color0}Květen 2012
${alignc}${voffset -2}${color0}Po Út St Čt Pá So Ne
${alignc}${voffset -2}${color}    1  2  3  4  5  6 
${alignc}${voffset -2}${color} 7  8  9 10${font DejaVu Sans Mono:size=10:style=bold}${color EE0000} 11${font}${color} 12 13 
${alignc}${voffset -2}${color}14 15 16 17 18 19 20 
${alignc}${voffset -2}${color}21 22 23 24 25 26 27 
${alignc}${voffset -2}${color}28 29 30 31  

But it is not appearing on my desktop when I launch conky...
Here is my conky file:

background no
use_xft yes
xftfont Bitstream Vera Sans Mono:size=9
xftalpha 0.8
update_interval 1.0
total_run_times 0
own_window no
double_buffer yes
minimum_size 400 0
maximum_width 800
draw_shades yes
draw_outline no
draw_borders no
draw_graph_borders yes
stippled_borders 8
border_margin 4
border_width 1
default_color white
default_shade_color black
default_outline_color white
own_window        yes
own_window_transparent    yes
own_window_type        override
own_window_hints    undecorated,below,sticky,skip_taskbar,skip_pager
alignment top_right
gap_x 10
gap_y 35
no_buffers yes
uppercase no
cpu_avg_samples 2
net_avg_samples 2
override_utf8_locale yes
use_spacer right
color1 white
color2 A7D33A
text_buffer_size 2048

TEXT



${offset 175}${voffset 10}${color2}${font Ubuntu-B:size=35}${time %a %d %b}${font}
${offset 175}${color2}${font openlogos:size=45}u${font}${color1}${offset 10}${voffset -45}${font Ubuntu-B:size=35}${time %H:%M}${font}

${offset 175}${color2}${font Ubuntu-B:size=35}Rhythmbox${font}
${offset 175}${voffset 20} ${color1}${exec conkyRhythmbox --datatype=TI}
${offset 175} ${color1}${exec conkyRhythmbox --datatype=AR}
${offset 175} ${color1}${exec conkyRhythmbox --datatype=PT}/${exec conkyRhythmbox --datatype=LE}

${offset 175}${color2}${font Ubuntu-B:size=35}Co dělat${font}
${offset 185}${voffset 20}${color1}${head /home/kerel/codelat.txt 30 20}

${execp  python  ~/evolutionconky.py}

I tried to copy output from terminal of script.py to conkyrc and it worked... So the problem isn't in ${color0} and ${color}. It is displaying fine... I think it is in that last line when its calling the script ${execp  python  ~/evolutionconky.py} . But in scripts before it worked fine, i don't see the problem.

Last edited by ZlyChleba (2012-05-11 11:48:38)

Offline

#14 2012-05-11 14:36:24

arclance
#! Die Hard
Registered: 2012-03-29
Posts: 987

Re: display icalendar file events in conky - v2.5.1

ZlyChleba wrote:

I tried to run it in terminal and it is ok :

kerel@kerel:~$ python evolutionconky.py
${voffset -8}${color0}${hr}
${voffset -5}${color}Today (Friday) ${color0}- ${color}2012-05-11
${voffset -2}${color0}12:30 PM ${color}-> ${color0}01:00 PM |${color}-01:04 ${color0}| ${color}něco
${voffset -6}${color0}${hr}
${alignc}${voffset -6}${color0}Květen 2012
${alignc}${voffset -2}${color0}Po Út St Čt Pá So Ne
${alignc}${voffset -2}${color}    1  2  3  4  5  6 
${alignc}${voffset -2}${color} 7  8  9 10${font DejaVu Sans Mono:size=10:style=bold}${color EE0000} 11${font}${color} 12 13 
${alignc}${voffset -2}${color}14 15 16 17 18 19 20 
${alignc}${voffset -2}${color}21 22 23 24 25 26 27 
${alignc}${voffset -2}${color}28 29 30 31  

But it is not appearing on my desktop when I launch conky...
Here is my conky file:

...

I tried to copy output from terminal of script.py to conkyrc and it worked... So the problem isn't in ${color0} and ${color}. It is displaying fine... I think it is in that last line when its calling the script ${execp  python  ~/evolutionconky.py} . But in scripts before it worked fine, i don't see the problem.

Good it is at least parsing your calendar correctly now.

I am not sure why it is not working in conky but there are a few things you can try.
First make your text_buffer_size larger just to be safe. 
Something in the range of 5000 - 10000 should cover just about any script.
You don't have a color0 defined that should not matter, conky will use a default value but execp may not work without one defined.
To be safe add a color0 to your .conkyrc or edit the script to use color1 instead of color0 (color0 = "${color1}").
The last thing is to use the full path to the script instead of ~/evolutionconky.py to remove another variable that could cause the problem.
Looking at the rest of your .conkyrc it would be ${execp  python  /home/kerel/evolutionconky.py}.
Last a suggestion, you can use ${execpi 30 python  /home/kerel/evolutionconky.py} to reduce cpu use since you are not making changes to your calendar every second.

Kill and restart your conky after you make these changes to be sure they are applied correctly.

Offline

#15 2012-05-11 14:59:12

ZlyChleba
Member
Registered: 2012-05-10
Posts: 11

Re: display icalendar file events in conky - v2.5.1

Still nothing... Could it be that I have Python2.6 and your script is in Python2.7 ?

Offline

#16 2012-05-11 15:12:22

arclance
#! Die Hard
Registered: 2012-03-29
Posts: 987

Re: display icalendar file events in conky - v2.5.1

ZlyChleba wrote:

Still nothing... Could it be that I have Python2.6 and your script is in Python2.7 ?

No if it works in a termial that would not be the case.
Maybe conky is not properly resizing the window for the lines produced by ${execp} objects.
Try increaseing the minimum window height (the second number in minimum_size) to something like 1000 to test this.

Offline

#17 2012-05-11 15:24:45

ZlyChleba
Member
Registered: 2012-05-10
Posts: 11

Re: display icalendar file events in conky - v2.5.1

Still nothing... And what about if something happened with icalendar installation.. could it have some effect to this ? In terminal it worked properly, so i think that no.

Offline

#18 2012-05-11 15:34:30

arclance
#! Die Hard
Registered: 2012-03-29
Posts: 987

Re: display icalendar file events in conky - v2.5.1

ZlyChleba wrote:

Still nothing... And what about if something happened with icalendar installation.. could it have some effect to this ? In terminal it worked properly, so i think that no.

No it is definately something to do with conky.
What version of conky are you using?
Post the output of conky -v to check that.
${execp} is broken in v1.8.1 but it should still produce some output.

Last edited by arclance (2012-05-11 15:36:01)

Offline

#19 2012-05-11 15:57:33

ZlyChleba
Member
Registered: 2012-05-10
Posts: 11

Re: display icalendar file events in conky - v2.5.1

Conky 1.8.0 compiled Fri Apr 23 10:38:37 UTC 2010 for Linux 2.6.24-27-server (i686)

Compiled in features:

System config file: /etc/conky/conky.conf
Package library path: /usr/lib/conky

 X11:
  * Xdamage extension
  * XDBE (double buffer extension)
  * Xft
  * ARGB visual

 Music detection:
  * MPD
  * MOC

 General:
  * math
  * hddtemp
  * portmon
  * Curl
  * RSS
  * Weather (METAR)
  * Weather (XOAP)
  * wireless
  * support for IBM/Lenovo notebooks
  * nvidia
  * eve-online
  * config-output
  * Imlib2
  * ALSA mixer support
  * apcupsd
  * iostats
  * ncurses
  * Lua

  Lua bindings:
   * Cairo
   * Imlib2

Offline

#20 2012-05-11 16:20:12

arclance
#! Die Hard
Registered: 2012-03-29
Posts: 987

Re: display icalendar file events in conky - v2.5.1

conky v1.8.0 should work fine, I used the old version of the script with it and the output is identical.
What OS and version are you using?
Are there any error messages in the terminal where you started the conky?
Try running the script with ${exec} to see if conky produces any output when it is not trying to parse the scripts output.
You can try changing line 38

calendarFont = "DejaVu Sans Mono:size=10"

to

calendarFont = "Bitstream Vera Sans Mono:size=9"

in the script so the calendar is using the same font as the rest of your conky when it bolds the current day on the calendar.
That might cause a problem and it will screw up the alignement of the calendar if you don't.
Try adding

max_specials 1000

to your .conkyrc above TEXT.
This will increase the limit of the number of conky objects (things like ${color}) that are allowed.
I don't think this is the problem as conky should crash if you use more specials than the max_specials allowed but we have not tried it yet.

Last edited by arclance (2012-05-11 16:59:31)

Offline

#21 2012-05-11 16:59:04

ZlyChleba
Member
Registered: 2012-05-10
Posts: 11

Re: display icalendar file events in conky - v2.5.1

I am using Ubuntu 10.04. When I start conky i get:

Conky: /home/kerel/.conkyrc: 16: no such configuration: 'border_margin'
Conky: desktop window (1a000a7) is subwindow of root window (99)
Conky: window type - override
Conky: drawing to created window (0x3c00001)
Conky: drawing to double buffer
Traceback (most recent call last):
  File "/home/kerel/evolutionconky.py", line 379, in <module>
    parseTest()
  File "/home/kerel/evolutionconky.py", line 372, in parseTest
    print eventHolder.conkyApts()
UnicodeEncodeError: 'ascii' codec can't encode character u'\u011b' in position 190: ordinal not in range(128)

Which is weird, that don't happen before.

Offline

#22 2012-05-11 17:11:53

arclance
#! Die Hard
Registered: 2012-03-29
Posts: 987

Re: display icalendar file events in conky - v2.5.1

ZlyChleba wrote:

I am using Ubuntu 10.04. When I start conky i get:

Conky: /home/kerel/.conkyrc: 16: no such configuration: 'border_margin'
Conky: desktop window (1a000a7) is subwindow of root window (99)
Conky: window type - override
Conky: drawing to created window (0x3c00001)
Conky: drawing to double buffer
Traceback (most recent call last):
  File "/home/kerel/evolutionconky.py", line 379, in <module>
    parseTest()
  File "/home/kerel/evolutionconky.py", line 372, in parseTest
    print eventHolder.conkyApts()
UnicodeEncodeError: 'ascii' codec can't encode character u'\u011b' in position 190: ordinal not in range(128)

Which is weird, that don't happen before.

Okay now I know what is going on!
Python by default tries to decode strings assuming they are encoded in ascii.
The accented characters you are getting are not in the ascii encoding, it is most likely utf-8.
I have run into this problem with other scripts so I know how to fix it.
Try changing lines 529-530 from

    print eventHolder.conkyApts()
    print eventHolder.conkyCalendar()

to

    print eventHolder.conkyApts().encode('utf-8')
    print eventHolder.conkyCalendar().encode('utf-8')

If that does not work could you PM me your calendar file so I can use it to fix the script to support non-ascii encodings?

Since the script works in a terminal for you this is most likely caused by

override_utf8_locale yes

in the your .conkyrc but you most likely don't want to change that setting.

Last edited by arclance (2012-05-11 17:26:41)

Offline

#23 2012-05-11 17:33:15

ZlyChleba
Member
Registered: 2012-05-10
Posts: 11

Re: display icalendar file events in conky - v2.5.1

Finally working!!! smile thank you for your long support, means lot for me smile

Offline

#24 2012-05-11 17:56:50

arclance
#! Die Hard
Registered: 2012-03-29
Posts: 987

Re: display icalendar file events in conky - v2.5.1

Update v2.5.1 - 2012-05-11
    fix for displaying non-ascii characters in conky

The script crashed when trying to ouput non-ascii characters when run from conky but not when run from a terminal.
The script now encodes it's output in utf-8 to fix this.

ZlyChleba wrote:

Finally working!!! smile thank you for your long support, means lot for me smile

Good that is an annoying error to fix sometimes I'm glad that is all it took.
Could you post a screenshot now that it is working?

Offline

Help fund CrunchBang, donate to the project!

#25 2012-05-11 19:04:51

ZlyChleba
Member
Registered: 2012-05-10
Posts: 11

Re: display icalendar file events in conky - v2.5.1

My desktop with conky + your excellent script smile
evolutionconky.jpg

Offline

Board footer

Powered by FluxBB

Copyright © 2012 CrunchBang Linux.
Proudly powered by Debian. Hosted by Linode.
Debian is a registered trademark of Software in the Public Interest, Inc.
Server: acrobat

Debian Logo