Grant and Brian have agreed to do a little code challenge experiment for us at the February meeting. This is for the fun and education of us all. Thanks guys!
Below are the details of their challenge for them to see and for those of us who wish to follow along.
Tim is going to give a talk for the other half of the meeting, showing off a few of his favorite libraries.
As you can see, it's shaping up to be a great meeting!
James Edward Gray II
Calendar Code Challenge =======================
The contestants will complete a simple calendar application with just two abilities: users should be able to enter events and see what events will occur on a given day. The primary challenge comes from the fact that the code is expected to handle multiple types of events.
Your Code ---------
The code you provide really only has to do two things: accept new event entries and provide a way to list all events occurring on a given day. No judging will be done on the interface, so you are free to provide a nice interface for these two action or just build methods to call in IRb or a testing environment. Whatever is easiest for you is fine.
No judging will be done on tests, documentation, or any other ancillary assets to the code itself. However, you will be judged on how easy it is for others to understand your code as outlined below and these items may help with that.
You also do not need to worry about the data stressing your code. You're program doesn't need to handle so much data that it can't all be stored in memory and event fields will be consistently sane. No dates will be used that are outside a two year range centered on today, so you needn't worry about calendar changes or the resolution of Ruby's built in Time class. You just need to build the core functionality of the problem.
Event Types -----------
The main challenge is that your calendar is expected to support multiple types of events. Users need to be able to create any or all of the types listed below and searches must check all known events returning all events that will fall on a given day, regardless of type.
Each event type can have different fields and the types share some fields. Some fields are optional and some have defaults you need to account for.
All events have a unique name (a String) and a start date and time (a Time) that are always provided. All events can also have an end date and time (also a Time), but this field can also be left nil to indicate that an event is ongoing and does not end.
You can mostly ignore the time portion of all dates and times for the purposes of this exercise. We're only interested in which events appear on a given day and the times don't play into that.
Beyond that, the event types differ as follows:
* Daily events also have no additional fields. They recur each day between their start date and end date. * Weekly events have a frequency field (an Integer) that defaults to one and a list of days (an Array containing one or more Strings from the set: "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", and "Saturday"). These events recur each week between their start date and end date on the days indicated. If frequency is anything but one, they only appear if the week in question is divisible by the frequency when counted as an offset from the start date. For example, a frequency of two means an event occurs every other week and thus would appear within the first seven days of the start date, skip the next seven, appear in the next seven, etc. * Monthly events can be set two ways. The first type of monthly event has a frequency field (same as the weekly version, but in the scope of months), an "on the" field (a String from the set: "first", "second", "third", and "last"), and a day field (a String from the same set mentioned in the weekly events days field). Again, this date recurs monthly bounded by the start and end date and as dictated by the frequency. The difference here is that the "on the" and day fields indicate which day of the month the event happens on. * If the "on the" and day fields are omitted, you have the second type of monthly event. This event just repeats on the same day of the month as the start date (if present in the current month). These too can have a frequency to skip months. * Finally, there are yearly events that just recur on the same day of the year as their start date. These events have no additional fields.
The Judging -----------
The spirit of this contest is totally for fun and there will be no official winner or loser. However, we do have some criteria upon which entries will be considered, just for friendly bragging rights!
The contestants are expected to make their solutions publicly available by posting them to the mailing list two days before the February meeting. That means solutions should be posted on February 10th by 6:30 PM.
At the meeting, each of you will explain the other contestant's code. You are expected to highlight how it works and point out things you like about the solution. You will be judged on how well you understand the code you are showing and on how easy your code was to understand.
If you notice any problems with the code, like edge case searches it wouldn't get right, feel free to point those out as well. This shows an even greater understanding of the code on your part.
> Grant and Brian have agreed to do a little code challenge experiment
> for us at the February meeting. This is for the fun and education of
> us all. Thanks guys!
> Below are the details of their challenge for them to see and for those
> of us who wish to follow along.
> Tim is going to give a talk for the other half of the meeting, showing
> off a few of his favorite libraries.
> As you can see, it's shaping up to be a great meeting!
> The contestants will complete a simple calendar application with just
> two abilities: users should be able to enter events and see what
> events will occur on a given day. The primary challenge comes from
> the fact that the code is expected to handle multiple types of events.
> Your Code
> ---------
> The code you provide really only has to do two things: accept new
> event entries and provide a way to list all events occurring on a
> given day. No judging will be done on the interface, so you are free
> to provide a nice interface for these two action or just build methods
> to call in IRb or a testing environment. Whatever is easiest for you
> is fine.
> No judging will be done on tests, documentation, or any other
> ancillary assets to the code itself. However, you will be judged on
> how easy it is for others to understand your code as outlined below
> and these items may help with that.
> You also do not need to worry about the data stressing your code.
> You're program doesn't need to handle so much data that it can't all
> be stored in memory and event fields will be consistently sane. No
> dates will be used that are outside a two year range centered on
> today, so you needn't worry about calendar changes or the resolution
> of Ruby's built in Time class. You just need to build the core
> functionality of the problem.
> Event Types
> -----------
> The main challenge is that your calendar is expected to support
> multiple types of events. Users need to be able to create any or all
> of the types listed below and searches must check all known events
> returning all events that will fall on a given day, regardless of type.
> Each event type can have different fields and the types share some
> fields. Some fields are optional and some have defaults you need to
> account for.
> All events have a unique name (a String) and a start date and time (a
> Time) that are always provided. All events can also have an end date
> and time (also a Time), but this field can also be left nil to
> indicate that an event is ongoing and does not end.
> You can mostly ignore the time portion of all dates and times for the
> purposes of this exercise. We're only interested in which events
> appear on a given day and the times don't play into that.
> Beyond that, the event types differ as follows:
> * Daily events also have no additional fields. They recur each day
> between their start date and end date.
> * Weekly events have a frequency field (an Integer) that defaults to
> one and a list of days (an Array containing one or more Strings from
> the set: "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday",
> "Friday", and "Saturday"). These events recur each week between their
> start date and end date on the days indicated. If frequency is
> anything but one, they only appear if the week in question is
> divisible by the frequency when counted as an offset from the start
> date. For example, a frequency of two means an event occurs every
> other week and thus would appear within the first seven days of the
> start date, skip the next seven, appear in the next seven, etc.
> * Monthly events can be set two ways. The first type of monthly event
> has a frequency field (same as the weekly version, but in the scope of
> months), an "on the" field (a String from the set: "first", "second",
> "third", and "last"), and a day field (a String from the same set
> mentioned in the weekly events days field). Again, this date recurs
> monthly bounded by the start and end date and as dictated by the
> frequency. The difference here is that the "on the" and day fields
> indicate which day of the month the event happens on.
> * If the "on the" and day fields are omitted, you have the second type
> of monthly event. This event just repeats on the same day of the
> month as the start date (if present in the current month). These too
> can have a frequency to skip months.
> * Finally, there are yearly events that just recur on the same day of
> the year as their start date. These events have no additional fields.
> The Judging
> -----------
> The spirit of this contest is totally for fun and there will be no
> official winner or loser. However, we do have some criteria upon
> which entries will be considered, just for friendly bragging rights!
> The contestants are expected to make their solutions publicly
> available by posting them to the mailing list two days before the
> February meeting. That means solutions should be posted on February
> 10th by 6:30 PM.
> At the meeting, each of you will explain the other contestant's
> code. You are expected to highlight how it works and point out
> things you like about the solution. You will be judged on how well
> you understand the code you are showing and on how easy your code was
> to understand.
> If you notice any problems with the code, like edge case searches it
> wouldn't get right, feel free to point those out as well. This shows
> an even greater understanding of the code on your part.
> Grant and Brian have agreed to do a little code challenge experiment
> for us at the February meeting. This is for the fun and education of
> us all. Thanks guys!
> Below are the details of their challenge for them to see and for those
> of us who wish to follow along.
> Tim is going to give a talk for the other half of the meeting, showing
> off a few of his favorite libraries.
> As you can see, it's shaping up to be a great meeting!
> The contestants will complete a simple calendar application with just
> two abilities: users should be able to enter events and see what
> events will occur on a given day. The primary challenge comes from
> the fact that the code is expected to handle multiple types of events.
> Your Code
> ---------
> The code you provide really only has to do two things: accept new
> event entries and provide a way to list all events occurring on a
> given day. No judging will be done on the interface, so you are free
> to provide a nice interface for these two action or just build methods
> to call in IRb or a testing environment. Whatever is easiest for you
> is fine.
> No judging will be done on tests, documentation, or any other
> ancillary assets to the code itself. However, you will be judged on
> how easy it is for others to understand your code as outlined below
> and these items may help with that.
> You also do not need to worry about the data stressing your code.
> You're program doesn't need to handle so much data that it can't all
> be stored in memory and event fields will be consistently sane. No
> dates will be used that are outside a two year range centered on
> today, so you needn't worry about calendar changes or the resolution
> of Ruby's built in Time class. You just need to build the core
> functionality of the problem.
> Event Types
> -----------
> The main challenge is that your calendar is expected to support
> multiple types of events. Users need to be able to create any or all
> of the types listed below and searches must check all known events
> returning all events that will fall on a given day, regardless of
> type.
> Each event type can have different fields and the types share some
> fields. Some fields are optional and some have defaults you need to
> account for.
> All events have a unique name (a String) and a start date and time (a
> Time) that are always provided. All events can also have an end date
> and time (also a Time), but this field can also be left nil to
> indicate that an event is ongoing and does not end.
> You can mostly ignore the time portion of all dates and times for the
> purposes of this exercise. We're only interested in which events
> appear on a given day and the times don't play into that.
> Beyond that, the event types differ as follows:
> * Daily events also have no additional fields. They recur each day
> between their start date and end date.
> * Weekly events have a frequency field (an Integer) that defaults to
> one and a list of days (an Array containing one or more Strings from
> the set: "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday",
> "Friday", and "Saturday"). These events recur each week between their
> start date and end date on the days indicated. If frequency is
> anything but one, they only appear if the week in question is
> divisible by the frequency when counted as an offset from the start
> date. For example, a frequency of two means an event occurs every
> other week and thus would appear within the first seven days of the
> start date, skip the next seven, appear in the next seven, etc.
> * Monthly events can be set two ways. The first type of monthly event
> has a frequency field (same as the weekly version, but in the scope of
> months), an "on the" field (a String from the set: "first", "second",
> "third", and "last"), and a day field (a String from the same set
> mentioned in the weekly events days field). Again, this date recurs
> monthly bounded by the start and end date and as dictated by the
> frequency. The difference here is that the "on the" and day fields
> indicate which day of the month the event happens on.
> * If the "on the" and day fields are omitted, you have the second type
> of monthly event. This event just repeats on the same day of the
> month as the start date (if present in the current month). These too
> can have a frequency to skip months.
> * Finally, there are yearly events that just recur on the same day of
> the year as their start date. These events have no additional fields.
> The Judging
> -----------
> The spirit of this contest is totally for fun and there will be no
> official winner or loser. However, we do have some criteria upon
> which entries will be considered, just for friendly bragging rights!
> The contestants are expected to make their solutions publicly
> available by posting them to the mailing list two days before the
> February meeting. That means solutions should be posted on February
> 10th by 6:30 PM.
> At the meeting, each of you will explain the other contestant's
> code. You are expected to highlight how it works and point out
> things you like about the solution. You will be judged on how well
> you understand the code you are showing and on how easy your code was
> to understand.
> If you notice any problems with the code, like edge case searches it
> wouldn't get right, feel free to point those out as well. This shows
> an even greater understanding of the code on your part.
Sorry for the delay, I was saving orphans from forrest fires and
helping Barack with the economy. In my time away from that I've been
solving this problem here http://github.com/bclubb/calendar/tree/master#.
Brian
On Feb 10, 9:30 pm, Grant Schofield <gr...@surlysoft.com> wrote:
> > Grant and Brian have agreed to do a little code challenge experiment
> > for us at the February meeting. This is for the fun and education of
> > us all. Thanks guys!
> > Below are the details of their challenge for them to see and for those
> > of us who wish to follow along.
> > Tim is going to give a talk for the other half of the meeting, showing
> > off a few of his favorite libraries.
> > As you can see, it's shaping up to be a great meeting!
> > The contestants will complete a simple calendar application with just
> > two abilities: users should be able to enter events and see what
> > events will occur on a given day. The primary challenge comes from
> > the fact that the code is expected to handle multiple types of events.
> > Your Code
> > ---------
> > The code you provide really only has to do two things: accept new
> > event entries and provide a way to list all events occurring on a
> > given day. No judging will be done on the interface, so you are free
> > to provide a nice interface for these two action or just build methods
> > to call in IRb or a testing environment. Whatever is easiest for you
> > is fine.
> > No judging will be done on tests, documentation, or any other
> > ancillary assets to the code itself. However, you will be judged on
> > how easy it is for others to understand your code as outlined below
> > and these items may help with that.
> > You also do not need to worry about the data stressing your code.
> > You're program doesn't need to handle so much data that it can't all
> > be stored in memory and event fields will be consistently sane. No
> > dates will be used that are outside a two year range centered on
> > today, so you needn't worry about calendar changes or the resolution
> > of Ruby's built in Time class. You just need to build the core
> > functionality of the problem.
> > Event Types
> > -----------
> > The main challenge is that your calendar is expected to support
> > multiple types of events. Users need to be able to create any or all
> > of the types listed below and searches must check all known events
> > returning all events that will fall on a given day, regardless of
> > type.
> > Each event type can have different fields and the types share some
> > fields. Some fields are optional and some have defaults you need to
> > account for.
> > All events have a unique name (a String) and a start date and time (a
> > Time) that are always provided. All events can also have an end date
> > and time (also a Time), but this field can also be left nil to
> > indicate that an event is ongoing and does not end.
> > You can mostly ignore the time portion of all dates and times for the
> > purposes of this exercise. We're only interested in which events
> > appear on a given day and the times don't play into that.
> > Beyond that, the event types differ as follows:
> > * Daily events also have no additional fields. They recur each day
> > between their start date and end date.
> > * Weekly events have a frequency field (an Integer) that defaults to
> > one and a list of days (an Array containing one or more Strings from
> > the set: "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday",
> > "Friday", and "Saturday"). These events recur each week between their
> > start date and end date on the days indicated. If frequency is
> > anything but one, they only appear if the week in question is
> > divisible by the frequency when counted as an offset from the start
> > date. For example, a frequency of two means an event occurs every
> > other week and thus would appear within the first seven days of the
> > start date, skip the next seven, appear in the next seven, etc.
> > * Monthly events can be set two ways. The first type of monthly event
> > has a frequency field (same as the weekly version, but in the scope of
> > months), an "on the" field (a String from the set: "first", "second",
> > "third", and "last"), and a day field (a String from the same set
> > mentioned in the weekly events days field). Again, this date recurs
> > monthly bounded by the start and end date and as dictated by the
> > frequency. The difference here is that the "on the" and day fields
> > indicate which day of the month the event happens on.
> > * If the "on the" and day fields are omitted, you have the second type
> > of monthly event. This event just repeats on the same day of the
> > month as the start date (if present in the current month). These too
> > can have a frequency to skip months.
> > * Finally, there are yearly events that just recur on the same day of
> > the year as their start date. These events have no additional fields.
> > The Judging
> > -----------
> > The spirit of this contest is totally for fun and there will be no
> > official winner or loser. However, we do have some criteria upon
> > which entries will be considered, just for friendly bragging rights!
> > The contestants are expected to make their solutions publicly
> > available by posting them to the mailing list two days before the
> > February meeting. That means solutions should be posted on February
> > 10th by 6:30 PM.
> > At the meeting, each of you will explain the other contestant's
> > code. You are expected to highlight how it works and point out
> > things you like about the solution. You will be judged on how well
> > you understand the code you are showing and on how easy your code was
> > to understand.
> > If you notice any problems with the code, like edge case searches it
> > wouldn't get right, feel free to point those out as well. This shows
> > an even greater understanding of the code on your part.
Some people wanted to see my solution to the code challenge.
Here's the original MySQL solution:
if not params[:date].blank? condition[<<-END_DATE_SQL, *([params[:date]] * 14)] start_date <= ? AND (end_date IS NULL OR ? <= end_date) AND IF(repeats IS NULL OR TRIM(repeats) = '' OR repeats = 'Daily', 1, IF(repeats = 'Weekly' AND #{params[:date].strftime("%A").downcase} = 1 AND FLOOR(DATEDIFF(?, start_date) / 7) % frequency = 0, 1, IF(repeats = 'Monthly' AND (((on_the IS NULL AND DAYOFMONTH(start_date) = DAYOFMONTH(?)) OR (#{params[:date].strftime("%A").downcase} = 1 AND IF(on_the = 'first' AND DAYOFMONTH(?) <= 7, 1, IF(on_the = 'second' AND DAYOFMONTH(?) > 7 AND DAYOFMONTH(?) <= 14, 1, IF(on_the = 'third' AND DAYOFMONTH(?) > 14 AND DAYOFMONTH(?) <= 21, 1, IF(on_the = 'last' AND MONTH(?) != MONTH(DATE_ADD(?, INTERVAL 8 DAY)), 1, 0)))))) AND PERIOD_DIFF(YEAR(?) * 100 + MONTH(?), YEAR(start_date) * 100 + MONTH(start_date)) % frequency = 0), 1, IF(repeats = 'Yearly' AND DAYOFYEAR(start_date) = DAYOFYEAR(?), 1, 0)))) END_DATE_SQL if params[:date] == Date.today condition["start_time IS NULL OR start_time >= TIME(?)", Time.now] end end
I'm pretty sure that one works right.
Here's another on in Ruby that may work, but I admit that I've only tested it a tiny bit. It likely still has bugs:
#!/usr/bin/env ruby -wKU
class Event DAY_IN_SECONDS = 24 * 60 * 60
def initialize(name, details) @name = name @start_date = details[:start_date] @end_date = details[:end_date] end
attr_reader :name, :start_date, :end_date
def on?(on) start_date <= on and (end_date.nil? or on <= end_date) end end
class DailyEvent < Event
end
class WeeklyEvent < Event def initialize(*args) super @days = args.last[:days] @frequency = args.last[:frequency] || 1 end
attr_reader :days, :frequency
def on?(on) super and days.include?(on.strftime("%A")) and ((on - start_date).abs.to_i / (7 * DAY_IN_SECONDS) % frequency).zero? end end
class MonthlyEvent < Event def initialize(*args) super @frequency = args.last[:frequency] || 1 @on_the = args.last[:on_the] @day = args.last[:day] end
attr_reader :on_the, :day
def on?(on) super and ( (on_the.nil? and start_date.mday == on.mday) or (day == on.strftime("%A") and is_on?(on)) ) and (month_diff(on) % frequency).zero? end
private
def is_on?(on) case on_the.to_sym when :first on.mday <= 7 when :second on.mday.between? 8, 14 when :third on.mday.between? 15, 21 when :last on.mon != (on + 8 * DAY_IN_SECONDS).mon end end
def month_diff(on) early, late = [start_date, on].sort year_diff = (late.year - early.year).abs if year_diff.nonzero? and late.mon < early.mon (year_diff - 1) * 12 + (start_date.mon - on.mon).abs else year_diff * 12 + (start_date.mon - on.mon).abs end end end
class YearlyEvent < Event def on?(on) super and @start_date.yday == on.yday end end
class EventList def initialize @events = [ ] end
def all_on(on) @events.select { |event| event.on? on } end
def method_missing(meth, *args, &blk) if meth.to_s =~ /\Aadd_(daily|weekly|monthly|yearly)_event\z/ @events << Object.const_get("#{$1.capitalize}Event").new(*args) else super end end end
__END__
Great work guys. I think it was a good learning experience for all!