Brion VIBBER has submitted this change and it was merged. Change subject: Work in progress: data migration from old app ......................................................................
Work in progress: data migration from old app Extracts the list of saved pages from the old app at app startup, but doesn't yet do anything but dump them to console log. Change-Id: I1dc4e230c2dc8320528045081fc834e4c992e992 --- M Wikipedia.xcodeproj/project.pbxproj M wikipedia/AppDelegate.m A wikipedia/DataMigrator.h A wikipedia/DataMigrator.m A wikipedia/SQLiteHelper.h A wikipedia/SQLiteHelper.m 6 files changed, 332 insertions(+), 0 deletions(-) Approvals: Brion VIBBER: Verified; Looks good to me, approved diff --git a/Wikipedia.xcodeproj/project.pbxproj b/Wikipedia.xcodeproj/project.pbxproj index 28e10c6..b8e7c5c 100644 --- a/Wikipedia.xcodeproj/project.pbxproj +++ b/Wikipedia.xcodeproj/project.pbxproj @@ -162,6 +162,9 @@ D499144C181D51DE00E6073C /* Main_iPhone.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = D499144A181D51DE00E6073C /* Main_iPhone.storyboard */; }; D4991454181D51DE00E6073C /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = D4991453181D51DE00E6073C /* Images.xcassets */; }; D4BC22B4181E9E6300CAC673 /* empty.png in Resources */ = {isa = PBXBuildFile; fileRef = D4BC22B3181E9E6300CAC673 /* empty.png */; }; + D4E8A8A4190835C100DA4765 /* DataMigrator.m in Sources */ = {isa = PBXBuildFile; fileRef = D4E8A8A3190835C100DA4765 /* DataMigrator.m */; }; + D4E8A8A719084F1300DA4765 /* SQLiteHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = D4E8A8A619084F1300DA4765 /* SQLiteHelper.m */; }; + D4E8A8A919085CEA00DA4765 /* libsqlite3.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = D4E8A8A819085CEA00DA4765 /* libsqlite3.dylib */; }; D4EE00B9182443FC0090790F /* MWPageTitle.m in Sources */ = {isa = PBXBuildFile; fileRef = D4EE00B8182443FC0090790F /* MWPageTitle.m */; }; /* End PBXBuildFile section */ @@ -583,6 +586,11 @@ D4991466181D51DF00E6073C /* Wikipedia_Tests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = Wikipedia_Tests.m; sourceTree = "<group>"; }; D4BC22B3181E9E6300CAC673 /* empty.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = empty.png; sourceTree = "<group>"; }; D4DE203018283FF200148CA2 /* CommunicationBridgeTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CommunicationBridgeTests.m; sourceTree = "<group>"; }; + D4E8A8A2190835C100DA4765 /* DataMigrator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DataMigrator.h; sourceTree = "<group>"; }; + D4E8A8A3190835C100DA4765 /* DataMigrator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DataMigrator.m; sourceTree = "<group>"; }; + D4E8A8A519084F1300DA4765 /* SQLiteHelper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SQLiteHelper.h; sourceTree = "<group>"; }; + D4E8A8A619084F1300DA4765 /* SQLiteHelper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SQLiteHelper.m; sourceTree = "<group>"; }; + D4E8A8A819085CEA00DA4765 /* libsqlite3.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libsqlite3.dylib; path = usr/lib/libsqlite3.dylib; sourceTree = SDKROOT; }; D4EE00B7182443FC0090790F /* MWPageTitle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MWPageTitle.h; path = "mw-support/MWPageTitle.h"; sourceTree = "<group>"; }; D4EE00B8182443FC0090790F /* MWPageTitle.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MWPageTitle.m; path = "mw-support/MWPageTitle.m"; sourceTree = "<group>"; }; D4EE00BC1824459D0090790F /* PageTitleTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = PageTitleTests.m; path = "mw-support/PageTitleTests.m"; sourceTree = "<group>"; }; @@ -593,6 +601,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + D4E8A8A919085CEA00DA4765 /* libsqlite3.dylib in Frameworks */, 04D34DB21863D39000610A87 /* libxml2.dylib in Frameworks */, D499143B181D51DE00E6073C /* CoreGraphics.framework in Frameworks */, D499143D181D51DE00E6073C /* UIKit.framework in Frameworks */, @@ -1180,6 +1189,7 @@ D4991437181D51DE00E6073C /* Frameworks */ = { isa = PBXGroup; children = ( + D4E8A8A819085CEA00DA4765 /* libsqlite3.dylib */, 04D34DB11863D39000610A87 /* libxml2.dylib */, 040E5C4E184566F4007AFE6F /* CoreData.framework */, D4991438181D51DE00E6073C /* Foundation.framework */, @@ -1203,6 +1213,7 @@ 045A9F0C18F6090E0057EA85 /* assets */, 04C43AB7183442FC006C643B /* Categories */, 040E5C50184673F2007AFE6F /* Data */, + D4E8A8A11908357600DA4765 /* Data Migration */, 04292FFB185FC026002A13FC /* Defines */, 0442F57C1900718600F55DF9 /* Fonts */, 04D34DA31863D2D600610A87 /* HTML Parsing */, @@ -1262,6 +1273,17 @@ D4991463181D51DF00E6073C /* InfoPlist.strings */, ); name = "Supporting Files"; + sourceTree = "<group>"; + }; + D4E8A8A11908357600DA4765 /* Data Migration */ = { + isa = PBXGroup; + children = ( + D4E8A8A2190835C100DA4765 /* DataMigrator.h */, + D4E8A8A3190835C100DA4765 /* DataMigrator.m */, + D4E8A8A519084F1300DA4765 /* SQLiteHelper.h */, + D4E8A8A619084F1300DA4765 /* SQLiteHelper.m */, + ); + name = "Data Migration"; sourceTree = "<group>"; }; D4EE00BB182445670090790F /* mw-support */ = { @@ -1471,6 +1493,7 @@ 04D34D97186216AF00610A87 /* MainMenuViewController.m in Sources */, 0433543718A1A7AE009305F0 /* NavController.m in Sources */, 0447866F1852B5010050563B /* SessionSingleton.m in Sources */, + D4E8A8A4190835C100DA4765 /* DataMigrator.m in Sources */, 04D34DD11863F6E600610A87 /* SectionImage.m in Sources */, 0447867C1852BBFE0050563B /* NSManagedObjectContext+SimpleFetch.m in Sources */, 04AE1C741891BB32002D5487 /* Article.m in Sources */, @@ -1526,6 +1549,7 @@ 04F0E2EE186FB2D100468738 /* TOCSectionCellView.m in Sources */, 04D122321899B8AC006B9A30 /* AlertWebView.m in Sources */, D4991449181D51DE00E6073C /* AppDelegate.m in Sources */, + D4E8A8A719084F1300DA4765 /* SQLiteHelper.m in Sources */, 04F39590186CF80100B0D6FC /* TOCViewController.m in Sources */, 04D75824189248E900CE2040 /* DownloadLangLinksOp.m in Sources */, 04B91AB218E406FB00FFAA1C /* MainMenuRowView.m in Sources */, diff --git a/wikipedia/AppDelegate.m b/wikipedia/AppDelegate.m index c2aacdf..e6f4598 100644 --- a/wikipedia/AppDelegate.m +++ b/wikipedia/AppDelegate.m @@ -3,6 +3,7 @@ #import "AppDelegate.h" #import "URLCache.h" +#import "DataMigrator.h" @implementation AppDelegate @@ -25,6 +26,19 @@ //[[NSUserDefaults standardUserDefaults] synchronize]; // Override point for customization after application launch. + + // This probably belongs elsewhere... maybe tie it into one of the main pages on the nav stack. + DataMigrator *dataMigrator = [[DataMigrator alloc] init]; + if ([dataMigrator hasData]) { + NSLog(@"Old data to migrate found!"); + NSArray *titles = [dataMigrator extractSavedPages]; + for (NSDictionary *item in titles) { + NSLog(@"Need to import saved page: %@ %@", item[@"lang"], item[@"title"]); + } + } else { + NSLog(@"No pld data to migrate."); + } + return YES; } diff --git a/wikipedia/DataMigrator.h b/wikipedia/DataMigrator.h new file mode 100644 index 0000000..786fd16 --- /dev/null +++ b/wikipedia/DataMigrator.h @@ -0,0 +1,34 @@ +// +// DataMigrator.h +// Wikipedia +// +// Created by Brion on 4/23/14. +// Copyright (c) 2014 Wikimedia Foundation. Some rights reserved. +// + +#import <Foundation/Foundation.h> + +@interface DataMigrator : NSObject + +- (id)init; + +/** + * Is there anything that needs to be migrated? + */ +- (BOOL)hasData; + +/** + * Return the extracted JSON blobs from the savedPagesDB database table. + * Each contains 'lang', 'title', and 'key' strings. + * + * @return (NSArray *) of (NSDictionary *)s. + */ +- (NSArray *)extractSavedPages; + +/** + * Delete the old files. + * @todo implement this + */ +- (void)removeOldData; + +@end diff --git a/wikipedia/DataMigrator.m b/wikipedia/DataMigrator.m new file mode 100644 index 0000000..98fdb5b --- /dev/null +++ b/wikipedia/DataMigrator.m @@ -0,0 +1,105 @@ +// +// DataMigrator.m +// Wikipedia +// +// Created by Brion on 4/23/14. +// Copyright (c) 2014 Wikimedia Foundation. Some rights reserved. +// + +#import "DataMigrator.h" +#import "SQLiteHelper.h" + +@implementation DataMigrator +{ + SQLiteHelper *masterDB; + SQLiteHelper *savedPagesDB; +} + +#pragma mark - Public methods + +- (id)init +{ + self = [super init]; + if (self) { + NSString *dbPath = [self localLibraryPath:@"Caches/Databases.db"]; + if ([[NSFileManager defaultManager] fileExistsAtPath:dbPath]) { + NSLog(@"Opening sqlite database from %@", dbPath); + masterDB = [[SQLiteHelper alloc] initWithPath:dbPath]; + } + } + return self; +} + +- (BOOL)hasData +{ + return (masterDB != NULL); +} + +- (NSArray *)extractSavedPages +{ + NSMutableArray *arr = [[NSMutableArray alloc] init]; + for (NSDictionary *row in [self fetchRawSavedPages]) { + NSString *jsonString = row[@"value"]; + NSData *jsonBlob = [jsonString dataUsingEncoding:NSUTF8StringEncoding]; + NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:jsonBlob options:0 error:nil]; + [arr addObject:dict]; + } + return [NSArray arrayWithArray:arr]; +} + +- (void)removeOldData +{ + if ([self hasData]) { + NSLog(@"todo: implement removeOldData in DataMigrator"); + } +} + +#pragma mark - Private methods + +/** + * Return absolute path for relative path to the installed app's documents folder. + */ +- (NSString *)localDocumentPath:(NSString *)local +{ + return [[self documentRootPath] stringByAppendingPathComponent:local]; +} + +- (NSString *)documentRootPath +{ + NSArray* documentPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); + NSString* documentRootPath = [documentPaths objectAtIndex:0]; + return documentRootPath; +} + +/** + * Return absolute path for relative path to the installed app's Library folder. + */ +- (NSString *)localLibraryPath:(NSString *)local +{ + return [[self libraryRootPath] stringByAppendingPathComponent:local]; +} + +- (NSString *)libraryRootPath +{ + NSArray* libraryPaths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES); + NSString* libraryRootPath = [libraryPaths objectAtIndex:0]; + return libraryRootPath; +} + +- (NSArray *)fetchRawSavedPages +{ + savedPagesDB = [self openDatabaseWithName:@"savedPagesDB"]; + return [savedPagesDB query:@"SELECT value FROM savedPagesDB" params:nil]; +} + +- (SQLiteHelper *)openDatabaseWithName:(NSString *)dbname +{ + NSArray *rows = [masterDB query:@"SELECT origin, path FROM Databases WHERE name=?" params:@[dbname]]; + NSDictionary *row = rows[0]; + NSLog(@"row: %@", row); + NSString *path = [[[self localLibraryPath:@"Caches"] stringByAppendingPathComponent:row[@"origin"]] stringByAppendingPathComponent:row[@"path"]]; + NSLog(@"opening path %@", path); + return [[SQLiteHelper alloc] initWithPath:path]; +} + +@end diff --git a/wikipedia/SQLiteHelper.h b/wikipedia/SQLiteHelper.h new file mode 100644 index 0000000..5a4bb2a --- /dev/null +++ b/wikipedia/SQLiteHelper.h @@ -0,0 +1,16 @@ +// +// SQLiteHelper.h +// Wikipedia +// +// Created by Brion on 4/23/14. +// Copyright (c) 2014 Wikimedia Foundation. Some rights reserved. +// + +#import <Foundation/Foundation.h> + +@interface SQLiteHelper : NSObject + +- (id)initWithPath:(NSString *)path; +- (NSArray *)query:(NSString *)query params:(NSArray *)params; + +@end diff --git a/wikipedia/SQLiteHelper.m b/wikipedia/SQLiteHelper.m new file mode 100644 index 0000000..73334ee --- /dev/null +++ b/wikipedia/SQLiteHelper.m @@ -0,0 +1,139 @@ +// +// SQLiteHelper.m +// Wikipedia +// +// Created by Brion on 4/23/14. +// Copyright (c) 2014 Wikimedia Foundation. All rights reserved. +// + +#import "SQLiteHelper.h" +#import <sqlite3.h> + +#define SQLITE_THROW(x) {\ + int sqlite_errno = (x); \ + if (sqlite_errno != SQLITE_OK) { \ + @throw [NSException exceptionWithName:@"SQLiteHelperException" \ + reason:[NSString stringWithUTF8String:sqlite3_errmsg(_database)] \ + userInfo:@{@"sqlite_errno": [NSNumber numberWithInt:sqlite_errno]}]; \ + } \ +} + +@implementation SQLiteHelper +{ + sqlite3 *_database; +} + +- (id)initWithPath:(NSString *)path { + self = [super init]; + if (self) { + SQLITE_THROW(sqlite3_open([path UTF8String], &_database)); + } + return self; +} + +- (void)dealloc { + SQLITE_THROW(sqlite3_close(_database)); +} + +/** + * Return an array of dictionaries for each matching row + */ +- (NSArray *)query:(NSString *)query params:(NSArray *)params +{ + sqlite3_stmt *statement; + SQLITE_THROW(sqlite3_prepare_v2(_database, [query UTF8String], -1, &statement, nil)); + if (params) { + [self bindParams:params statement:statement]; + } + + NSMutableArray *rows = [[NSMutableArray alloc] init]; + + while (true) { + int ret = sqlite3_step(statement); + if (ret == SQLITE_ROW) { + NSDictionary *row = [self extractRowFromStatement:statement]; + [rows addObject: row]; + } else if (ret == SQLITE_DONE) { + // We're done! + break; + } else { + SQLITE_THROW(ret); + } + } + + SQLITE_THROW(sqlite3_finalize(statement)); + + return rows; +} + +#pragma mark - Private methods + +- (void)bindParams:(NSArray *)params statement:(sqlite3_stmt *)statement +{ + for (int i = 0; i < [params count]; i++) { + int col = i + 1; + NSObject *param = params[i]; + if ([param isKindOfClass:[NSString class]]) { + NSString *str = (NSString *)param; + SQLITE_THROW(sqlite3_bind_text(statement, col, [str UTF8String], -1, nil)); + } else { + @throw [NSException exceptionWithName:@"SQLiteHelperException" + reason:@"Unimplemented type in SQLHelper bindParams:statement:" + userInfo:@{}]; + } + } +} + +- (NSDictionary *)extractRowFromStatement:(sqlite3_stmt *)statement +{ + NSMutableDictionary *row = [[NSMutableDictionary alloc] init]; + + int columnCount = sqlite3_column_count(statement); + for (int i = 0; i < columnCount; i++) { + int col = i; + NSString *name = [NSString stringWithUTF8String:sqlite3_column_name(statement, col)]; + NSObject *value; + switch (sqlite3_column_type(statement, col)) { + case SQLITE_INTEGER: + value = [NSNumber numberWithInt:sqlite3_column_int(statement, col)]; + break; + case SQLITE_FLOAT: + value = [NSNumber numberWithDouble:sqlite3_column_double(statement, col)]; + break; + case SQLITE_TEXT: + { + const char *bytes = (const char *)sqlite3_column_text(statement, col); + int nbytes = sqlite3_column_bytes(statement, col); + if (nbytes > 0) { + value = [NSString stringWithUTF8String:bytes]; + } else { + value = @""; + } + break; + } + case SQLITE_BLOB: + { + const char *bytes = sqlite3_column_blob(statement, col); + int nbytes = sqlite3_column_bytes(statement, col); + if (nbytes > 0) { + value = [NSData dataWithBytes:bytes length:nbytes]; + } else { + value = [[NSData alloc] init]; + } + break; + } + case SQLITE_NULL: + value = [NSNull null]; + break; + default: + @throw [NSException exceptionWithName:@"SQLiteHelperException" + reason:@"Unimplmemented column type in SQLHelper extractRowFromStatement:" + userInfo:@{}]; + } + row[name] = value; + } + + return [NSDictionary dictionaryWithDictionary:row]; +} + +@end -- To view, visit https://gerrit.wikimedia.org/r/129307 To unsubscribe, visit https://gerrit.wikimedia.org/r/settings Gerrit-MessageType: merged Gerrit-Change-Id: I1dc4e230c2dc8320528045081fc834e4c992e992 Gerrit-PatchSet: 5 Gerrit-Project: apps/ios/wikipedia Gerrit-Branch: master Gerrit-Owner: Brion VIBBER <br...@wikimedia.org> Gerrit-Reviewer: Brion VIBBER <br...@wikimedia.org> _______________________________________________ MediaWiki-commits mailing list MediaWiki-commits@lists.wikimedia.org https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits