module Network.Beeminder.Internal where
import Blaze.ByteString.Builder
import Control.Applicative
import Control.Lens hiding ((&), (.=))
import Control.Monad
import Control.Monad.IO.Class
import Data.Aeson hiding (encode)
import Data.Aeson.Encode.Shim (encode)
import Data.Aeson.Types
import Data.ByteString (ByteString)
import Data.ByteString.Lazy (toStrict)
import Data.Char
import Data.Conduit
import Data.Default.Class
import Data.Default.Instances.Base
import Data.List
import Data.Maybe
import Data.Monoid hiding (All, Last)
import Data.Ratio
import Data.Scientific (Scientific)
import Data.Set (Set)
import Data.String
import Data.Text (Text)
import Data.Text.Encoding
import Data.Time.Clock.POSIX
import Data.Universe
import Data.Universe.Helpers
import Network.HTTP.Conduit
import Network.HTTP.Types
import qualified Blaze.ByteString.Builder.Char.Utf8 as Builder
import qualified Data.ByteString as BS
import qualified Data.Set as Set
import qualified Data.Vector as Vector
renderSimpleQueryText b xs = toByteString (renderQueryText b [(x, Just y) | (x, y) <- xs])
urlEncodedBodyText xs = urlEncodedBody [(encodeUtf8 x, encodeUtf8 y) | (x, y) <- xs]
instance Default Text where def = ""
type Token = ByteString
baseReq token segments = def
{ secure = True
, host = "www.beeminder.com"
, port = 443
, path = toByteString (encodePathSegments ("api":"v1":segments) <> Builder.fromString ".json")
, queryString = "?auth_token=" <> token
}
infixl 4 &
req & q
| BS.null (queryString req) = req { queryString = renderSimpleQueryText True q }
| otherwise = req { queryString = queryString req <> "&" <> renderSimpleQueryText False q }
class HasID a where _ID :: Simple Lens a Text
class HasUpdatedAt a where _UpdatedAt :: Simple Lens a Integer
class HasName a where _Name :: Simple Lens a Text
class HasTimezone a where _Timezone :: Simple Lens a Text
class HasUsername a where _Username :: Simple Lens a (Maybe Text)
class HasGoals a where _Goals :: Simple Lens a UserGoals
class HasGoalsFilter a where _GoalsFilter :: Simple Lens a (Maybe Burner)
class HasLevelOfDetail a where _LevelOfDetail :: Simple Lens a LevelOfGoalDetail
class HasPointCount a where _PointCount :: Simple Lens a (Maybe Integer)
class HasTimestamp a where _Timestamp :: Simple Lens a Integer
class HasValue a where _Value :: Simple Lens a Double
class HasComment a where _Comment :: Simple Lens a Text
class HasRequestID a where _RequestID :: Simple Lens a (Maybe Text)
class HasGoal a where _Goal :: Simple Lens a Text
class HasPointRequest a where _PointRequest :: Simple Lens a PointRequest
class HasPointRequests a where _PointRequests :: Simple Lens a [PointRequest]
class HasGetPoints a where _GetPoints :: Simple Lens a Bool
class HasTitle a where _Title :: Simple Lens a Text
class HasType a where _Type :: Simple Lens a GoalType
class HasTarget a where _Target :: Simple Lens a Target
class HasBehavior a where _Behavior :: Simple Lens a (Set Behavior)
class HasPanic a where _Panic :: Simple Lens a Double
data UserGoals
= Slugs [Text]
| Hashes [Goal]
| Diff [Goal] [Text]
deriving (Eq, Ord, Show, Read)
data User = User
{ uName :: Text
, uTimezone :: Text
, uGoals :: UserGoals
, uID :: Text
, uUpdatedAt :: Integer
} deriving (Eq, Ord, Show, Read)
instance HasName User where _Name = lens uName (\s b -> s { uName = b })
instance HasTimezone User where _Timezone = lens uTimezone (\s b -> s { uTimezone = b })
instance HasGoals User where _Goals = lens uGoals (\s b -> s { uGoals = b })
instance HasID User where _ID = lens uID (\s b -> s { uID = b })
instance HasUpdatedAt User where _UpdatedAt = lens uUpdatedAt (\s b -> s { uUpdatedAt = b })
data ID = ID { idID :: Text } deriving (Eq, Ord, Show, Read)
instance HasID ID where _ID = lens idID (\s b -> s { idID = b })
instance FromJSON ID where
parseJSON (Object v) = ID <$> v .: "id"
parseJSON o = typeMismatch "ID" o
instance FromJSON UserGoals where
parseJSON (Object v) = slugs <|> diff <|> hashes where
slugs = Slugs <$> v .: "goals"
hashes = Hashes <$> v .: "goals"
diff = Diff <$> v .: "goals" <*> (map idID <$> v .: "deleted_goals")
parseJSON o = typeMismatch "hash with goals (either a list of slugs or a list of goal objects)" o
instance FromJSON User where
parseJSON o@(Object v) = User
<$> v .: "username"
<*> v .: "timezone"
<*> parseJSON o
<*> v .: "id"
<*> v .: "updated_at"
parseJSON o = typeMismatch "user object" o
data Burner = Front | Back deriving (Eq, Ord, Show, Read, Bounded, Enum)
instance FromJSON Burner where parseJSON = showStringChoices "burner" (++"burner")
data LevelOfGoalDetail
= JustTheSlugs
| EverythingCurrent
| DiffSince
{ since :: Integer
, skinny :: Bool
}
deriving (Eq, Ord, Show, Read)
instance Default LevelOfGoalDetail where def = JustTheSlugs
data UserParameters = UserParameters
{ upUsername :: Maybe Text
, upGoalsFilter :: Maybe Burner
, upLevelOfDetail :: LevelOfGoalDetail
, upPointCount :: Maybe Integer
} deriving (Eq, Ord, Show, Read)
instance Default UserParameters where def = UserParameters def def def def
instance HasUsername UserParameters where _Username = lens upUsername (\s b -> s { upUsername = b })
instance HasGoalsFilter UserParameters where _GoalsFilter = lens upGoalsFilter (\s b -> s { upGoalsFilter = b })
instance HasLevelOfDetail UserParameters where _LevelOfDetail = lens upLevelOfDetail (\s b -> s { upLevelOfDetail = b })
instance HasPointCount UserParameters where _PointCount = lens upPointCount (\s b -> s { upPointCount = b })
maybeMe :: HasUsername a => a -> Text
maybeMe v = fromMaybe "me" (view _Username v)
textShow, lowerShow :: Show a => a -> Text
textShow = fromString . show
lowerShow = fromString . map toLower . show
user :: Token -> UserParameters -> Request
user t p
= baseReq t ["users", maybeMe p]
& case view _LevelOfDetail p of
JustTheSlugs -> []
EverythingCurrent -> [("associations", "true")]
DiffSince t d -> [("diff_since", lowerShow t), ("skinny", lowerShow d)]
++ [("goals_filter", lowerShow b) | Just b <- [view _GoalsFilter p]]
++ [("datapoints_count", lowerShow n) | Just n <- [view _PointCount p]]
data TimeFrame = Year | Month | Week | Day | Hour deriving (Eq, Ord, Show, Read, Bounded, Enum)
data Direction = Up | Down deriving (Eq, Ord, Show, Read, Bounded, Enum)
data Aggregate = Last | First | All | Min | Max | Mean | Sum
deriving (Eq, Ord, Show, Read, Bounded, Enum)
instance FromJSON TimeFrame where parseJSON = showStringChoices "timeframe" (take 1)
instance FromJSON Aggregate where parseJSON = showStringChoices "aggregate" id
instance FromJSON Direction where
parseJSON (Number (toRational -> 1)) = return Up
parseJSON (Number (toRational -> 1)) = return Down
parseJSON v = typeMismatch "direction (either 1 or -1)" v
showStringChoices s f = stringChoices s [(f . map toLower . show $ a, a) | a <- universeF]
stringChoices s cs_ v =
case v of
String t -> case lookup t cs of
Just r -> return r
Nothing -> wrong
_ -> wrong
where
cs = [(fromString n, r) | (n, r) <- cs_]
ns = map fst cs
ns' = init ns
n = last ns
wrong = typeMismatch errorMessage v
errorMessage = s <> " (" <> errorInternal <> ")"
errorInternal = case ns of
[] -> "there are no values of this type!"
[n] -> "must be " <> show n
[n, n'] -> "either " <> show n <> " or " <> show n'
_ -> "one of " <> intercalate "," (map show ns') <> ", or " <> show n
data Behavior
= Exponential
| Cumulative
| Odometer
| Edgy
| Noisy
| StepLine
| Rosy
| MovingAverage
| Aura
| Ephemeral
| Secret
| SecretPoints
deriving (Eq, Ord, Show, Read, Bounded, Enum)
parseBehaviorSet v = do
behaviors <- mapM (parseBehavior v) universeF
return (Set.fromList [b | (b, h) <- behaviors, polarity b h])
where
parseBehavior v b = (,) b <$> (v .: fieldName b)
fieldName Exponential = "exprd"
fieldName Cumulative = "kyoom"
fieldName Odometer = "odom"
fieldName Edgy = "edgy"
fieldName Noisy = "noisy"
fieldName StepLine = "steppy"
fieldName Rosy = "rosy"
fieldName MovingAverage = "movingav"
fieldName Aura = "aura"
fieldName Ephemeral = "ephem"
fieldName Secret = "secret"
fieldName SecretPoints = "datapublic"
polarity SecretPoints = not
polarity _ = id
data Target
= MissingDate { tValue :: Double, tRate :: Double }
| MissingValue { tDate :: Integer, tRate :: Double }
| MissingRate { tDate :: Integer, tValue :: Double }
deriving (Eq, Ord, Show, Read)
toDouble :: Scientific -> Double
toDouble = fromRational . toRational
toIntegerMaybe :: Scientific -> Maybe Integer
toIntegerMaybe s
| denominator r == 1 = Just $ numerator r
| otherwise = Nothing
where
r = toRational s
toTarget o (Null ) (Number v) (Number r) = return $ MissingDate (toDouble v) (toDouble r)
toTarget o (Number (toIntegerMaybe -> Just t)) (Null ) (Number r) = return $ MissingValue t (toDouble r)
toTarget o (Number (toIntegerMaybe -> Just t)) (Number v) (Null ) = return $ MissingRate t (toDouble v)
toTarget o _ _ _ = typeMismatch "target: two out of three values of [goal date,value,rate]" o
instance FromJSON Target where
parseJSON o@(Array vs) = case Vector.toList vs of
[t, v, r] -> toTarget o t v r
_ -> typeMismatch "target: two out of three values of [goal date,value,rate]" o
parseJSON o = typeMismatch "target (array)" o
data Contract
= Free
| Pledge { cPledge :: Integer }
| Stepdown { cPledge :: Integer, cAt :: Integer }
deriving (Eq, Ord, Show, Read)
instance FromJSON Contract where
parseJSON o@(Object v) = stepdown <|> pledge where
stepdown = Stepdown <$> v .: "amount" <*> v .: "stepdown_at"
pledge = Pledge <$> v .: "amount"
parseJSON Null = pure Free
parseJSON o = typeMismatch "contract (object)" o
data Goal = Goal
{ gID :: Text
, gGoal :: Text
, gUpdatedAt :: Integer
, gBurner :: Burner
, gTitle :: Text
, gTarget :: Target
, gRatePeriod :: TimeFrame
, gGraph :: Text
, gThumb :: Text
, gLoseDate :: Integer
, gPanic :: Double
, gQueued :: Bool
, gPoints :: [Point]
, gPointCount :: Integer
, gPledge :: Integer
, gStartDate :: Integer
, gStartValue :: Double
, gCurrentDate :: Integer
, gCurrentValue :: Double
, gReportedDate :: Integer
, gYaw :: Direction
, gSlope :: Direction
, gLane :: Integer
, gMathIsHard :: (Integer, Double, Double)
, gSummary :: (Text, Text, Text)
, gWon :: Bool
, gFrozen :: Bool
, gLost :: Bool
, gContract :: Contract
, gRoad :: [Target]
, gAggregate :: Aggregate
, gBehavior :: Set Behavior
} deriving (Eq, Ord, Show, Read)
instance FromJSON Goal where
parseJSON o@(Object v) = Goal
<$> v .: "id"
<*> v .: "slug"
<*> v .: "updated_at"
<*> v .: "burner"
<*> v .: "title"
<*> join (liftA3 (toTarget o) (v .: "goaldate") (v .: "goalval") (v .: "rate"))
<*> v .: "runits"
<*> v .: "graph_url"
<*> v .: "thumb_url"
<*> v .: "losedate"
<*> v .: "panic"
<*> v .: "queued"
<*> (v .: "datapoints" <|> pure [])
<*> v .: "numpts"
<*> v .: "pledge"
<*> v .: "initday"
<*> v .: "initval"
<*> v .: "curday"
<*> v .: "curval"
<*> v .: "lastday"
<*> v .: "yaw"
<*> v .: "dir"
<*> v .: "lane"
<*> v .: "mathishard"
<*> liftA3 (,,) (v .: "headsum") (v .: "limsum") (v .: "graphsum")
<*> v .: "won"
<*> v .: "frozen"
<*> v .: "lost"
<*> v .: "contract"
<*> v .: "road"
<*> v .: "aggday"
<*> parseBehaviorSet v
parseJSON o = typeMismatch "goal (object)" o
data GoalType = Hustler | Biker | FatLoser | Gainer | Inboxer | Drinker deriving (Eq, Ord, Show, Read, Bounded, Enum)
instance Default GoalType where def = minBound
gType :: Goal -> Maybe GoalType
gType g = case (gYaw g, gSlope g, gAggregate g, mungeBehavior g) of
(Up , Up , All , [Cumulative, StepLine] ) -> Just Hustler
(Up , Up , All , [Odometer, StepLine] ) -> Just Biker
(Down, Down, Min , [Noisy, Rosy, MovingAverage, Aura]) -> Just FatLoser
(Up , Up , Last, [Noisy, Rosy, MovingAverage, Aura]) -> Just Gainer
(Down, Down, Min , [StepLine] ) -> Just Inboxer
(Down, Up , All , [Cumulative, Edgy, StepLine] ) -> Just Drinker
_ -> Nothing
where
mungeBehavior = filter (`notElem` [Secret, SecretPoints]) . Set.toAscList . gBehavior
data GoalParameters = GoalParameters
{ gpUsername :: Maybe Text
, gpGoal :: Text
, gpGetPoints :: Bool
} deriving (Eq, Ord, Show, Read)
instance Default GoalParameters where def = GoalParameters def def False
instance HasUsername GoalParameters where _Username = lens gpUsername (\s b -> s { gpUsername = b })
instance HasGoal GoalParameters where _Goal = lens gpGoal (\s b -> s { gpGoal = b })
instance HasGetPoints GoalParameters where _GetPoints = lens gpGetPoints (\s b -> s { gpGetPoints = b })
goal :: Token -> GoalParameters -> Request
goal t p = baseReq t ["users", maybeMe p, "goals", view _Goal p]
& [("datapoints", "true") | view _GetPoints p]
data AllGoalsParameters = AllGoalsParameters
{ agpUsername :: Maybe Text
, agpGoalsFilter :: Maybe Burner
} deriving (Eq, Ord, Show, Read)
instance Default AllGoalsParameters where def = AllGoalsParameters def def
instance HasUsername AllGoalsParameters where _Username = lens agpUsername (\s b -> s { agpUsername = b })
instance HasGoalsFilter AllGoalsParameters where _GoalsFilter = lens agpGoalsFilter (\s b -> s { agpGoalsFilter = b })
allGoals :: Token -> AllGoalsParameters -> Request
allGoals t p = baseReq t ["users", maybeMe p, "goals"]
& [("filter", lowerShow b <> "burner") | Just b <- [view _GoalsFilter p]]
data CreateGoalParameters = CreateGoalParameters
{ cgpUsername :: Maybe Text
, cgpGoal :: Text
, cgpTitle :: Text
, cgpType :: GoalType
, cgpTarget :: Target
, cgpValue :: Double
, cgpBehavior :: Set Behavior
, cgpPanic :: Double
} deriving (Eq, Ord, Show, Read)
defaultPanic = 54000
instance Default CreateGoalParameters where def = CreateGoalParameters def def def def (MissingDate 1 1) def (Set.singleton SecretPoints) defaultPanic
instance HasUsername CreateGoalParameters where _Username = lens cgpUsername (\s b -> s { cgpUsername = b })
instance HasGoal CreateGoalParameters where _Goal = lens cgpGoal (\s b -> s { cgpGoal = b })
instance HasTitle CreateGoalParameters where _Title = lens cgpTitle (\s b -> s { cgpTitle = b })
instance HasType CreateGoalParameters where _Type = lens cgpType (\s b -> s { cgpType = b })
instance HasTarget CreateGoalParameters where _Target = lens cgpTarget (\s b -> s { cgpTarget = b })
instance HasValue CreateGoalParameters where _Value = lens cgpValue (\s b -> s { cgpValue = b })
instance HasBehavior CreateGoalParameters where _Behavior = lens cgpBehavior (\s b -> s { cgpBehavior = b })
instance HasPanic CreateGoalParameters where _Panic = lens cgpPanic (\s b -> s { cgpPanic = b })
createGoal :: Token -> CreateGoalParameters -> Request
createGoal t p = urlEncodedBodyText (
[ ("slug" , view _Goal $ p)
, ("title" , view _Title $ p)
, ("goal_type" , lowerShow . view _Type $ p)
, ("initval" , lowerShow . view _Value $ p)
, ("ephem" , lowerShow . Set.member Ephemeral . view _Behavior $ p)
, ("panic" , lowerShow . view _Panic $ p)
, ("secret" , lowerShow . Set.member Secret . view _Behavior $ p)
, ("datapublic", lowerShow . Set.notMember SecretPoints . view _Behavior $ p)
, ("dryrun" , "false")
]
++ renderTarget (view _Target p)
)
(baseReq t ["users", maybeMe p, "goals"])
where
renderTarget (MissingDate v r) = [ ("goalval", lowerShow v), ("rate", lowerShow r)]
renderTarget (MissingValue t r) = [("goaldate", lowerShow t), ("rate", lowerShow r)]
renderTarget (MissingRate t v ) = [("goaldate", lowerShow t), ("goalval", lowerShow v) ]
data Point = Point
{ pTimestamp :: Integer
, pValue :: Double
, pComment :: Text
, pRequestID :: Maybe Text
, pID :: Text
, pUpdatedAt :: Integer
} deriving (Eq, Ord, Show, Read)
instance HasTimestamp Point where _Timestamp = lens pTimestamp (\s b -> s { pTimestamp = b })
instance HasValue Point where _Value = lens pValue (\s b -> s { pValue = b })
instance HasComment Point where _Comment = lens pComment (\s b -> s { pComment = b })
instance HasRequestID Point where _RequestID = lens pRequestID (\s b -> s { pRequestID = b })
instance HasID Point where _ID = lens pID (\s b -> s { pID = b })
instance HasUpdatedAt Point where _UpdatedAt = lens pUpdatedAt (\s b -> s { pUpdatedAt = b })
instance FromJSON Point where
parseJSON o@(Object v) = Point
<$> v .: "timestamp"
<*> v .: "value"
<*> v .: "comment"
<*> v .: "requestid"
<*> v .: "id"
<*> v .: "updated_at"
parseJSON o = typeMismatch "point" o
data PointsParameters = PointsParameters
{ ppUsername :: Maybe Text
, ppGoal :: Text
} deriving (Eq, Ord, Show, Read)
instance Default PointsParameters where def = PointsParameters def def
instance HasUsername PointsParameters where _Username = lens ppUsername (\s b -> s { ppUsername = b })
instance HasGoal PointsParameters where _Goal = lens ppGoal (\s b -> s { ppGoal = b })
points :: Token -> PointsParameters -> Request
points t p = baseReq t ["users", maybeMe p, "goals", view _Goal p, "datapoints"]
data PointRequest = PointRequest
{ prTimestamp :: Integer
, prValue :: Double
, prComment :: Text
, prRequestID :: Maybe Text
} deriving (Eq, Ord, Show, Read)
instance Default PointRequest where def = PointRequest def def def def
instance HasTimestamp PointRequest where _Timestamp = lens prTimestamp (\s b -> s { prTimestamp = b })
instance HasValue PointRequest where _Value = lens prValue (\s b -> s { prValue = b })
instance HasComment PointRequest where _Comment = lens prComment (\s b -> s { prComment = b })
instance HasRequestID PointRequest where _RequestID = lens prRequestID (\s b -> s { prRequestID = b })
instance ToJSON PointRequest where
toJSON p = object $
[ "timestamp" .= view _Timestamp p
, "value" .= view _Value p
, "comment" .= view _Comment p
] ++
[ "requestid" .= requestid | Just requestid <- [view _RequestID p]]
now :: (MonadIO m, HasTimestamp a) => a -> m a
now a = liftIO $ flip (set _Timestamp) a . round <$> getPOSIXTime
data CreatePointParameters = CreatePointParameters
{ cppUsername :: Maybe Text
, cppGoal :: Text
, cppPointRequest :: PointRequest
} deriving (Eq, Ord, Show, Read)
instance Default CreatePointParameters where def = CreatePointParameters def def def
instance HasUsername CreatePointParameters where _Username = lens cppUsername (\s b -> s { cppUsername = b })
instance HasGoal CreatePointParameters where _Goal = lens cppGoal (\s b -> s { cppGoal = b })
instance HasPointRequest CreatePointParameters where _PointRequest = lens cppPointRequest (\s b -> s { cppPointRequest = b })
instance HasTimestamp CreatePointParameters where _Timestamp = _PointRequest . _Timestamp
instance HasValue CreatePointParameters where _Value = _PointRequest . _Value
instance HasComment CreatePointParameters where _Comment = _PointRequest . _Comment
instance HasRequestID CreatePointParameters where _RequestID = _PointRequest . _RequestID
data CreatePointsParameters = CreatePointsParameters
{ cpspUsername :: Maybe Text
, cpspGoal :: Text
, cpspPointRequests :: [PointRequest]
} deriving (Eq, Ord, Show, Read)
instance Default CreatePointsParameters where def = CreatePointsParameters def def def
instance HasUsername CreatePointsParameters where _Username = lens cpspUsername (\s b -> s { cpspUsername = b })
instance HasGoal CreatePointsParameters where _Goal = lens cpspGoal (\s b -> s { cpspGoal = b })
instance HasPointRequests CreatePointsParameters where _PointRequests = lens cpspPointRequests (\s b -> s { cpspPointRequests = b })
createPoint , createPointNotify :: Token -> CreatePointParameters -> Request
createPoints, createPointsNotify :: Token -> CreatePointsParameters -> Request
createPointNotify = createPointInternal True
createPoint = createPointInternal False
tsvcArgs p =
[ ("timestamp", (textShow . view _Timestamp) p)
, ("value" , (textShow . view _Value ) p)
, ("comment" , ( view _Comment ) p)
]
createPointInternal sendmail t p = urlEncodedBodyText
( tsvcArgs p
++ [("requestid", r) | Just r <- [view _RequestID p]]
++ [("sendmail" , "true") | sendmail]
)
(baseReq t ["users", maybeMe p, "goals", view _Goal p, "datapoints"])
createPointsNotify = createPointsInternal True
createPoints = createPointsInternal False
createPointsInternal sendmail t p = urlEncodedBody
([("datapoints", toStrict . encode . view _PointRequests $ p)] ++
[("sendmail" , "true") | sendmail]
)
(baseReq t ["users", maybeMe p, "goals", view _Goal p, "datapoints", "create_all"])
data UpdatePointParameters = UpdatePointParameters
{ uppUsername :: Maybe Text
, uppGoal :: Text
, uppID :: Text
, uppTimestamp :: Integer
, uppValue :: Double
, uppComment :: Text
} deriving (Eq, Ord, Show, Read)
instance Default UpdatePointParameters where def = UpdatePointParameters def def def def def def
instance HasUsername UpdatePointParameters where _Username = lens uppUsername (\s b -> s { uppUsername = b })
instance HasGoal UpdatePointParameters where _Goal = lens uppGoal (\s b -> s { uppGoal = b })
instance HasID UpdatePointParameters where _ID = lens uppID (\s b -> s { uppID = b })
instance HasTimestamp UpdatePointParameters where _Timestamp = lens uppTimestamp (\s b -> s { uppTimestamp = b })
instance HasValue UpdatePointParameters where _Value = lens uppValue (\s b -> s { uppValue = b })
instance HasComment UpdatePointParameters where _Comment = lens uppComment (\s b -> s { uppComment = b })
updatePoint :: Token -> UpdatePointParameters -> Request
updatePoint t p = (urlEncodedBodyText
(tsvcArgs p)
(baseReq t ["users", maybeMe p, "goals", view _Goal p, "datapoints", view _ID p])
) { method = "PUT" }
data DeletePointParameters = DeletePointParameters
{ dppUsername :: Maybe Text
, dppGoal :: Text
, dppID :: Text
} deriving (Eq, Ord, Show, Read)
instance Default DeletePointParameters where def = DeletePointParameters def def def
instance HasUsername DeletePointParameters where _Username = lens dppUsername (\s b -> s { dppUsername = b })
instance HasGoal DeletePointParameters where _Goal = lens dppGoal (\s b -> s { dppGoal = b })
instance HasID DeletePointParameters where _ID = lens dppID (\s b -> s { dppID = b })
deletePoint :: Token -> DeletePointParameters -> Request
deletePoint t p = (baseReq t ["users", maybeMe p, "goals", view _Goal p, "datapoints", view _ID p]) { method = "DELETE" }
instance Universe Burner where universe = universeDef
instance Universe TimeFrame where universe = universeDef
instance Universe Aggregate where universe = universeDef
instance Universe Direction where universe = universeDef
instance Universe Behavior where universe = universeDef
instance Universe GoalType where universe = universeDef
instance Finite Burner
instance Finite TimeFrame
instance Finite Aggregate
instance Finite Direction
instance Finite Behavior
instance Finite GoalType