Add visits info (#3)
Co-authored-by: Smaug123 <patrick+github@patrickstevens.co.uk> Reviewed-on: #3
This commit is contained in:
@@ -21,8 +21,12 @@ module Program =
|
|||||||
RequiresAuth (fun auth -> ArgsCrate.make (FullnessArgs.Parse auth) Fullness.run))
|
RequiresAuth (fun auth -> ArgsCrate.make (FullnessArgs.Parse auth) Fullness.run))
|
||||||
|
|
||||||
"activity",
|
"activity",
|
||||||
("Get information about your gym usage",
|
("Get information about your aggregate gym usage",
|
||||||
RequiresAuth (fun auth -> ArgsCrate.make (MemberActivityArgs.Parse auth) MemberActivity.run))
|
RequiresAuth (fun auth -> ArgsCrate.make (MemberActivityArgs.Parse auth) MemberActivity.run))
|
||||||
|
|
||||||
|
"sessions",
|
||||||
|
("Get information about your individual sessions",
|
||||||
|
RequiresAuth (fun auth -> ArgsCrate.make (SessionsArgs.Parse auth) Sessions.run))
|
||||||
|]
|
|]
|
||||||
|> Map.ofArray
|
|> Map.ofArray
|
||||||
|
|
||||||
|
@@ -14,6 +14,7 @@
|
|||||||
<Compile Include="Fullness.fs" />
|
<Compile Include="Fullness.fs" />
|
||||||
<Compile Include="LookupGym.fs" />
|
<Compile Include="LookupGym.fs" />
|
||||||
<Compile Include="MemberActivity.fs" />
|
<Compile Include="MemberActivity.fs" />
|
||||||
|
<Compile Include="Sessions.fs" />
|
||||||
<Compile Include="Program.fs" />
|
<Compile Include="Program.fs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
49
PureGym.App/Sessions.fs
Normal file
49
PureGym.App/Sessions.fs
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
namespace PureGym.App
|
||||||
|
|
||||||
|
open Argu
|
||||||
|
open PureGym
|
||||||
|
open System
|
||||||
|
|
||||||
|
type SessionsArgsFragment =
|
||||||
|
| [<Mandatory ; EqualsAssignmentOrSpaced>] From_Date of string
|
||||||
|
| [<Mandatory ; EqualsAssignmentOrSpaced>] To_Date of string
|
||||||
|
|
||||||
|
interface IArgParserTemplate with
|
||||||
|
member s.Usage =
|
||||||
|
match s with
|
||||||
|
| From_Date _ -> "start of date range (inclusive) for query, which needs to parse as a DateTime"
|
||||||
|
| To_Date _ -> "end of date range (inclusive) for query, which needs to parse as a DateTime"
|
||||||
|
|
||||||
|
type SessionsArgs =
|
||||||
|
{
|
||||||
|
Creds : Auth
|
||||||
|
FromDate : DateTime
|
||||||
|
ToDate : DateTime
|
||||||
|
}
|
||||||
|
|
||||||
|
static member Parse
|
||||||
|
(auth : Auth)
|
||||||
|
(args : SessionsArgsFragment ParseResults)
|
||||||
|
: Result<SessionsArgs, ArguParseException>
|
||||||
|
=
|
||||||
|
let fromDate = args.GetResult SessionsArgsFragment.From_Date
|
||||||
|
let toDate = args.GetResult SessionsArgsFragment.To_Date
|
||||||
|
|
||||||
|
{
|
||||||
|
Creds = auth
|
||||||
|
FromDate = DateTime.Parse fromDate
|
||||||
|
ToDate = DateTime.Parse toDate
|
||||||
|
}
|
||||||
|
|> Ok
|
||||||
|
|
||||||
|
[<RequireQualifiedAccess>]
|
||||||
|
module Sessions =
|
||||||
|
|
||||||
|
let run (args : SessionsArgs) =
|
||||||
|
task {
|
||||||
|
let! client = Api.make args.Creds
|
||||||
|
let! activity = client.GetSessions args.FromDate args.ToDate
|
||||||
|
|
||||||
|
System.Console.WriteLine (string<Sessions> activity)
|
||||||
|
return 0
|
||||||
|
}
|
@@ -260,6 +260,74 @@ type MemberActivityDto =
|
|||||||
LastRefreshed = this.LastRefreshed
|
LastRefreshed = this.LastRefreshed
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type SessionsAggregate =
|
||||||
|
{
|
||||||
|
/// Number of gym "activities" within some query-defined time period; presumably this is like classes?
|
||||||
|
/// It's always 0 for me.
|
||||||
|
Activities : int
|
||||||
|
/// Number of visits to the gym within some query-defined time period.
|
||||||
|
Visits : int
|
||||||
|
/// In minutes: total time spent in gym during the query-defined time period.
|
||||||
|
Duration : int
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The DTO for gym info returned from the Sessions endpoint.
|
||||||
|
type VisitGym =
|
||||||
|
{
|
||||||
|
// Omitting Location, GymAccess, ContactInfo, TimeZone because these were all null for me
|
||||||
|
/// The PureGym ID of this gym, e.g. 19
|
||||||
|
Id : int
|
||||||
|
/// E.g. "London Oval", the canonical name of this gym
|
||||||
|
Name : string
|
||||||
|
/// For some reason this always seems to be "Blocked"
|
||||||
|
Status : string
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Summary of a single visit to a gym.
|
||||||
|
type Visit =
|
||||||
|
{
|
||||||
|
// Omitted Name because it always was null for me
|
||||||
|
/// Whether the Duration field is estimated.
|
||||||
|
IsDurationEstimated : bool
|
||||||
|
/// When the visit began.
|
||||||
|
StartTime : DateTime
|
||||||
|
/// In minutes.
|
||||||
|
Duration : int
|
||||||
|
/// Which gym was visited
|
||||||
|
Gym : VisitGym
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Human-readable non-round-trip representation.
|
||||||
|
override this.ToString () =
|
||||||
|
let startTime = this.StartTime.ToString "yyyy-MM-dd HH:mm"
|
||||||
|
$"%s{this.Gym.Name}: %s{startTime} (%i{this.Duration} minutes)"
|
||||||
|
|
||||||
|
/// Aggregate statistics for gym visits across a time period.
|
||||||
|
type SessionsSummary =
|
||||||
|
{
|
||||||
|
/// Aggregate stats for gym visits within the query-dependent time period.
|
||||||
|
Total : SessionsAggregate
|
||||||
|
/// Aggregate stats for gym visits "this week", whatever that means to PureGym.
|
||||||
|
ThisWeek : SessionsAggregate
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Human-readable non-round-trip representation.
|
||||||
|
override this.ToString () =
|
||||||
|
$"%i{this.Total.Visits} visits, totalling %i{this.Total.Duration} minutes"
|
||||||
|
|
||||||
|
type Sessions =
|
||||||
|
{
|
||||||
|
Summary : SessionsSummary
|
||||||
|
Visits : Visit list
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Human-readable non-round-trip representation.
|
||||||
|
override this.ToString () =
|
||||||
|
let summary = string<SessionsSummary> this.Summary
|
||||||
|
let visits = this.Visits |> Seq.map string<Visit> |> String.concat "\n"
|
||||||
|
|
||||||
|
$"%s{summary}\n%s{visits}"
|
||||||
|
|
||||||
/// The PureGym REST API. You probably want to instantiate one of these with `Api.make`.
|
/// The PureGym REST API. You probably want to instantiate one of these with `Api.make`.
|
||||||
[<Header("User-Agent", "PureGym/1523 CFNetwork/1312 Darwin/21.0.0")>]
|
[<Header("User-Agent", "PureGym/1523 CFNetwork/1312 Darwin/21.0.0")>]
|
||||||
type IPureGymApi =
|
type IPureGymApi =
|
||||||
@@ -283,6 +351,10 @@ type IPureGymApi =
|
|||||||
[<Get "v1/member/activity">]
|
[<Get "v1/member/activity">]
|
||||||
abstract GetMemberActivity : unit -> Task<MemberActivityDto>
|
abstract GetMemberActivity : unit -> Task<MemberActivityDto>
|
||||||
|
|
||||||
|
/// Get information about the individual visits to all PureGyms the currently-authenticated PureGym human has made.
|
||||||
|
[<Get "v2/gymSessions/member">]
|
||||||
|
abstract GetSessions : [<Query>] fromDate : DateTime -> [<Query>] toDate : DateTime -> Task<Sessions>
|
||||||
|
|
||||||
// [<Get "v1/member/activity/history">]
|
// [<Get "v1/member/activity/history">]
|
||||||
// abstract GetMemberActivityAll : unit -> Task<string>
|
// abstract GetMemberActivityAll : unit -> Task<string>
|
||||||
|
|
||||||
|
@@ -10,11 +10,16 @@ open System.Threading.Tasks
|
|||||||
// System.Text.Json does not support internal F# records as of .NET 8, presumably because it can't find the constructor.
|
// System.Text.Json does not support internal F# records as of .NET 8, presumably because it can't find the constructor.
|
||||||
// So we end up with this gruesome soup of C#.
|
// So we end up with this gruesome soup of C#.
|
||||||
// TODO(net8): make this internal
|
// TODO(net8): make this internal
|
||||||
|
/// An internal type. Don't use it.
|
||||||
type AuthResponseRaw [<JsonConstructor>] (access_token : string, expires_in : int, token_type : string, scope : string)
|
type AuthResponseRaw [<JsonConstructor>] (access_token : string, expires_in : int, token_type : string, scope : string)
|
||||||
=
|
=
|
||||||
|
/// Don't use this internal type.
|
||||||
member _.access_token = access_token
|
member _.access_token = access_token
|
||||||
|
/// Don't use this internal type.
|
||||||
member _.expires_in = expires_in
|
member _.expires_in = expires_in
|
||||||
|
/// Don't use this internal type.
|
||||||
member _.token_type = token_type
|
member _.token_type = token_type
|
||||||
|
/// Don't use this internal type.
|
||||||
member _.scope = scope
|
member _.scope = scope
|
||||||
|
|
||||||
/// A token which can be used to authenticate with the PureGym API.
|
/// A token which can be used to authenticate with the PureGym API.
|
||||||
|
@@ -138,12 +138,13 @@ PureGym.GymSelector.NewName [static method]: string -> PureGym.GymSelector
|
|||||||
PureGym.GymSelector.Tag [property]: [read-only] int
|
PureGym.GymSelector.Tag [property]: [read-only] int
|
||||||
PureGym.GymSelectorModule inherit obj
|
PureGym.GymSelectorModule inherit obj
|
||||||
PureGym.GymSelectorModule.canonicalId [static method]: PureGym.IPureGymApi -> PureGym.GymSelector -> int System.Threading.Tasks.Task
|
PureGym.GymSelectorModule.canonicalId [static method]: PureGym.IPureGymApi -> PureGym.GymSelector -> int System.Threading.Tasks.Task
|
||||||
PureGym.IPureGymApi - interface with 5 member(s)
|
PureGym.IPureGymApi - interface with 6 member(s)
|
||||||
PureGym.IPureGymApi.GetGym [method]: int -> PureGym.Gym System.Threading.Tasks.Task
|
PureGym.IPureGymApi.GetGym [method]: int -> PureGym.Gym System.Threading.Tasks.Task
|
||||||
PureGym.IPureGymApi.GetGymAttendance [method]: int -> PureGym.GymAttendance System.Threading.Tasks.Task
|
PureGym.IPureGymApi.GetGymAttendance [method]: int -> PureGym.GymAttendance System.Threading.Tasks.Task
|
||||||
PureGym.IPureGymApi.GetGyms [method]: unit -> PureGym.Gym list System.Threading.Tasks.Task
|
PureGym.IPureGymApi.GetGyms [method]: unit -> PureGym.Gym list System.Threading.Tasks.Task
|
||||||
PureGym.IPureGymApi.GetMember [method]: unit -> PureGym.Member System.Threading.Tasks.Task
|
PureGym.IPureGymApi.GetMember [method]: unit -> PureGym.Member System.Threading.Tasks.Task
|
||||||
PureGym.IPureGymApi.GetMemberActivity [method]: unit -> PureGym.MemberActivityDto System.Threading.Tasks.Task
|
PureGym.IPureGymApi.GetMemberActivity [method]: unit -> PureGym.MemberActivityDto System.Threading.Tasks.Task
|
||||||
|
PureGym.IPureGymApi.GetSessions [method]: System.DateTime -> System.DateTime -> PureGym.Sessions System.Threading.Tasks.Task
|
||||||
PureGym.Member inherit obj, implements PureGym.Member System.IEquatable, System.Collections.IStructuralEquatable, PureGym.Member System.IComparable, System.IComparable, System.Collections.IStructuralComparable
|
PureGym.Member inherit obj, implements PureGym.Member System.IEquatable, System.Collections.IStructuralEquatable, PureGym.Member System.IComparable, System.IComparable, System.Collections.IStructuralComparable
|
||||||
PureGym.Member..ctor [constructor]: (int, string, string, string, int, string, string, string, System.DateOnly, string, string, string, int, int, int)
|
PureGym.Member..ctor [constructor]: (int, string, string, string, int, string, string, string, System.DateOnly, string, string, string, int, int, int)
|
||||||
PureGym.Member.CompoundMemberId [property]: [read-only] string
|
PureGym.Member.CompoundMemberId [property]: [read-only] string
|
||||||
@@ -205,9 +206,47 @@ PureGym.MemberActivityThisMonth.LastRefreshed [property]: [read-only] System.Dat
|
|||||||
PureGym.MemberActivityThisMonth.TotalClasses [property]: [read-only] int
|
PureGym.MemberActivityThisMonth.TotalClasses [property]: [read-only] int
|
||||||
PureGym.MemberActivityThisMonth.TotalDurationMinutes [property]: [read-only] int
|
PureGym.MemberActivityThisMonth.TotalDurationMinutes [property]: [read-only] int
|
||||||
PureGym.MemberActivityThisMonth.TotalVisits [property]: [read-only] int
|
PureGym.MemberActivityThisMonth.TotalVisits [property]: [read-only] int
|
||||||
|
PureGym.Sessions inherit obj, implements PureGym.Sessions System.IEquatable, System.Collections.IStructuralEquatable, PureGym.Sessions System.IComparable, System.IComparable, System.Collections.IStructuralComparable
|
||||||
|
PureGym.Sessions..ctor [constructor]: (PureGym.SessionsSummary, PureGym.Visit list)
|
||||||
|
PureGym.Sessions.get_Summary [method]: unit -> PureGym.SessionsSummary
|
||||||
|
PureGym.Sessions.get_Visits [method]: unit -> PureGym.Visit list
|
||||||
|
PureGym.Sessions.Summary [property]: [read-only] PureGym.SessionsSummary
|
||||||
|
PureGym.Sessions.Visits [property]: [read-only] PureGym.Visit list
|
||||||
|
PureGym.SessionsAggregate inherit obj, implements PureGym.SessionsAggregate System.IEquatable, System.Collections.IStructuralEquatable, PureGym.SessionsAggregate System.IComparable, System.IComparable, System.Collections.IStructuralComparable
|
||||||
|
PureGym.SessionsAggregate..ctor [constructor]: (int, int, int)
|
||||||
|
PureGym.SessionsAggregate.Activities [property]: [read-only] int
|
||||||
|
PureGym.SessionsAggregate.Duration [property]: [read-only] int
|
||||||
|
PureGym.SessionsAggregate.get_Activities [method]: unit -> int
|
||||||
|
PureGym.SessionsAggregate.get_Duration [method]: unit -> int
|
||||||
|
PureGym.SessionsAggregate.get_Visits [method]: unit -> int
|
||||||
|
PureGym.SessionsAggregate.Visits [property]: [read-only] int
|
||||||
|
PureGym.SessionsSummary inherit obj, implements PureGym.SessionsSummary System.IEquatable, System.Collections.IStructuralEquatable, PureGym.SessionsSummary System.IComparable, System.IComparable, System.Collections.IStructuralComparable
|
||||||
|
PureGym.SessionsSummary..ctor [constructor]: (PureGym.SessionsAggregate, PureGym.SessionsAggregate)
|
||||||
|
PureGym.SessionsSummary.get_ThisWeek [method]: unit -> PureGym.SessionsAggregate
|
||||||
|
PureGym.SessionsSummary.get_Total [method]: unit -> PureGym.SessionsAggregate
|
||||||
|
PureGym.SessionsSummary.ThisWeek [property]: [read-only] PureGym.SessionsAggregate
|
||||||
|
PureGym.SessionsSummary.Total [property]: [read-only] PureGym.SessionsAggregate
|
||||||
PureGym.UsernamePin inherit obj, implements PureGym.UsernamePin System.IEquatable, System.Collections.IStructuralEquatable, PureGym.UsernamePin System.IComparable, System.IComparable, System.Collections.IStructuralComparable
|
PureGym.UsernamePin inherit obj, implements PureGym.UsernamePin System.IEquatable, System.Collections.IStructuralEquatable, PureGym.UsernamePin System.IComparable, System.IComparable, System.Collections.IStructuralComparable
|
||||||
PureGym.UsernamePin..ctor [constructor]: (string, string)
|
PureGym.UsernamePin..ctor [constructor]: (string, string)
|
||||||
PureGym.UsernamePin.get_Pin [method]: unit -> string
|
PureGym.UsernamePin.get_Pin [method]: unit -> string
|
||||||
PureGym.UsernamePin.get_Username [method]: unit -> string
|
PureGym.UsernamePin.get_Username [method]: unit -> string
|
||||||
PureGym.UsernamePin.Pin [property]: [read-only] string
|
PureGym.UsernamePin.Pin [property]: [read-only] string
|
||||||
PureGym.UsernamePin.Username [property]: [read-only] string
|
PureGym.UsernamePin.Username [property]: [read-only] string
|
||||||
|
PureGym.Visit inherit obj, implements PureGym.Visit System.IEquatable, System.Collections.IStructuralEquatable, PureGym.Visit System.IComparable, System.IComparable, System.Collections.IStructuralComparable
|
||||||
|
PureGym.Visit..ctor [constructor]: (bool, System.DateTime, int, PureGym.VisitGym)
|
||||||
|
PureGym.Visit.Duration [property]: [read-only] int
|
||||||
|
PureGym.Visit.get_Duration [method]: unit -> int
|
||||||
|
PureGym.Visit.get_Gym [method]: unit -> PureGym.VisitGym
|
||||||
|
PureGym.Visit.get_IsDurationEstimated [method]: unit -> bool
|
||||||
|
PureGym.Visit.get_StartTime [method]: unit -> System.DateTime
|
||||||
|
PureGym.Visit.Gym [property]: [read-only] PureGym.VisitGym
|
||||||
|
PureGym.Visit.IsDurationEstimated [property]: [read-only] bool
|
||||||
|
PureGym.Visit.StartTime [property]: [read-only] System.DateTime
|
||||||
|
PureGym.VisitGym inherit obj, implements PureGym.VisitGym System.IEquatable, System.Collections.IStructuralEquatable, PureGym.VisitGym System.IComparable, System.IComparable, System.Collections.IStructuralComparable
|
||||||
|
PureGym.VisitGym..ctor [constructor]: (int, string, string)
|
||||||
|
PureGym.VisitGym.get_Id [method]: unit -> int
|
||||||
|
PureGym.VisitGym.get_Name [method]: unit -> string
|
||||||
|
PureGym.VisitGym.get_Status [method]: unit -> string
|
||||||
|
PureGym.VisitGym.Id [property]: [read-only] int
|
||||||
|
PureGym.VisitGym.Name [property]: [read-only] string
|
||||||
|
PureGym.VisitGym.Status [property]: [read-only] string
|
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"version": "1.0",
|
"version": "2.0",
|
||||||
"publicReleaseRefSpec": [
|
"publicReleaseRefSpec": [
|
||||||
"^refs/heads/main$"
|
"^refs/heads/main$"
|
||||||
],
|
],
|
||||||
|
Reference in New Issue
Block a user