packages feed

coin-1.0: src/Coin/DB/Tables.hs

{-
 *  Programmer:	Piotr Borek
 *  E-mail:     piotrborek@op.pl
 *  Copyright 2015 Piotr Borek
 *
 *  Distributed under the terms of the GPL (GNU Public License)
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
-}

{-# LANGUAGE EmptyDataDecls             #-}
{-# LANGUAGE FlexibleContexts           #-}
{-# LANGUAGE GADTs                      #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE MultiParamTypeClasses      #-}
{-# LANGUAGE OverloadedStrings          #-}
{-# LANGUAGE QuasiQuotes                #-}
{-# LANGUAGE TemplateHaskell            #-}
{-# LANGUAGE TypeFamilies               #-}
{-# LANGUAGE FlexibleInstances          #-}

module Coin.DB.Tables where

import qualified Data.Text as T
import qualified Data.Binary as B
import qualified Data.ByteString.Builder as BB
import qualified Data.ByteString.Lazy as BL

import System.Directory
import Control.Monad
import Data.Int
import Data.ByteString (ByteString)
import Database.Persist.Sqlite
import Database.Persist.TH

import Coin.DB.Functions
import Coin.Utils.I18N
import Coin.Config.Dirs

share [mkPersist sqlSettings, mkMigrate "migrateAll"] [persistUpperCase|
TagsTable json
    name String
    deriving Show

AccountsTable json
    name String
    balance Int64
    deriving Show

OperationsTable json
    date Int
    from AccountsTableId
    to AccountsTableId
    value Int
    tag TagsTableId
    description String
    deriving Show

ReservedTable
    en String
    ref Int64

AccountsOptionsTable
    indexes ByteString
|]

instance B.Binary AccountsTableId where
    put accountsID =
        case toPersistValue accountsID of
            PersistInt64 val -> B.put val
            _                -> error "Tables.hs: Binary AccountsTableId: something went wrong."
    get = do
        val <- B.get :: B.Get Int64
        case fromPersistValue $ PersistInt64 val of
            Right val' -> return val'
            Left err   -> error $ "Tables.hs: Binary AccountsTableId: Error: " ++ T.unpack err

accountsOptionsTableUpdate :: [AccountsTableId] -> IO ()
accountsOptionsTableUpdate accountIDs = do
    let blob = BL.toStrict . B.encode $ accountIDs
    runDB $ do
        options <- selectList [] []
        case options of
            []                   -> void $ insert $ AccountsOptionsTable blob
            [Entity optionsID _] -> replace optionsID $ AccountsOptionsTable blob
            _                    -> error "Tables.hs: accountsOptionsTableUpdate: something went wrong."

accountsOptionsTableRead :: IO [AccountsTableId]
accountsOptionsTableRead =
    runDB $ do
        option <- selectFirst [] []
        case option of
            Just (Entity _ (AccountsOptionsTable blob)) -> return $ B.decode . BB.toLazyByteString . BB.byteString $ blob
            Nothing -> return []

checkReservedID :: PersistField a => String -> (String -> IO a) -> IO a
checkReservedID name insertAction = do
    reserved <- runDB $ selectFirst [ ReservedTableEn ==. name] []

    case reserved of
        Just (Entity _ entity) ->
            case fromPersistValue $ PersistInt64 $ reservedTableRef entity of
                Right val -> return val
                Left err -> error $ "Tables.hs: Error: " ++ T.unpack err

        Nothing -> do
            tableID <- insertAction $ __ name

            case toPersistValue tableID of
                PersistInt64 val -> void $ runDB $ insert $ ReservedTable name val
                _                -> error "Tables.hs: Something went wrong."

            return tableID

tagsTableNoneID :: IO TagsTableId
tagsTableNoneID = checkReservedID "None" $ \name -> runDB $ insert $ TagsTable name

accountsTableIncomeID :: IO AccountsTableId
accountsTableIncomeID = checkReservedID "Income" $ \name -> runDB $ insert $ AccountsTable name 0

accountsTableOutcomeID :: IO AccountsTableId
accountsTableOutcomeID = checkReservedID "Outcome" $ \name -> runDB $ insert $ AccountsTable name 0

accountsTableCashID :: IO AccountsTableId
accountsTableCashID = checkReservedID "Cash" $ \name -> runDB $ insert $ AccountsTable name 0

initializeDatabase :: IO ()
initializeDatabase = do
    dir <- configGetDirectory
    createDirectoryIfMissing True dir

    runDB $ runMigration migrateAll

    void tagsTableNoneID
    void accountsTableIncomeID
    void accountsTableOutcomeID
    void accountsTableCashID

tagsTableSelectAll :: IO [Entity TagsTable]
tagsTableSelectAll = runDB $ selectList [] []

tagsTableSelectAllNames :: IO [String]
tagsTableSelectAllNames = do
    list <- tagsTableSelectAll
    return $
        map (\(Entity _ tag) -> tagsTableName tag) list

tagsTableSelectID :: String -> IO TagsTableId
tagsTableSelectID tagName = do
    (Just (Entity tagID _)) <- runDB $ selectFirst [ TagsTableName ==. tagName ] []
    return tagID

tagsTableSelectName :: TagsTableId -> IO String
tagsTableSelectName tagID = do
    (Just (Entity _ tagEntity))  <- runDB $ selectFirst [ TagsTableId ==. tagID ] []
    return $ tagsTableName tagEntity

accountsTableSelectAll :: IO [Entity AccountsTable]
accountsTableSelectAll = runDB $ selectList [] []

accountsTableSelectAllNames :: IO [String]
accountsTableSelectAllNames = do
    list <- accountsTableSelectAll
    return $
        map (\(Entity _ account) -> accountsTableName account) list

accountsTableSelectID :: String -> IO AccountsTableId
accountsTableSelectID accountName = do
    (Just (Entity accountID _)) <- runDB $ selectFirst [ AccountsTableName ==. accountName ] []
    return accountID

accountsTableSelectName :: AccountsTableId -> IO String
accountsTableSelectName accountID = do
    (Just (Entity _ accountEntity))  <- runDB $ selectFirst [ AccountsTableId ==. accountID ] []
    return $ accountsTableName accountEntity

accountsTableSelectBalance :: AccountsTableId -> IO Int64
accountsTableSelectBalance accountID = do
    (Just (Entity _ account)) <- runDB $ selectFirst [AccountsTableId ==. accountID] []
    return $ accountsTableBalance account

operationsTableSelectList :: Int -> Int -> IO [Entity OperationsTable]
operationsTableSelectList from to = runDB $ selectList [OperationsTableDate >=. from, OperationsTableDate <=. to] []