From 80e7947ae28415ac15eb5b7f9350a1354c529e21 Mon Sep 17 00:00:00 2001 From: patrick Date: Sat, 14 Oct 2023 17:08:17 +0000 Subject: [PATCH] Better semantics for MemberActivity (#1) Co-authored-by: Smaug123 Reviewed-on: https://gitea.patrickstevens.co.uk/patrick/puregym-unofficial-dotnet/pulls/1 --- PureGym.App/Authenticate.fs | 41 +++++++++++++++++++++------- PureGym.App/Fullness.fs | 6 ++--- PureGym.App/LookupGym.fs | 4 +-- PureGym.App/MemberActivity.fs | 3 ++- PureGym/Api.fs | 50 +++++++++++++++++++++++++++++------ PureGym/SurfaceBaseline.txt | 45 ++++++++++++++++++++----------- 6 files changed, 110 insertions(+), 39 deletions(-) diff --git a/PureGym.App/Authenticate.fs b/PureGym.App/Authenticate.fs index ff0084a..2f61c36 100644 --- a/PureGym.App/Authenticate.fs +++ b/PureGym.App/Authenticate.fs @@ -5,24 +5,45 @@ open System open PureGym type GetTokenArg = - | [] User_Email of string - | [] Pin of string + | [] User_Email of string + | [] Pin of string + | [] StdIn interface IArgParserTemplate with member s.Usage = match s with | GetTokenArg.Pin _ -> "Eight-digit PureGym user's PIN" | GetTokenArg.User_Email _ -> "PureGym user's email address" + | GetTokenArg.StdIn _ -> "Read anything not specified on the command line from stdin" static member Parse (args : ParseResults) : Result = - try - { - Username = args.GetResult GetTokenArg.User_Email - Pin = args.GetResult GetTokenArg.Pin - } - |> Ok - with :? ArguParseException as e -> - Error e + let canUseStdin = args.TryGetResult(GetTokenArg.StdIn).IsSome + + let username = + match args.TryGetResult GetTokenArg.User_Email, canUseStdin with + | None, true -> + Console.Error.Write "Enter username: " + Console.ReadLine () + | None, false -> + // TODO: proper exception handling, this should be surfaced through the Error + failwith "You must supply --user-email or --stdin" + | Some s, _ -> s + + let pin = + match args.TryGetResult GetTokenArg.Pin, canUseStdin with + | None, true -> + Console.Error.Write "Enter eight-digit PIN: " + Console.ReadLine () + | None, false -> + // TODO: proper exception handling, this should be surfaced through the Error + failwith "You must supply --pin or --stdin" + | Some s, _ -> s + + { + Username = username + Pin = pin + } + |> Ok [] module Authenticate = diff --git a/PureGym.App/Fullness.fs b/PureGym.App/Fullness.fs index c303910..749498c 100644 --- a/PureGym.App/Fullness.fs +++ b/PureGym.App/Fullness.fs @@ -4,9 +4,9 @@ open Argu open PureGym type FullnessArgsFragment = - | [] Gym_Id of int - | [] Gym_Name of string - | [] Terse of bool + | [] Gym_Id of int + | [] Gym_Name of string + | [] Terse of bool interface IArgParserTemplate with member s.Usage = diff --git a/PureGym.App/LookupGym.fs b/PureGym.App/LookupGym.fs index 8803e4e..2ca9a82 100644 --- a/PureGym.App/LookupGym.fs +++ b/PureGym.App/LookupGym.fs @@ -4,8 +4,8 @@ open Argu open PureGym type LookupGymArgsFragment = - | [] Gym_Id of int - | [] Gym_Name of string + | [] Gym_Id of int + | [] Gym_Name of string interface IArgParserTemplate with member s.Usage = diff --git a/PureGym.App/MemberActivity.fs b/PureGym.App/MemberActivity.fs index 9c079b0..98138e7 100644 --- a/PureGym.App/MemberActivity.fs +++ b/PureGym.App/MemberActivity.fs @@ -33,6 +33,7 @@ module MemberActivity = task { let! client = Api.make args.Creds let! activity = client.GetMemberActivity () - System.Console.WriteLine (string activity) + let activity = activity.ToMemberActivity () + System.Console.WriteLine (string activity) return 0 } diff --git a/PureGym/Api.fs b/PureGym/Api.fs index 54944f6..274ce85 100644 --- a/PureGym/Api.fs +++ b/PureGym/Api.fs @@ -168,6 +168,7 @@ type Member = /// Statistics for how many people are currently at a gym type GymAttendance = { + /// This appears always to be just equal to TotalPeopleInGym, but a string. [] Description : string /// How many people are in the gym as of this statistics snapshot @@ -207,25 +208,55 @@ type GymAttendance = $"\n%i{this.TotalPeopleInClasses} in classes" $"""%i{this.TotalPeopleInGym} in gym%s{capacity} (is exact: %c{Char.emoji (not this.IsApproximate)})%s{classes} -Query made at %s{this.AttendanceTime.ToString ("s")}%s{this.AttendanceTime.ToString ("zzz")} -Snapshot correct as of %s{this.LastRefreshed.ToString ("s")}%s{this.LastRefreshed.ToString ("zzz")} -Classes info correct as of %s{this.LastRefreshedPeopleInClasses.ToString ("s")}%s{this.LastRefreshedPeopleInClasses.ToString ("zzz")}""" +Query made at %s{this.AttendanceTime.ToString "s"}%s{this.AttendanceTime.ToString "zzz"} +Snapshot correct as of %s{this.LastRefreshed.ToString "s"}%s{this.LastRefreshed.ToString "zzz"} +Classes info correct as of %s{this.LastRefreshedPeopleInClasses.ToString "s"}%s{this.LastRefreshedPeopleInClasses.ToString "zzz"}""" /// The visit statistics for a particular human to a particular gym. /// The semantics of this class are basically unknown. -type MemberActivity = +type MemberActivityThisMonth = { - /// ??? semantics unknown; this was 2852 for me - [] + /// How many minutes, including classes, have been logged so far this month TotalDurationMinutes : int - [] + /// How long, in minutes, each visit has been on average this month AverageDurationMinutes : int + /// How many visits have been made this month, excluding classes TotalVisits : int + /// How many classes have been attended this month TotalClasses : int + /// Whether this block of statistics is estimated rather than exact IsEstimated : bool + /// When this data was constructed LastRefreshed : DateTime } +/// Don't use this type. It's public because System.Text.Json can't do private types. +type MemberActivityDto = + { + [] + TotalDuration : int + [] + AverageDuration : int + [] + TotalVisits : int + [] + TotalClasses : int + [] + IsEstimated : bool + [] + LastRefreshed : DateTime + } + + member this.ToMemberActivity () = + { + TotalDurationMinutes = this.TotalDuration + AverageDurationMinutes = this.AverageDuration + TotalVisits = this.TotalVisits + TotalClasses = this.TotalClasses + IsEstimated = this.IsEstimated + LastRefreshed = this.LastRefreshed + } + /// The PureGym REST API. You probably want to instantiate one of these with `Api.make`. [] type IPureGymApi = @@ -247,7 +278,10 @@ type IPureGymApi = /// Get information about the activities logged against the currently authenticated PureGym human. [] - abstract GetMemberActivity : unit -> Task + abstract GetMemberActivity : unit -> Task + +// [] +// abstract GetMemberActivityAll : unit -> Task /// Methods for interacting with the PureGym REST API. [] diff --git a/PureGym/SurfaceBaseline.txt b/PureGym/SurfaceBaseline.txt index 3100d4f..b4ec859 100644 --- a/PureGym/SurfaceBaseline.txt +++ b/PureGym/SurfaceBaseline.txt @@ -143,7 +143,7 @@ PureGym.IPureGymApi.GetGym [method]: int -> PureGym.Gym System.Threading.Tasks.T 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.MemberActivity System.Threading.Tasks.Task +PureGym.IPureGymApi.GetMemberActivity [method]: unit -> PureGym.MemberActivityDto 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 @@ -176,20 +176,35 @@ PureGym.Member.MemberStatus [property]: [read-only] int PureGym.Member.MobileNumber [property]: [read-only] string PureGym.Member.Postcode [property]: [read-only] string PureGym.Member.SuspendedReason [property]: [read-only] int -PureGym.MemberActivity inherit obj, implements PureGym.MemberActivity System.IEquatable, System.Collections.IStructuralEquatable, PureGym.MemberActivity System.IComparable, System.IComparable, System.Collections.IStructuralComparable -PureGym.MemberActivity..ctor [constructor]: (int, int, int, int, bool, System.DateTime) -PureGym.MemberActivity.AverageDurationMinutes [property]: [read-only] int -PureGym.MemberActivity.get_AverageDurationMinutes [method]: unit -> int -PureGym.MemberActivity.get_IsEstimated [method]: unit -> bool -PureGym.MemberActivity.get_LastRefreshed [method]: unit -> System.DateTime -PureGym.MemberActivity.get_TotalClasses [method]: unit -> int -PureGym.MemberActivity.get_TotalDurationMinutes [method]: unit -> int -PureGym.MemberActivity.get_TotalVisits [method]: unit -> int -PureGym.MemberActivity.IsEstimated [property]: [read-only] bool -PureGym.MemberActivity.LastRefreshed [property]: [read-only] System.DateTime -PureGym.MemberActivity.TotalClasses [property]: [read-only] int -PureGym.MemberActivity.TotalDurationMinutes [property]: [read-only] int -PureGym.MemberActivity.TotalVisits [property]: [read-only] int +PureGym.MemberActivityDto inherit obj, implements PureGym.MemberActivityDto System.IEquatable, System.Collections.IStructuralEquatable, PureGym.MemberActivityDto System.IComparable, System.IComparable, System.Collections.IStructuralComparable +PureGym.MemberActivityDto..ctor [constructor]: (int, int, int, int, bool, System.DateTime) +PureGym.MemberActivityDto.AverageDuration [property]: [read-only] int +PureGym.MemberActivityDto.get_AverageDuration [method]: unit -> int +PureGym.MemberActivityDto.get_IsEstimated [method]: unit -> bool +PureGym.MemberActivityDto.get_LastRefreshed [method]: unit -> System.DateTime +PureGym.MemberActivityDto.get_TotalClasses [method]: unit -> int +PureGym.MemberActivityDto.get_TotalDuration [method]: unit -> int +PureGym.MemberActivityDto.get_TotalVisits [method]: unit -> int +PureGym.MemberActivityDto.IsEstimated [property]: [read-only] bool +PureGym.MemberActivityDto.LastRefreshed [property]: [read-only] System.DateTime +PureGym.MemberActivityDto.ToMemberActivity [method]: unit -> PureGym.MemberActivityThisMonth +PureGym.MemberActivityDto.TotalClasses [property]: [read-only] int +PureGym.MemberActivityDto.TotalDuration [property]: [read-only] int +PureGym.MemberActivityDto.TotalVisits [property]: [read-only] int +PureGym.MemberActivityThisMonth inherit obj, implements PureGym.MemberActivityThisMonth System.IEquatable, System.Collections.IStructuralEquatable, PureGym.MemberActivityThisMonth System.IComparable, System.IComparable, System.Collections.IStructuralComparable +PureGym.MemberActivityThisMonth..ctor [constructor]: (int, int, int, int, bool, System.DateTime) +PureGym.MemberActivityThisMonth.AverageDurationMinutes [property]: [read-only] int +PureGym.MemberActivityThisMonth.get_AverageDurationMinutes [method]: unit -> int +PureGym.MemberActivityThisMonth.get_IsEstimated [method]: unit -> bool +PureGym.MemberActivityThisMonth.get_LastRefreshed [method]: unit -> System.DateTime +PureGym.MemberActivityThisMonth.get_TotalClasses [method]: unit -> int +PureGym.MemberActivityThisMonth.get_TotalDurationMinutes [method]: unit -> int +PureGym.MemberActivityThisMonth.get_TotalVisits [method]: unit -> int +PureGym.MemberActivityThisMonth.IsEstimated [property]: [read-only] bool +PureGym.MemberActivityThisMonth.LastRefreshed [property]: [read-only] System.DateTime +PureGym.MemberActivityThisMonth.TotalClasses [property]: [read-only] int +PureGym.MemberActivityThisMonth.TotalDurationMinutes [property]: [read-only] int +PureGym.MemberActivityThisMonth.TotalVisits [property]: [read-only] int 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