summaryrefslogtreecommitdiff
path: root/verve-wireless/main.hs
blob: 9e9fb56a4e253725bbacadf8dc05e0d1fcb04314 (plain)
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
208
209
210
211
212
213
214
215
216
217
218
219
#!/usr/bin/env stack
{- stack
     --nix
     --resolver lts-10.3
     --install-ghc
     runghc
     --package http-types
     --package yesod
     --package yesod-core
     --package text
     --package aeson
     --package acid-state
     --package ixset
     --package split
     --package conduit
     --package conduit-extra
-}


{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE RecordWildCards #-}

import Control.Exception (bracket)
import Control.Monad.Reader (ask)
import Control.Monad.State (get, put)
import Data.Acid.Advanced (groupUpdates)
import Data.Acid.Local (createCheckpointAndClose)
import Data.Acid
import Data.Conduit
import qualified Data.Conduit.Text as CT
import Data.Conduit.Binary (sourceFile)
import qualified Data.Conduit.List as CL
import Data.Data (Data, Typeable)
import Data.IxSet (Indexable(..), IxSet(..), (@=), Proxy(..), getOne, ixFun, ixSet)
import qualified Data.IxSet as IxSet
import Data.List.Split (splitOn)
import Data.SafeCopy
import qualified Data.Text as T
import Data.Text (Text, pack, unpack)
import Data.Text.Encoding (decodeUtf8)
import qualified Data.Vector as V
import qualified Data.Vector.Generic as GV
import qualified Data.Vector.Generic.Mutable as GMV
import GHC.Generics
import Yesod hiding (Update, update, get)

newtype BusinessId = BusinessId { unBusinessId :: Int }
  deriving (Show, Eq, Enum, Ord, Data, Typeable, Generic)

$(deriveSafeCopy 0 'base ''BusinessId)

instance ToJSON BusinessId where
  toJSON BusinessId{..} = toJSON unBusinessId

instance FromJSON BusinessId

data Business = Business
  { businessId :: BusinessId
  , name :: Text
  , address :: Text
  , city :: Text
  , state :: Text
  , postalCode :: Text
  , country :: Text
  , latitude :: Double
  , longitude :: Double
  }
  deriving (Show, Eq, Ord, Generic, ToJSON, FromJSON)

$(deriveSafeCopy 0 'base ''Business)

newtype Name = Name Text deriving (Eq, Ord, Data, Typeable)
newtype Address = Address Text deriving (Eq, Ord, Data, Typeable)
newtype City = City Text deriving (Eq, Ord, Data, Typeable)
newtype State = State Text deriving (Eq, Ord, Data, Typeable)
newtype PostalCode = PostalCode Text deriving (Eq, Ord, Data, Typeable)
newtype Country = Country Text deriving (Eq, Ord, Data, Typeable)
newtype Latitude = Latitude Double deriving (Eq, Ord, Data, Typeable)
newtype Longitude = Longitude Double deriving (Eq, Ord, Data, Typeable)

$(deriveSafeCopy 0 'base ''Name)
$(deriveSafeCopy 0 'base ''Address)
$(deriveSafeCopy 0 'base ''City)
$(deriveSafeCopy 0 'base ''State)
$(deriveSafeCopy 0 'base ''PostalCode)
$(deriveSafeCopy 0 'base ''Country)
$(deriveSafeCopy 0 'base ''Latitude)
$(deriveSafeCopy 0 'base ''Longitude)

instance Indexable Business where
  empty = ixSet [ ixFun $ \b -> [ Name $ name b ]
                , ixFun $ \b -> [ Address $ address b ]
                , ixFun $ \b -> [ City $ city b ]
                , ixFun $ \b -> [ State $ state b ]
                , ixFun $ \b -> [ PostalCode $ postalCode b ]
                , ixFun $ \b -> [ Country $ country b ]
                , ixFun $ \b -> [ Latitude $ latitude b ]
                , ixFun $ \b -> [ Longitude $ longitude b ]
                ]

data Database = Database
  { nextBusinessId :: BusinessId
  , businesses :: IxSet Business
  }
  deriving (Typeable)

instance Data Database
$(deriveSafeCopy 0 'base ''Database)

initDatabase :: Database
initDatabase = Database
  { nextBusinessId = BusinessId 1 -- ^ Index starting at 1
  , businesses = empty
  }

data AddBusinessData = AddBusinessData
  { _name :: Text
  , _address :: Text
  , _city :: Text
  , _state :: Text
  , _postalCode :: Text
  , _country :: Text
  , _latitude :: Double
  , _longitude :: Double
  }

addBusiness :: AddBusinessData -> Update Database Business
addBusiness AddBusinessData{..} = do
  db@Database{..} <- get
  let b = Business { businessId = nextBusinessId
                   , name = _name
                   , address = _address
                   , city = _city
                   , state = _state
                   , postalCode = _postalCode
                   , country = _country
                   , latitude = _latitude
                   , longitude = _longitude
                   }
  put $ db { nextBusinessId = succ nextBusinessId
           , businesses = IxSet.insert b businesses
           }
  return b

$(makeAcidic ''Database [])

mkYesod "App" [parseRoutes|/upload UploadR GET POST|]

data App = App
  { appState :: AcidState Database
  }

instance Yesod App

instance RenderMessage App FormMessage where
  renderMessage _ _ = defaultFormMessage

uploadForm = renderDivs $ fileAFormReq "File: "

getUploadR = do
  ((_, widget), enctype) <- runFormPost uploadForm
  defaultLayout [whamlet|
<p>Upload "offers_poi.tsv" here:
<form method=post enctype=#{enctype}>
  ^{widget}
  <br>
  <input type=submit>
|]


-- toBusinessData = map AddBusinessData . map (splitOn "\t") . lines

postUploadR = do
  ((result, widget), enctype) <- runFormPost uploadForm
  let mFile = case result of
        FormSuccess res -> Just res
        _ -> Nothing
  defaultLayout $ do
    [whamlet|
$maybe f <- mFile
  <p>File received: #{fileName f}
<p>Upload "offers_poi.tsv" here:
<form method=post enctype=#{enctype}>
  ^{widget}
  <br>
  <input type=submit>
|]

main = do
  bracket
    (openLocalState initDatabase)
    (createCheckpointAndClose)
    (\db -> do
        putStrLn "Ready"
        warp 3000 (App db))



-- TSV File utils
--------------------


readTsvFile :: _
readTsvFile fp = runConduitRes
  $ sourceFile fp
  .| CT.decode CT.utf8
  .| CT.lines
  .| CL.map (T.split (=='\t'))