First cut of migration program
This commit is contained in:
		
							parent
							
								
									42e3a58131
								
							
						
					
					
						commit
						194cd2b5cc
					
				| @ -5,6 +5,31 @@ open Npgsql | ||||
| open Npgsql.FSharp | ||||
| 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 | ||||
| [<AutoOpen>] | ||||
| module private Helpers = | ||||
| @ -100,6 +125,7 @@ module private Helpers = | ||||
|             IsAdmin      = row.bool   "is_admin" | ||||
|             PasswordHash = row.string "password_hash" | ||||
|             LastSeen     = row.fieldValueOrNone<Instant> "last_seen" | ||||
|             SmallGroups  = [] | ||||
|         } | ||||
| 
 | ||||
| 
 | ||||
|  | ||||
| @ -127,6 +127,15 @@ type RequestSort = | ||||
|         | _ -> 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 | ||||
| 
 | ||||
| /// PK type for the Church entity | ||||
| @ -173,15 +182,6 @@ type SmallGroupId = | ||||
|             | 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 | ||||
| type UserId = | ||||
|     | 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) | ||||
|         LastSeen: Instant option | ||||
| 
 | ||||
|         /// The small groups to which this user is authorized | ||||
|         SmallGroups: SmallGroupId list | ||||
|     } | ||||
| 
 | ||||
|     /// The full name of the user | ||||
| @ -536,7 +539,8 @@ type User = | ||||
|           Email = "" | ||||
|           IsAdmin = false | ||||
|           PasswordHash = "" | ||||
|           LastSeen = None } | ||||
|           LastSeen = None | ||||
|           SmallGroups = [] } | ||||
| 
 | ||||
| 
 | ||||
| /// Cross-reference between user and small group | ||||
|  | ||||
| @ -8,6 +8,7 @@ | ||||
| 
 | ||||
|   <ItemGroup> | ||||
|     <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="NodaTime" Version="3.2.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 | ||||
| 	EndProjectSection | ||||
| EndProject | ||||
| Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "PrayerTracker.MigrateV9", "PrayerTracker.MigrateV9\PrayerTracker.MigrateV9.fsproj", "{CE7C5972-AC9A-44A8-8265-771483FD87DB}" | ||||
| EndProject | ||||
| Global | ||||
| 	GlobalSection(SolutionConfigurationPlatforms) = preSolution | ||||
| 		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}.Release|Any CPU.ActiveCfg = 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 | ||||
| 	GlobalSection(SolutionProperties) = preSolution | ||||
| 		HideSolutionNode = FALSE | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user