From 2741c5e36cf0bdb203b12b78a8062e25af9d89c7 Mon Sep 17 00:00:00 2001 From: patrick Date: Wed, 1 Nov 2023 16:49:56 +0000 Subject: [PATCH] Add visits info (#3) Co-authored-by: Smaug123 Reviewed-on: https://gitea.patrickstevens.co.uk/patrick/puregym-unofficial-dotnet/pulls/3 --- PureGym.App/Program.fs | 6 ++- PureGym.App/PureGym.App.fsproj | 1 + PureGym.App/Sessions.fs | 49 +++++++++++++++++++++++ PureGym/Api.fs | 72 ++++++++++++++++++++++++++++++++++ PureGym/Auth.fs | 5 +++ PureGym/SurfaceBaseline.txt | 43 +++++++++++++++++++- PureGym/version.json | 2 +- 7 files changed, 174 insertions(+), 4 deletions(-) create mode 100644 PureGym.App/Sessions.fs diff --git a/PureGym.App/Program.fs b/PureGym.App/Program.fs index 7443d71..ae84bc6 100644 --- a/PureGym.App/Program.fs +++ b/PureGym.App/Program.fs @@ -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 diff --git a/PureGym.App/PureGym.App.fsproj b/PureGym.App/PureGym.App.fsproj index 1e026b5..0a7b218 100644 --- a/PureGym.App/PureGym.App.fsproj +++ b/PureGym.App/PureGym.App.fsproj @@ -14,6 +14,7 @@ + diff --git a/PureGym.App/Sessions.fs b/PureGym.App/Sessions.fs new file mode 100644 index 0000000..4e641f6 --- /dev/null +++ b/PureGym.App/Sessions.fs @@ -0,0 +1,49 @@ +namespace PureGym.App + +open Argu +open PureGym +open System + +type SessionsArgsFragment = + | [] From_Date of string + | [] 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 + = + 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 + +[] +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 activity) + return 0 + } diff --git a/PureGym/Api.fs b/PureGym/Api.fs index a98a6e8..6ff4793 100644 --- a/PureGym/Api.fs +++ b/PureGym/Api.fs @@ -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 this.Summary + let visits = this.Visits |> Seq.map string |> String.concat "\n" + + $"%s{summary}\n%s{visits}" + /// The PureGym REST API. You probably want to instantiate one of these with `Api.make`. [] type IPureGymApi = @@ -283,6 +351,10 @@ type IPureGymApi = [] abstract GetMemberActivity : unit -> Task + /// Get information about the individual visits to all PureGyms the currently-authenticated PureGym human has made. + [] + abstract GetSessions : [] fromDate : DateTime -> [] toDate : DateTime -> Task + // [] // abstract GetMemberActivityAll : unit -> Task diff --git a/PureGym/Auth.fs b/PureGym/Auth.fs index 4aca107..c63c16a 100644 --- a/PureGym/Auth.fs +++ b/PureGym/Auth.fs @@ -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 [] (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. diff --git a/PureGym/SurfaceBaseline.txt b/PureGym/SurfaceBaseline.txt index b4ec859..5102d5c 100644 --- a/PureGym/SurfaceBaseline.txt +++ b/PureGym/SurfaceBaseline.txt @@ -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 \ No newline at end of file +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 \ No newline at end of file diff --git a/PureGym/version.json b/PureGym/version.json index 5b7dc86..958fb13 100644 --- a/PureGym/version.json +++ b/PureGym/version.json @@ -1,5 +1,5 @@ { - "version": "1.0", + "version": "2.0", "publicReleaseRefSpec": [ "^refs/heads/main$" ],