Context analysis: Co-Occurrence

This commit is contained in:
relikd
2020-06-24 13:09:11 +02:00
parent 4b32df5683
commit 96656438c6
15 changed files with 526 additions and 40 deletions

View File

@@ -0,0 +1,88 @@
import UIKit
class VCCoOccurrence: UIViewController, UITableViewDataSource {
var fqdn: String!
private var dataSource: [ContextAnalysisResult] = []
@IBOutlet private var tableView: UITableView!
@IBOutlet private var timeSegment: UISegmentedControl!
private let availableTimes = [0, 5, 15, 30]
private var selectedTime = -1 {
didSet { logTimeDelta = log(CGFloat(max(2, selectedTime+1))) }
}
private var logTimeDelta: CGFloat = 1
private var logMaxCount: CGFloat = 1
override func viewDidLoad() {
super.viewDidLoad()
selectedTime = Pref.ContextAnalyis.CoOccurrenceTime ?? 5 // calls `didSet` and `logTimeDelta`
timeSegment.removeAllSegments() // clear IB values
for (i, time) in availableTimes.enumerated() {
timeSegment.insertSegment(withTitle: TimeFormat(.abbreviated).from(seconds: time), at: i, animated: false)
if time == selectedTime {
timeSegment.selectedSegmentIndex = i
}
}
reloadDataSource()
}
func reloadDataSource() {
dataSource = [("Loading …", 0, 0, 0)]
logMaxCount = 1
tableView.reloadData()
let domain = fqdn!
let time = Timestamp(selectedTime)
DispatchQueue.global().async { [weak self] in
guard let db = AppDB, let times = db.dnsLogsUniqTs(domain), times.count > 0 else {
return // should never happen, or what did you tap then?
}
guard let result = db.contextAnalysis(coOccurrence: times, plusMinus: time, exclude: domain) else {
return
}
self?.dataSource = result
self?.logMaxCount = log(CGFloat(result.reduce(0) { max($0, $1.count) }))
DispatchQueue.main.sync { [weak self] in
self?.tableView.reloadData()
}
}
}
@IBAction func didChangeTime(_ sender: UISegmentedControl) {
selectedTime = availableTimes[sender.selectedSegmentIndex]
Pref.ContextAnalyis.CoOccurrenceTime = selectedTime
reloadDataSource()
}
@IBAction func didClose(_ sender: UIBarButtonItem) {
dismiss(animated: true)
}
// MARK: - Table View Data Source
func tableView(_ _: UITableView, numberOfRowsInSection _: Int) -> Int {
dataSource.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "CoOccurrenceCell") as! CoOccurrenceCell
let src = dataSource[indexPath.row]
cell.title.text = src.domain
cell.rank.text = "\(indexPath.row + 1)."
cell.count.text = "\(src.count)"
cell.avgdiff.text = String(format: "%.2fs", src.avg)
cell.countMeter.percent = (log(CGFloat(src.count)) / logMaxCount)
cell.avgdiffMeter.percent = 1 - (log(CGFloat(src.avg + 1)) / logTimeDelta)
return cell
}
}
class CoOccurrenceCell: UITableViewCell {
@IBOutlet var title: UILabel!
@IBOutlet var rank: TagLabel!
@IBOutlet var count: TagLabel!
@IBOutlet var avgdiff: TagLabel!
@IBOutlet var countMeter: MeterBar!
@IBOutlet var avgdiffMeter: MeterBar!
}

View File

@@ -1,7 +1,9 @@
import UIKit
class TVCHostDetails: UITableViewController, SyncUpdateDelegate {
class TVCHostDetails: UITableViewController, SyncUpdateDelegate, UITabBarDelegate {
@IBOutlet private var actionsBar: UITabBar!
public var fullDomain: String!
private var dataSource: [GroupedTsOccurrence] = []
// TODO: respect date reverse sort order
@@ -12,7 +14,9 @@ class TVCHostDetails: UITableViewController, SyncUpdateDelegate {
sync.addObserver(self) // calls `syncUpdate(reset:)`
if #available(iOS 10.0, *) {
sync.allowPullToRefresh(onTVC: self, forObserver: self)
actionsBar.unselectedItemTintColor = .systemBlue
}
UIDevice.orientationDidChangeNotification.observe(call: #selector(didChangeOrientation), on: self)
}
// MARK: - Table View Data Source
@@ -29,6 +33,30 @@ class TVCHostDetails: UITableViewController, SyncUpdateDelegate {
}
}
// #########################
// #
// # MARK: - Tab Bar
// #
// #########################
extension TVCHostDetails {
@objc private func didChangeOrientation(_ sender: Notification) {
tableView.sizeHeaderToFit() // otherwise TabBar won't compress
}
func tabBar(_ tabBar: UITabBar, didSelect item: UITabBarItem) {
tabBar.selectedItem = nil
performSegue(withIdentifier: "segueAnalysisCoOccurrence", sender: nil)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "segueAnalysisCoOccurrence" {
(segue.destination as? VCCoOccurrence)?.fqdn = fullDomain
}
}
}
// ################################
// #
// # MARK: - Partial Update

View File

@@ -118,34 +118,3 @@ class VCDateFilter: UIViewController, UIGestureRecognizerDelegate {
}
}
}
// MARK: White Triangle Popup Arrow
@IBDesignable
class PopupTriangle: UIView {
@IBInspectable var rotation: CGFloat = 0
@IBInspectable var color: UIColor = .black
override func draw(_ rect: CGRect) {
guard let c = UIGraphicsGetCurrentContext() else { return }
let w = rect.width, h = rect.height
switch rotation {
case 90: // right
c.lineFromTo(x1: 0, y1: 0, x2: w, y2: h/2)
c.addLine(to: CGPoint(x: 0, y: h))
case 180: // bottom
c.lineFromTo(x1: w, y1: 0, x2: w/2, y2: h)
c.addLine(to: CGPoint(x: 0, y: 0))
case 270: // left
c.lineFromTo(x1: w, y1: h, x2: 0, y2: h/2)
c.addLine(to: CGPoint(x: w, y: 0))
default: // top
c.lineFromTo(x1: 0, y1: h, x2: w/2, y2: 0)
c.addLine(to: CGPoint(x: w, y: h))
}
c.closePath()
c.setFillColor(color.cgColor)
c.fillPath()
}
}