From 4a5c309b24d78b1f48851e7bf82dc5a23c010c60 Mon Sep 17 00:00:00 2001 From: Glenn Jackman Date: Thu, 6 Mar 2025 10:51:57 -0500 Subject: [PATCH] Add swift-scheduling exercise (#372) --- config.json | 8 ++ .../swift-scheduling/.docs/instructions.md | 43 +++++++++ .../swift-scheduling/.docs/introduction.md | 6 ++ .../swift-scheduling/.meta/config.json | 17 ++++ .../swift-scheduling/.meta/example.tcl | 79 ++++++++++++++++ .../swift-scheduling/.meta/tests.toml | 58 ++++++++++++ .../swift-scheduling/swift-scheduling.tcl | 3 + .../swift-scheduling/swift-scheduling.test | 89 +++++++++++++++++++ .../practice/swift-scheduling/testHelpers.tcl | 20 +++++ 9 files changed, 323 insertions(+) create mode 100644 exercises/practice/swift-scheduling/.docs/instructions.md create mode 100644 exercises/practice/swift-scheduling/.docs/introduction.md create mode 100644 exercises/practice/swift-scheduling/.meta/config.json create mode 100644 exercises/practice/swift-scheduling/.meta/example.tcl create mode 100644 exercises/practice/swift-scheduling/.meta/tests.toml create mode 100644 exercises/practice/swift-scheduling/swift-scheduling.tcl create mode 100644 exercises/practice/swift-scheduling/swift-scheduling.test create mode 100644 exercises/practice/swift-scheduling/testHelpers.tcl diff --git a/config.json b/config.json index 728b56bd..6c57ff50 100644 --- a/config.json +++ b/config.json @@ -1630,6 +1630,14 @@ "practices": [], "prerequisites": [], "difficulty": 5 + }, + { + "slug": "swift-scheduling", + "name": "Swift Scheduling", + "uuid": "e6ab1dea-ad65-499b-ab5e-140d4d76420e", + "practices": [], + "prerequisites": [], + "difficulty": 4 } ], "foregone": [ diff --git a/exercises/practice/swift-scheduling/.docs/instructions.md b/exercises/practice/swift-scheduling/.docs/instructions.md new file mode 100644 index 00000000..267f7932 --- /dev/null +++ b/exercises/practice/swift-scheduling/.docs/instructions.md @@ -0,0 +1,43 @@ +# Instructions + +Your task is to convert delivery date descriptions to _actual_ delivery dates, based on when the meeting started. + +There are two types of delivery date descriptions: + +1. Fixed: a predefined set of words. +2. Variable: words that have a variable component, but follow a predefined set of patterns. + +## Fixed delivery date descriptions + +There are three fixed delivery date descriptions: + +- `"NOW"` +- `"ASAP"` (As Soon As Possible) +- `"EOW"` (End Of Week) + +The following table shows how to translate them: + +| Description | Meeting start | Delivery date | +| ----------- | ----------------------------- | ----------------------------------- | +| `"NOW"` | - | Two hours after the meeting started | +| `"ASAP"` | Before 13:00 | Today at 17:00 | +| `"ASAP"` | After or at 13:00 | Tomorrow at 13:00 | +| `"EOW"` | Monday, Tuesday, or Wednesday | Friday at 17:00 | +| `"EOW"` | Thursday or Friday | Sunday at 20:00 | + +## Variable delivery date descriptions + +There are two variable delivery date description patterns: + +- `"M"` (N-th month) +- `"Q"` (N-th quarter) + +| Description | Meeting start | Delivery date | +| ----------- | -------------------------- | ----------------------------------------------------------- | +| `"M"` | Before N-th month | At 8:00 on the _first_ workday¹ of this year's N-th month | +| `"M"` | After or in N-th month | At 8:00 on the _first_ workday¹ of next year's N-th month | +| `"Q"` | Before or in N-th quarter² | At 8:00 on the _last_ workday¹ of this year's N-th quarter² | +| `"Q"` | After N-th quarter² | At 8:00 on the _last_ workday¹ of next year's N-th quarter² | + +¹ A workday is a Monday, Tuesday, Wednesday, Thursday, or Friday. +² A year has four quarters, each with three months: January/February/March, April/May/June, July/August/September, and October/November/December. diff --git a/exercises/practice/swift-scheduling/.docs/introduction.md b/exercises/practice/swift-scheduling/.docs/introduction.md new file mode 100644 index 00000000..2322f813 --- /dev/null +++ b/exercises/practice/swift-scheduling/.docs/introduction.md @@ -0,0 +1,6 @@ +# Introduction + +This week, it is your turn to take notes in the department's planning meeting. +In this meeting, your boss will set delivery dates for all open work items. +Annoyingly, instead of specifying the _actual_ delivery dates, your boss will only _describe them_ in an abbreviated format. +As many of your colleagues won't be familiar with this corporate lingo, you'll need to convert these delivery date descriptions to actual delivery dates. diff --git a/exercises/practice/swift-scheduling/.meta/config.json b/exercises/practice/swift-scheduling/.meta/config.json new file mode 100644 index 00000000..2484e4a9 --- /dev/null +++ b/exercises/practice/swift-scheduling/.meta/config.json @@ -0,0 +1,17 @@ +{ + "authors": [ + "glennj" + ], + "files": { + "solution": [ + "swift-scheduling.tcl" + ], + "test": [ + "swift-scheduling.test" + ], + "example": [ + ".meta/example.tcl" + ] + }, + "blurb": "Convert delivery date descriptions to actual delivery dates." +} diff --git a/exercises/practice/swift-scheduling/.meta/example.tcl b/exercises/practice/swift-scheduling/.meta/example.tcl new file mode 100644 index 00000000..4b047ab4 --- /dev/null +++ b/exercises/practice/swift-scheduling/.meta/example.tcl @@ -0,0 +1,79 @@ +proc deliveryDate {timestamp target} { + set t [clock scan $timestamp -format {%Y-%m-%dT%T}] + + switch -regexp -matchvar m -- $target { + {^NOW$} { set t [now $t] } + {^ASAP$} { set t [asap $t] } + {^EOW$} { set t [eow $t] } + {^(\d+)M$} { set t [month $t [lindex $m 1]] } + {^Q(\d)$} { set t [quarter $t [lindex $m 1]] } + default { error "Don't understand $target" } + } + + return [clock format $t -format {%Y-%m-%dT%T}] +} + +######################################################### +proc now {timeVal} { + return [clock add $timeVal 2 hours] +} + +proc asap {timeVal} { + set hour [clock format $timeVal -format {%k}] + # truncate time to midnight + set midnight [clock scan [clock format $timeVal -format {%Y-%m-%d}] -format {%Y-%m-%d}] + + if {$hour < 13} { + return [clock add $midnight 17 hours] + } else { + return [clock add $midnight 1 day 13 hours] + } +} + +proc eow {timeVal} { + set dow [clock format $timeVal -format {%w}] + set midnight [clock scan [clock format $timeVal -format {%Y-%m-%d}] -format {%Y-%m-%d}] + + if {$dow in {1 2 3}} { + set days [expr {5 - $dow}] + set hours 17 + } else { + set days [expr {7 - $dow}] + set hours 20 + } + return [clock add $midnight $days days $hours hours] +} + +proc month {timeVal targetMonth} { + # %N is the month number with no leading zero + lassign [clock format $timeVal -format {%Y %N}] year month + if {$month >= $targetMonth} { + incr year + } + set firstDay [clock scan "$year-$targetMonth-01" -format {%Y-%N-%d}] + + set dow [clock format $firstDay -format {%u}] + if {$dow > 5} { + # add one or two days + set firstDay [clock add $firstDay [expr {8 - $dow}] days] + } + + return [clock add $firstDay 8 hours] +} +proc quarter {timeVal targetQuarter} { + set lastQuarterMonth [expr {3 * $targetQuarter}] + lassign [clock format $timeVal -format {%Y %N}] year month + if {$month > $lastQuarterMonth} { + incr year + } + set lastQuarterMonthFirstDay [clock scan "$year-$lastQuarterMonth-01" -format {%Y-%N-%d}] + set targetDay [clock add $lastQuarterMonthFirstDay 1 month -1 day] + + set dow [clock format $targetDay -format {%u}] + if {$dow > 5} { + # subtract one or two days + set targetDay [clock add $targetDay [expr {5 - $dow}] days] + } + + return [clock add $targetDay 8 hours] +} diff --git a/exercises/practice/swift-scheduling/.meta/tests.toml b/exercises/practice/swift-scheduling/.meta/tests.toml new file mode 100644 index 00000000..7cc3e415 --- /dev/null +++ b/exercises/practice/swift-scheduling/.meta/tests.toml @@ -0,0 +1,58 @@ +# This is an auto-generated file. +# +# Regenerating this file via `configlet sync` will: +# - Recreate every `description` key/value pair +# - Recreate every `reimplements` key/value pair, where they exist in problem-specifications +# - Remove any `include = true` key/value pair (an omitted `include` key implies inclusion) +# - Preserve any other key/value pair +# +# As user-added comments (using the # character) will be removed when this file +# is regenerated, comments can be added via a `comment` key. + +[1d0e6e72-f370-408c-bc64-5dafa9c6da73] +description = "NOW translates to two hours later" + +[93325e7b-677d-4d96-b017-2582af879dc2] +description = "ASAP before one in the afternoon translates to today at five in the afternoon" + +[cb4252a3-c4c1-41f6-8b8c-e7269733cef8] +description = "ASAP at one in the afternoon translates to tomorrow at one in the afternoon" + +[6fddc1ea-2fe9-4c60-81f7-9220d2f45537] +description = "ASAP after one in the afternoon translates to tomorrow at one in the afternoon" + +[25f46bf9-6d2a-4e95-8edd-f62dd6bc8a6e] +description = "EOW on Monday translates to Friday at five in the afternoon" + +[0b375df5-d198-489e-acee-fd538a768616] +description = "EOW on Tuesday translates to Friday at five in the afternoon" + +[4afbb881-0b5c-46be-94e1-992cdc2a8ca4] +description = "EOW on Wednesday translates to Friday at five in the afternoon" + +[e1341c2b-5e1b-4702-a95c-a01e8e96e510] +description = "EOW on Thursday translates to Sunday at eight in the evening" + +[bbffccf7-97f7-4244-888d-bdd64348fa2e] +description = "EOW on Friday translates to Sunday at eight in the evening" + +[d651fcf4-290e-407c-8107-36b9076f39b2] +description = "EOW translates to leap day" + +[439bf09f-3a0e-44e7-bad5-b7b6d0c4505a] +description = "2M before the second month of this year translates to the first workday of the second month of this year" + +[86d82e83-c481-4fb4-9264-625de7521340] +description = "11M in the eleventh month translates to the first workday of the eleventh month of next year" + +[0d0b8f6a-1915-46f5-a630-1ff06af9da08] +description = "4M in the ninth month translates to the first workday of the fourth month of next year" + +[06d401e3-8461-438f-afae-8d26aa0289e0] +description = "Q1 in the first quarter translates to the last workday of the first quarter of this year" + +[eebd5f32-b16d-4ecd-91a0-584b0364b7ed] +description = "Q4 in the second quarter translates to the last workday of the fourth quarter of this year" + +[c920886c-44ad-4d34-a156-dc4176186581] +description = "Q3 in the fourth quarter translates to the last workday of the third quarter of next year" diff --git a/exercises/practice/swift-scheduling/swift-scheduling.tcl b/exercises/practice/swift-scheduling/swift-scheduling.tcl new file mode 100644 index 00000000..a6325be4 --- /dev/null +++ b/exercises/practice/swift-scheduling/swift-scheduling.tcl @@ -0,0 +1,3 @@ +proc deliveryDate {timestamp target} { + throw {NOT_IMPLEMENTED} "Implement this procedure." +} diff --git a/exercises/practice/swift-scheduling/swift-scheduling.test b/exercises/practice/swift-scheduling/swift-scheduling.test new file mode 100644 index 00000000..9257144e --- /dev/null +++ b/exercises/practice/swift-scheduling/swift-scheduling.test @@ -0,0 +1,89 @@ +#!/usr/bin/env tclsh +package require tcltest +namespace import ::tcltest::* +source testHelpers.tcl + +############################################################ +source "swift-scheduling.tcl" + +test swift-scheduling-01 "NOW translates to two hours later" -body { + deliveryDate "2012-02-13T09:00:00" "NOW" +} -returnCodes ok -result "2012-02-13T11:00:00" + +skip swift-scheduling-02 +test swift-scheduling-02 "ASAP before one in the afternoon translates to today at five in the afternoon" -body { + deliveryDate "1999-06-03T09:45:00" "ASAP" +} -returnCodes ok -result "1999-06-03T17:00:00" + +skip swift-scheduling-03 +test swift-scheduling-03 "ASAP at one in the afternoon translates to tomorrow at one in the afternoon" -body { + deliveryDate "2008-12-21T13:00:00" "ASAP" +} -returnCodes ok -result "2008-12-22T13:00:00" + +skip swift-scheduling-04 +test swift-scheduling-04 "ASAP after one in the afternoon translates to tomorrow at one in the afternoon" -body { + deliveryDate "2008-12-21T14:50:00" "ASAP" +} -returnCodes ok -result "2008-12-22T13:00:00" + +skip swift-scheduling-05 +test swift-scheduling-05 "EOW on Monday translates to Friday at five in the afternoon" -body { + deliveryDate "2025-02-03T16:00:00" "EOW" +} -returnCodes ok -result "2025-02-07T17:00:00" + +skip swift-scheduling-06 +test swift-scheduling-06 "EOW on Tuesday translates to Friday at five in the afternoon" -body { + deliveryDate "1997-04-29T10:50:00" "EOW" +} -returnCodes ok -result "1997-05-02T17:00:00" + +skip swift-scheduling-07 +test swift-scheduling-07 "EOW on Wednesday translates to Friday at five in the afternoon" -body { + deliveryDate "2005-09-14T11:00:00" "EOW" +} -returnCodes ok -result "2005-09-16T17:00:00" + +skip swift-scheduling-08 +test swift-scheduling-08 "EOW on Thursday translates to Sunday at eight in the evening" -body { + deliveryDate "2011-05-19T08:30:00" "EOW" +} -returnCodes ok -result "2011-05-22T20:00:00" + +skip swift-scheduling-09 +test swift-scheduling-09 "EOW on Friday translates to Sunday at eight in the evening" -body { + deliveryDate "2022-08-05T14:00:00" "EOW" +} -returnCodes ok -result "2022-08-07T20:00:00" + +skip swift-scheduling-10 +test swift-scheduling-10 "EOW translates to leap day" -body { + deliveryDate "2008-02-25T10:30:00" "EOW" +} -returnCodes ok -result "2008-02-29T17:00:00" + +skip swift-scheduling-11 +test swift-scheduling-11 "2M before the second month of this year translates to the first workday of the second month of this year" -body { + deliveryDate "2007-01-02T14:15:00" "2M" +} -returnCodes ok -result "2007-02-01T08:00:00" + +skip swift-scheduling-12 +test swift-scheduling-12 "11M in the eleventh month translates to the first workday of the eleventh month of next year" -body { + deliveryDate "2013-11-21T15:30:00" "11M" +} -returnCodes ok -result "2014-11-03T08:00:00" + +skip swift-scheduling-13 +test swift-scheduling-13 "4M in the ninth month translates to the first workday of the fourth month of next year" -body { + deliveryDate "2019-11-18T15:15:00" "4M" +} -returnCodes ok -result "2020-04-01T08:00:00" + +skip swift-scheduling-14 +test swift-scheduling-14 "Q1 in the first quarter translates to the last workday of the first quarter of this year" -body { + deliveryDate "2003-01-01T10:45:00" "Q1" +} -returnCodes ok -result "2003-03-31T08:00:00" + +skip swift-scheduling-15 +test swift-scheduling-15 "Q4 in the second quarter translates to the last workday of the fourth quarter of this year" -body { + deliveryDate "2001-04-09T09:00:00" "Q4" +} -returnCodes ok -result "2001-12-31T08:00:00" + +skip swift-scheduling-16 +test swift-scheduling-16 "Q3 in the fourth quarter translates to the last workday of the third quarter of next year" -body { + deliveryDate "2022-10-06T11:00:00" "Q3" +} -returnCodes ok -result "2023-09-29T08:00:00" + + +cleanupTests diff --git a/exercises/practice/swift-scheduling/testHelpers.tcl b/exercises/practice/swift-scheduling/testHelpers.tcl new file mode 100644 index 00000000..292829fb --- /dev/null +++ b/exercises/practice/swift-scheduling/testHelpers.tcl @@ -0,0 +1,20 @@ +############################################################# +# Override some tcltest procs with additional functionality + +# Allow an environment variable to override `skip` +proc skip {patternList} { + if { [info exists ::env(RUN_ALL)] + && [string is boolean -strict $::env(RUN_ALL)] + && $::env(RUN_ALL) + } then return else { + uplevel 1 [list ::tcltest::skip $patternList] + } +} + +# Exit non-zero if any tests fail. +# The cleanupTests resets the numTests array, so capture it first. +proc cleanupTests {} { + set failed [expr {$::tcltest::numTests(Failed) > 0}] + uplevel 1 ::tcltest::cleanupTests + if {$failed} then {exit 1} +}