Add visits info #3

Merged
patrick merged 1 commits from visits into main 2023-11-01 16:49:57 +00:00
7 changed files with 174 additions and 4 deletions

View File

@@ -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

View File

@@ -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
View 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
}

View File

@@ -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>

View File

@@ -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.

View File

@@ -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

View File

@@ -1,5 +1,5 @@
{
"version": "1.0",
"version": "2.0",
"publicReleaseRefSpec": [
"^refs/heads/main$"
],