summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJed Barber <jjbarber@y7mail.com>2017-01-14 00:57:12 +1100
committerJed Barber <jjbarber@y7mail.com>2017-01-14 00:57:12 +1100
commitcedc60f86a22eddef26d8db4cb3ec36d50a7fd75 (patch)
tree35dfaad94d48591c4aedba7eaf49bd943092de55
parentc0d0b285cf2a6d6151e66148a022d67d46daca31 (diff)
Basic command line interface done
-rw-r--r--src/Election.hs31
-rw-r--r--src/main.hs223
2 files changed, 193 insertions, 61 deletions
diff --git a/src/Election.hs b/src/Election.hs
new file mode 100644
index 0000000..082ee4c
--- /dev/null
+++ b/src/Election.hs
@@ -0,0 +1,31 @@
+module Election(
+ Election,
+
+ createElection,
+ doCount
+ ) where
+
+
+
+
+import qualified Counter as Sen
+
+
+
+
+data Election = Election String
+
+
+
+
+createElection :: FilePath -> Sen.SenateCounter -> IO Election
+createElection outDir counter = return (Election "testcode")
+
+
+
+
+doCount :: Election -> Int -> IO ()
+doCount election numToElect =
+ putStrLn "run election here"
+
+
diff --git a/src/main.hs b/src/main.hs
index 9effc4a..ef7589e 100644
--- a/src/main.hs
+++ b/src/main.hs
@@ -1,75 +1,176 @@
import qualified System.Environment as Env
-import qualified Counter as Sen
-import qualified Candidate as Typ
+import qualified System.Console.GetOpt as Opt
+import qualified System.Exit as Ex
+import qualified System.Directory as Dir
+import qualified Control.Monad as Con
import qualified Data.Maybe as Maybe
+import qualified Counter as Sen
+import qualified Candidate as Cand
+import qualified Election as Elt
+
+
+
+
+data Options = Options
+ { isVerbose :: Bool
+ , isVersion :: Bool
+ , isHelp :: Bool
+ , getCandFile :: Maybe FilePath
+ , getPrefFile :: Maybe FilePath
+ , getOutDir :: Maybe FilePath
+ , getNumToElect :: Maybe Int
+ , getState :: Maybe String }
+ deriving Show
+
+
+
+
+defaultOptions = Options
+ { isVerbose = False
+ , isVersion = False
+ , isHelp = False
+ , getCandFile = Nothing
+ , getPrefFile = Nothing
+ , getOutDir = Nothing
+ , getNumToElect = Nothing
+ , getState = Nothing }
+
+
+
+
+readMaybe :: Read a => String -> Maybe a
+readMaybe s =
+ case reads s of
+ [(val, "")] -> Just val
+ _ -> Nothing
+
+
+
+
+electOpt :: String -> (Options -> Options)
+electOpt str =
+ let r = readMaybe str :: Maybe Int
+ jr = if (Maybe.isJust r && Maybe.fromJust r > 0) then r else Nothing
+ in (\opts -> opts { getNumToElect = jr })
+
+
+
+
+stateOpt :: String -> (Options -> Options)
+stateOpt str =
+ let validStates = ["NSW", "VIC", "TAS", "QLD", "SA", "WA", "NT", "ACT"]
+ sr = if (str `elem` validStates) then Just str else Nothing
+ in (\opts -> opts { getState = sr } )
+
+
+
+
+optionHeader = "Usage: stv [OPTION...]"
+
+optionData :: [Opt.OptDescr (Options -> Options)]
+optionData =
+ [ Opt.Option ['v'] ["verbose"]
+ (Opt.NoArg (\opts -> opts { isVerbose = True}) )
+ "chatty output on stderr"
+
+ , Opt.Option ['V'] ["version"]
+ (Opt.NoArg (\opts -> opts { isVersion = True}) )
+ "show version number"
+
+ , Opt.Option ['h'] ["help"]
+ (Opt.NoArg (\opts -> opts { isHelp = True }) )
+ "show this help information"
+
+ , Opt.Option ['c'] ["candidates"]
+ (Opt.ReqArg (\c opts -> opts { getCandFile = Just c }) "FILE")
+ "file containing AEC candidate data"
+
+ , Opt.Option ['p'] ["preferences"]
+ (Opt.ReqArg (\p opts -> opts { getPrefFile = Just p}) "FILE")
+ "file containing AEC formal preferences"
+
+ , Opt.Option ['o'] ["outdir"]
+ (Opt.ReqArg (\d opts -> opts { getOutDir = Just d}) "DIR")
+ "directory to output count logging"
+
+ , Opt.Option ['e'] ["elect"]
+ (Opt.ReqArg electOpt "NUM")
+ "number of candidates to elect"
+
+ , Opt.Option ['s'] ["state"]
+ (Opt.ReqArg stateOpt "STATE")
+ "state or territory the data corresponds to" ]
+
+getOpts :: [String] -> IO (Options, [String])
+getOpts argv =
+ case Opt.getOpt Opt.Permute optionData argv of
+ (o,n, [] ) -> return (foldl (flip id) defaultOptions o, n)
+ (_,_,errs) -> ioError (userError (concat errs ++ Opt.usageInfo optionHeader optionData))
--- this is all messy test code
-
-
-
-
--- maps for NT Senate data
--- will be removed when candidate info parsing complete
-above = [[1,2],[3,4],[5,6],[7,8],[9,10],[11,12],[13,14]]
-below = [ "Pile, Jan"
- , "Gimini, Jimmy"
- , "Kavasilas, Andrew"
- , "Jones, Timothy"
- , "Campbell, Trudy"
- , "Barry, Ian"
- , "Connard, Michael"
- , "Bannister, Kathy"
- , "Scullion, Nigel"
- , "Lillis, Jenni"
- , "McCarthy, Malarndirri"
- , "Honan, Pat"
- , "Ordish, Carol"
- , "Ordish, John"
- , "Lee, TS"
- , "Marshall, Tristan"
- , "Ryan, Maurie Japarta"
- , "MacDonald, Marney"
- , "Strettles, Greg" ]
-
-
-above2 = [[1,2],[3,4],[5,6],[7,8],[9,10],[11,12],[13,14],[15,16],[17,18],[19,20]]
-below2 = [ "Donnelly, Matt"
- , "Hennings, Cawley"
- , "Edwards, David"
- , "Mihaljevic, Denis"
- , "Gallagher, Katy"
- , "Smith, David"
- , "O'Connor, Sandie"
- , "Wyatt, Jess"
- , "Haydon, John"
- , "Tye, Martin"
- , "Seselja, Zed"
- , "Hiatt, Jane"
- , "Field, Deborah"
- , "Montagne, Jessica"
- , "Hobbs, Christina"
- , "Wareham, Sue"
- , "Kim, David William"
- , "Tadros, Elizabeth"
- , "Bailey, Steven"
- , "Swan, Robbie"
- , "Hay, Michael Gerard"
- , "Hanson, Anthony" ]
main = do
- args <- Env.getArgs
- counter <- Sen.createSenateCounter (head args) above2 below2
- let testTraces = (map (:[]) (zip [1,1..] below2))
- results <- mapM (Sen.doCount counter) testTraces
- let func (n,c) = putStrLn (c ++ " " ++ (show n))
- output = map func (zip results below2)
- sequence_ output
+ rawArgs <- Env.getArgs
+ (options, arguments) <- getOpts rawArgs
+
+
+ -- options that abort the main program
+ Con.when (isHelp options) $ do
+ putStrLn (Opt.usageInfo optionHeader optionData)
+ Ex.exitFailure
+
+ Con.when (isVersion options) $ do
+ putStrLn "Australian STV Counter v0.1"
+ Ex.exitFailure
+
+
+ -- check that all necessary parameters are
+ -- both present and valid
+ let candidateFile = Maybe.fromJust (getCandFile options)
+ Con.when (Maybe.isNothing (getCandFile options)) $
+ Ex.die "Candidate data file not provided"
+ doesExist <- Dir.doesFileExist candidateFile
+ Con.when (not doesExist) $
+ Ex.die "Candidate data file does not exist"
+
+ let preferenceFile = Maybe.fromJust (getPrefFile options)
+ Con.when (Maybe.isNothing (getPrefFile options)) $
+ Ex.die "Formal preference data file not provided"
+ doesExist <- Dir.doesFileExist preferenceFile
+ Con.when (not doesExist) $
+ Ex.die "Formal preference data file does not exist"
+
+ let outputDir = Maybe.fromJust (getOutDir options)
+ Con.when (Maybe.isNothing (getOutDir options)) $
+ Ex.die "Output logging directory not provided"
+ doesExist <- Dir.doesDirectoryExist outputDir
+ Con.when doesExist $
+ Ex.die "Output directory already exists"
+
+ let numToElect = Maybe.fromJust (getNumToElect options)
+ Con.when (Maybe.isNothing (getNumToElect options)) $
+ Ex.die "Invalid number of candidates to elect or number not provided"
+
+ let state = Maybe.fromJust (getState options)
+ Con.when (Maybe.isNothing (getState options)) $
+ Ex.die "Invalid state/territory or state/territory not provided"
+
+
+ -- set up the election processing
+ (aboveBallot, belowBallot) <- Cand.readCandidates candidateFile state
+ counter <- Sen.createSenateCounter preferenceFile aboveBallot belowBallot
+ Dir.createDirectory outputDir
+ election <- Elt.createElection outputDir counter
+
+
+ -- run the show
+ Elt.doCount election numToElect
+ Ex.exitSuccess