* Started to add some of the framework for iCal integration (not working yet)

* Added validation for next actions (on description and notes field)



git-svn-id: http://www.rousette.org.uk/svn/tracks-repos/trunk@7 a4c988fc-2ded-0310-b66e-134b36920a42
This commit is contained in:
bsag 2005-01-16 15:24:34 +00:00
parent 2b65b3162b
commit 49b8fa1007
6 changed files with 326 additions and 7 deletions

View file

@ -3,6 +3,7 @@
require_dependency "login_system" require_dependency "login_system"
require_dependency "redcloth" require_dependency "redcloth"
require_dependency "iCal"
require 'date' require 'date'
$delete_img = "<img src=\"/images/delete.png\" width=\"10\" height=\"10\" />" $delete_img = "<img src=\"/images/delete.png\" width=\"10\" height=\"10\" />"

View file

@ -32,24 +32,34 @@ class TodoController < ApplicationController
# Parameters from form fields should be passed to create new item # Parameters from form fields should be passed to create new item
# #
def add_item def add_item
item = Todo.new @item = Todo.new
item.attributes = @params["new_item"] @item.attributes = @params["new_item"]
# Convert the date format entered (as set in config/settings.yml) # Convert the date format entered (as set in config/settings.yml)
# to the mysql format YYYY-MM-DD # to the mysql format YYYY-MM-DD
if @params["new_item"]["due"] != "" if @params["new_item"]["due"] != ""
date_fmt = app_configurations["formats"]["date"] date_fmt = app_configurations["formats"]["date"]
formatted_date = DateTime.strptime(@params["new_item"]["due"], "#{date_fmt}") formatted_date = DateTime.strptime(@params["new_item"]["due"], "#{date_fmt}")
item.due = formatted_date.strftime("%Y-%m-%d") @item.due = formatted_date.strftime("%Y-%m-%d")
else else
item.due = "0000-00-00" @item.due = "0000-00-00"
end end
if item.save # This doesn't seem to be working. No error, but the error
# message isn't printed either.
unless @item.errors.empty?
error_msg = ''
@item.errors.each_full do |message|
error_msg << message
end
return error_msg
end
if @item.save
flash["confirmation"] = "Next action was successfully added" flash["confirmation"] = "Next action was successfully added"
redirect_to( :action => "list" ) redirect_to( :action => "list" )
else else
flash["warning"] = "Couldn't add the action" flash["warning"] = "Couldn't add the action because of an error: #{error_msg}"
redirect_to( :action => "list" ) redirect_to( :action => "list" )
end end
end end

View file

@ -3,6 +3,14 @@ class Todo < ActiveRecord::Base
belongs_to :context belongs_to :context
belongs_to :project belongs_to :project
# Description field can't be empty, and must be < 100 bytes
# Notes must be < 60,000 bytes (65,000 actually, but I'm being cautious)
validates_presence_of :description, :message => "no description provided"
validates_length_of :description, :maximum => 100, :message => "description is too long"
validates_lenght_of :notes, :maximum => 60000, :message => "notes are too long"
#validates_format_of :due, :with => /^[\d]{2,2}\/[\d]{2,2}\/[\d]{4,4}$/, :message => "date format incorrect"
def before_save def before_save
# Add a creation date (Ruby object format) to item before it's saved # Add a creation date (Ruby object format) to item before it's saved
# if there is no existing creation date (this prevents creation date # if there is no existing creation date (this prevents creation date
@ -13,4 +21,5 @@ class Todo < ActiveRecord::Base
end end
end end
end end

View file

@ -55,6 +55,15 @@
</div><!- End of display_box --> </div><!- End of display_box -->
<div id="input_box"> <div id="input_box">
<h2>Today's events are: </h2>
<ul>
<% ical = ICal::ICalReader.new("Personal") %>
<% ical.todaysEvents do |event| %>
<%= puts "<li>#{event.startTime} - #{event.summary}</li>" %>
<% end %>
</ul>
<h2>Add next action</h2> <h2>Add next action</h2>
<form method="post" action="add_item"> <form method="post" action="add_item">
<label for="new_item_description">Next action</label><br /> <label for="new_item_description">Next action</label><br />
@ -83,6 +92,7 @@
<br /> <br />
<input type="submit" value="Add item"> <input type="submit" value="Add item">
</form> </form>
</div> </div>
<% if @flash["confirmation"] %><div class="confirmation"><%= @flash["confirmation"] %></div><% end %> <% if @flash["confirmation"] %><div class="confirmation"><%= @flash["confirmation"] %></div><% end %>

289
tracks/lib/iCal.rb Normal file
View file

