Add visits info #3
@@ -21,8 +21,12 @@ module Program =
|
||||
RequiresAuth (fun auth -> ArgsCrate.make (FullnessArgs.Parse auth) Fullness.run))
|
||||
|
||||
"activity",
|
||||
("Get information about your gym usage",
|
||||
("Get information about your aggregate gym usage",
|
||||
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
|
||||
|
||||
|
@@ -14,6 +14,7 @@
|
||||
<Compile Include="Fullness.fs" />
|
||||
<Compile Include="LookupGym.fs" />
|
||||
<Compile Include="MemberActivity.fs" />
|
||||
<Compile Include="Sessions.fs" />
|
||||
<Compile Include="Program.fs" />
|
||||
</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
|
||||
}
|
||||
|
||||
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`.
|
||||
[<Header("User-Agent", "PureGym/1523 CFNetwork/1312 Darwin/21.0.0")>]
|
||||
type IPureGymApi =
|
||||
@@ -283,6 +351,10 @@ type IPureGymApi =
|
||||
[<Get "v1/member/activity">]
|
||||
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">]
|
||||
// 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.
|
||||
// So we end up with this gruesome soup of C#.
|
||||
// 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)
|
||||
=
|
||||
/// Don't use this internal type.
|
||||
member _.access_token = access_token
|
||||
/// Don't use this internal type.
|
||||
member _.expires_in = expires_in
|
||||
/// Don't use this internal type.
|
||||
member _.token_type = token_type
|
||||
/// Don't use this internal type.
|
||||
member _.scope = scope
|
||||
|
||||
/// 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.GymSelectorModule inherit obj
|
||||
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.GetGymAttendance [method]: int -> PureGym.GymAttendance 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.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..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
|
||||
@@ -205,9 +206,47 @@ PureGym.MemberActivityThisMonth.LastRefreshed [property]: [read-only] System.Dat
|
||||
PureGym.MemberActivityThisMonth.TotalClasses [property]: [read-only] int
|
||||
PureGym.MemberActivityThisMonth.TotalDurationMinutes [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..ctor [constructor]: (string, string)
|
||||
PureGym.UsernamePin.get_Pin [method]: unit -> string
|
||||
PureGym.UsernamePin.get_Username [method]: unit -> 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": [
|
||||
"^refs/heads/main$"
|
||||
],
|
||||
|
Reference in New Issue
Block a user