packages feed

Monadoro 0.2.1.11 → 0.2.5.0

raw patch · 17 files changed

+398/−202 lines, 17 filesdep +hspecdep −processsetup-changedPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

Dependencies added: hspec

Dependencies removed: process

API changes (from Hackage documentation)

- ArgParse: parse_args :: IO ()
- Clock: countdown :: IO ()
- Pomodoro: worker :: IO ()
- Timer: main :: IO ()
- Timer: secToTimestamp :: Int -> String
- Version: version :: String
+ CountdownLoop: countdown :: String -> IO String
+ Monadoro: monadoro :: IO ()
+ ParseTime: asTimePeriod :: String -> TimePeriod
+ ParseTime: count_down_time :: String -> String
+ ParseTime: instance GHC.Read.Read ParseTime.TimePeriod
+ ParseTime: instance GHC.Show.Show ParseTime.TimePeriod
+ ParseTime: subtract_second :: TimePeriod -> TimePeriod
+ Pomodoro: countdown :: IO ()
+ Pomodoro: session :: IO ()

Files

LICENSE view
@@ -1,7 +1,21 @@-Copyright Patryk Kocielnik (c) 2019+MIT License -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:+Copyright (c) 2018-2021 Patryk Kocielnik -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.+Permission is hereby granted, free of charge, to any person obtaining a copy+of this software and associated documentation files (the "Software"), to deal+in the Software without restriction, including without limitation the rights+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell+copies of the Software, and to permit persons to whom the Software is+furnished to do so, subject to the following conditions: -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.>+The above copyright notice and this permission notice shall be included in all+copies or substantial portions of the Software.++THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE+SOFTWARE.
Monadoro.cabal view
@@ -1,19 +1,17 @@ cabal-version: 1.12 --- This file has been generated from package.yaml by hpack version 0.31.2.+-- This file has been generated from package.yaml by hpack version 0.34.4. -- -- see: https://github.com/sol/hpack------ hash: b14e5b960dd8f4cd42b98cc5c1bcf096614133a4d6f76503728a2b8ff65940ca  name:           Monadoro-version:        0.2.1.11+version:        0.2.5.0 synopsis:       A minimalistic CLI Pomodoro timer. description:    A Pomodoro timer with two modes: single-pomodoro (default), and four-pomodoro (`--session`). category:       Tools author:         Patryk Kocielnik maintainer:     patryk@kocielnik.pl-copyright:      2018-2019 Patryk Kocielnik+copyright:      2018-2020 Patryk Kocielnik license:        MIT license-file:   LICENSE build-type:     Simple@@ -28,11 +26,10 @@  library   exposed-modules:-      ArgParse-      Clock+      CountdownLoop+      Monadoro+      ParseTime       Pomodoro-      Timer-      Version   other-modules:       Paths_Monadoro   hs-source-dirs:@@ -40,7 +37,6 @@   build-depends:       ansi-terminal >=0.8 && <1     , base >=4.7 && <5-    , process >=1.0 && <2     , time >=1.8 && <2   default-language: Haskell2010 @@ -55,14 +51,14 @@       Monadoro     , ansi-terminal >=0.8 && <1     , base >=4.7 && <5-    , process >=1.0 && <2     , time >=1.8 && <2   default-language: Haskell2010  test-suite Monadoro-test   type: exitcode-stdio-1.0-  main-is: Spec.hs+  main-is: Main.hs   other-modules:+      DocTests       Paths_Monadoro   hs-source-dirs:       test@@ -72,6 +68,6 @@     , ansi-terminal >=0.8 && <1     , base >=4.7 && <5     , doctest >=0.8-    , process >=1.0 && <2+    , hspec     , time >=1.8 && <2   default-language: Haskell2010
README.md view
@@ -1,12 +1,60 @@ # Monadoro -A Pomodoro timer to track work time in 4-pomodoro sessions.+A simple pomodoro counter. Type `monadoro` to get started. -To run, execute `monadoro`.+![A completed session of 4 pomodoros.](doc/session_completed.png) -## Goal+# The goal  The simplest Pomodoro counter, ready to deploy at once on most command-line platforms to enable you to focus on your work in just a few moments, for weeks to come. +# The method++There are six steps in the original technique [^cirillo_pomodoro]:++1. Choose a task you'd like to get done,+2. Set the Pomodoro for 25 minutes,+3. Work on the task until the Pomodoro rings,+4. When the Pomodoro rings, put a checkmark on a paper,+5. If you have fewer than four checkmarks, take a short break (3-5 minutes), then go to step 2;+6. After four pomodoros, take a longer break (15-30 minutes), reset your checkmark count to zero, then go to step 1.++# Installing++## Install Stack++To install Stack on Ubuntu, you can use the command `sudo apt install+haskell-stack`.++## Installing Monadoro++Before installing, it is important to update the Stack index using `stack+update`.++Having updated the Stack index, we can now run `stack install Monadoro`, paying+attention to capitalization of the first letter, which is important.++## Test suite dependencies++DocTest 0.16.1 Haskell library requires libtinfo to compile. On Ubuntu,+libtinfo can be installed using the command `apt install libtinfo-dev`.++# Credits++Initial draft of functionality by The Haskell Blog [^haskell_blog] author -+GitHub user Elektroingenieur [^elektroingenieur].++Manpage generation from Markdown courtesy of Jérôme Belleman [^belleman] and+Pandoc [^pandoc] project contributors.++"Pomodoro Technique (illustration)" (CC BY 2.0) by [Michael Zero Mayer](https://www.flickr.com/photos/michael_mayer/6969282632/).++# References++[^cirillo_pomodoro]: [The Pomodoro Technique](https://francescocirillo.com/pages/pomodoro-technique), Francesco Cirillo+[^haskell_blog]: https://haskellblog.wordpress.com/2015/08/05/pomodoro-timer-in-haskell/+[^elektroingenieur]: https://github.com/Elektroingenieur+[^belleman]: http://jeromebelleman.gitlab.io/posts/publishing/manpages/+[^pandoc]: pandoc.org
− Setup.hs
@@ -1,2 +0,0 @@-import Distribution.Simple-main = defaultMain
app/Main.hs view
@@ -1,7 +1,6 @@ module Main where -import ArgParse ( parse_args )+import Monadoro ( monadoro )  main :: IO ()-main = parse_args-+main = monadoro
changelog view
@@ -1,3 +1,24 @@+monadoro (2.5.0)++	* Clean up lots of dead code.++monadoro (2.4.0)++	* Add option to resume a pomodoro from a given time.++	Should you need to reboot your computer while on a pomodoro, it is now+	possible to resume the timer from a specified time.++monadoro (2.2.0)++	* Complete and integrate unit tests.++	* Read the "-v" version number directly from the package.yaml file.++	This allowed to remove one module from the package. I handed the+	responsibility of that module to dedicated mechanisms that were already+	available.+ monadoro (2.1.11)  	* Fix -v switch.
− src/ArgParse.hs
@@ -1,28 +0,0 @@-#!/usr/bin/env stack---stack --install-ghc runghc--module ArgParse (parse_args) where---- Here we will service arguments passed to the program in the simplest manner--- possible.--import System.Environment-import System.Exit-import Pomodoro (worker)-import Clock    (countdown)-import Version  (version)--usage   = putStrLn "Usage: monadoro [-vh] [--session]"-soft_version = putStrLn $ version-exit    = exitWith ExitSuccess-warn    = putStrLn "Invalid argument." >> usage >> exitWith (ExitFailure 1)--parse ["-h"]        = usage   >> exit-parse ["-v"]        = soft_version >> exit-parse ["--session"] = worker-parse []            = countdown-parse _             = warn--parse_args = getArgs >>= parse--main = parse_args
− src/Clock.lhs
@@ -1,58 +0,0 @@-#!/usr/bin/env stack-> --stack --install-ghc runghc --package markdown-unlit -- "-pgmL markdown-unlit"--> {-# LANGUAGE OverloadedStrings #-}--This is a simple countdown clock module.--> module Clock (countdown) where--> import Control.Concurrent  (threadDelay)-> import System.Console.ANSI (clearLine,->                             clearFromCursorToScreenEnd,->                             setCursorColumn,->                             hideCursor,->                             showCursor,->                             saveCursor,->                             restoreCursor)-> import System.Environment  (getArgs)-> import System.IO           (hFlush,->                             stdout)-> import Timer               (secToTimestamp)->  -> -- >>> putStrLn "Hello world!"-> -- Hello world!-> ---> -- >>> putStrLn "Test complete!"-> -- Test complete!- -> countdown :: IO()-> countdown = do->     wait_seconds $ 25 * 60 + 1--> wait_seconds :: Int -> IO()-> wait_seconds 0 = do->     putStrLn ""-> wait_seconds n = do->     putStr ""->     setCursorColumn 0->     putStr $ secToTimestamp $ n - 1->     hFlush stdout->     delay 1->     wait_seconds $ n - 1--> delay :: Int -> IO()-> delay n = del n-> -> del :: Int -> IO()-> del n = do->     threadDelay $ n * 1000 * 1000->-> no_del :: Int -> IO()-> no_del n = do->     return ()-> -> main :: IO()-> main = do->     countdown-
+ src/CountdownLoop.hs view
@@ -0,0 +1,69 @@+#!/usr/bin/env stack+--stack --install-ghc runghc++module CountdownLoop       (countdown) where++import System.Environment  (getArgs)+import System.Console.ANSI (saveCursor,+                            restoreCursor)+import System.IO (hFlush,+                  stdout)+import Control.Concurrent  (threadDelay)+import ParseTime           (count_down_time)++milisec_per_second :: Int+milisec_per_second = 10 ^ (6 :: Int)++{-| wait: Pauses for the given number of seconds.++>>> wait 0+-}++wait :: Int -> IO()+wait n = threadDelay (n * milisec_per_second)++with_delay :: IO()+with_delay = wait 1++-- Mocks a delay for test purposes.+no_delay :: IO()+no_delay = wait 0++{-| countdown_loop: Runs a Pomodoro timer.++>>> result <- countdown_loop no_delay "00:00"+...+>>> result+"00:00"++>>> result' <- countdown_loop no_delay "00:59"+...+>>> result'+"00:00"++>>> result'' <- countdown_loop no_delay "01:59"+...+>>> result''+"00:00"++-}++countdown :: String -> IO (String)+countdown = countdown_loop with_delay++countdown_loop :: IO() -> String -> IO (String)+countdown_loop delayer "00:00" = putStrLn "00:00" >> return "00:00"+countdown_loop delayer s = do+  saveCursor+  putStr s+  hFlush stdout+  delayer+  restoreCursor+  (countdown_loop delayer . count_down_time) s++parse :: [String] -> IO ()+parse [s] = countdown_loop with_delay s >> return ()+parse _   = countdown_loop with_delay "00:00" >> return ()++main :: IO ()+main = getArgs >>= parse
+ src/Monadoro.hs view
@@ -0,0 +1,33 @@+module Monadoro (monadoro) where++import Data.List          (intersect)+import Data.Version       (showVersion)+import Paths_Monadoro     (version)+import System.Environment (getArgs)++import CountdownLoop      (countdown)+import Pomodoro           (session)++main :: IO ()+main = monadoro++monadoro :: IO ()+monadoro = getArgs >>= parseArgs++parseArgs :: [String] -> IO ()+parseArgs xs +  | xs `containsAnyOf` ["--help", "-h"]    = putStrLn usage+  | xs `containsAnyOf` ["--version", "-v"] = putStrLn . showVersion $ version+  | xs `containsAnyOf` ["--session", "-s"] = session+  | otherwise = runTimer xs++containsAnyOf :: (Eq a) => [a] -> [a] -> Bool+containsAnyOf a = not . null . intersect a++usage :: String+usage = "Usage: monadoro [-vh] [--session] [INTERVAL [...]]"++runTimer :: [String] -> IO ()+runTimer []               = countdown "25:00" >> return ()+runTimer (t:[])           = countdown t >> return ()+runTimer (t:ts)           = parseArgs [t] >> parseArgs ts
+ src/ParseTime.lhs view
@@ -0,0 +1,134 @@+#!/usr/bin/env stack+> --stack --install-ghc runghc++> module ParseTime (+>   count_down_time,+>   subtract_second,+>   asTimePeriod+> )+> where+>+> data TimePeriod = TimePeriod Integer Integer deriving (Read)+> +> split :: (Char -> Bool) -> String -> [String]+> split p s =  case dropWhile p s of+>     "" -> []+>     s' -> w : split p s''+>         where (w, s'') = break p s'++> -- | Given a string denoting a time interval, function "split" returns the+> -- corresponding time in ["MM","SS"].+> --+> -- >>> split (==':') "10:20"+> -- ["10","20"]++> split_by_colon :: String -> [String]+> split_by_colon timeString = split (==':') timeString+> +> {-| split_by_colon: Returns time in `["MM","SS"]`, corresponding to the time+> interval given in a string.+> +> >>> split_by_colon "10:20"+> ["10","20"]+> -}+> +> many_to_int :: [String] -> [Integer]+> many_to_int [] = []+> many_to_int (x:xs) = (read $ x :: Integer):(many_to_int xs)+> +> {-| many_to_int+> +> >>> many_to_int ["10","20"]+> [10,20]+> -}+> +> list_to_time_periods :: [Integer] -> [TimePeriod]+> list_to_time_periods [m,s] = [TimePeriod m s]+> list_to_time_periods _ = []+> +> {-| list_to_time_periods+> +> >>> list_to_time_periods [10,20]+> [10:20]+> -}+> +> subtract_second :: TimePeriod -> TimePeriod+> subtract_second (TimePeriod 0 0) = TimePeriod 0 0+> subtract_second (TimePeriod 0 s) = TimePeriod 0 (s-1)+> subtract_second (TimePeriod m 0) = TimePeriod (m-1) 59+> subtract_second (TimePeriod m s) = TimePeriod m (s-1)+> +> {-| subtract_second: Given a TimePeriod, returns a TimePeriod that is one+>  second shorter.+> +> >>> subtract_second $ TimePeriod 00 00+> 00:00+> +> >>> subtract_second $ TimePeriod 00 01+> 00:00+> +> >>> subtract_second $ TimePeriod 01 00+> 00:59+> +> >>> subtract_second $ TimePeriod 02 00+> 01:59+> -}+> +> asTimePeriod :: String -> TimePeriod+> asTimePeriod s = head $ list_to_time_periods $ many_to_int $ split_by_colon s+> +> {-| asTimePeriod: Reads a string as a TimePeriod.+> +> >>> asTimePeriod "00:00"+> 00:00+>+> >>> asTimePeriod "99:99"+> 99:99+> -}+> +> {-| count_down_time: Subtracts a second from time given as a string.+> +> >>> count_down_time "00:00"+> "00:00"+> +> >>> count_down_time "00:01"+> "00:00"+> +> >>> count_down_time "00:02"+> "00:01"+>+> >>> count_down_time "01:00"+> "00:59"+> -}+> +> count_down_time :: String -> String+> count_down_time = show . subtract_second . asTimePeriod+>+> {-| show_two_digits+>+> >>> show_two_digits 0+> "00"+> -}+>+> show_two_digits :: Integer -> String+> show_two_digits x+>   | x < 10 = "0" ++ show x+>   | otherwise = show x+>+> {-| show TimePeriod+>+> >>> show (TimePeriod 0 0)+> "00:00"+>+> >>> a = TimePeriod 0 0+> >>> show a+> "00:00"+> -}+>+> instance Show TimePeriod where+>   show (TimePeriod m s) = show_two_digits m ++ ":" ++ show_two_digits s++Related materials:++1. Generic time processing example: https://stackoverflow.com/a/14006938+
src/Pomodoro.lhs view
@@ -1,24 +1,38 @@+#!/usr/bin/env stack+> --stack --install-ghc runghc+ This is a command-line Pomodoro counter. -> module Pomodoro (worker) where+> module Pomodoro (session, countdown) where  > import Control.Concurrent (threadDelay) > import System.Console.ANSI (clearLine,->                             clearFromCursorToScreenEnd,->                             setCursorColumn,->                             hideCursor,->                             showCursor, >                             saveCursor, >                             restoreCursor)-> import System.Environment (getArgs) > import System.IO (hFlush, >                   stdout)-> import Timer (secToTimestamp)+> import Data.Time.Clock.POSIX (posixSecondsToUTCTime)+> import Data.Time.Format (formatTime, defaultTimeLocale) >   > data Pomodoro = First | Second | Third | Fourth >     deriving (Eq, Ord, Show, Read, Bounded, Enum) +> {-| Given an integer number of seconds, secToTimestamp+>  returns the corresponding time in MM:SS.+> +> >>> secToTimestamp 0+> "00:00"+> >>> secToTimestamp 1500+> "25:00"+> >>> secToTimestamp 3000+> "50:00"+> -}+> +> secToTimestamp :: Int -> String+> secToTimestamp = formatTime defaultTimeLocale "%M:%S" . posixSecondsToUTCTime . fromIntegral+ > -- | Manage Pomodoro sessions+> --  > -- Examples: > -- > -- >>> putStrLn "Hello world!"@@ -26,7 +40,17 @@ > -- > -- >>> putStrLn "Test complete!" > -- Test complete!++> countdown :: IO()+> countdown = do+>     wait_seconds $ 25 * 60 + 1++> -- | Replace a delay with no delay for testing purposes.+> no_del :: Int -> IO()+> no_del _ = do+>     return ()  +> pom :: Show a => a -> IO () > pom m = do >     putStr $ show m ++ " pomodoro" ++ " | " >     saveCursor@@ -34,16 +58,19 @@ >     restoreCursor >     putStrLn "Finished, take a 5 minute rest." +> to_work :: IO () > to_work = do >     saveCursor >     putStr "Get back to work" >     delay 2 >     restoreCursor >     clearLine++> pomodoro :: Pomodoro -> IO () > pomodoro Fourth = do >     pom Fourth >     putStrLn "Take a 30-minute rest now. You just completed 4 pomodoros."->     putStrLn "Thank you for tracking your time with Monadoro."+>     putStrLn "Congratulations on 4 pomodoros finished in a row!" > pomodoro m = do >     pom m >     delay $ 5 * 60@@ -51,31 +78,27 @@ >     pomodoro $ succ m >   > wait_seconds :: Int -> IO()-> wait_seconds 0 = do->     putStr ""+> wait_seconds 0 = putStr "" > wait_seconds n = do->     restoreCursor+>     saveCursor >     putStr $ secToTimestamp $ n - 1 >     hFlush stdout+>     restoreCursor >     delay 1 >     wait_seconds $ n - 1 +> -- | delay: Pauses the thread for the given number of seconds.+> --+> -- Example:+> --+> -- >>> delay 0+ > delay :: Int -> IO()-> delay n = del n-> -> del :: Int -> IO()-> del n = do->     threadDelay $ n * 1000 * 1000->-> no_del :: Int -> IO()-> no_del n = do->     return ()-> -> worker :: IO()-> worker = do->     pomodoro First->+> delay n = threadDelay $ n * 1000 * 1000++> session :: IO()+> session = pomodoro First+ > main :: IO()-> main = do->     worker+> main = session 
− src/Timer.hs
@@ -1,30 +0,0 @@-#!/usr/bin/env stack---stack --install-ghc runghc--{-# LANGUAGE OverloadedStrings #-}--module Timer where--import Data.Time.Clock.POSIX-import Data.Time.Format--{-| Given an integer number of seconds, secToTimestamp- returns the corresponding time in MM:SS.-->>> secToTimestamp 0-"00:00"->>> secToTimestamp 1500-"25:00"->>> secToTimestamp 3000-"50:00"--}--secToTimestamp :: Int -> String-secToTimestamp = formatTime defaultTimeLocale "%M:%S" . posixSecondsToUTCTime . fromIntegral--main :: IO ()-main = do-    putStrLn $ secToTimestamp 0-    putStrLn $ secToTimestamp 1500-    putStrLn $ secToTimestamp 3000-
− src/Version.hs
@@ -1,30 +0,0 @@-#!/usr/bin/env stack---stack --install-ghc runghc--module Version (version) where--import System.Environment-import System.Exit-import System.Process---- | Read current package version------ Example:------ >>> current_version >>= putStrLn :: IO ()--- 0.2.1.8-----pkg_version :: FilePath -> IO String-pkg_version file_name = do-    contents <- readFile file_name-    let version_line = lines contents !! 1-    let version_word = words version_line !! 1-    return version_word--version :: String-version = "0.2.1.11"--main :: IO ()-main = putStrLn $ version-
+ test/DocTests.hs view
@@ -0,0 +1,10 @@+module DocTests where++import Test.DocTest++main :: IO ()+main = doctest ["-isrc",+                "src/ParseTime.lhs",+                "src/CountdownLoop.hs",+                "src/Pomodoro.lhs",+                "src/Monadoro.hs"]
+ test/Main.hs view
@@ -0,0 +1,1 @@+import DocTests
− test/Spec.hs
@@ -1,4 +0,0 @@-import Test.DocTest--main :: IO ()-main = doctest ["-isrc", "src/Pomodoro.lhs", "src/Clock.lhs", "src/Timer.hs", "src/Version.hs"]