// // SettingsController.swift // CapCollector // // Created by Christoph on 15.10.18. // Copyright © 2018 CH. All rights reserved. // import UIKit class SettingsController: UITableViewController { @IBOutlet weak var totalCapsLabel: UILabel! @IBOutlet weak var totalImagesLabel: UILabel! @IBOutlet weak var recognizedCapsLabel: UILabel! @IBOutlet weak var imagesStatsLabel: UILabel! @IBOutlet weak var databaseUpdatesLabel: UILabel! @IBOutlet weak var dropboxAccountLabel: UILabel! @IBOutlet weak var countsLabel: UILabel! private var isUpdatingCounts = false private var isUploadingNameFile = false private var isUpdatingThumbnails = false private var isUpdatingColors = false override var supportedInterfaceOrientations: UIInterfaceOrientationMask { return .portrait } override func viewDidLoad() { super.viewDidLoad() } override func viewWillAppear(_ animated: Bool) { updateDropboxStatus() updateNameFileStats() updateDatabaseStats() (navigationController as! NavigationController).allowLandscape = false } override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) setClassifierChoice(Persistence.useMobileNet) } private func updateThumbnails() { isUpdatingThumbnails = true for cap in Cap.all.values { cap.makeThumbnail() } isUpdatingThumbnails = false } private func updateColors() { isUpdatingColors = true Cap.shouldSave = false for cap in Cap.all.values { cap.makeAverageColor() } Cap.shouldSave = true isUpdatingColors = false } private func updateDatabaseStats() { let totalCaps = Cap.totalCapCount totalCapsLabel.text = "\(totalCaps) caps" totalImagesLabel.text = "\(Cap.imageCount) images" let recognizedCaps = Persistence.recognizedCapCount let newCapCount = totalCaps - recognizedCaps recognizedCapsLabel.text = "\(newCapCount) new caps" let ratio = Float(Cap.imageCount)/Float(Cap.totalCapCount) let (images, count) = Cap.getCapStatistics().enumerated().first(where: { $0.element != 0 })! imagesStatsLabel.text = String(format: "%.2f images per cap, lowest count: %d (%dx)", ratio, images, count) } private func updateNameFileStats() { let capCount = Cap.totalCapCount - Persistence.lastUploadedCapCount let imageCount = Cap.imageCount - Persistence.lastUploadedImageCount databaseUpdatesLabel.text = "\(capCount) new caps and \(imageCount) new images" } private func updateDropboxStatus() { dropboxAccountLabel.text = DropboxController.shared.isEnabled ? "Sign out" : "Sign in" } private func setClassifierChoice(_ useMobileNet: Bool) { tableView.cellForRow(at: IndexPath(row: 0, section: 0))?.accessoryType = useMobileNet ? .checkmark : .none } private func toggleClassifier() { let newValue = !Persistence.useMobileNet Persistence.useMobileNet = newValue setClassifierChoice(newValue) } override func tableView(_ tableView: UITableView, shouldHighlightRowAt indexPath: IndexPath) -> Bool { switch indexPath.section { case 0: // Choose models return true case 1: // Mosaic return true case 2: // Database return indexPath.row == 2 && !isUploadingNameFile case 3: // Refresh switch indexPath.row { case 0: return !isUpdatingCounts case 1: return !isUpdatingThumbnails case 2: return !isUpdatingColors default: return false } case 4: // Dropbox account return true case 5: // Log file return true default: return false } } override func tableView(_ tableView: UITableView, willSelectRowAt indexPath: IndexPath) -> IndexPath? { switch indexPath.section { case 0: // Choose models return indexPath case 1: // Mosaic return indexPath case 2: // Database return (indexPath.row == 2 && !isUploadingNameFile) ? indexPath : nil case 3: // Refresh count switch indexPath.row { case 0: return isUpdatingCounts ? nil : indexPath case 1: return isUpdatingThumbnails ? nil : indexPath case 2: return isUpdatingColors ? nil : indexPath default: return nil } case 4: // Dropbox account return indexPath case 5: // Log file return indexPath default: return nil } } override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { tableView.deselectRow(at: indexPath, animated: true) switch indexPath.section { case 0: // Choose models toggleClassifier() case 2: // Upload if indexPath.row == 2 && !isUploadingNameFile { uploadNameFile() } case 3: // Refresh count switch indexPath.row { case 0: updateCounts() case 1: updateThumbnails() case 2: updateColors() default: break } default: break } } private func updateCounts() { isUpdatingCounts = true Cap.shouldSave = false // TODO: Don't make all requests at the same time DispatchQueue.global(qos: .userInitiated).async { let list = Cap.all.keys.sorted() let total = list.count var finished = 0 let chunks = list.chunked(into: 10) for chunk in chunks { self.updateCounts(ids: chunk) finished += 10 DispatchQueue.main.async { self.countsLabel.text = "\(finished) / \(total) finished" } } self.isUpdatingCounts = false Cap.shouldSave = true DispatchQueue.main.async { self.countsLabel.text = "Refresh image counts" self.updateDatabaseStats() } } } private func updateCounts(ids: [Int]) { var count = ids.count let s = DispatchSemaphore(value: 0) for cap in ids { Cap.all[cap]!.updateCount { _ in count -= 1 if count == 0 { s.signal() } } } _ = s.wait(timeout: .now() + .seconds(30)) event("Finished updating ids \(ids.first!) to \(ids.last!)") } private func uploadNameFile() { event("Uploading name file") isUploadingNameFile = true Cap.saveAndUpload() { _ in self.isUploadingNameFile = false self.updateNameFileStats() } } private func toggleDropbox() { guard !DropboxController.shared.isEnabled else { DropboxController.shared.signOut() updateDropboxStatus() return } DropboxController.shared.setup(in: self) updateDropboxStatus() } override func prepare(for segue: UIStoryboardSegue, sender: Any?) { guard let id = segue.identifier else { return } switch id { case "showMosaic": (navigationController as! NavigationController).allowLandscape = true case "showLog": return default: return } } } extension SettingsController: Logger { static let logToken = "[Settings]" } private extension Array { func chunked(into size: Int) -> [[Element]] { return stride(from: 0, to: count, by: size).map { Array(self[$0 ..< Swift.min($0 + size, count)]) } } }