diff --git a/src/MyPrayerJournal.Api/Domain.fs b/src/MyPrayerJournal.Api/Domain.fs index 498da26..b2179b2 100644 --- a/src/MyPrayerJournal.Api/Domain.fs +++ b/src/MyPrayerJournal.Api/Domain.fs @@ -44,6 +44,7 @@ type Recurrence = | Weeks module Recurrence = /// The string reprsentation used in the database and the web app + // TODO/FIXME: will this be true in v2? it's not in the database... let toString x = match x with | Immediate -> "immediate" @@ -67,13 +68,30 @@ module Recurrence = | Weeks -> 604800000L +/// The action taken on a request as part of a history entry +type RequestAction = + | Created + | Prayed + | Updated + | Answered +module RequestAction = + /// Create a RequestAction from a string + let fromString x = + match x with + | "Created" -> Created + | "Prayed" -> Prayed + | "Updated" -> Updated + | "Answered" -> Answered + | _ -> (sprintf "Bad request action %s" >> invalidOp) x + + /// History is a record of action taken on a prayer request, including updates to its text [] type History = { /// The time when this history entry was made asOf : Ticks /// The status for this history entry - status : string + status : RequestAction /// The text of the update, if applicable text : string option } @@ -81,7 +99,7 @@ with /// An empty history entry static member empty = { asOf = Ticks 0L - status = "" + status = Created text = None } diff --git a/src/MyPrayerJournal.Api/Handlers.fs b/src/MyPrayerJournal.Api/Handlers.fs index ef84130..8fb55ae 100644 --- a/src/MyPrayerJournal.Api/Handlers.fs +++ b/src/MyPrayerJournal.Api/Handlers.fs @@ -175,7 +175,7 @@ module Request = history = [ { History.empty with asOf = now - status = "Created" + status = Created text = Some r.requestText } ] @@ -197,14 +197,15 @@ module Request = | Some req -> let! hist = ctx.BindJsonAsync () let now = jsNow () + let act = RequestAction.fromString hist.status { History.empty with asOf = now - status = hist.status + status = act text = match hist.updateText with null | "" -> None | x -> Some x } |> sess.AddHistory reqId - match hist.status with - | "Prayed" -> + match act with + | Prayed -> (Ticks.toLong now) + (Recurrence.duration req.recurType * int64 req.recurCount) |> (Ticks >> sess.UpdateShowAfter reqId) | _ -> () diff --git a/src/migrate/Program.fs b/src/migrate/Program.fs new file mode 100644 index 0000000..e08a33b --- /dev/null +++ b/src/migrate/Program.fs @@ -0,0 +1,94 @@ + +open Microsoft.FSharpLu.Json +open MyPrayerJournal +open Npgsql +open Raven.Client.Documents + +type NpgsqlDataReader with + member this.getShort = this.GetOrdinal >> this.GetInt16 + member this.getString = this.GetOrdinal >> this.GetString + member this.getTicks = this.GetOrdinal >> this.GetInt64 >> Ticks + member this.isNull = this.GetOrdinal >> this.IsDBNull + +let pgConn () = + let c = new NpgsqlConnection "Host=severus-server;Database=mpj;Username=mpj;Password=devpassword;Application Name=myPrayerJournal" + c.Open () + c + +let isValidStatus stat = + try + (RequestAction.fromString >> ignore) stat + true + with _ -> false + +let getHistory reqId = + use conn = pgConn () + use cmd = conn.CreateCommand () + cmd.CommandText <- """SELECT "asOf", status, text FROM mpj.history WHERE "requestId" = @reqId ORDER BY "asOf" """ + (cmd.Parameters.Add >> ignore) (NpgsqlParameter ("@reqId", reqId :> obj)) + use rdr = cmd.ExecuteReader () + seq { + while rdr.Read () do + match (rdr.getString >> isValidStatus) "status" with + | true -> + yield + { asOf = rdr.getTicks "asOf" + status = (rdr.getString >> RequestAction.fromString) "status" + text = match rdr.isNull "text" with true -> None | false -> (rdr.getString >> Some) "text" + } + | false -> + printf "Invalid status %s; skipped history entry %s/%i\n" (rdr.getString "status") reqId + ((rdr.getTicks >> Ticks.toLong) "asOf") + } + |> List.ofSeq + +let getNotes reqId = + use conn = pgConn () + use cmd = conn.CreateCommand () + cmd.CommandText <- """SELECT "asOf", notes FROM mpj.note WHERE "requestId" = @reqId""" + (cmd.Parameters.Add >> ignore) (NpgsqlParameter ("@reqId", reqId :> obj)) + use rdr = cmd.ExecuteReader () + seq { + while rdr.Read () do + yield + { asOf = rdr.getTicks "asOf" + notes = rdr.getString "notes" + } + } + |> List.ofSeq + +let migrateRequests (store : IDocumentStore) = + use sess = store.OpenSession () + use conn = pgConn () + use cmd = conn.CreateCommand () + cmd.CommandText <- + """SELECT "requestId", "enteredOn", "userId", "snoozedUntil", "showAfter", "recurType", "recurCount" FROM mpj.request""" + use rdr = cmd.ExecuteReader () + while rdr.Read () do + let reqId = rdr.getString "requestId" + sess.Store ( + { Id = (RequestId.fromIdString >> RequestId.toString) reqId + enteredOn = rdr.getTicks "enteredOn" + userId = (rdr.getString >> UserId) "userId" + snoozedUntil = rdr.getTicks "snoozedUntil" + showAfter = rdr.getTicks "showAfter" + recurType = (rdr.getString >> Recurrence.fromString) "recurType" + recurCount = rdr.getShort "recurCount" + history = getHistory reqId + notes = getNotes reqId + }) + sess.SaveChanges () + +[] +let main argv = + let raven = new DocumentStore (Urls = [| "http://localhost:8080" |], Database = "myPrayerJournal") + raven.Conventions.CustomizeJsonSerializer <- + fun x -> + x.Converters.Add (RequestIdJsonConverter ()) + x.Converters.Add (TicksJsonConverter ()) + x.Converters.Add (UserIdJsonConverter ()) + x.Converters.Add (CompactUnionJsonConverter ()) + let store = raven.Initialize () + migrateRequests store + printfn "fin" + 0 // return an integer exit code diff --git a/src/migrate/migrate.fsproj b/src/migrate/migrate.fsproj new file mode 100644 index 0000000..408f4a0 --- /dev/null +++ b/src/migrate/migrate.fsproj @@ -0,0 +1,20 @@ + + + + Exe + netcoreapp3.0 + + + + + + + + + + + + + + +