A program to dump the output of the old HouseTab's state from Happstack-state into a parseable format
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 | {-# LANGUAGE TemplateHaskell, TypeFamilies, DeriveDataTypeable,
FlexibleInstances, MultiParamTypeClasses, FlexibleContexts,
UndecidableInstances, TypeOperators
#-}
module HouseTab.State where
import Happstack.Data
import Happstack.State
import Control.Monad (replicateM)
import Control.Monad.Reader (ask)
import Control.Monad.State (modify)
import Data.List (find)
import Data.List.Split (splitOn)
import Happstack.Crypto.SHA1 (sha1)
import Data.List (sortBy)
import qualified Data.Map as M
import Happstack.Auth (AuthState(..))
import HouseTab.Utils
import Data.Maybe (fromJust)
type HouseTabId = String
$(deriveAll [''Eq, ''Ord, ''Default]
[d|
-- |Date: simple date.
data Date = Date {year :: Integer
,month :: Integer
,day :: Integer}
|])
instance Show Date where
show (Date year month day) = (show year) ++ "." ++ (show month) ++ "." ++ (show day)
$(deriveSerialize ''Date)
instance Version Date
instance Read Date where
readsPrec _ value = pd $ splitOn (".") value
where pd (year:month:day:[]) = mkDate (maybeRead year) (maybeRead month) (maybeRead day)
pd _ = []
mkDate (Just y) (Just m) (Just d) = [(Date y m d, "")]
mkDate _ _ _ = []
$(deriveAll [''Show, ''Eq, ''Default]
[d|
-- |Person: a person on the housetab
data Person = Person { name :: String
, letter :: Char
, percs :: [(Date, Double)]}
|])
$(deriveSerialize ''Person)
instance Version Person
instance Ord Person where
compare (Person n1 _ _) (Person n2 _ _)
| n1 == n2 = EQ
| n1 <= n2 = LT
| otherwise = GT
$(deriveAll [''Show, ''Eq, ''Ord, ''Default]
[d|
data HouseTabEntry = HouseTabEntry
{ eid :: HouseTabId
, ewho :: String
, ewhat :: String
, ewhen :: Date
, ehowmuch :: Double
, ewhopays :: String
}
data HouseTab = HouseTab { houseTabEntries :: [HouseTabEntry]
, houseTabPeople :: [Person]}
data Account = Account
{ aname :: String
, aemails :: [String]
, ahousetab :: HouseTab }
data Accounts = Accounts
{ active :: M.Map String Account -- key is account name
, resets :: M.Map String String -- key is token, value is account name
, inactive :: M.Map String Account } -- key is activation token
|])
$(deriveSerialize ''HouseTabEntry)
instance Version HouseTabEntry
$(deriveSerialize ''HouseTab)
instance Version HouseTab
$(deriveSerialize ''Account)
instance Version Account
$(deriveSerialize ''Accounts)
instance Version Accounts
readAccounts :: Query Accounts Accounts
readAccounts = ask
replaceAccounts :: Accounts -> Update Accounts ()
replaceAccounts accounts = modify $ \_ -> accounts
readAccount :: String -> Query Accounts (Maybe Account)
readAccount name = do acnts <- ask
return $ M.lookup name (active acnts)
makeActive :: String -> Account -> Update Accounts ()
makeActive token acnt = modify $ \(Accounts a r i) -> (Accounts (M.insert (aname acnt) acnt a) r (M.delete token i))
addAccount :: String -> Account -> Update Accounts ()
addAccount token a = modify $ \accounts -> accounts{inactive=M.insert token a (inactive accounts)}
delAccount :: Account -> Update Accounts ()
delAccount a = modify $ \accounts -> accounts{active=M.delete (aname a) (active accounts)}
delInactiveAccount :: String -> Update Accounts ()
delInactiveAccount token = modify $ \accounts -> accounts{inactive=M.delete token (inactive accounts)}
addReset :: String -> String -> Update Accounts ()
addReset token name = modify $ \accounts -> accounts{resets=M.insert token name (resets accounts)}
delReset :: String -> Update Accounts ()
delReset token = modify $ \accounts -> accounts{resets=M.delete token (resets accounts)}
modAccount acnt fn = modify $ \accounts -> accounts{active=(adj (active accounts))}
where adj m = M.update (\a -> Just (fn a)) (aname acnt) m
modEntries acnt fn = modAccount acnt $ \a -> a{ahousetab = (ahousetab a){houseTabEntries = fn (houseTabEntries (ahousetab a))}}
modPeople acnt fn = modAccount acnt $ \a -> a{ahousetab = (ahousetab a){houseTabPeople = fn (houseTabPeople (ahousetab a))}}
changeEmails :: Account -> [String] -> Update Accounts ()
changeEmails acnt ems = modAccount acnt $ \a -> a{aemails = ems}
-- | replaces the entire 'HouseTab' with another one. For use with backups.
replaceHouseTab :: Account -> HouseTab -> Update Accounts ()
replaceHouseTab acnt ht = modAccount acnt (\a -> a{ahousetab = ht})
hashEntry fields = sha1 $ concat fields
-- | add a 'HouseTabEntry' to the 'HouseTab'
addHouseTabEntry :: Account -> HouseTabEntry -> Update Accounts ()
addHouseTabEntry a e = modEntries a $ \es ->
(e{eid=hashEntry([ewho e,
ewhat e,
show $ ewhen e,
show $ ehowmuch e,
ewhopays e])}:es)
-- | get a 'HouseTabEntry' from it's id
getHouseTabEntry :: Account -> HouseTabId -> (Maybe HouseTabEntry)
getHouseTabEntry a id = find (\e -> eid e == id) (houseTabEntries $ ahousetab a)
-- | delete a 'HouseTabEntry' from the 'HouseTab', by id.
-- will do nothing if the entry is not found.
delHouseTabEntry :: Account -> HouseTabId -> Update Accounts ()
delHouseTabEntry a id = modEntries a $ \es -> (filter (\e -> id /= eid e) es)
-- | add a 'Person' to the 'HouseTab'. unique identifier is letter.
-- will silently not add anything if a duplicate is attempted.
addPerson :: Account -> Person -> Update Accounts ()
addPerson a p = modPeople a $ \ps -> (if (letter p) `notElem` (map letter ps)
then (p:ps)
else ps)
-- | delete a 'Person' from the 'HouseTab'
delPerson :: Account -> Char -> Update Accounts ()
delPerson a l = modPeople a $ \ps -> (filter (\p -> l /= letter p) ps)
-- | get all the initials from an account
getInitials :: Account -> [Char]
getInitials a = map letter (houseTabPeople (ahousetab a))
-- | get a 'Person' by their letter from the Account
getPerson :: Account -> Char -> (Maybe Person)
getPerson a l = find (\p -> letter p == l) (houseTabPeople (ahousetab a))
-- | get a 'Person's entries by their letter form the Account
getPersonEntries :: Account-> Char -> [HouseTabEntry]
getPersonEntries a l = filter (\e -> ewho e == (l:[])) (houseTabEntries (ahousetab a))
-- | add a 'Percent' to the 'HouseTab'. unique identifier for person is letter.
-- will silently not add anything if a letter is not found.
-- uniqueness guaranteed on Date, percents ordered by date.
addPercent :: Account -> Char -> (Date, Double) -> Update Accounts ()
addPercent a l perc = modPeople a $ \ps -> (addP (find (\p -> l == letter p) ps)
(filter (\p -> l /= letter p) ps))
where addP Nothing persons = persons
addP (Just p) persons = if (fst perc) `elem` (map fst (percs p))
then p:persons
else p{percs =
(sortBy (\a b -> compare (fst a) (fst b))
((percs p) ++ [perc]))} : persons
-- | delete a 'Percent' from a 'HouseTab'. unique identifier for person is letter.
-- will silently not delete anything if a letter is not found.
delPercent :: Account -> Char -> Date -> Update Accounts ()
delPercent a l date = modPeople a $ \ps -> (delP (find (\p -> l == letter p) ps)
(filter (\p -> l /= letter p) ps))
where delP Nothing persons = persons
delP (Just p) persons = p{percs = (filter (\(d,_) -> d /= date) (percs p))} : persons
-- |make HouseTab its own Component
instance Component Accounts where
type Dependencies Accounts = AuthState :+: End
initialValue = defaultValue
$(mkMethods ''Accounts ['readAccounts, 'replaceAccounts, 'readAccount, 'makeActive, 'addAccount, 'delInactiveAccount, 'addReset, 'delReset, 'delAccount, 'replaceHouseTab, 'addHouseTabEntry, 'delHouseTabEntry, 'addPerson, 'delPerson, 'addPercent, 'delPercent, 'changeEmails])
|