Decide on preliminary public API for Database.swift. Stub-out everything.

This commit is contained in:
Brent Simmons 2017-08-20 21:23:17 -07:00
parent c164c29cde
commit 9ddaaf5f5d
7 changed files with 405 additions and 376 deletions

View File

@ -39,7 +39,7 @@ public final class Article: Hashable {
}
}
init(account: Account, databaseID: String?, feedID: String, uniqueID: String, title: String?, contentHTML: String?, contentText: String?, url: String?, externalURL: String?, summary: String?, imageURL: String?, bannerImageURL: String?, datePublished: Date?, dateModified: Date?, authors: [Author]?, tags: Set<String>?, attachments: [Attachment]?, accountInfo: AccountInfo?) {
init(account: Account, articleID: String?, feedID: String, uniqueID: String, title: String?, contentHTML: String?, contentText: String?, url: String?, externalURL: String?, summary: String?, imageURL: String?, bannerImageURL: String?, datePublished: Date?, dateModified: Date?, authors: [Author]?, tags: Set<String>?, attachments: [Attachment]?, accountInfo: AccountInfo?) {
self.account = account
self.feedID = feedID
@ -59,16 +59,14 @@ public final class Article: Hashable {
self.attachments = attachments
self.accountInfo = accountInfo
let _databaseID: String
if let databaseID = databaseID {
_databaseID = databaseID
if let articleID = articleID {
self.articleID = articleID
}
else {
_databaseID = databaseIDWithString("\(feedID) \(uniqueID)")
self.articleID = databaseIDWithString("\(feedID) \(uniqueID)")
}
self.databaseID = _databaseID
self.hashValue = account.hashValue ^ _databaseID.hashValue
self.hashValue = account.hashValue ^ self.articleID.hashValue
}
public class func ==(lhs: Article, rhs: Article) -> Bool {

View File

@ -44,6 +44,6 @@ public struct Author: Hashable {
public static func ==(lhs: Author, rhs: Author) -> Bool {
return lhs.hashValue == rhs.hashValue && lhs.databaseID == rhs.databaseID
return lhs.hashValue == rhs.hashValue && lhs.authorID == rhs.authorID
}
}

View File

@ -69,4 +69,5 @@ public struct RelationshipName {
static let authors = "authors"
static let tags = "tags"
static let attachments = "attachments"
}

View File

@ -51,7 +51,7 @@ final class Database {
self.tagsLookupTable = DatabaseLookupTable(name: DatabaseTableName.tags, objectIDKey: DatabaseKey.articleID, relatedObjectIDKey: DatabaseKey.tagName, relatedTable: tagsTable, relationshipName: RelationshipName.tags)
let attachmentsTable = AttachmentsTable(name: DatabaseTableName.attachments)
self.attachmentsLookupTable = DatabaseLookupTable(name: DatabaseTableName.tags, objectIDKey: DatabaseKey.articleID, relatedObjectIDKey: DatabaseKey.tagName, relatedTable: tagsTable, relationshipName: RelationshipName.tags)
self.attachmentsLookupTable = DatabaseLookupTable(name: DatabaseTableName.attachmentsLookup, objectIDKey: DatabaseKey.articleID, relatedObjectIDKey: DatabaseKey.attachmentID, relatedTable: attachmentsTable, relationshipName: RelationshipName.attachments)
let createStatementsPath = Bundle(for: type(of: self)).path(forResource: "CreateStatements", ofType: "sql")!
let createStatements = try! NSString(contentsOfFile: createStatementsPath, encoding: String.Encoding.utf8.rawValue)
@ -59,399 +59,407 @@ final class Database {
queue.vacuumIfNeeded()
}
// MARK: Fetching Articles
// MARK: - Fetching Articles
func fetchArticlesForFeed(_ feed: Feed) -> Set<Article> {
var fetchedArticles = Set<Article>()
let feedID = feed.feedID
queue.fetchSync { (database: FMDatabase!) -> Void in
fetchedArticles = self.fetchArticlesForFeedID(feedID, database: database)
}
let articles = articleCache.uniquedArticles(fetchedArticles, statusesTable: statusesTable)
return filteredArticles(articles, feedCounts: [feed.feedID: fetchedArticles.count])
return Set<Article>() // TODO
// var fetchedArticles = Set<Article>()
// let feedID = feed.feedID
//
// queue.fetchSync { (database: FMDatabase!) -> Void in
//
// fetchedArticles = self.fetchArticlesForFeedID(feedID, database: database)
// }
//
// let articles = articleCache.uniquedArticles(fetchedArticles, statusesTable: statusesTable)
// return filteredArticles(articles, feedCounts: [feed.feedID: fetchedArticles.count])
}
func fetchArticlesForFeedAsync(_ feed: Feed, _ resultBlock: @escaping ArticleResultBlock) {
let feedID = feed.feedID
queue.fetch { (database: FMDatabase!) -> Void in
let fetchedArticles = self.fetchArticlesForFeedID(feedID, database: database)
DispatchQueue.main.async() { () -> Void in
let articles = self.articleCache.uniquedArticles(fetchedArticles, statusesTable: self.statusesTable)
let filteredArticles = self.filteredArticles(articles, feedCounts: [feed.feedID: fetchedArticles.count])
resultBlock(filteredArticles)
}
}
}
func feedIDCountDictionariesWithResultSet(_ resultSet: FMResultSet) -> [String: Int] {
var counts = [String: Int]()
while (resultSet.next()) {
if let oneFeedID = resultSet.string(forColumnIndex: 0) {
let count = resultSet.int(forColumnIndex: 1)
counts[oneFeedID] = Int(count)
}
}
return counts
}
func countsForAllFeeds(_ database: FMDatabase) -> [String: Int] {
let sql = "select distinct feedID, count(*) as count from articles group by feedID;"
if let resultSet = database.executeQuery(sql, withArgumentsIn: []) {
return feedIDCountDictionariesWithResultSet(resultSet)
}
return [String: Int]()
}
func countsForFeedIDs(_ feedIDs: [String], _ database: FMDatabase) -> [String: Int] {
let placeholders = NSString.rs_SQLValueList(withPlaceholders: UInt(feedIDs.count))!
let sql = "select distinct feedID, count(*) from articles where feedID in \(placeholders) group by feedID;"
logSQL(sql)
if let resultSet = database.executeQuery(sql, withArgumentsIn: feedIDs) {
return feedIDCountDictionariesWithResultSet(resultSet)
}
return [String: Int]()
// let feedID = feed.feedID
//
// queue.fetch { (database: FMDatabase!) -> Void in
//
// let fetchedArticles = self.fetchArticlesForFeedID(feedID, database: database)
//
// DispatchQueue.main.async() { () -> Void in
//
// let articles = self.articleCache.uniquedArticles(fetchedArticles, statusesTable: self.statusesTable)
// let filteredArticles = self.filteredArticles(articles, feedCounts: [feed.feedID: fetchedArticles.count])
// resultBlock(filteredArticles)
// }
// }
}
func fetchUnreadArticlesForFolder(_ folder: Folder) -> Set<Article> {
return fetchUnreadArticlesForFeedIDs(folder.flattenedFeedIDs())
return Set<Article>() // TODO
// return fetchUnreadArticlesForFeedIDs(folder.flattenedFeedIDs())
}
func fetchUnreadArticlesForFeedIDs(_ feedIDs: [String]) -> Set<Article> {
if feedIDs.isEmpty {
return Set<Article>()
}
var fetchedArticles = Set<Article>()
var counts = [String: Int]()
queue.fetchSync { (database: FMDatabase!) -> Void in
counts = self.countsForFeedIDs(feedIDs, database)
// select * from articles natural join statuses where feedID in ('http://ranchero.com/xml/rss.xml') and read = 0
let placeholders = NSString.rs_SQLValueList(withPlaceholders: UInt(feedIDs.count))!
let sql = "select * from articles natural join statuses where feedID in \(placeholders) and read=0;"
logSQL(sql)
if let resultSet = database.executeQuery(sql, withArgumentsIn: feedIDs) {
fetchedArticles = self.articlesWithResultSet(resultSet)
}
}
let articles = articleCache.uniquedArticles(fetchedArticles, statusesTable: statusesTable)
return filteredArticles(articles, feedCounts: counts)
}
typealias UnreadCountCompletionBlock = ([String: Int]) -> Void //feedID: unreadCount
func updateUnreadCounts(for feedIDs: Set<String>, completion: @escaping UnreadCountCompletionBlock) {
queue.fetch { (database: FMDatabase!) -> Void in
var unreadCounts = [String: Int]()
for oneFeedID in feedIDs {
unreadCounts[oneFeedID] = self.unreadCount(oneFeedID, database)
}
DispatchQueue.main.async() {
completion(unreadCounts)
}
}
}
// MARK: Updating Articles
// MARK: - Unread Counts
typealias UnreadCountTable = [String: Int] // feedID: unreadCount
typealias UnreadCountCompletionBlock = (UnreadCountTable) -> Void //feedID: unreadCount
func fetchUnreadCounts(for feeds: Set<Feed>, completion: @escaping UnreadCountCompletionBlock) {
// let feedIDs = feeds.feedIDs()
//
// queue.fetch { (database: FMDatabase!) -> Void in
//
// var unreadCounts = UnreadCountTable()
// for oneFeedID in feedIDs {
// unreadCounts[oneFeedID] = self.unreadCount(oneFeedID, database)
// }
//
// DispatchQueue.main.async() {
// completion(unreadCounts)
// }
// }
}
// MARK: - Updating Articles
func updateFeedWithParsedFeed(_ feed: Feed, parsedFeed: ParsedFeed, completionHandler: @escaping RSVoidCompletionBlock) {
if parsedFeed.items.isEmpty {
completionHandler()
return
}
let parsedArticlesDictionary = self.articlesDictionary(parsedFeed.items as NSSet) as! [String: ParsedItem]
fetchArticlesForFeedAsync(feed) { (articles) -> Void in
let articlesDictionary = self.articlesDictionary(articles as NSSet) as! [String: Article]
self.updateArticles(articlesDictionary, parsedArticles: parsedArticlesDictionary, feed: feed, completionHandler: completionHandler)
}
// if parsedFeed.items.isEmpty {
// completionHandler()
// return
// }
//
// let parsedArticlesDictionary = self.articlesDictionary(parsedFeed.items as NSSet) as! [String: ParsedItem]
//
// fetchArticlesForFeedAsync(feed) { (articles) -> Void in
//
// let articlesDictionary = self.articlesDictionary(articles as NSSet) as! [String: Article]
// self.updateArticles(articlesDictionary, parsedArticles: parsedArticlesDictionary, feed: feed, completionHandler: completionHandler)
// }
}
// MARK: Status
// MARK: - Status
func markArticles(_ articles: NSSet, statusKey: ArticleStatusKey, flag: Bool) {
func markArticles(_ articles: Set<Article>, statusKey: ArticleStatusKey, flag: Bool) {
statusesTable.markArticles(articles as! Set<Article>, statusKey: statusKey, flag: flag)
// statusesTable.markArticles(articles, statusKey: statusKey, flag: flag)
}
}
// MARK: Private
// MARK: - Private
private extension Database {
// func feedIDCountDictionariesWithResultSet(_ resultSet: FMResultSet) -> [String: Int] {
//
// var counts = [String: Int]()
//
// while (resultSet.next()) {
//
// if let oneFeedID = resultSet.string(forColumnIndex: 0) {
// let count = resultSet.int(forColumnIndex: 1)
// counts[oneFeedID] = Int(count)
// }
// }
//
// return counts
// }
// func countsForAllFeeds(_ database: FMDatabase) -> [String: Int] {
//
// let sql = "select distinct feedID, count(*) as count from articles group by feedID;"
//
// if let resultSet = database.executeQuery(sql, withArgumentsIn: []) {
// return feedIDCountDictionariesWithResultSet(resultSet)
// }
//
// return [String: Int]()
// }
// func countsForFeedIDs(_ feedIDs: [String], _ database: FMDatabase) -> [String: Int] {
//
// let placeholders = NSString.rs_SQLValueList(withPlaceholders: UInt(feedIDs.count))!
// let sql = "select distinct feedID, count(*) from articles where feedID in \(placeholders) group by feedID;"
// logSQL(sql)
//
// if let resultSet = database.executeQuery(sql, withArgumentsIn: feedIDs) {
// return feedIDCountDictionariesWithResultSet(resultSet)
// }
//
// return [String: Int]()
//
// }
// func fetchUnreadArticlesForFeedIDs(_ feedIDs: [String]) -> Set<Article> {
//
// if feedIDs.isEmpty {
// return Set<Article>()
// }
//
// var fetchedArticles = Set<Article>()
// var counts = [String: Int]()
//
// queue.fetchSync { (database: FMDatabase!) -> Void in
//
// counts = self.countsForFeedIDs(feedIDs, database)
//
// // select * from articles natural join statuses where feedID in ('http://ranchero.com/xml/rss.xml') and read = 0
//
// let placeholders = NSString.rs_SQLValueList(withPlaceholders: UInt(feedIDs.count))!
// let sql = "select * from articles natural join statuses where feedID in \(placeholders) and read=0;"
// logSQL(sql)
//
// if let resultSet = database.executeQuery(sql, withArgumentsIn: feedIDs) {
// fetchedArticles = self.articlesWithResultSet(resultSet)
// }
// }
//
// let articles = articleCache.uniquedArticles(fetchedArticles, statusesTable: statusesTable)
// return filteredArticles(articles, feedCounts: counts)
// }
// MARK: Saving Articles
func saveUpdatedAndNewArticles(_ articleChanges: Set<NSDictionary>, newArticles: Set<Article>) {
if articleChanges.isEmpty && newArticles.isEmpty {
return
}
statusesTable.assertNoMissingStatuses(newArticles)
articleCache.cacheArticles(newArticles)
let newArticleDictionaries = newArticles.map { (oneArticle) in
return oneArticle.databaseDictionary()
}
queue.update { (database: FMDatabase!) -> Void in
if !articleChanges.isEmpty {
for oneDictionary in articleChanges {
let oneArticleDictionary = oneDictionary.mutableCopy() as! NSMutableDictionary
let articleID = oneArticleDictionary[DatabaseKey.articleID]!
oneArticleDictionary.removeObject(forKey: DatabaseKey.articleID)
let _ = database.rs_updateRows(with: oneArticleDictionary as [NSObject: AnyObject], whereKey: DatabaseKey.articleID, equalsValue: articleID, tableName: DatabaseTableName.articles)
}
}
if !newArticleDictionaries.isEmpty {
for oneNewArticleDictionary in newArticleDictionaries {
let _ = database.rs_insertRow(with: oneNewArticleDictionary as [NSObject: AnyObject], insertType: RSDatabaseInsertOrReplace, tableName: DatabaseTableName.articles)
}
}
}
}
// MARK: Updating Articles
func updateArticles(_ articles: [String: Article], parsedArticles: [String: ParsedItem], feed: Feed, completionHandler: @escaping RSVoidCompletionBlock) {
statusesTable.ensureStatusesForParsedArticles(Set(parsedArticles.values)) {
let articleChanges = self.updateExistingArticles(articles, parsedArticles)
let newArticles = self.createNewArticles(articles, parsedArticles: parsedArticles, feedID: feed.feedID)
self.saveUpdatedAndNewArticles(articleChanges, newArticles: newArticles)
completionHandler()
}
}
func articlesDictionary(_ articles: NSSet) -> [String: AnyObject] {
var d = [String: AnyObject]()
for oneArticle in articles {
let oneArticleID = (oneArticle as AnyObject).value(forKey: DatabaseKey.articleID) as! String
d[oneArticleID] = oneArticle as AnyObject
}
return d
}
func updateExistingArticles(_ articles: [String: Article], _ parsedArticles: [String: ParsedItem]) -> Set<NSDictionary> {
var articleChanges = Set<NSDictionary>()
for oneArticle in articles.values {
if let oneParsedArticle = parsedArticles[oneArticle.articleID] {
if let oneArticleChanges = oneArticle.updateWithParsedArticle(oneParsedArticle) {
articleChanges.insert(oneArticleChanges)
}
}
}
return articleChanges
}
// MARK: Creating Articles
func createNewArticlesWithParsedArticles(_ parsedArticles: Set<ParsedItem>, feedID: String) -> Set<Article> {
return Set(parsedArticles.map { Article(account: account, feedID: feedID, parsedArticle: $0) })
}
func articlesWithParsedArticles(_ parsedArticles: Set<ParsedItem>, feedID: String) -> Set<Article> {
var localArticles = Set<Article>()
for oneParsedArticle in parsedArticles {
let oneLocalArticle = Article(account: self.account, feedID: feedID, parsedArticle: oneParsedArticle)
localArticles.insert(oneLocalArticle)
}
return localArticles
}
func createNewArticles(_ existingArticles: [String: Article], parsedArticles: [String: ParsedItem], feedID: String) -> Set<Article> {
let newParsedArticles = parsedArticlesMinusExistingArticles(parsedArticles, existingArticles: existingArticles)
let newArticles = createNewArticlesWithParsedArticles(newParsedArticles, feedID: feedID)
statusesTable.attachCachedUniqueStatuses(newArticles)
return newArticles
}
func parsedArticlesMinusExistingArticles(_ parsedArticles: [String: ParsedItem], existingArticles: [String: Article]) -> Set<ParsedItem> {
var result = Set<ParsedItem>()
for oneParsedArticle in parsedArticles.values {
if let _ = existingArticles[oneParsedArticle.databaseID] {
continue
}
result.insert(oneParsedArticle)
}
return result
}
// MARK: Fetching Articles
func fetchArticlesWithWhereClause(_ database: FMDatabase, whereClause: String, parameters: [AnyObject]?) -> Set<Article> {
let sql = "select * from articles where \(whereClause);"
logSQL(sql)
if let resultSet = database.executeQuery(sql, withArgumentsIn: parameters) {
return articlesWithResultSet(resultSet, database)
}
return Set<Article>()
}
func articlesWithResultSet(_ resultSet: FMResultSet, _ database: FMDatabase) -> Set<Article> {
let fetchedArticles = resultSet.mapToSet { Article(account: self.account, row: $0) }
statusesTable.attachStatuses(fetchedArticles, database)
authorsTable.attachAuthors(fetchedArticles, database)
tagsTable.attachTags(fetchedArticles, database)
attachmentsTable.attachAttachments(fetchedArticles, database)
return fetchedArticles
}
func fetchArticlesForFeedID(_ feedID: String, database: FMDatabase) -> Set<Article> {
return fetchArticlesWithWhereClause(database, whereClause: "articles.feedID = ?", parameters: [feedID as AnyObject])
}
// MARK: Unread counts
func numberOfArticles(_ feedID: String, _ database: FMDatabase) -> Int {
let sql = "select count(*) from articles where feedID = ?;"
logSQL(sql)
return numberWithSQLAndParameters(sql, parameters: [feedID], database)
}
func unreadCount(_ feedID: String, _ database: FMDatabase) -> Int {
let totalNumberOfArticles = numberOfArticles(feedID, database)
if totalNumberOfArticles <= minimumNumberOfArticles {
return unreadCountIgnoringCutoffDate(feedID, database)
}
return unreadCountRespectingCutoffDate(feedID, database)
}
func unreadCountIgnoringCutoffDate(_ feedID: String, _ database: FMDatabase) -> Int {
let sql = "select count(*) from articles natural join statuses where feedID=? and read=0 and userDeleted=0;"
logSQL(sql)
return numberWithSQLAndParameters(sql, parameters: [feedID], database)
}
func unreadCountRespectingCutoffDate(_ feedID: String, _ database: FMDatabase) -> Int {
let sql = "select count(*) from articles natural join statuses where feedID=? and read=0 and userDeleted=0 and (starred=1 or dateArrived>?);"
logSQL(sql)
return numberWithSQLAndParameters(sql, parameters: [feedID, articleArrivalCutoffDate], database)
}
// MARK: Filtering out old articles
func articleIsOlderThanCutoffDate(_ article: Article) -> Bool {
if let dateArrived = article.status?.dateArrived {
return dateArrived < articleArrivalCutoffDate
}
return false
}
func articleShouldBeSavedForever(_ article: Article) -> Bool {
return article.status.starred
}
func articleShouldAppearToUser(_ article: Article, _ numberOfArticlesInFeed: Int) -> Bool {
if numberOfArticlesInFeed <= minimumNumberOfArticles {
return true
}
return articleShouldBeSavedForever(article) || !articleIsOlderThanCutoffDate(article)
}
private static let minimumNumberOfArticlesInFeed = 10
func filteredArticles(_ articles: Set<Article>, feedCounts: [String: Int]) -> Set<Article> {
var articlesSet = Set<Article>()
for oneArticle in articles {
if let feedCount = feedCounts[oneArticle.feedID], articleShouldAppearToUser(oneArticle, feedCount) {
articlesSet.insert(oneArticle)
}
}
return articlesSet
}
func feedIDsFromArticles(_ articles: Set<Article>) -> Set<String> {
return Set(articles.map { $0.feedID })
}
func deletePossibleOldArticles(_ articles: Set<Article>) {
let feedIDs = feedIDsFromArticles(articles)
if feedIDs.isEmpty {
return
}
}
// func saveUpdatedAndNewArticles(_ articleChanges: Set<NSDictionary>, newArticles: Set<Article>) {
//
// if articleChanges.isEmpty && newArticles.isEmpty {
// return
// }
//
// statusesTable.assertNoMissingStatuses(newArticles)
// articleCache.cacheArticles(newArticles)
//
// let newArticleDictionaries = newArticles.map { (oneArticle) in
// return oneArticle.databaseDictionary()
// }
//
// queue.update { (database: FMDatabase!) -> Void in
//
// if !articleChanges.isEmpty {
//
// for oneDictionary in articleChanges {
//
// let oneArticleDictionary = oneDictionary.mutableCopy() as! NSMutableDictionary
// let articleID = oneArticleDictionary[DatabaseKey.articleID]!
// oneArticleDictionary.removeObject(forKey: DatabaseKey.articleID)
//
// let _ = database.rs_updateRows(with: oneArticleDictionary as [NSObject: AnyObject], whereKey: DatabaseKey.articleID, equalsValue: articleID, tableName: DatabaseTableName.articles)
// }
//
// }
// if !newArticleDictionaries.isEmpty {
//
// for oneNewArticleDictionary in newArticleDictionaries {
// let _ = database.rs_insertRow(with: oneNewArticleDictionary as [NSObject: AnyObject], insertType: RSDatabaseInsertOrReplace, tableName: DatabaseTableName.articles)
// }
// }
// }
// }
//
// // MARK: Updating Articles
//
// func updateArticles(_ articles: [String: Article], parsedArticles: [String: ParsedItem], feed: Feed, completionHandler: @escaping RSVoidCompletionBlock) {
//
// statusesTable.ensureStatusesForParsedArticles(Set(parsedArticles.values)) {
//
// let articleChanges = self.updateExistingArticles(articles, parsedArticles)
// let newArticles = self.createNewArticles(articles, parsedArticles: parsedArticles, feedID: feed.feedID)
//
// self.saveUpdatedAndNewArticles(articleChanges, newArticles: newArticles)
//
// completionHandler()
// }
// }
//
// func articlesDictionary(_ articles: NSSet) -> [String: AnyObject] {
//
// var d = [String: AnyObject]()
// for oneArticle in articles {
// let oneArticleID = (oneArticle as AnyObject).value(forKey: DatabaseKey.articleID) as! String
// d[oneArticleID] = oneArticle as AnyObject
// }
// return d
// }
//
// func updateExistingArticles(_ articles: [String: Article], _ parsedArticles: [String: ParsedItem]) -> Set<NSDictionary> {
//
// var articleChanges = Set<NSDictionary>()
//
// for oneArticle in articles.values {
// if let oneParsedArticle = parsedArticles[oneArticle.articleID] {
// if let oneArticleChanges = oneArticle.updateWithParsedArticle(oneParsedArticle) {
// articleChanges.insert(oneArticleChanges)
// }
// }
// }
//
// return articleChanges
// }
//
// // MARK: Creating Articles
//
// func createNewArticlesWithParsedArticles(_ parsedArticles: Set<ParsedItem>, feedID: String) -> Set<Article> {
//
// return Set(parsedArticles.map { Article(account: account, feedID: feedID, parsedArticle: $0) })
// }
//
// func articlesWithParsedArticles(_ parsedArticles: Set<ParsedItem>, feedID: String) -> Set<Article> {
//
// var localArticles = Set<Article>()
//
// for oneParsedArticle in parsedArticles {
// let oneLocalArticle = Article(account: self.account, feedID: feedID, parsedArticle: oneParsedArticle)
// localArticles.insert(oneLocalArticle)
// }
//
// return localArticles
// }
//
// func createNewArticles(_ existingArticles: [String: Article], parsedArticles: [String: ParsedItem], feedID: String) -> Set<Article> {
//
// let newParsedArticles = parsedArticlesMinusExistingArticles(parsedArticles, existingArticles: existingArticles)
// let newArticles = createNewArticlesWithParsedArticles(newParsedArticles, feedID: feedID)
//
// statusesTable.attachCachedUniqueStatuses(newArticles)
//
// return newArticles
// }
//
// func parsedArticlesMinusExistingArticles(_ parsedArticles: [String: ParsedItem], existingArticles: [String: Article]) -> Set<ParsedItem> {
//
// var result = Set<ParsedItem>()
//
// for oneParsedArticle in parsedArticles.values {
//
// if let _ = existingArticles[oneParsedArticle.databaseID] {
// continue
// }
// result.insert(oneParsedArticle)
// }
//
// return result
// }
//
// // MARK: Fetching Articles
//
// func fetchArticlesWithWhereClause(_ database: FMDatabase, whereClause: String, parameters: [AnyObject]?) -> Set<Article> {
//
// let sql = "select * from articles where \(whereClause);"
// logSQL(sql)
//
// if let resultSet = database.executeQuery(sql, withArgumentsIn: parameters) {
// return articlesWithResultSet(resultSet, database)
// }
//
// return Set<Article>()
// }
//
// func articlesWithResultSet(_ resultSet: FMResultSet, _ database: FMDatabase) -> Set<Article> {
//
// let fetchedArticles = resultSet.mapToSet { Article(account: self.account, row: $0) }
//
// statusesTable.attachStatuses(fetchedArticles, database)
// authorsTable.attachAuthors(fetchedArticles, database)
// tagsTable.attachTags(fetchedArticles, database)
// attachmentsTable.attachAttachments(fetchedArticles, database)
//
// return fetchedArticles
// }
//
// func fetchArticlesForFeedID(_ feedID: String, database: FMDatabase) -> Set<Article> {
//
// return fetchArticlesWithWhereClause(database, whereClause: "articles.feedID = ?", parameters: [feedID as AnyObject])
// }
//
// // MARK: Unread counts
//
// func numberOfArticles(_ feedID: String, _ database: FMDatabase) -> Int {
//
// let sql = "select count(*) from articles where feedID = ?;"
// logSQL(sql)
//
// return numberWithSQLAndParameters(sql, parameters: [feedID], database)
// }
//
// func unreadCount(_ feedID: String, _ database: FMDatabase) -> Int {
//
// let totalNumberOfArticles = numberOfArticles(feedID, database)
//
// if totalNumberOfArticles <= minimumNumberOfArticles {
// return unreadCountIgnoringCutoffDate(feedID, database)
// }
// return unreadCountRespectingCutoffDate(feedID, database)
// }
//
// func unreadCountIgnoringCutoffDate(_ feedID: String, _ database: FMDatabase) -> Int {
//
// let sql = "select count(*) from articles natural join statuses where feedID=? and read=0 and userDeleted=0;"
// logSQL(sql)
//
// return numberWithSQLAndParameters(sql, parameters: [feedID], database)
// }
//
// func unreadCountRespectingCutoffDate(_ feedID: String, _ database: FMDatabase) -> Int {
//
// let sql = "select count(*) from articles natural join statuses where feedID=? and read=0 and userDeleted=0 and (starred=1 or dateArrived>?);"
// logSQL(sql)
//
// return numberWithSQLAndParameters(sql, parameters: [feedID, articleArrivalCutoffDate], database)
// }
//
// // MARK: Filtering out old articles
//
// func articleIsOlderThanCutoffDate(_ article: Article) -> Bool {
//
// if let dateArrived = article.status?.dateArrived {
// return dateArrived < articleArrivalCutoffDate
// }
// return false
// }
//
// func articleShouldBeSavedForever(_ article: Article) -> Bool {
//
// return article.status.starred
// }
//
// func articleShouldAppearToUser(_ article: Article, _ numberOfArticlesInFeed: Int) -> Bool {
//
// if numberOfArticlesInFeed <= minimumNumberOfArticles {
// return true
// }
// return articleShouldBeSavedForever(article) || !articleIsOlderThanCutoffDate(article)
// }
//
// private static let minimumNumberOfArticlesInFeed = 10
//
// func filteredArticles(_ articles: Set<Article>, feedCounts: [String: Int]) -> Set<Article> {
//
// var articlesSet = Set<Article>()
//
// for oneArticle in articles {
// if let feedCount = feedCounts[oneArticle.feedID], articleShouldAppearToUser(oneArticle, feedCount) {
// articlesSet.insert(oneArticle)
// }
//
// }
//
// return articlesSet
// }
//
//
// func feedIDsFromArticles(_ articles: Set<Article>) -> Set<String> {
//
// return Set(articles.map { $0.feedID })
// }
//
// func deletePossibleOldArticles(_ articles: Set<Article>) {
//
// let feedIDs = feedIDsFromArticles(articles)
// if feedIDs.isEmpty {
// return
// }
// }
}

View File

@ -18,6 +18,7 @@
8455807A1F0AF67D003CCFA1 /* ArticleStatus+Database.swift in Sources */ = {isa = PBXBuildFile; fileRef = 845580791F0AF67D003CCFA1 /* ArticleStatus+Database.swift */; };
8455807C1F0C0DBD003CCFA1 /* Attachment+Database.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8455807B1F0C0DBD003CCFA1 /* Attachment+Database.swift */; };
846146271F0ABC7B00870CB3 /* RSParser.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 846146241F0ABC7400870CB3 /* RSParser.framework */; };
846FB36B1F4A937B00EAB81D /* Feed+Database.swift in Sources */ = {isa = PBXBuildFile; fileRef = 846FB36A1F4A937B00EAB81D /* Feed+Database.swift */; };
84BB4BA21F119C5400858766 /* RSCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 84BB4B981F119C4900858766 /* RSCore.framework */; };
84BB4BA91F11A32800858766 /* TagsTable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84BB4BA81F11A32800858766 /* TagsTable.swift */; };
84D0DEA11F4A429800073503 /* String+Database.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84D0DEA01F4A429800073503 /* String+Database.swift */; };
@ -123,6 +124,7 @@
845580791F0AF67D003CCFA1 /* ArticleStatus+Database.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "ArticleStatus+Database.swift"; path = "Extensions/ArticleStatus+Database.swift"; sourceTree = "<group>"; };
8455807B1F0C0DBD003CCFA1 /* Attachment+Database.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "Attachment+Database.swift"; path = "Extensions/Attachment+Database.swift"; sourceTree = "<group>"; };
8461461E1F0ABC7300870CB3 /* RSParser.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RSParser.xcodeproj; path = ../RSParser/RSParser.xcodeproj; sourceTree = "<group>"; };
846FB36A1F4A937B00EAB81D /* Feed+Database.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = "Feed+Database.swift"; path = "Extensions/Feed+Database.swift"; sourceTree = "<group>"; };
84BB4B8F1F119C4900858766 /* RSCore.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RSCore.xcodeproj; path = ../RSCore/RSCore.xcodeproj; sourceTree = "<group>"; };
84BB4BA81F11A32800858766 /* TagsTable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TagsTable.swift; sourceTree = "<group>"; };
84D0DEA01F4A429800073503 /* String+Database.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = "String+Database.swift"; path = "Extensions/String+Database.swift"; sourceTree = "<group>"; };
@ -210,6 +212,7 @@
isa = PBXGroup;
children = (
845580771F0AF678003CCFA1 /* Folder+Database.swift */,
846FB36A1F4A937B00EAB81D /* Feed+Database.swift */,
845580751F0AF670003CCFA1 /* Article+Database.swift */,
845580791F0AF67D003CCFA1 /* ArticleStatus+Database.swift */,
84F20F901F1810DD00D8E682 /* Author+Database.swift */,
@ -472,6 +475,7 @@
845580671F0AEBCD003CCFA1 /* Constants.swift in Sources */,
840405CF1F1A963700DF0296 /* AttachmentsTable.swift in Sources */,
84D0DEA11F4A429800073503 /* String+Database.swift in Sources */,
846FB36B1F4A937B00EAB81D /* Feed+Database.swift in Sources */,
843CB9961F34174100EE6581 /* Author+Database.swift in Sources */,
845580781F0AF678003CCFA1 /* Folder+Database.swift in Sources */,
845580761F0AF670003CCFA1 /* Article+Database.swift in Sources */,

View File

@ -0,0 +1,18 @@
//
// Feed+Database.swift
// Database
//
// Created by Brent Simmons on 8/20/17.
// Copyright © 2017 Ranchero Software. All rights reserved.
//
import Foundation
import Data
extension Set where Element == Feed {
func feedIDs() -> Set<String> {
return Set(map { $0.feedID })
}
}

Binary file not shown.