@ -0,0 +1,289 @@
require 'date'
require 'ParseDate'
module ICal
SECONDS_PER_DAY = 24 * 60 * 60
class ICalReader
@@iCalFolder = "/Users/jchappell/Library/Calendars"
def initialize(calendarName = nil)
@events = []
@iCalFiles = Dir[@@iCalFolder + "/*.ics"]
@iCalFiles.sort! {|x, y| x.downcase <=> y.downcase}
if calendarName then
fullName = @@iCalFolder + "/" + calendarName + ".ics"
@iCalFiles = @iCalFiles.select {|name| name == fullName}
end
end
def calendars
@iCalFiles.collect {|name| File.basename(name, ".ics")}
end
def readEvents
@events = []
@iCalFiles.each do |fileName|
lines = File.readlines(fileName);
inEvent = false
eventLines = []
lines.each do |line|
if line =~ /^BEGIN:VEVENT/ then
inEvent = true
eventLines = []
end
if inEvent
eventLines << line
if line =~ /^END:VEVENT/ then
inEvent = false
@events << parseEvent(eventLines)
end
end
end
end
@events
end
def parseEvent(lines)
event = ICalEvent.new()
startDate = nil
rule = nil
lines.each do |line|
if line =~ /^SUMMARY:(.*)/ then
event.summary = $1
elsif line =~ /^DTSTART;.*:(.*).*/ then
startDate = parseDate($1)
elsif line =~ /^EXDATE.*:(.*)/ then
event.addExceptionDate(parseDate($1))
elsif line =~ /^RRULE:(.*)/ then
rule = $1
end
end
event.startDate = startDate
event.addRecurrenceRule(rule)
event
end
def parseDate(dateStr)
# We constrain the year to 1970 because Time won't handle lesser years
# If it is less than 1970 then its probably a birthday or something
# in which case, we don't really care about the year
year = dateStr[0,4].to_i
year = 1970 if year < 1970
month = dateStr[4,2].to_i
day = dateStr[6,2].to_i
hour = dateStr[9,2].to_i
minute = dateStr[11,2].to_i
Time.local(year, month, day, hour, minute)
end
def events
readEvents if @events == []
@events
end
def selectEvents(&predicate)
#
# The start date of each event could be different due to recurring events
# Since we can assume the date is the same for all events, we just compare
# the times when sorting
#
now = Time.now
events.select(&predicate).sort do |event1, event2|
time1 = Time.local(now.year, now.month, now.day, event1.startDate.hour,
event1.startDate.min)
time2 = Time.local(now.year, now.month, now.day, event2.startDate.hour,
event2.startDate.min)
time1 <=> time2
end
end
def todaysEvents
#events.select {|event| event.startsToday? }
selectEvents {|event| event.startsToday? }
end
def tomorrowsEvents
#events.select {|event| event.startsTomorrow? }
selectEvents {|event| event.startsTomorrow?}
end
def eventsFor(date)
#events.select {|event| event.startsOn?(date)}
selectEvents {|event| event.startsOn?(date)}
end
end
class DateParser
# Given a date as a string, returns a Time object
def DateParser.parse(dateStr)
dateValues = ParseDate::parsedate(dateStr)
Time.local(*dateValues[0, 3])
end
def DateParser.format(date)
date.strftime("%m/%d/%Y")
end
end
class ICalEvent
def initialize
@exceptionDates = []
end
def <=>(otherEvent)
return @startDate <=> otherEvent.startDate
end
def addExceptionDate(date)
@exceptionDates << date
end
def addRecurrenceRule(rule)
@dateSet = DateSet.new(@startDate, rule)
end
def startsToday?
startsOn?(Time.now)
end
def startsTomorrow?
tomorrow = Time.now + SECONDS_PER_DAY;
startsOn?(tomorrow)
end
def startsOn?(date)
(startDate.year == date.year and startDate.month == date.month and
startDate.day == date.day) or @dateSet.includes?(date)
end
def to_s
"#{@startDate.strftime("%m/%d/%Y (%I:%M %p)")} - #{@summary}"
end
def startTime
@startDate
end
attr_accessor :startDate, :summary
end
class DateSet
def initialize(startDate, rule)
@startDate = startDate
@frequency = nil
@count = nil
@untilDate = nil
@byMonth = nil
@byDay = nil
parseRecurrenceRule(rule)
end
def parseRecurrenceRule(rule)
if rule =~ /FREQ=(.*?);/ then
@frequency = $1
end
if rule =~ /COUNT=(\d*)/ then
@count = $1.to_i
end
if rule =~ /UNTIL=(.*?);/ then
@untilDate = DateParser.parse($1)
#puts @untilDate
end
if rule =~ /INTERVAL=(\d*)/ then
@interval = $1.to_i
end
if rule =~ /BYMONTH=(.*?);/ then
@byMonth = $1
end
if rule =~ /BYDAY=(.*?);/ then
@byDay = $1
#puts "byDay = #{@byDay}"
end
end
def to_s
puts "#<DateSet: starts: #{@startDate.strftime("%m/%d/%Y")}, occurs: #{@frequency}, count: #{@count}, until: #{@until}, byMonth: #{@byMonth}, byDay: #{@byDay}>"
end
def includes?(date)
return true if date == @startDate
return false if @untilDate and date > @untilDate
case @frequency
when 'DAILY'
#if @untilDate then
# return (@startDate..@untilDate).include?(date)
#end
increment = @interval ? @interval : 1
d = @startDate
counter = 0
until d > date
if @count then
counter += 1
if counter >= @count
return false
end
end
d += (increment * SECONDS_PER_DAY)
if d.day == date.day and
d.year == date.year and
d.month == date.month then
return true
end
end
when 'WEEKLY'
return true if @startDate.wday == date.wday
when 'MONTHLY'
when 'YEARLY'
end
false
end
attr_reader :frequency
attr_accessor :startDate
end
if $0 == __FILE__ then
#reader = ICalReader.new("Test")
reader = ICalReader.new
puts
puts "Today"
puts "====="
puts reader.todaysEvents
puts
puts "Tomorrow"
puts "========"
puts reader.tomorrowsEvents
puts
puts "08/14/2003 Events"
puts "================="
puts reader.eventsFor(Time.local(2003, 8, 14))
puts
end
end

View file

@ -185,7 +185,7 @@ h2 a:hover {
border: 1px solid #ED2E38; border: 1px solid #ED2E38;
background-color: #F6979C; background-color: #F6979C;
padding: 5px; padding: 5px;
color: #007E00; color: #ED2E38;
text-align: center; text-align: center;
} }