Make progress on saving updated articles.
This commit is contained in:
parent
9ee20ee270
commit
f40b400dd5
@ -335,7 +335,7 @@ private extension ArticlesTable {
|
|||||||
|
|
||||||
assert(Thread.isMainThread)
|
assert(Thread.isMainThread)
|
||||||
|
|
||||||
updateRelatedObjects(_ parsedItems: [String: ParsedItem], _ articles: [String: Article])
|
// updateRelatedObjects(_ parsedItems: [String: ParsedItem], _ articles: [String: Article])
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -391,80 +391,93 @@ private extension ArticlesTable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateRelatedAttachments(_ parsedItems: [String: ParsedItem], _ articles: [String: Article]) {
|
// MARK: Save New Articles
|
||||||
|
|
||||||
var articlesWithChanges = Set<Article>()
|
func saveNewArticles(_ articles: Set<Article>, _ database: FMDatabase) {
|
||||||
|
|
||||||
for (articleID, parsedItem) in parsedItems {
|
saveRelatedObjectsForNewArticles(articles, database)
|
||||||
guard let article = articles[articleID] else {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if !parsedItemTagsMatchArticlesTag(parsedItem, article) {
|
|
||||||
articlesChanges.insert(article)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if articlesWithChanges.isEmpty {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
queue.update { (database) in
|
|
||||||
tagsLookupTable.saveRelatedObjects(for: articlesWithChanges.databaseObjects(), in: database)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
let databaseDictionaries = articles.map { $0.databaseDictionary() }
|
||||||
|
insertRows(databaseDictionaries, insertType: .orReplace, in: database)
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateRelatedTags(_ parsedItems: [String: ParsedItem], _ articles: [String: Article]) {
|
func saveRelatedObjectsForNewArticles(_ articles: Set<Article>, _ database: FMDatabase) {
|
||||||
|
|
||||||
var articlesWithChanges = Set<Article>()
|
let databaseObjects = articles.databaseObjects()
|
||||||
|
|
||||||
for (articleID, parsedItem) in parsedItems {
|
authorsLookupTable.saveRelatedObjects(for: databaseObjects, in: database)
|
||||||
guard let article = articles[articleID] else {
|
attachmentsLookupTable.saveRelatedObjects(for: databaseObjects, in: database)
|
||||||
continue
|
tagsLookupTable.saveRelatedObjects(for: databaseObjects, in: database)
|
||||||
}
|
}
|
||||||
if !parsedItemTagsMatchArticlesTag(parsedItem, article) {
|
|
||||||
articlesChanges.insert(article)
|
// MARK: Update Existing Articles
|
||||||
|
|
||||||
|
// TODO: use a keypath instead of separate functions. Fix code duplication.
|
||||||
|
|
||||||
|
func articlesWithTagChanges(_ updatedArticles: Set<Article>, _ fetchedArticles: [String: Article]) -> Set<Article> {
|
||||||
|
|
||||||
|
return updatedArticles.filter{ (updatedArticle) -> Bool in
|
||||||
|
if let fetchedArticle = fetchedArticles[updatedArticle.articleID] {
|
||||||
|
return updatedArticle.tags != fetchedArticles.tags
|
||||||
}
|
}
|
||||||
|
assertionFailure("Expected to find matching fetched article.");
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if articlesWithChanges.isEmpty {
|
func articlesWithAttachmentChanges(_ updatedArticles: Set<Article>, _ fetchedArticles: [String: Article]) -> Set<Article> {
|
||||||
return
|
|
||||||
|
return updatedArticles.filter{ (updatedArticle) -> Bool in
|
||||||
|
if let fetchedArticle = fetchedArticles[updatedArticle.articleID] {
|
||||||
|
return updatedArticle.attachments != fetchedArticles.attachments
|
||||||
|
}
|
||||||
|
assertionFailure("Expected to find matching fetched article.");
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
queue.update { (database) in
|
}
|
||||||
|
|
||||||
|
func articlesWithAuthorChanges(_ updatedArticles: Set<Article>, _ fetchedArticles: [String: Article]) -> Set<Article> {
|
||||||
|
|
||||||
|
return updatedArticles.filter{ (updatedArticle) -> Bool in
|
||||||
|
if let fetchedArticle = fetchedArticles[updatedArticle.articleID] {
|
||||||
|
return updatedArticle.authors != fetchedArticles.authors
|
||||||
|
}
|
||||||
|
assertionFailure("Expected to find matching fetched article.");
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateRelatedTags(_ updatedArticles: Set<Article>, _ fetchedArticles: [String: Article], _ database: FMDatabase) {
|
||||||
|
|
||||||
|
let articlesWithChanges = articlesWithTagChanges(updatedArticles, fetchedArticles)
|
||||||
|
if !articlesWithChanges.isEmpty {
|
||||||
tagsLookupTable.saveRelatedObjects(for: articlesWithChanges.databaseObjects(), in: database)
|
tagsLookupTable.saveRelatedObjects(for: articlesWithChanges.databaseObjects(), in: database)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func parsedItemTagsMatchArticlesTag(_ parsedItem: ParsedItem, _ article: Article) -> Bool {
|
func updateRelatedAttachments(_ updatedArticles: Set<Article>, _ fetchedArticles: [String: Article], _ database: FMDatabase) {
|
||||||
|
|
||||||
let parsedItemTags = parsedItem.tags
|
let articlesWithChanges = articlesWithAttachmentChanges(updatedArticles, fetchedArticles)
|
||||||
let articleTags = article.tags
|
if !articlesWithChanges.isEmpty {
|
||||||
|
attachmentsLookupTable.saveRelatedObjects(for: articlesWithChanges.databaseObjects(), in: database)
|
||||||
if parsedItemTags == nil && articleTags == nil {
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
if parsedItemTags != nil && articleTags == nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if parsedItemTags == nil && articleTags != nil {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return Set(parsedItemTags!) == articleTags!
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func saveNewParsedItems(_ parsedItems: [String: ParsedItem], _ feed: Feed) {
|
func updateRelatedAuthors(_ updatedArticles: Set<Article>, _ fetchedArticles: [String: Article], _ database: FMDatabase) {
|
||||||
|
|
||||||
// These parsedItems have no existing status or Article.
|
let articlesWithChanges = articlesWithAuthorChanges(updatedArticles, fetchedArticles)
|
||||||
|
if !articlesWithChanges.isEmpty {
|
||||||
queue.update { (database) in
|
authorsLookupTable.saveRelatedObjects(for: articlesWithChanges.databaseObjects(), in: database)
|
||||||
|
|
||||||
let articleIDs = Set(parsedItems.keys)
|
|
||||||
self.statusesTable.ensureStatusesForArticleIDs(articleIDs, database)
|
|
||||||
|
|
||||||
let articles = self.articlesWithParsedItems(Set(parsedItems.values), feed)
|
|
||||||
self.saveUncachedNewArticles(articles, database)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func saveUpdatedArticles(_ updatedArticles: Set<Article>, _ fetchedArticles: [String: Article], _ database: FMDatabase) {
|
||||||
|
|
||||||
|
updateRelatedTags(updatedArticles, fetchedArticles, database)
|
||||||
|
updateRelatedAttachments(updatedArticles, fetchedArticles, database)
|
||||||
|
updatedRelatedAuthors(updatedArticles, fetchedArticles, database)
|
||||||
|
}
|
||||||
|
|
||||||
func articlesWithParsedItems(_ parsedItems: Set<ParsedItem>, _ feed: Feed) -> Set<Article> {
|
func articlesWithParsedItems(_ parsedItems: Set<ParsedItem>, _ feed: Feed) -> Set<Article> {
|
||||||
|
|
||||||
// These Articles don’t get cached. Background-queue only.
|
// These Articles don’t get cached. Background-queue only.
|
||||||
@ -474,31 +487,9 @@ private extension ArticlesTable {
|
|||||||
|
|
||||||
func articleWithParsedItem(_ parsedItem: ParsedItem, _ feedID: String) -> Article? {
|
func articleWithParsedItem(_ parsedItem: ParsedItem, _ feedID: String) -> Article? {
|
||||||
|
|
||||||
guard let account = account else {
|
return Article(parsedItem: parsedItem, feedID: feedID, accountID: accountID)
|
||||||
assertionFailure("account is unexpectedly nil.")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return Article(parsedItem: parsedItem, feedID: feedID, account: account)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func saveUncachedNewArticles(_ articles: Set<Article>, _ database: FMDatabase) {
|
|
||||||
|
|
||||||
saveRelatedObjects(articles, database)
|
|
||||||
|
|
||||||
let databaseDictionaries = articles.map { $0.databaseDictionary() }
|
|
||||||
insertRows(databaseDictionaries, insertType: .orIgnore, in: database)
|
|
||||||
}
|
|
||||||
|
|
||||||
func saveRelatedObjects(_ articles: Set<Article>, _ database: FMDatabase) {
|
|
||||||
|
|
||||||
let databaseObjects = articles.databaseObjects()
|
|
||||||
|
|
||||||
authorsLookupTable.saveRelatedObjects(for: databaseObjects, in: database)
|
|
||||||
attachmentsLookupTable.saveRelatedObjects(for: databaseObjects, in: database)
|
|
||||||
tagsLookupTable.saveRelatedObjects(for: databaseObjects, in: database)
|
|
||||||
}
|
|
||||||
|
|
||||||
func statusIndicatesArticleIsIgnorable(_ status: ArticleStatus) -> Bool {
|
func statusIndicatesArticleIsIgnorable(_ status: ArticleStatus) -> Bool {
|
||||||
|
|
||||||
// Ignorable articles: either userDeleted==1 or (not starred and arrival date > 4 months).
|
// Ignorable articles: either userDeleted==1 or (not starred and arrival date > 4 months).
|
||||||
|
@ -24,12 +24,9 @@ public final class Database {
|
|||||||
private let articlesTable: ArticlesTable
|
private let articlesTable: ArticlesTable
|
||||||
private var articleArrivalCutoffDate = NSDate.rs_dateWithNumberOfDays(inThePast: 3 * 31)!
|
private var articleArrivalCutoffDate = NSDate.rs_dateWithNumberOfDays(inThePast: 3 * 31)!
|
||||||
private let minimumNumberOfArticles = 10
|
private let minimumNumberOfArticles = 10
|
||||||
private weak var delegate: AccountDelegate?
|
|
||||||
private weak var account: Account?
|
|
||||||
|
|
||||||
public init(databaseFile: String, delegate: AccountDelegate, account: Account) {
|
public init(databaseFile: String) {
|
||||||
|
|
||||||
self.delegate = delegate
|
|
||||||
self.account = account
|
self.account = account
|
||||||
self.databaseFile = databaseFile
|
self.databaseFile = databaseFile
|
||||||
self.queue = RSDatabaseQueue(filepath: databaseFile, excludeFromBackup: false)
|
self.queue = RSDatabaseQueue(filepath: databaseFile, excludeFromBackup: false)
|
||||||
|
@ -13,7 +13,7 @@ import RSParser
|
|||||||
|
|
||||||
extension Article {
|
extension Article {
|
||||||
|
|
||||||
convenience init?(row: FMResultSet, authors: Set<Author>, attachments: Set<Attachment>, tags: Set<String>, accountID: String) {
|
init?(row: FMResultSet, authors: Set<Author>, attachments: Set<Attachment>, tags: Set<String>, accountID: String) {
|
||||||
|
|
||||||
guard let feedID = row.string(forColumn: DatabaseKey.feedID) else {
|
guard let feedID = row.string(forColumn: DatabaseKey.feedID) else {
|
||||||
return nil
|
return nil
|
||||||
@ -38,7 +38,7 @@ extension Article {
|
|||||||
self.init(account: account, articleID: articleID, feedID: feedID, uniqueID: uniqueID, title: title, contentHTML: contentHTML, contentText: contentText, url: url, externalURL: externalURL, summary: summary, imageURL: imageURL, bannerImageURL: bannerImageURL, datePublished: datePublished, dateModified: dateModified, authors: authors, tags: tags, attachments: attachments, accountInfo: accountInfo)
|
self.init(account: account, articleID: articleID, feedID: feedID, uniqueID: uniqueID, title: title, contentHTML: contentHTML, contentText: contentText, url: url, externalURL: externalURL, summary: summary, imageURL: imageURL, bannerImageURL: bannerImageURL, datePublished: datePublished, dateModified: dateModified, authors: authors, tags: tags, attachments: attachments, accountInfo: accountInfo)
|
||||||
}
|
}
|
||||||
|
|
||||||
convenience init(parsedItem: ParsedItem, accountID: String, feedID: String) {
|
init(parsedItem: ParsedItem, accountID: String, feedID: String) {
|
||||||
|
|
||||||
let authors = Author.authorsWithParsedAuthors(parsedItem.authors)
|
let authors = Author.authorsWithParsedAuthors(parsedItem.authors)
|
||||||
let attachments = Attachment.attachmentsWithParsedAttachments(parsedItem.attachments)
|
let attachments = Attachment.attachmentsWithParsedAttachments(parsedItem.attachments)
|
||||||
|
@ -34,7 +34,7 @@ extension Author {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
let authors = parsedAuthors.flatMap { Author(parsedAuthor: $0) }
|
let authors = Set(parsedAuthors.flatMap { Author(parsedAuthor: $0) })
|
||||||
return authors.isEmpty ? nil : authors
|
return authors.isEmpty ? nil : authors
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user