Skip to content

Commit

Permalink
Implement final loni generation logic
Browse files Browse the repository at this point in the history
  • Loading branch information
hellais committed Nov 18, 2023
1 parent bea7811 commit 6229caf
Show file tree
Hide file tree
Showing 2 changed files with 178 additions and 36 deletions.
209 changes: 174 additions & 35 deletions oonidata/analysis/website_experiment_results.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,6 @@
log = logging.getLogger("oonidata.analysis")


class LoniValue(NamedTuple):
ok_value: float
down: Dict[str, float]
blocked: Dict[str, float]
blocking_scope: Optional[str]


def map_analysis_to_target_name(analysis):
# Poormans mapping to target name
# TODO(arturo) we eventually want to look these up in some sort of database that groups together related domains
Expand Down Expand Up @@ -60,10 +53,21 @@ def sum(self) -> float:
return s


@dataclass
class LoNI:
ok_final: float

ok: OutcomeSpace
down: OutcomeSpace
blocked: OutcomeSpace
blocking_scope: Optional[str]


def calculate_web_loni(
web_analysis: WebAnalysis,
) -> Tuple[LoniValue, List[str]]:
) -> Tuple[LoNI, List[str]]:
ok_value = 0
ok = OutcomeSpace()
down = OutcomeSpace()
blocked = OutcomeSpace()

