Skip to content

Commit

Permalink
Added scaled notifications
Browse files Browse the repository at this point in the history
  • Loading branch information
Bill Pugh committed May 18, 2022
1 parent 5e17b4a commit 247348c
Show file tree
Hide file tree
Showing 5 changed files with 52 additions and 16 deletions.
8 changes: 6 additions & 2 deletions AnalyticsAnalyzer.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@
16856EB027C2F58200F44960 /* functionality.swift in Sources */ = {isa = PBXBuildFile; fileRef = 16515AA525C9939200C97BB8 /* functionality.swift */; };
16856EB127C2F5BB00F44960 /* configuration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1673F54027B002B80037D340 /* configuration.swift */; };
16856EB227C2F70100F44960 /* TabularData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1673F56427B0A7C80037D340 /* TabularData.swift */; };
168933ED282952E600A3FDC2 /* Est. scaled notifications.md in Resources */ = {isa = PBXBuildFile; fileRef = 168933EC282952D900A3FDC2 /* Est. scaled notifications.md */; };
169FB8592642F0C10082DDD2 /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = 169FB8582642F0C10082DDD2 /* main.swift */; };
169FB85E2642F1300082DDD2 /* CSV in Frameworks */ = {isa = PBXBuildFile; productRef = 169FB85D2642F1300082DDD2 /* CSV */; };
169FB8602642F1360082DDD2 /* ArgumentParser in Frameworks */ = {isa = PBXBuildFile; productRef = 169FB85F2642F1360082DDD2 /* ArgumentParser */; };
Expand Down Expand Up @@ -219,6 +220,7 @@
16856E9827BFEFFA00F44960 /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = "<group>"; };
16856EA027C04EBE00F44960 /* SmokeTest.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SmokeTest.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
16856EA227C04EBE00F44960 /* SmokeTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SmokeTest.swift; sourceTree = "<group>"; };
168933EC282952D900A3FDC2 /* Est. scaled notifications.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = "Est. scaled notifications.md"; sourceTree = "<group>"; };
169FB8562642F0C10082DDD2 /* AnalyticsTool */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = AnalyticsTool; sourceTree = BUILT_PRODUCTS_DIR; };
169FB8582642F0C10082DDD2 /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = "<group>"; };
16A292E7282062F40068F29B /* ENCV API key.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = "ENCV API key.md"; sourceTree = "<group>"; };
Expand Down Expand Up @@ -469,6 +471,7 @@
1615FA5627EE09B10089F0B6 /* Excess secondary attack rate 3.md */,
1615FA5827EE09CB0089F0B6 /* Excess secondary attack rate 4.md */,
16AF2DB127E39D59000B1529 /* Users with notifications.md */,
168933EC282952D900A3FDC2 /* Est. scaled notifications.md */,
);
path = "ENPA charts";
sourceTree = "<group>";
Expand Down Expand Up @@ -727,6 +730,7 @@
16B2B5E327D40F86000F763A /* combined analysis.md in Resources */,
16856E6827BC021900F44960 /* tokens claimed.md in Resources */,
16A292EB2821C4920068F29B /* privacy policy.md in Resources */,
168933ED282952E600A3FDC2 /* Est. scaled notifications.md in Resources */,
161A123527DA3D650026C53D /* Secondary attack rate.md in Resources */,
16B2B5E127D40F80000F763A /* ENCV data.md in Resources */,
16856E6627BC021000F44960 /* ENPA opt in.md in Resources */,
Expand Down Expand Up @@ -1060,7 +1064,7 @@
CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
CODE_SIGN_ENTITLEMENTS = "GAEN Analytics/GAEN Analytics.entitlements";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
CURRENT_PROJECT_VERSION = 2;
DEVELOPMENT_ASSET_PATHS = "\"GAEN Analytics/Preview Content\"";
DEVELOPMENT_TEAM = H2Z73245NN;
ENABLE_PREVIEWS = YES;
Expand Down Expand Up @@ -1099,7 +1103,7 @@
CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
CODE_SIGN_ENTITLEMENTS = "GAEN Analytics/GAEN Analytics.entitlements";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
CURRENT_PROJECT_VERSION = 2;
DEVELOPMENT_ASSET_PATHS = "\"GAEN Analytics/Preview Content\"";
DEVELOPMENT_TEAM = H2Z73245NN;
ENABLE_PREVIEWS = YES;
Expand Down
12 changes: 12 additions & 0 deletions AnalyticsAnalyzer/TabularData.swift
Original file line number Diff line number Diff line change
Expand Up @@ -394,6 +394,18 @@ extension DataFrame {
return true
}

