First cut of migration program
This commit is contained in:
parent
42e3a58131
commit
194cd2b5cc
@ -5,6 +5,31 @@ open Npgsql
|
|||||||
open Npgsql.FSharp
|
open Npgsql.FSharp
|
||||||
open PrayerTracker.Entities
|
open PrayerTracker.Entities
|
||||||
|
|
||||||
|
/// Table names
|
||||||
|
[<RequireQualifiedAccess>]
|
||||||
|
module Table =
|
||||||
|
|
||||||
|
/// The church table
|
||||||
|
[<Literal>]
|
||||||
|
let Church = "church"
|
||||||
|
|
||||||
|
/// The small group table
|
||||||
|
[<Literal>]
|
||||||
|
let Group = "small_group"
|
||||||
|
|
||||||
|
/// The small group member table
|
||||||
|
[<Literal>]
|
||||||
|
let Member = "member"
|
||||||
|
|
||||||
|
/// The prayer request table
|
||||||
|
[<Literal>]
|
||||||
|
let Request = "prayer_request"
|
||||||
|
|
||||||
|
/// The user table
|
||||||
|
[<Literal>]
|
||||||
|
let User = "pt_user"
|
||||||
|
|
||||||
|
|
||||||
/// Helper functions for the PostgreSQL data implementation
|
/// Helper functions for the PostgreSQL data implementation
|
||||||
[<AutoOpen>]
|
[<AutoOpen>]
|
||||||
module private Helpers =
|
module private Helpers =
|
||||||
@ -100,6 +125,7 @@ module private Helpers =
|
|||||||
IsAdmin = row.bool "is_admin"
|
IsAdmin = row.bool "is_admin"
|
||||||
PasswordHash = row.string "password_hash"
|
PasswordHash = row.string "password_hash"
|
||||||
LastSeen = row.fieldValueOrNone<Instant> "last_seen"
|
LastSeen = row.fieldValueOrNone<Instant> "last_seen"
|
||||||
|
SmallGroups = []
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -127,6 +127,15 @@ type RequestSort =
|
|||||||
| _ -> invalidArg "code" $"Unknown code {code}"
|
| _ -> invalidArg "code" $"Unknown code {code}"
|
||||||
|
|
||||||
|
|
||||||
|
/// Type for a time zone ID
|
||||||
|
type TimeZoneId =
|
||||||
|
| TimeZoneId of string
|
||||||
|
|
||||||
|
override this.ToString() =
|
||||||
|
match this with
|
||||||
|
| TimeZoneId it -> it
|
||||||
|
|
||||||
|
|
||||||
open System
|
open System
|
||||||
|
|
||||||
/// PK type for the Church entity
|
/// PK type for the Church entity
|
||||||
@ -173,15 +182,6 @@ type SmallGroupId =
|
|||||||
| SmallGroupId guid -> guid
|
| SmallGroupId guid -> guid
|
||||||
|
|
||||||
|
|
||||||
/// PK type for the TimeZone entity
|
|
||||||
type TimeZoneId =
|
|
||||||
| TimeZoneId of string
|
|
||||||
|
|
||||||
override this.ToString() =
|
|
||||||
match this with
|
|
||||||
| TimeZoneId it -> it
|
|
||||||
|
|
||||||
|
|
||||||
/// PK type for the User entity
|
/// PK type for the User entity
|
||||||
type UserId =
|
type UserId =
|
||||||
| UserId of Guid
|
| UserId of Guid
|
||||||
@ -523,6 +523,9 @@ type User =
|
|||||||
|
|
||||||
/// The last time the user was seen (set whenever the user is loaded into a session)
|
/// The last time the user was seen (set whenever the user is loaded into a session)
|
||||||
LastSeen: Instant option
|
LastSeen: Instant option
|
||||||
|
|
||||||
|
/// The small groups to which this user is authorized
|
||||||
|
SmallGroups: SmallGroupId list
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The full name of the user
|
/// The full name of the user
|
||||||
@ -536,7 +539,8 @@ type User =
|
|||||||
Email = ""
|
Email = ""
|
||||||
IsAdmin = false
|
IsAdmin = false
|
||||||
PasswordHash = ""
|
PasswordHash = ""
|
||||||
LastSeen = None }
|
LastSeen = None
|
||||||
|
SmallGroups = [] }
|
||||||
|
|
||||||
|
|
||||||
/// Cross-reference between user and small group
|
/// Cross-reference between user and small group
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="BitBadger.Documents.Postgres" Version="3.1.0" />
|
<PackageReference Include="BitBadger.Documents.Postgres" Version="3.1.0" />
|
||||||
|
<PackageReference Include="BitBadger.Documents.Sqlite" Version="4.0.1" />
|
||||||
<PackageReference Include="Giraffe" Version="7.0.2" />
|
<PackageReference Include="Giraffe" Version="7.0.2" />
|
||||||
<PackageReference Include="NodaTime" Version="3.2.0" />
|
<PackageReference Include="NodaTime" Version="3.2.0" />
|
||||||
<PackageReference Include="Npgsql.FSharp" Version="5.7.0" />
|
<PackageReference Include="Npgsql.FSharp" Version="5.7.0" />
|
||||||
|
20
src/PrayerTracker.MigrateV9/PrayerTracker.MigrateV9.fsproj
Normal file
20
src/PrayerTracker.MigrateV9/PrayerTracker.MigrateV9.fsproj
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>Exe</OutputType>
|
||||||
|
<TargetFramework>net9.0</TargetFramework>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Include="Program.fs" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\PrayerTracker.Data\PrayerTracker.Data.fsproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="BitBadger.Documents.Postgres" Version="4.0.1" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
141
src/PrayerTracker.MigrateV9/Program.fs
Normal file
141
src/PrayerTracker.MigrateV9/Program.fs
Normal file
@ -0,0 +1,141 @@
|
|||||||
|
|
||||||
|
open NodaTime
|
||||||
|
open Npgsql.FSharp
|
||||||
|
open PrayerTracker.Data
|
||||||
|
open PrayerTracker.Entities
|
||||||
|
|
||||||
|
module PgMappings =
|
||||||
|
/// Map a row to a Church instance
|
||||||
|
let mapToChurch (row : RowReader) =
|
||||||
|
{ Id = ChurchId (row.uuid "id")
|
||||||
|
Name = row.string "church_name"
|
||||||
|
City = row.string "city"
|
||||||
|
State = row.string "state"
|
||||||
|
HasVpsInterface = row.bool "has_vps_interface"
|
||||||
|
InterfaceAddress = row.stringOrNone "interface_address"
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Map a row to a ListPreferences instance
|
||||||
|
let mapToListPreferences (row : RowReader) =
|
||||||
|
{ SmallGroupId = SmallGroupId (row.uuid "small_group_id")
|
||||||
|
DaysToKeepNew = row.int "days_to_keep_new"
|
||||||
|
DaysToExpire = row.int "days_to_expire"
|
||||||
|
LongTermUpdateWeeks = row.int "long_term_update_weeks"
|
||||||
|
EmailFromName = row.string "email_from_name"
|
||||||
|
EmailFromAddress = row.string "email_from_address"
|
||||||
|
Fonts = row.string "fonts"
|
||||||
|
HeadingColor = row.string "heading_color"
|
||||||
|
LineColor = row.string "line_color"
|
||||||
|
HeadingFontSize = row.int "heading_font_size"
|
||||||
|
TextFontSize = row.int "text_font_size"
|
||||||
|
GroupPassword = row.string "group_password"
|
||||||
|
IsPublic = row.bool "is_public"
|
||||||
|
PageSize = row.int "page_size"
|
||||||
|
TimeZoneId = TimeZoneId (row.string "time_zone_id")
|
||||||
|
RequestSort = RequestSort.Parse (row.string "request_sort")
|
||||||
|
DefaultEmailType = EmailFormat.Parse (row.string "default_email_type")
|
||||||
|
AsOfDateDisplay = AsOfDateDisplay.Parse (row.string "as_of_date_display")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Map a row to a Member instance
|
||||||
|
let mapToMember (row : RowReader) =
|
||||||
|
{ Id = MemberId (row.uuid "id")
|
||||||
|
SmallGroupId = SmallGroupId (row.uuid "small_group_id")
|
||||||
|
Name = row.string "member_name"
|
||||||
|
Email = row.string "email"
|
||||||
|
Format = row.stringOrNone "email_format" |> Option.map EmailFormat.Parse
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Map a row to a Prayer Request instance
|
||||||
|
let mapToPrayerRequest (row : RowReader) =
|
||||||
|
{ Id = PrayerRequestId (row.uuid "id")
|
||||||
|
UserId = UserId (row.uuid "user_id")
|
||||||
|
SmallGroupId = SmallGroupId (row.uuid "small_group_id")
|
||||||
|
EnteredDate = row.fieldValue<Instant> "entered_date"
|
||||||
|
UpdatedDate = row.fieldValue<Instant> "updated_date"
|
||||||
|
Requestor = row.stringOrNone "requestor"
|
||||||
|
Text = row.string "request_text"
|
||||||
|
NotifyChaplain = row.bool "notify_chaplain"
|
||||||
|
RequestType = PrayerRequestType.Parse (row.string "request_type")
|
||||||
|
Expiration = Expiration.Parse (row.string "expiration")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Map a row to a Small Group instance
|
||||||
|
let mapToSmallGroup (row : RowReader) =
|
||||||
|
{ Id = SmallGroupId (row.uuid "id")
|
||||||
|
ChurchId = ChurchId (row.uuid "church_id")
|
||||||
|
Name = row.string "group_name"
|
||||||
|
Preferences = ListPreferences.Empty
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Map a row to a Small Group instance with populated list preferences
|
||||||
|
let mapToSmallGroupWithPreferences (row : RowReader) =
|
||||||
|
{ mapToSmallGroup row with
|
||||||
|
Preferences = mapToListPreferences row
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Map a row to a User instance
|
||||||
|
let mapToUser (row : RowReader) =
|
||||||
|
{ Id = UserId (row.uuid "id")
|
||||||
|
FirstName = row.string "first_name"
|
||||||
|
LastName = row.string "last_name"
|
||||||
|
Email = row.string "email"
|
||||||
|
IsAdmin = row.bool "is_admin"
|
||||||
|
PasswordHash = row.string "password_hash"
|
||||||
|
LastSeen = row.fieldValueOrNone<Instant> "last_seen"
|
||||||
|
SmallGroups = []
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Configure PostgreSQL and SQLite connections
|
||||||
|
|
||||||
|
task {
|
||||||
|
|
||||||
|
let source = BitBadger.Documents.Postgres.Configuration.dataSource ()
|
||||||
|
|
||||||
|
let! churches =
|
||||||
|
Sql.fromDataSource source
|
||||||
|
|> Sql.query "SELECT * FROM pt.church"
|
||||||
|
|> Sql.executeAsync PgMappings.mapToChurch
|
||||||
|
for church in churches do
|
||||||
|
do! BitBadger.Documents.Sqlite.Document.insert Table.Church church
|
||||||
|
printfn "Migrated %d churches" churches.Length
|
||||||
|
|
||||||
|
let! groups =
|
||||||
|
Sql.fromDataSource source
|
||||||
|
|> Sql.query "SELECT sg.*, lp.* FROM pt.small_group sg
|
||||||
|
INNER JOIN pt.list_preference lp ON lp.small_group_id = sg.id"
|
||||||
|
|> Sql.executeAsync PgMappings.mapToSmallGroupWithPreferences
|
||||||
|
for group in groups do
|
||||||
|
do! BitBadger.Documents.Sqlite.Document.insert Table.Group group
|
||||||
|
printfn "Migrated %d groups" groups.Length
|
||||||
|
|
||||||
|
let! members =
|
||||||
|
Sql.fromDataSource source
|
||||||
|
|> Sql.query "SELECT * from pt.member"
|
||||||
|
|> Sql.executeAsync PgMappings.mapToMember
|
||||||
|
for mbr in members do
|
||||||
|
do! BitBadger.Documents.Sqlite.Document.insert Table.Member mbr
|
||||||
|
printfn "Migrated %d members" members.Length
|
||||||
|
|
||||||
|
let! requests =
|
||||||
|
Sql.fromDataSource source
|
||||||
|
|> Sql.query "SELECT * from pt.prayer_request"
|
||||||
|
|> Sql.executeAsync PgMappings.mapToPrayerRequest
|
||||||
|
for request in requests do
|
||||||
|
do! BitBadger.Documents.Sqlite.Document.insert Table.Request request
|
||||||
|
printfn "Migrated %d requests" requests.Length
|
||||||
|
|
||||||
|
let! users =
|
||||||
|
Sql.fromDataSource source
|
||||||
|
|> Sql.query "SELECT * FROM pt.pt_user"
|
||||||
|
|> Sql.executeAsync PgMappings.mapToUser
|
||||||
|
for user in users do
|
||||||
|
let! groups =
|
||||||
|
Sql.fromDataSource source
|
||||||
|
|> Sql.query "SELECT small_group_id FROM pt.user_small_group WHERE user_id = :user_id"
|
||||||
|
|> Sql.parameters [ ":user_id", Sql.uuid user.Id.Value ]
|
||||||
|
|> Sql.executeAsync (fun row -> (row.uuid >> SmallGroupId) "small_group_id")
|
||||||
|
do! BitBadger.Documents.Sqlite.Document.insert Table.User { user with SmallGroups = groups }
|
||||||
|
printfn "Migrated %d users" users.Length
|
||||||
|
|
||||||
|
} |> Async.AwaitTask |> Async.RunSynchronously
|
@ -16,6 +16,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
|
|||||||
Directory.Build.props = Directory.Build.props
|
Directory.Build.props = Directory.Build.props
|
||||||
EndProjectSection
|
EndProjectSection
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "PrayerTracker.MigrateV9", "PrayerTracker.MigrateV9\PrayerTracker.MigrateV9.fsproj", "{CE7C5972-AC9A-44A8-8265-771483FD87DB}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
@ -38,6 +40,10 @@ Global
|
|||||||
{2B5BA107-9BDA-4A1D-A9AF-AFEE6BF12270}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{2B5BA107-9BDA-4A1D-A9AF-AFEE6BF12270}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{2B5BA107-9BDA-4A1D-A9AF-AFEE6BF12270}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{2B5BA107-9BDA-4A1D-A9AF-AFEE6BF12270}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{2B5BA107-9BDA-4A1D-A9AF-AFEE6BF12270}.Release|Any CPU.Build.0 = Release|Any CPU
|
{2B5BA107-9BDA-4A1D-A9AF-AFEE6BF12270}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{CE7C5972-AC9A-44A8-8265-771483FD87DB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{CE7C5972-AC9A-44A8-8265-771483FD87DB}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{CE7C5972-AC9A-44A8-8265-771483FD87DB}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{CE7C5972-AC9A-44A8-8265-771483FD87DB}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
|
Loading…
x
Reference in New Issue
Block a user