Expand Down Expand Up @@ -332,6 +336,7 @@ def calculate_web_loni(
# final analysis to blocked and down dictionaries.
blocked.dns = OutcomeStatus(key=blocked_key, value=blocked_value)
down.dns = OutcomeStatus(key=down_key, value=down_value)
ok.dns = OutcomeStatus(key="dns", value=ok_value)
assert (
blocked.sum() + down.sum() + ok_value == 1
), f"{blocked} + {down} + {ok_value} != 1"
Expand All @@ -343,10 +348,11 @@ def calculate_web_loni(
# TODO(arturo): do we want to have different thresholds here?
analysis_transcript.append("ok_value < 0.5")
return (
LoniValue(
ok_value=ok_value,
blocked=blocked.to_dict(),
down=down.to_dict(),
LoNI(
ok_final=ok_value,
ok=ok,
blocked=blocked,
down=down,
blocking_scope=blocking_scope,
),
analysis_transcript,
Expand Down Expand Up @@ -421,6 +427,8 @@ def calculate_web_loni(

blocked.tcp = OutcomeStatus(key=blocked_key, value=blocked_value * ok_value)
down.tcp = OutcomeStatus(key=down_key, value=down_value * ok_value)
# TODO(arturo): double check this is correct
ok.tcp = OutcomeStatus(key="tcp", value=1 - (blocked.sum() + down.sum()))

old_ok_value = ok_value
ok_value = 1 - (blocked.sum() + down.sum())
Expand All @@ -437,10 +445,11 @@ def calculate_web_loni(
f"ok_value < 0.5 # OK went from {old_ok_value} -> {ok_value}"
)
return (
LoniValue(
ok_value=ok_value,
blocked=blocked.to_dict(),
down=down.to_dict(),
LoNI(
ok_final=ok_value,
ok=ok,
blocked=blocked,
down=down,
blocking_scope=blocking_scope,
),
analysis_transcript,
Expand Down Expand Up @@ -528,6 +537,8 @@ def calculate_web_loni(

blocked.tls = OutcomeStatus(key=blocked_key, value=blocked_value * ok_value)
down.tls = OutcomeStatus(key=down_key, value=down_value * ok_value)
# TODO(arturo): double check this is correct
ok.tls = OutcomeStatus(key="tls", value=1 - (blocked.sum() + down.sum()))

old_ok_value = ok_value
ok_value = 1 - (blocked.sum() + down.sum())
Expand All @@ -542,10 +553,11 @@ def calculate_web_loni(
f"ok_value < 0.5 # OK went from {old_ok_value} -> {ok_value}"
)
return (
LoniValue(
ok_value=ok_value,
blocked=blocked.to_dict(),
down=down.to_dict(),
LoNI(
ok_final=ok_value,
ok=ok,
blocked=blocked,
down=down,
blocking_scope=blocking_scope,
),
analysis_transcript,
Expand Down Expand Up @@ -707,11 +719,15 @@ def calculate_web_loni(
)
blocked.tls = OutcomeStatus(key=blocked_key, value=blocked_value * ok_value)
down.tls = OutcomeStatus(key=down_key, value=down_value * ok_value)
# TODO(arturo): double check this is correct
ok.tls = OutcomeStatus(key="tls", value=1 - (blocked.sum() + down.sum()))
else:
blocked.http = OutcomeStatus(
key=blocked_key, value=blocked_value * ok_value
)
down.http = OutcomeStatus(key=down_key, value=down_value * ok_value)
# TODO(arturo): double check this is correct
ok.http = OutcomeStatus(key="http", value=1 - (blocked.sum() + down.sum()))

old_ok_value = ok_value
ok_value = 1 - (blocked.sum() + down.sum())
Expand All @@ -720,10 +736,11 @@ def calculate_web_loni(
), f"{blocked} + {down} + {ok_value} != 1"

return (
LoniValue(
ok_value=ok_value,
blocked=blocked.to_dict(),
down=down.to_dict(),
LoNI(
ok_final=ok_value,
ok=ok,
blocked=blocked,
down=down,
blocking_scope=blocking_scope,
),
analysis_transcript,
Expand All @@ -750,21 +767,141 @@ def make_website_experiment_results(
target_domain_name = first_analysis.target_domain_name
target_detail = first_analysis.target_detail

loni_ok_value = 0
analysis_transcript_list = []
loni_list = []
loni_blocked_list = []
loni_down_list = []
loni_ok_list = []
for wa in web_analysis:
loni, analysis_transcript = calculate_web_loni(wa)
analysis_transcript_list.append(analysis_transcript)
loni_list.append(loni)
loni_blocked_list.append(loni.blocked)
loni_down_list.append(loni.down)
loni_ok_list.append(loni.ok)

final_blocked = OutcomeSpace()
final_down = OutcomeSpace()
final_ok = OutcomeSpace()
ok_value = 0
blocking_scope = None

loni_down_keys = []
loni_down_values = []
### FINAL DNS
max_dns_blocked = max(
map(lambda x: x.dns, loni_blocked_list), key=lambda d: d.value if d else 0
)
max_dns_down = max(
map(lambda x: x.dns, loni_down_list), key=lambda d: d.value if d else 0
)
min_dns_ok = min(
map(lambda x: x.dns, loni_ok_list), key=lambda d: d.value if d else 0
)
ok_value = min_dns_ok
final_ok.dns = OutcomeStatus(key="dns", value=min_dns_ok)
final_blocked.dns = OutcomeStatus(key=max_dns_blocked.key, value=max_dns_blocked)
final_down.dns = OutcomeStatus(
# TODO(arturo): this is overestimating blocking.
key=max_dns_down.key,
value=1 - (min_dns_ok + max_dns_blocked),
)
if max_dns_blocked.blocking_scope:
blocking_scope = max_dns_blocked.blocking_scope

loni_blocked_keys = []
loni_blocked_values = []
### FINAL TCP
max_tcp_blocked = max(
map(lambda x: x.tcp, loni_blocked_list), key=lambda d: d.value if d else 0
)
max_tcp_down = max(
map(lambda x: x.tcp, loni_down_list), key=lambda d: d.value if d else 0
)
min_tcp_ok = min(
map(lambda x: x.tcp, loni_ok_list), key=lambda d: d.value if d else 0
)
final_blocked.tcp = OutcomeStatus(
key=max_tcp_blocked.key, value=max_tcp_blocked.value * ok_value
)
final_down.tcp = OutcomeStatus(
key=max_tcp_down.value,
value=1 - (min_tcp_ok.value + max_tcp_blocked.value) * ok_value,
)
final_ok.tcp = OutcomeStatus(key="tcp", value=min_tcp_ok.value)

analysis_transcript = []
old_ok_value = ok_value
ok_value = 1 - (final_blocked.sum() + final_down.sum())
log.info(f"TCP done {old_ok_value} -> {ok_value}")

is_anomaly = None
is_confirmed = None
### FINAL TLS
max_tls_blocked = max(
map(lambda x: x.tls, loni_blocked_list), key=lambda d: d.value if d else 0
)
max_tls_down = max(
map(lambda x: x.tls, loni_down_list), key=lambda d: d.value if d else 0
)
min_tls_ok = min(
map(lambda x: x.tls, loni_ok_list), key=lambda d: d.value if d else 0
)
final_blocked.tls = OutcomeStatus(
key=max_tls_blocked.key, value=max_tls_blocked.value * ok_value
)
final_down.tls = OutcomeStatus(
key=max_tls_down.value,
value=1 - (min_tls_ok.value + max_tls_blocked.value) * ok_value,
)
final_ok.tls = OutcomeStatus(key="http", value=min_tls_ok.value)
old_ok_value = ok_value
ok_value = 1 - (final_blocked.sum() + final_down.sum())
log.info(f"TLS done {old_ok_value} -> {ok_value}")

for wa in web_analysis:
loni = calculate_web_loni(wa)
### FINAL HTTP
max_http_blocked = max(
map(lambda x: x.http, loni_blocked_list), key=lambda d: d.value if d else 0
)
max_http_down = max(
map(lambda x: x.http, loni_down_list), key=lambda d: d.value if d else 0
)
min_http_ok = min(
map(lambda x: x.http, loni_ok_list), key=lambda d: d.value if d else 0
)
final_blocked.http = OutcomeStatus(
key=max_http_blocked.key, value=max_http_blocked.value * ok_value
)
final_down.http = OutcomeStatus(
key=max_http_down.value,
value=1 - (min_http_ok.value + max_http_blocked.value) * ok_value,
)
final_ok.http = OutcomeStatus(key="http", value=min_http_ok.value)
if max_http_blocked.blocking_scope:
if blocking_scope is not None:
log.warning(f"overwriting blocking_scope key: {blocking_scope}")
blocking_scope = max_http_blocked.blocking_scope

old_ok_value = ok_value
ok_value = 1 - (final_blocked.sum() + final_down.sum())
log.info(f"HTTP done {old_ok_value} -> {ok_value}")

final_loni = LoNI(
ok_final=ok_value,
ok=final_ok,
down=final_down,
blocked=final_blocked,
blocking_scope=blocking_scope,
)

loni_ok_value = final_loni.ok_final

loni_down = final_loni.down.to_dict()
loni_down_keys, loni_down_values = list(loni_down.keys()), list(loni_down.values())

loni_blocked = final_loni.down.to_dict()
loni_blocked_keys, loni_blocked_values = list(loni_blocked.keys()), list(
loni_blocked.values()
)

loni_ok = final_loni.ok.to_dict()
loni_ok_keys, loni_ok_values = list(loni_ok.keys()), list(loni_ok.values())

is_anomaly = loni_ok_value < 0.6
is_confirmed = final_loni.blocked.sum() > 0.9

er = ExperimentResultInstant(
experiment_result_id=experiment_result_id,
Expand Down Expand Up @@ -792,7 +929,9 @@ def make_website_experiment_results(
loni_down_values=loni_down_values,
loni_blocked_keys=loni_blocked_keys,
loni_blocked_values=loni_blocked_values,
analysis_transcript=analysis_transcript,
loni_ok_keys=loni_ok_keys,
loni_ok_values=loni_ok_values,
analysis_transcript_list=analysis_transcript_list,
measurement_count=1,
observation_count=len(web_analysis),
vp_count=1,
Expand Down
5 changes: 4 additions & 1 deletion oonidata/models/experiment_result.py
Original file line number Diff line number Diff line change
Expand Up @@ -163,9 +163,12 @@ class ExperimentResultInstant:
loni_blocked_keys: List[str]
loni_blocked_values: List[float]

loni_ok_keys: List[str]
loni_ok_values: List[float]

# Inside this string we include a representation of the logic that lead us
# to produce the above loni values
analysis_transcript: Optional[List[str]]
analysis_transcript_list: Optional[List[List[str]]]

# Number of measurements used to produce this experiment result
measurement_count: int
Expand Down

0 comments on commit 6229caf

Please sign in to comment.