View raw

1 (** Persistence port. A pure module type: no database or web framework. 2 3 Identity lives here rather than in the core, which carries none — a routine, 4 a stored workout, or a trainee needs a name only once something has to 5 remember it. 6 7 Every operation is scoped to a {!Trainee.id}. There is no server-wide state: 8 the active routine and the workout in progress belong to a trainee and are 9 stored, so nothing is lost across a restart and no two trainees share a 10 slot. *) 11 12 type routine_id = private string 13 type workout_id = private string 14 15 val routine_id : string -> routine_id 16 val workout_id : string -> workout_id 17 18 type record = { id : workout_id; workout : Evidence.Workout.t } 19 (** A stored workout. It already knows its prescription, its timestamps, and the 20 basis on which it was begun. *) 21 22 type app_feedback_id = private string 23 24 val app_feedback_id : string -> app_feedback_id 25 val app_feedback_id_to_string : app_feedback_id -> string 26 27 type app_feedback = { 28 feedback_id : app_feedback_id; 29 author : string; 30 contributions : int; 31 submitted_at : Recovery.timestamp; 32 message : string; 33 upvotes : int; 34 viewer_upvoted : bool; 35 viewer_owns : bool; 36 } 37 (** A ranked application comment with viewer vote and ownership state. *) 38 39 (** Effects run in Lwt: an adapter may talk to a database. *) 40 module type S = sig 41 type t 42 43 (** {2 Accounts} *) 44 45 val create_trainee : 46 t -> 47 username:Trainee.username -> 48 credential:Trainee.credential -> 49 (Trainee.t, [ `Username_taken ]) result Lwt.t 50 51 val find_trainee_by_username : t -> Trainee.username -> Trainee.t option Lwt.t 52 val find_trainee : t -> Trainee.id -> Trainee.t option Lwt.t 53 54 val update_username : 55 t -> 56 Trainee.id -> 57 Trainee.username -> 58 (Trainee.t, [ `Username_taken ]) result Lwt.t 59 (** Rename the account. [`Username_taken] if another account holds the name. 60 The trainee's own current name is accepted as a no-op. Returns the updated 61 trainee on success. *) 62 63 val update_credential : 64 t -> Trainee.id -> Trainee.credential -> Trainee.t option Lwt.t 65 (** Replace the account's password verifier. [None] if unknown. *) 66 67 (** {2 Catalog} 68 69 Shared, not trainee-scoped. *) 70 71 val list_routines : t -> (routine_id * Prescription.Routine.t) list 72 val find_routine : t -> routine_id -> Prescription.Routine.t option 73 74 (** {2 Per-trainee selection and workout in progress} *) 75 76 val active_routine : t -> Trainee.id -> routine_id option Lwt.t 77 val set_active_routine : t -> Trainee.id -> routine_id -> unit Lwt.t 78 val in_progress : t -> Trainee.id -> Evidence.Workout.t option Lwt.t 79 80 val set_in_progress : 81 t -> Trainee.id -> Evidence.Workout.t option -> unit Lwt.t 82 (** [None] clears the slot. *) 83 84 (** {2 History} *) 85 86 val finish_workout : t -> Trainee.id -> Evidence.Workout.t -> record Lwt.t 87 (** Store a finished workout and clear the in-progress slot as one unit. An 88 adapter with transactions performs both in a single transaction, so a 89 workout is never both saved to history and still shown as in progress. *) 90 91 val find : t -> Trainee.id -> workout_id -> record option Lwt.t 92 93 val replace : t -> Trainee.id -> record -> bool Lwt.t 94 (** Replace an existing record by identity. *) 95 96 val log : t -> Trainee.id -> Evidence.Log.t Lwt.t 97 (** The stored log — the only source of evidence. *) 98 99 val history : t -> Trainee.id -> record list Lwt.t 100 (** Most recent first. *) 101 102 (** {2 Subjective feedback} *) 103 104 val save_feedback : t -> Trainee.id -> Evidence.Feedback.t -> unit Lwt.t 105 (** Store a subjective feedback report. Feedback is a standalone observation, 106 not tied to a workout: a trainee may report it at any time. *) 107 108 val feedback : t -> Trainee.id -> Evidence.Feedback.t list Lwt.t 109 (** Stored feedback reports, most recent first. *) 110 111 val save_app_feedback : 112 t -> 113 Trainee.id -> 114 submitted_at:Recovery.timestamp -> 115 message:string -> 116 app_feedback Lwt.t 117 (** Store a freeform application feedback report and return its identity. *) 118 119 val app_feedback : t -> viewer:Trainee.id -> app_feedback list Lwt.t 120 (** Return all reports, ranked by upvotes, with the viewer's vote state. *) 121 122 val upvote_app_feedback : 123 t -> voter:Trainee.id -> app_feedback_id -> bool Lwt.t 124 (** Add one vote from another trainee. [false] means no vote was added. *) 125 126 val update_app_feedback : 127 t -> author:Trainee.id -> app_feedback_id -> message:string -> bool Lwt.t 128 (** Update a report only when [author] owns it. *) 129 130 val delete_app_feedback : 131 t -> author:Trainee.id -> app_feedback_id -> bool Lwt.t 132 (** Remove a report and its votes only when [author] owns it. *) 133 end 134