Kata: Conference tickets
Few weeks ago, with one of the other organizer of the Software Crafters Lyon, we had to setup the ticket office for Lyon Craft our conference in Lyon, next May.
It was a nightmare, the software we have is really rigid and we spent one hour to configure a two-days event.
Let's turn it into a code kata!
Rule 0: an event can have a maximum of attendees and a purchase can have multiple tickets.
We will focus on, given a list of attendees and tickets, tell whether there are enough tickets.
Purchases will not be handled.
We can start by only checking the number of attendees as follows:
-- spec :: Spec
-- spec =
-- describe "Conference tickets" $ do
-- it "with no maximum attendees should have enough tickets" $
-- hasEnoughTickets (Event Nothing) (replicate 5 Attendee) `shouldBe` True
-- it "with maximum attendees should have enough tickets" $
-- hasEnoughTickets (Event (Just 4)) (replicate 3 Attendee) `shouldBe` True
-- it "with maximum attendees should not have enough tickets" $
-- hasEnoughTickets (Event (Just 4)) (replicate 5 Attendee) `shouldBe` False
newtype Event = Event
{ makeAttendees :: Maybe Int
}
data Attendee = Attendee
hasEnoughTickets Event {..} attendees =
case makeAttendees of
Nothing -> True
Just m -> length attendees <= m
Rule 1: Events can span over days, each attendee can attend to at least one day and a most one each day, each day can have a limit.
We can can come up we an naive implementation adding days information as follows:
-- spec :: Spec
-- spec =
-- describe "Conference tickets" $ do
-- describe "Total limit" $ do
-- let oneDayNoLimit = Map.singleton 1 Nothing
-- attendee = Attendee (Set.singleton 1)
-- it "with no maximum attendees should have enough tickets" $
-- hasEnoughTickets (Event Nothing oneDayNoLimit) (replicate 5 attendee) `shouldBe` True
-- it "with maximum attendees should have enough tickets" $
-- hasEnoughTickets (Event (Just 4) oneDayNoLimit) (replicate 3 attendee) `shouldBe` True
-- it "with maximum attendees should not have enough tickets" $
-- hasEnoughTickets (Event (Just 4) oneDayNoLimit) (replicate 5 attendee) `shouldBe` False
-- describe "Daily limit" $ do
-- let oneDayWithLimit = Map.singleton 1 (Just 5)
-- attendee = Attendee (Set.singleton 1)
-- it "with daily maximum attendees should have enough tickets" $
-- hasEnoughTickets (Event Nothing oneDayWithLimit) (replicate 3 attendee) `shouldBe` True
-- it "with daily maximum attendees should not have enough tickets" $
-- hasEnoughTickets (Event Nothing oneDayWithLimit) (replicate 6 attendee) `shouldBe` False
data Event = Event
{ makeAttendees :: Maybe Int,
}
newtype Attendee = Attendee {bookedDays :: Set.Set Int}
hasEnoughTickets Event {..} attendees =
checkMaxAttendees
&& checkNumberOfDays
where
checkMaxAttendees =
case makeAttendees of
Nothing -> True
Just m -> length attendees <= m
checkNumberOfDays =
all (\(day, limit) -> maybe True (\limit' -> countDays day <= limit') limit) $ Map.toList days
countDays day =
length $ filter (Set.member day . (.bookedDays)) attendees
And that it, that's disappointing, and that took us one hour.
In the actual system, there are extra rules such as:
- Tickets have price
- There could be two tickets, with different prices, for the same day
- There could be multiple days-tickets
We will handle them next weeks.