mutating func addColumnComputation(_ name1: String, _ name2: String, giving: String, _ function: (Double?, Int?) -> Double?) {
logger.info("addColumComputation(\(name1, privacy: .public), \(name2, privacy: .public), giving \(giving, privacy: .public))")
guard requireColumn(name1, Double.self), requireColumn(name2, Int.self) else {
return
}
let column1 = self[name1, Double.self]
let column2 = self[name2, Int.self]
let resultData = zip(column1, column2).map { function($0, $1) }
append(column: Column(name: giving, contents: resultData))
logger.info("added column \(giving, privacy: .public)")
}

mutating func addColumnPercentage(_ name1: String, _ name2: String, giving: String) {
logger.info("addColumnPercentage(\(name1, privacy: .public), \(name2, privacy: .public), giving \(giving, privacy: .public))")
guard requireColumn(name1, Int.self), requireColumn(name2, Int.self) else {
Expand Down
4 changes: 2 additions & 2 deletions AnalyticsAnalyzer/functionality.swift
Original file line number Diff line number Diff line change
Expand Up @@ -483,7 +483,7 @@ struct Accumulators {
let kuHeader = "ku std,ku,ku-n," + range.map { "ku+n\($0)" }.joined(separator: ",")
let ntHeader = "nt std,nt," + range.map { "nt\($0)," }.joined() + range.map { "nt\($0)%," }.joined() + "nt/ku," + range.map { "nt\($0)/ku," }.joined()
let esHeader = range.map { "nts\($0)%," }.joined()
let sarHeader = range.map { "sar\($0)%," }.joined() + range.map { "sar\($0) stdev%," }.joined() + range.map { "xsar\($0)%" }.joined(separator: ",")
let sarHeader = range.map { "sar\($0)%," }.joined() + range.map { "sar\($0)% stdev," }.joined() + range.map { "xsar\($0)%" }.joined(separator: ",")

let inHeader = "in std," + range.map { "in+\($0)," }.joined() + range.map { "in-\($0)," }.joined() + range.map { "in\($0)%," }.joined()
let deHeader = "dec count,de std," + range.map { "nt\($0) days 0-3,nt\($0) days 4-6,nt\($0) days 7-10,nt\($0) days 11+" }.joined(separator: ",") + ","
Expand Down Expand Up @@ -1466,7 +1466,7 @@ func dotProduct(_ x: [Double], _ y: [Double]) -> Double {

func presentValue(_ name: String, _ x: Double?) -> String {
if let x = x {
if name.hasSuffix("rate") || name.hasSuffix("share") || name.hasSuffix("%") {
if name.hasSuffix("rate") || name.hasSuffix("share") || name.contains("%") {
return "\(Int((x * 100).rounded()))%"
} else if x < 10, x > -10 {
return "\((x * 100).rounded() / 100.0)"
Expand Down
39 changes: 27 additions & 12 deletions GAEN Analytics/AnalysisState.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ class AnalysisState: NSObject, ObservableObject {
var progressSteps: Double = 0.0
@Published var progress: Double = 0.0
var progressCount: Double {
let enpaCount = 7 + additionalMetrics.count
let enpaCount = standardMetrics.count + additionalMetrics.count
return Double(enpaCount + 3)
}

Expand Down Expand Up @@ -285,8 +285,8 @@ class AnalysisState: NSObject, ObservableObject {
progressSteps = 0
available = false

enpaSummary = ""
encvSummary = ""
enpaSummary = "waiting for ENPA data..."
encvSummary = "waiting for ENCV data..."
nextAction = "Fetching analytics"
enpaCharts = []
encvCharts = []
Expand Down Expand Up @@ -372,6 +372,7 @@ class AnalysisState: NSObject, ObservableObject {
arrivingPromptly(enpa: enpa, config: config),
estimatedUsers(enpa: enpa, config: config),
enpaOptIn(enpa: enpa, config: config),
scaledNotifications(enpa: enpa, config: config),
]
enpaCharts = maybeCharts.compactMap { $0 }
let maybeAppendixENPACharts: [ChartOptions?] = [showingNotifications(enpa: enpa, config: config)]
Expand Down Expand Up @@ -468,17 +469,25 @@ func computeEstimatedUsers(platform: String, encv: DataFrame, _ encvColumn: Stri
return result
}

func estimatedNotifications(nt: Double?, estUsers: Int?) -> Double? {
if let nt = nt, let estUsers = estUsers {
return nt / 100_000.0 * Double(estUsers)
}
return nil
}

let standardMetrics = ["userRisk",
"notification",
"notificationInteractions",
"codeVerified",
"keysUploaded",
"dateExposure", "codeVerifiedWithReportType14d", "keysUploadedWithReportType14d", "secondaryAttack14d"]
actor AnalysisTask {
func getAndAnalyzeENPA(config: Configuration, encvAverage: DataFrame?, result: AnalysisState) async {
do {
var raw = RawMetrics(config)
let readThese = ["userRisk",
"notification",
"notificationInteractions",
"codeVerified",
"keysUploaded",
"dateExposure", "codeVerifiedWithReportType14d", "keysUploadedWithReportType14d", "secondaryAttack14d"]
for m in readThese {

for m in standardMetrics {
await result.update(enpa: "fetching ENPA \(m)")
let errors = raw.addMetric(names: [m])
if !errors.isEmpty {
Expand Down Expand Up @@ -507,6 +516,7 @@ actor AnalysisTask {
if let encv = encvAverage {
combinedDataFrame = computeEstimatedUsers(platform: "", encv: encv, "codes claimed", enpa: combinedDataFrame, "vc")
combinedDataFrame = computeEstimatedUsers(platform: "", encv: encv, "publish requests", enpa: combinedDataFrame, "ku")
combinedDataFrame.addColumnComputation("nt", "est users from vc", giving: "est scaled notifications", estimatedNotifications)
iOSDataFrame = computeEstimatedUsers(platform: "iOS ", encv: encv, "publish requests ios", enpa: iOSDataFrame, "ku")
androidDataFrame = computeEstimatedUsers(platform: "Android ", encv: encv, "publish requests android", enpa: androidDataFrame, "ku")
combinedDataFrame.requireColumns("date", "vc count", "vc", "ku", "nt", "codes issued", "est users from vc", "vc ENPA %")
Expand Down Expand Up @@ -707,7 +717,7 @@ func secondaryAttackRate(enpa: DataFrame, config: Configuration) -> ChartOptions

func secondaryAttackRateSpread(enpa: DataFrame, config _: Configuration, notification: Int) -> ChartOptions? {
let sar = "sar\(notification)%"
let stdev = "sar\(notification) stdev%"
let stdev = "sar\(notification)% stdev"
let sarplus = "+1 stdev"
let sarminus = "-1 stdev"
var data = enpa.selecting(columnNames: ["date", sar, stdev])
Expand All @@ -722,7 +732,7 @@ func secondaryAttackRateSpread(enpa: DataFrame, config _: Configuration, notific

func excessSecondaryAttackRateSpread(enpa: DataFrame, config _: Configuration, notification: Int) -> ChartOptions? {
let sar = "xsar\(notification)%"
let stdev = "sar\(notification) stdev%"
let stdev = "sar\(notification)% stdev"
let sarplus = "+1 stdev"
let sarminus = "-1 stdev"
var data = enpa.selecting(columnNames: ["date", sar, stdev])
Expand All @@ -747,6 +757,11 @@ func estimatedUsers(enpa: DataFrame, config _: Configuration) -> ChartOptions? {
ChartOptions.maybe(title: "Estimated users", data: enpa, columns: ["est users from vc"])
}

// est. users
func scaledNotifications(enpa: DataFrame, config _: Configuration) -> ChartOptions? {
ChartOptions.maybe(title: "Est. scaled notifications", data: enpa, columns: ["est scaled notifications"])
}

func showingNotifications(enpa: DataFrame, config: Configuration) -> ChartOptions? {
let columns = Array((1 ... config.numCategories).map { "nts\($0)%" })

Expand Down
5 changes: 5 additions & 0 deletions GAEN Analytics/docs/ENPA charts/Est. scaled notifications.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
The graph shows an estimate of the actual number of notifications sent each day to all EN users.

It is calculated as:

(# of notifications per 100K ENPA users)/100K * (est users from vc)

0 comments on commit 247348c

Please sign in to comment.