prosper (empty) → 0.1.0.0
raw patch · 14 files changed
+866/−0 lines, 14 filesdep +HsOpenSSLdep +aesondep +basesetup-changed
Dependencies added: HsOpenSSL, aeson, base, bytestring, cereal, containers, http-streams, io-streams, mtl, text, transformers, vector
Files
- LICENSE +30/−0
- Setup.hs +2/−0
- prosper.cabal +48/−0
- src/Prosper.hs +50/−0
- src/Prosper/Account.hs +53/−0
- src/Prosper/Commands.hs +77/−0
- src/Prosper/Internal/CSV.hs +31/−0
- src/Prosper/Internal/JSON.hs +49/−0
- src/Prosper/Internal/Request.hs +39/−0
- src/Prosper/Invest.hs +107/−0
- src/Prosper/Listing.hs +241/−0
- src/Prosper/Money.hs +3/−0
- src/Prosper/Note.hs +120/−0
- src/Prosper/User.hs +16/−0
+ LICENSE view
@@ -0,0 +1,30 @@+Copyright (c) 2015, Matthew Wraith++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++ * Redistributions of source code must retain the above copyright+ notice, this list of conditions and the following disclaimer.++ * Redistributions in binary form must reproduce the above+ copyright notice, this list of conditions and the following+ disclaimer in the documentation and/or other materials provided+ with the distribution.++ * Neither the name of Matthew Wraith nor the names of other+ contributors may be used to endorse or promote products derived+ from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ prosper.cabal view
@@ -0,0 +1,48 @@+-- Initial prosper.cabal generated by cabal init. For further +-- documentation, see http://haskell.org/cabal/users-guide/++name: prosper+version: 0.1.0.0+synopsis: Bindings to the Prosper marketplace API+-- description: +homepage: https://api.prosper.com+license: BSD3+license-file: LICENSE+author: Matthew Wraith+maintainer: wraithm@gmail.com+copyright: (c) 2015 Matthew Wraith+category: Finance+build-type: Simple+cabal-version: >=1.10++library+ exposed-modules: + Prosper, + Prosper.Account, + Prosper.Listing, + Prosper.User, + Prosper.Note, + Prosper.Money, + Prosper.Commands, + Prosper.Invest,+ Prosper.Internal.Request, + Prosper.Internal.JSON, + Prosper.Internal.CSV++ build-depends: + base >=4.8 && <4.9, + transformers >=0.4 && <0.5, + text >=1.2 && <1.3, + bytestring >=0.10 && <0.11, + mtl >=2.2 && <2.3, + containers >=0.5 && <0.6,+ vector,+ aeson,+ cereal,+ io-streams,+ HsOpenSSL,+ http-streams+++ hs-source-dirs: src+ default-language: Haskell2010
+ src/Prosper.hs view
@@ -0,0 +1,50 @@+-- | These are bindings to the Prosper marketplace API.+-- The documentation for the API can be seen at+-- https://api.prosper.com/.+module Prosper+ (+ -- * Commands+ invest+ , account+ , allListings+ , notes+ , listingFromNote++ , AccountException (..)++ -- * User+ , User (..)++ -- * Account+ , Account (..)+ , emptyAccount++ -- * Listing+ , Listing (..)+ , Offer (..)+ , Credit (..)+ , Rating (..)+ , Category (..)+ , Status (..)++ -- * Note+ , NoteStatus (..)+ , NoteDefaultReason (..)+ , Note (..)++ -- * Invest+ , InvestStatus (..)+ , InvestMessage (..)+ , InvestResponse (..)++ -- * Types+ , Money+ ) where++import Prosper.Account+import Prosper.Commands+import Prosper.Invest+import Prosper.Listing+import Prosper.Money+import Prosper.Note+import Prosper.User
+ src/Prosper/Account.hs view
@@ -0,0 +1,53 @@+{-# LANGUAGE OverloadedStrings #-}++module Prosper.Account+ ( Account (..)+ , emptyAccount+ ) where++import Control.Applicative ((<$>), (<*>))+import Control.Monad (mzero)++import Data.Aeson++import Prosper.Money++-- | A data structure for holding account information for Prosper+data Account = Account+ { availableCash :: !Money+ -- ^ Available cash for investing+ , pendingInvestments :: !Money+ -- ^ Amount of money invested that is pending origination+ , totPrincipalRecvdOnActiveNotes :: !Money+ -- ^ Total principal payments received on active notes+ , totalInvestedOnActiveNotes :: !Money+ -- ^ Total amount invested+ , outstandingPrincipalActiveNotes :: !Money+ -- ^ Total principal outstanding on active notes+ , -- | Total dollars held in investible cash,+ -- pending orders in the market, outstanding principal+ totalAccountValue :: !Money+ , pendingInvestmentsSecondaryMkt :: !Money+ -- ^ Investments on Folio+ , pendingQuickInvestOrders :: !Money+ -- ^ Prosper Quick Invests+ } deriving (Show, Eq)++-- | An empty account. All values are zero.+emptyAccount :: Account+emptyAccount = Account 0 0 0 0 0 0 0 0++-- | Parser for account info+--+-- Located at api/Account+instance FromJSON Account where+ parseJSON (Object v) = Account+ <$> v .: "AvailableCashBalance"+ <*> v .: "PendingInvestmentsPrimaryMkt"+ <*> v .: "TotalPrincipalReceivedOnActiveNotes"+ <*> v .: "TotalAmountInvestedOnActiveNotes"+ <*> v .: "OutstandingPrincipalOnActiveNotes"+ <*> v .: "TotalAccountValue"+ <*> v .: "PendingInvestmentsSecondaryMkt"+ <*> v .: "PendingQuickInvestOrders"+ parseJSON _ = mzero
+ src/Prosper/Commands.hs view
@@ -0,0 +1,77 @@+{-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE OverloadedStrings #-}++module Prosper.Commands+ ( invest+ , account+ , allListings+ , notes+ , listingFromNote++ , listingCSV++ , AccountException (..)+ ) where++import Control.Exception (Exception, throwIO)+import Control.Monad (when)++import Data.ByteString.Char8 as C+import Data.Maybe (listToMaybe)+import Data.Monoid ((<>))+import Data.Typeable (Typeable)+import Data.Vector (Vector)++import Network.Http.Client+import System.IO.Streams (InputStream)++import Prosper.Account+import Prosper.Internal.JSON+import Prosper.Internal.CSV+import Prosper.Invest+import Prosper.Listing+import Prosper.Money+import Prosper.Note+import Prosper.User++-- | An investment request. This requires a 'User', an amount of 'Money', and a 'Listing'.+invest :: User -> Money -> Listing -> IO InvestResponse+invest user amt l = investRequest user (listingId l) amt++-- | Request 'Account' information from Prosper+account :: User -> IO Account+account user = jsonGetHandler user "Account" accountHandler+ where+ accountHandler resp is =+ if getStatusCode resp == 500+ then throwIO (AccountException (getStatusMessage resp))+ else jsonHandler resp is++-- | Used as a hack around the 500 Critical Exception error+data AccountException = AccountException ByteString+ deriving (Typeable, Show)++instance Exception AccountException++-- | Request notes for a Prosper user+notes :: User -> IO (Vector Note)+notes user = jsonGet user "Notes"++-- | Given a 'Note', look up the associated 'Listing' by the ListingId+listingFromNote :: User -> Note -> IO (Maybe Listing)+listingFromNote user (Note { listingNumber = lid }) = do+ ls <- jsonGet user command+ return $ listToMaybe ls+ where+ command = "ListingsHistorical?$filter=ListingNumber eq " <> C.pack (show lid)++-- | Send a request to the Listings end-point at Prosper+allListings :: User -> IO (Vector Listing)+allListings user = jsonGet user "Listings"++-- | Request a particular 'Listing' via the CSV endpoint+listingCSV :: User -> Listing -> (InputStream ByteString -> IO a) -> IO a+listingCSV user l = csvGetStream user url+ where+ lid = listingId l+ url = "Listings?$filter=ListingNumber%20eq%20" <> C.pack (show lid)
+ src/Prosper/Internal/CSV.hs view
@@ -0,0 +1,31 @@+{-# LANGUAGE OverloadedStrings #-}++module Prosper.Internal.CSV+ ( csvGet+ , csvGetStream+ ) where++import Network.Http.Client+import System.IO.Streams (InputStream)++import Data.ByteString (ByteString)++import Prosper.Money+import Prosper.User+import Prosper.Internal.Request++-- | Make a GET request to Prosper's CSV api, return the raw CSV data+csvGet+ :: User -- ^ The user name and password for the prosper user+ -> ByteString -- ^ The name of the API service, relative to the API url+ -> IO ByteString -- ^ Raw CSV response+csvGet userInfo url = issueRequest userInfo url GET "text/csv" concatHandler []++-- | Make a GET request to Prosper's CSV api, return the raw CSV data+csvGetStream+ :: User -- ^ The user name and password for the prosper user+ -> ByteString -- ^ The name of the API service, relative to the API url+ -> (InputStream ByteString -> IO a)+ -> IO a -- ^ Raw CSV response+csvGetStream userInfo url handler =+ issueRequest userInfo url GET "text/csv" (\_ is -> handler is) []
+ src/Prosper/Internal/JSON.hs view
@@ -0,0 +1,49 @@+{-# LANGUAGE OverloadedStrings #-}++module Prosper.Internal.JSON+ ( jsonGet+ , jsonGetHandler+ , investRequest+ ) where++import Network.Http.Client+import System.IO.Streams (InputStream)++import Data.Aeson (FromJSON)+import Data.ByteString (ByteString)+import qualified Data.ByteString.Char8 as C+import Data.Monoid ((<>))++import Prosper.Internal.Request+import Prosper.Invest+import Prosper.Money+import Prosper.User++-- | Make a GET request to Prosper's JSON api, return the parsed JSON data+jsonGet+ :: FromJSON a+ => User -- ^ The user name and password for the prosper user+ -> ByteString -- ^ The name of the API service, relative to the API url+ -> IO a -- ^ JSON response+jsonGet userInfo url =+ issueRequest userInfo url GET "application/json" jsonHandler []++jsonGetHandler+ :: FromJSON a+ => User -- ^ The user name and password for the prosper user+ -> ByteString -- ^ The name of the API service, relative to the API url+ -> (Response -> InputStream ByteString -> IO a)+ -> IO a -- ^ JSON response+jsonGetHandler userInfo url handler =+ issueRequest userInfo url GET "application/json" handler []++investRequest+ :: User+ -> Int -- ^ The listing id+ -> Money -- ^ Amount+ -> IO InvestResponse -- ^ JSON response from Prosper+investRequest userInfo listingId amount =+ issueRequest userInfo "Invest" POST "application/x-www-form-urlencoded" jsonHandler+ [ ("listingId", C.pack $ show listingId)+ , ("amount", C.pack $ show amount)+ ]
+ src/Prosper/Internal/Request.hs view
@@ -0,0 +1,39 @@+{-# LANGUAGE OverloadedStrings #-}++module Prosper.Internal.Request where++import Network.Http.Client+import OpenSSL+import System.IO.Streams (InputStream)++import Data.ByteString (ByteString)+import qualified Data.ByteString.Char8 as C+import Data.Monoid ((<>))++import Prosper.User++apiUrl :: ByteString+apiUrl = "api.prosper.com"++issueRequest+ :: User+ -> ByteString+ -> Method+ -> ContentType+ -> (Response -> InputStream ByteString -> IO a)+ -> [(ByteString, ByteString)]+ -> IO a+issueRequest (User user pass) url method ct handler params = withOpenSSL $ do+ ctx <- baselineContextSSL+ con <- openConnectionSSL ctx apiUrl 443+ req <- buildRequest $ do+ http method $ "/api/" <> url+ setAuthorizationBasic user pass+ setContentType ct+ sendRequest con req (setBody params)+ resp <- receiveResponse con handler+ closeConnection con+ return resp+ where+ setBody [] = emptyBody+ setBody xs = encodedFormBody xs
+ src/Prosper/Invest.hs view
@@ -0,0 +1,107 @@+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE OverloadedStrings #-}++module Prosper.Invest+ ( InvestStatus (..)+ , InvestMessage (..)+ , InvestResponse (..)+ ) where++import Control.Applicative (pure, (<$>), (<*>))+import Control.Monad (mzero)++import Data.Aeson hiding (Error, Success)+import Data.Serialize+import Data.Text.Read as R++import GHC.Generics++import Prosper.Money++-- | Status of an invest request+data InvestStatus+ = Success+ | Failed+ | Error+ | PartialSuccess+ deriving (Show, Eq, Read, Generic)++-- | Status of Invest request response JSON parser+instance FromJSON InvestStatus where+ parseJSON (String "SUCCESS") = pure Success+ parseJSON (String "FAILED") = pure Failed+ parseJSON (String "ERROR") = pure Error+ parseJSON (String "PARTIAL_SUCCESS") = pure PartialSuccess+ parseJSON _ = mzero++instance Serialize InvestStatus where++-- | Message associated with an error+data InvestMessage+ = NoError+ | InternalError+ | InvestedAmountLessThanRequested+ | ListingNotAvailable+ | ListingNotFound+ | InsufficientFunds+ | ListingClosedBeforeBidPlaced+ | ServerBusy+ | SuitabilityRequirementsNotMet+ | InvestedAmountLessThanMinimumRequired+ | OtherError -- ^ Not in the prosper docs, this is a catch all+ deriving (Show, Eq, Read, Generic)++-- | Parser for message in an error+instance FromJSON InvestMessage where+ parseJSON (String "NO_ERROR") = pure NoError+ parseJSON (String "INTERNAL_ERROR") = pure InternalError+ parseJSON (String "INVESTED_AMOUNT_LESS_THAN_REQUESTED") =+ pure InvestedAmountLessThanRequested+ parseJSON (String "LISTING_NOT_AVAILABLE") = pure ListingNotAvailable+ parseJSON (String "LISTING_NOT_FOUND") = pure ListingNotFound+ parseJSON (String "INSUFFICIENT_FUNDS") = pure InsufficientFunds+ parseJSON (String "NOT_ENOUGH_BALANCE_AVAILABLE") = pure InsufficientFunds -- Isn't in the Prosper Docs+ parseJSON (String "LISTING_CLOSED_BEFORE_BID_PLACED") =+ pure ListingClosedBeforeBidPlaced+ parseJSON (String "SERVER_BUSY") = pure ServerBusy+ parseJSON (String "SUITABILITY_REQUIREMENTS_NOT_MET") =+ pure SuitabilityRequirementsNotMet+ parseJSON (String "INVESTED_AMOUNT_LESS_THAN_MINIMUM_REQUIRED") =+ pure InvestedAmountLessThanMinimumRequired+ parseJSON _ = pure OtherError++instance Serialize InvestMessage where++-- | JSON response to an invest request+data InvestResponse = InvestResponse+ { investStatus :: InvestStatus+ , requestedAmount :: Money+ , investListingId :: Int+ , investMessage :: InvestMessage+ , amountInvested :: Money+ } deriving (Show, Generic)++newtype Money' = MkMoney { unMoney :: Money } deriving (Eq, Generic)++instance Show Money' where+ show (MkMoney x) = show x++instance FromJSON Money' where+ parseJSON (String x) = readDouble x+ where+ readDouble y = case R.double y of+ Right (z,_) -> pure $ MkMoney z+ Left _ -> mzero+ parseJSON _ = mzero++-- | Parser for invest request response on api/Invest+instance FromJSON InvestResponse where+ parseJSON (Object v) = InvestResponse+ <$> v .: "Status"+ <*> (unMoney <$> v .: "RequestedAmount")+ <*> v .: "ListingId"+ <*> v .: "Message"+ <*> (unMoney <$> v .: "AmountInvested")+ parseJSON _ = mzero++instance Serialize InvestResponse where
+ src/Prosper/Listing.hs view
@@ -0,0 +1,241 @@+{-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE OverloadedStrings #-}++module Prosper.Listing+ ( Listing(..)+ , Offer(..)+ , Credit(..)+ , Rating(..)+ , Category(..)+ , Status(..)+ ) where++import Control.Applicative (pure, (<$>), (<*>))+import Control.Monad (mzero)++import Data.Aeson+import Data.Text.Read as R+import Data.Typeable+import GHC.Generics++import Prosper.Money++data Listing = Listing+ { listingId :: !Int+ , status :: !Status+ , rating :: !Rating -- ^ Letter score assigned by Prosper+ , score :: !(Maybe Int) -- ^ Prosper score+ , category :: !Category -- ^ The reason for the loan (e.g. Auto, Student, etc)++ -- Market data+ , amountRemaining :: !Money++ , offer :: !Offer -- ^ Contract data+ , credit :: !Credit -- ^ Credit data+ } deriving Show+-- investmentType :: (Fractional | Whole)++-- | Two 'Listing's are equivalent if their 'listingId's are equal+instance Eq Listing where+ (Listing { listingId = id1 }) == (Listing { listingId = id2 }) =+ id1 == id2++-- | Data related to the listing's offer and contract terms+data Offer = Offer+ { requestAmount :: !Money+ , rate :: !Double -- ^ Interest rate for the borrower+ , termInMonths :: !Int -- ^ Integer number of months+ , yield :: !Double+ , effectiveYield :: !Double+ , apr :: !Double -- ^ APR for the borrower+ } deriving Show+-- estimatedLossRate :: Double -- Maybe Money?+-- estimatedReturn :: Double -- Maybe Money?+-- startDate :: Date -- Add dates later+-- creationDate :: Date+-- verificationStage :: Maybe Int -- Don't know what VerificationStage is...+-- Add WholeLoanStartDate and WholeLoanEndDate++-- | Data related to the credibility of the listing+data Credit = Credit+ { fico :: !Int+ , bankcardUtilization :: !Double+ , isHomeowner :: !Bool+ , debtToIncome :: !Double+ , monthsEmployed :: !(Maybe Int)+ , currentDelinquencies :: !(Maybe Int)+ , amountDelinquent :: !(Maybe Money)+ , openCreditLines :: !(Maybe Int)+ , totOpenRevolvingAccts :: !(Maybe Int)+ , revolvingBalance :: !(Maybe Money)+ , revolvingAvailableCredit :: !(Maybe Int) -- ^ Percent+ , incomeRange :: !(Maybe (Money, Money)) -- ^ It is possible that they're unemployed, that's Nothing+ , statedMonthlyIncome :: !(Maybe Money)+ , currentCreditLines :: !(Maybe Int)+ , nowDelinquentDerog :: !(Maybe Int)+ , wasDelinquentDerog :: !(Maybe Int)+ } deriving Show+-- firstCreditLine :: Date+-- creditLinesLast7Years :: Int+-- inquiriesLast6Months :: Int+-- delinquenciesLast7Years :: Int -- These are maybe too specific to Prosper+-- publicRecordsLast10Years :: Int+-- oldestTradeOpenDate :: Date++-- | Parse a 'Listing' from JSON+instance FromJSON Listing where+ parseJSON (Object v) = Listing+ <$> v .: "ListingNumber" -- Is listingid?++ -- Prosper-specific data+ <*> v .: "ListingStatus"+ <*> v .: "ProsperRating"+ <*> v .:? "ProsperScore"+ <*> v .: "ListingCategory"++ <*> v .: "AmountRemaining"++ <*> (Offer+ <$> v .: "ListingRequestAmount"+ <*> v .: "BorrowerRate"+ <*> v .: "ListingTerm"+ <*> v .: "LenderYield"+ <*> v .: "EffectiveYield"+ <*> v .: "BorrowerAPR")++ <*> (Credit+ <$> (unFico <$> v .: "FICOScore")+ <*> v .: "BankcardUtilization"+ <*> v .: "IsHomeowner"+ <*> v .: "DTIwProsperLoan"+ <*> v .:? "MonthsEmployed"+ <*> v .:? "CurrentDelinquencies"+ <*> v .:? "AmountDelinquent"+ <*> v .:? "OpenCreditLines"+ <*> v .:? "TotalOpenRevolvingAccounts"+ <*> v .:? "RevolvingBalance"+ <*> v .:? "RevolvingAvailablePercent"+ -- Prosper credit data+ <*> (unIR <$> v .: "IncomeRange")+ <*> v .:? "StatedMonthlyIncome"+ <*> v .:? "CurrentCreditLines"+ <*> v .:? "NowDelinquentDerog"+ <*> v .:? "WasDelinquentDerog")+ parseJSON _ = mzero++-- Prosper's API is really weird and has the FICO type as a String+-- in JSON.+newtype FICO = FICO { unFico :: Int } + deriving Eq++instance Show FICO where+ show (FICO x) = show x++instance FromJSON FICO where+ parseJSON (String s) = readInt s+ where+ readInt x = case R.decimal x of+ Right (y,_) -> pure $ FICO y+ Left _ -> mzero+ parseJSON _ = mzero++newtype IncomeRange = IR { unIR :: Maybe (Money, Money) } + deriving Eq++instance Show IncomeRange where+ show (IR (Just (l, h))) = "$" ++ show l ++ "-$" ++ show h+ show (IR Nothing) = "Not displayed or unemployed"++instance FromJSON IncomeRange where+ parseJSON (Number 0) = pure $ IR Nothing -- Not displayed+ parseJSON (Number 1) = pure $ IR (Just (0, 0)) -- Make $0+ parseJSON (Number 2) = pure $ IR (Just (1, 24999))+ parseJSON (Number 3) = pure $ IR (Just (25000, 49999))+ parseJSON (Number 4) = pure $ IR (Just (50000, 74999))+ parseJSON (Number 5) = pure $ IR (Just (75000, 99999))+ parseJSON (Number 6) = pure $ IR (Just (100000, 500000)) -- DOCTHIS 500,000 is just an arbitrary high value+ parseJSON (Number 7) = pure $ IR Nothing -- Unemployed+ parseJSON _ = pure $ IR Nothing++-- | Prosper ratings, 'AA' is the most credible. 'HR' is the least credible.+data Rating+ = HR+ | E+ | D+ | C+ | B+ | A+ | AA+ deriving (Show, Eq, Ord, Typeable, Generic, Read, Enum)++instance FromJSON Rating where+instance ToJSON Rating where++-- | The 'Category' of a loan is the type of loan.+-- This is basically copied verbatim from the Prosper API.+data Category+ = NotAvailable+ | DebtConsolidation+ | HomeImprovement+ | Business+ | PersonalLoan+ | StudentUse+ | Auto+ | Other+ | BabyAdoptionLoans+ | Boat+ | CosmeticProcedures+ | EngagementRingFinancing+ | GreenLoans+ | HouseholdExpenses+ | LargePurchases+ | MedicalDental+ | Motorcycle+ | RV+ | Taxes+ | Vacation+ | WeddingLoans+ deriving (Show, Eq, Typeable, Generic, Read)++instance FromJSON Category where+ parseJSON (Number 0) = pure NotAvailable+ parseJSON (Number 1) = pure DebtConsolidation+ parseJSON (Number 2) = pure HomeImprovement+ parseJSON (Number 3) = pure Business+ parseJSON (Number 4) = pure PersonalLoan+ parseJSON (Number 5) = pure StudentUse+ parseJSON (Number 6) = pure Auto+ parseJSON (Number 7) = pure Other+ parseJSON (Number 8) = pure BabyAdoptionLoans+ parseJSON (Number 9) = pure Boat+ parseJSON (Number 10) = pure CosmeticProcedures+ parseJSON (Number 11) = pure EngagementRingFinancing+ parseJSON (Number 12) = pure GreenLoans+ parseJSON (Number 13) = pure HouseholdExpenses+ parseJSON (Number 14) = pure LargePurchases+ parseJSON (Number 15) = pure MedicalDental+ parseJSON (Number 16) = pure Motorcycle+ parseJSON (Number 17) = pure RV+ parseJSON (Number 18) = pure Taxes+ parseJSON (Number 19) = pure Vacation+ parseJSON (Number 20) = pure WeddingLoans+ parseJSON _ = pure Other++data Status+ = Active+ | Withdrawn+ | Expired+ | ListingCompleted+ | ListingCancelled+ | Pending+ deriving (Show, Eq, Typeable, Generic)++instance FromJSON Status where+ parseJSON (Number 2) = pure Active+ parseJSON (Number 4) = pure Withdrawn+ parseJSON (Number 5) = pure Expired+ parseJSON (Number 6) = pure ListingCompleted+ parseJSON (Number 7) = pure ListingCancelled+ parseJSON (Number 8) = pure Pending+ parseJSON _ = mzero
+ src/Prosper/Money.hs view
@@ -0,0 +1,3 @@+module Prosper.Money where++type Money = Double
+ src/Prosper/Note.hs view
@@ -0,0 +1,120 @@+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE OverloadedStrings #-}++module Prosper.Note + ( NoteStatus (..)+ , NoteDefaultReason (..)+ , Note (..)+ ) where++import Control.Applicative (pure, (<$>), (<*>))++import Data.Aeson+import Data.Text++import GHC.Generics++import Prosper.Listing (Rating (..))+import Prosper.Money++data NoteStatus+ = OriginationDelayed+ | Current+ | ChargeOff+ | Defaulted+ | NoteCompleted+ | FinalPaymentInProgress+ | NoteCancelled+ deriving (Show, Eq)++instance ToJSON NoteStatus where+ toJSON OriginationDelayed = Number 0+ toJSON Current = Number 1+ toJSON ChargeOff = Number 2+ toJSON Defaulted = Number 3+ toJSON NoteCompleted = Number 4+ toJSON FinalPaymentInProgress = Number 5+ toJSON NoteCancelled = Number 6++instance FromJSON NoteStatus where+ parseJSON (Number 0) = pure OriginationDelayed+ parseJSON (Number 1) = pure Current+ parseJSON (Number 2) = pure ChargeOff+ parseJSON (Number 3) = pure Defaulted+ parseJSON (Number 4) = pure NoteCompleted+ parseJSON (Number 5) = pure FinalPaymentInProgress+ parseJSON (Number 6) = pure NoteCancelled+ parseJSON _ = fail "Could not parse NoteStatus"++data NoteDefaultReason+ = Delinquency+ | Bankruptcy+ | Deceased+ | Repurchased+ | PaidInFull+ | SettledInFull+ deriving (Show, Eq)++instance ToJSON NoteDefaultReason where+ toJSON Delinquency = Number 1+ toJSON Bankruptcy = Number 2+ toJSON Deceased = Number 3+ toJSON Repurchased = Number 4+ toJSON PaidInFull = Number 5+ toJSON SettledInFull = Number 6++instance FromJSON NoteDefaultReason where+ parseJSON (Number 1) = pure Delinquency+ parseJSON (Number 2) = pure Bankruptcy+ parseJSON (Number 3) = pure Deceased+ parseJSON (Number 4) = pure Repurchased+ parseJSON (Number 5) = pure PaidInFull+ parseJSON (Number 6) = pure SettledInFull+ parseJSON _ = fail "Could not parse NoteDefaultReason"++data Note = Note+ { loanNoteId :: Text+ , listingNumber :: Int+ , amountParticipation :: Money+ , totalAmountBorrowed :: Money+ , borrowerRate :: Double+ , noteRating :: Rating+ , term :: Int+ , ageInMonths :: Int+-- , originationDate :: Date+ , daysPastDue :: Int+ , principalBalance :: Money+ , principalRepaid :: Money+ , interestPaid :: Money+ , serviceFees :: Money+ , prosperFees :: Money+ , lateFees :: Money+ -- groupLeaderReward+ , noteStatus :: NoteStatus+ , noteDefaultReason :: Maybe NoteDefaultReason+ , isSold :: Bool+ } deriving (Show, Eq, Generic)++instance FromJSON Note where+ parseJSON (Object v) = Note+ <$> v .: "LoanNoteID"+ <*> v .: "ListingNumber"+ <*> v .: "AmountParticipation"+ <*> v .: "TotalAmountBorrowed"+ <*> v .: "BorrowerRate"+ <*> v .: "ProsperRating"+ <*> v .: "Term"+ <*> v .: "AgeInMonths"+ <*> v .: "DaysPastDue"+ <*> v .: "PrincipalBalance"+ <*> v .: "PrincipalRepaid"+ <*> v .: "InterestPaid"+ <*> v .: "ServiceFees"+ <*> v .: "ProsperFees"+ <*> v .: "LateFees"+ <*> v .: "NoteStatus"+ <*> v .:? "NoteDefaultReason"+ <*> v .: "IsSold"+ parseJSON _ = fail "Could not parse Note"++instance ToJSON Note where
+ src/Prosper/User.hs view
@@ -0,0 +1,16 @@+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RecordWildCards #-}++module Prosper.User+ ( User(..)+ ) where++import Control.Monad (when)++import Data.ByteString (ByteString)++-- | User info for Prosper data+data User = User+ { username :: !ByteString -- ^ Username for Prosper+ , password :: !ByteString -- ^ Password for Prosper+ } deriving (Show, Eq)