Compare group of apps
This commit is contained in:
@@ -128,6 +128,12 @@ footer .links {
|
||||
.pagination a { margin: .5em; padding: .2em; }
|
||||
.pagination a.active { border: 1pt solid black; border-radius: .2em; }
|
||||
|
||||
/* Lists */
|
||||
.title-sub h4, h2.title { margin-bottom: 0; }
|
||||
.title-sub span, p.subtitle { margin-top: .2em; }
|
||||
.title-sub p { margin: .2em .5em 1.5em; font-style: italic; }
|
||||
.title-sub span, .found-in span, .snd { color: #586472; font-size: .85em; }
|
||||
|
||||
|
||||
#categories h3 { margin-top: 2em; }
|
||||
|
||||
@@ -136,7 +142,6 @@ footer .links {
|
||||
#dom-top10>div { margin: .4em; }
|
||||
#dom-top10 a, #dom-toc a { word-wrap: break-word; }
|
||||
#dom-toc span { display: table; }
|
||||
.found-in span, .snd { color: #586472; font-size: .85em; }
|
||||
.fillbar {
|
||||
display: block;
|
||||
background: #DDD;
|
||||
@@ -159,8 +164,6 @@ footer .links {
|
||||
|
||||
/* app bundle */
|
||||
.squeeze { max-width: 700px; }
|
||||
h2.title { margin-bottom: 0; }
|
||||
p.subtitle { margin-top: .2em; }
|
||||
.mg_lr { margin: 0 .4em; }
|
||||
.mg_top { margin-top: 2em; }
|
||||
.right { text-align: right; }
|
||||
@@ -172,9 +175,11 @@ p.subtitle { margin-top: .2em; }
|
||||
.xscroll { overflow-x: scroll; max-width: 100%; }
|
||||
.yscroll { overflow-y: scroll; max-height: 80vh; }
|
||||
.floatr, #main-nav { float: right; }
|
||||
table { border-collapse: collapse; }
|
||||
table.alternate td, table.alternate th { padding: .5em; }
|
||||
table.alternate tr:nth-child(even) { background: #DDD; }
|
||||
table.alternate tr:nth-child(odd) { background: #F9F9F9; }
|
||||
table.stick-first td:nth-child(1) { position: sticky; left: 0; background: inherit; }
|
||||
|
||||
/*#meta { margin-bottom: 2em; }*/
|
||||
#meta .icons { margin-bottom: 2em; }
|
||||
@@ -282,14 +287,16 @@ p.trckr { font-size: .9em; margin-left: .5em; }
|
||||
#rank-list th:not(:first-child) { min-width: 12ex; }
|
||||
|
||||
#rank-list img { width: 25px; height: 25px; vertical-align: bottom; }
|
||||
#rank-list td { text-align: center; min-width: max-content; }
|
||||
#rank-list td:nth-child(1) {
|
||||
position: sticky; left: 0;
|
||||
background: inherit;
|
||||
text-align: right;
|
||||
}
|
||||
#rank-list td { text-align: center; }
|
||||
#rank-list td:nth-child(1) { text-align: right; white-space: nowrap; }
|
||||
#rank-list td:nth-child(2) { text-align: left; }
|
||||
|
||||
/* rank-cluster */
|
||||
table.cluster tr { font-size: 0.8em; }
|
||||
table.cluster td { padding: 0.75em; }
|
||||
table.cluster .sep { border-left: 1pt solid #000; }
|
||||
table.cluster td:not(:first-child) { text-align: center; }
|
||||
|
||||
/* responsive */
|
||||
@media(max-width: 900px) {
|
||||
#stats { grid-template-columns: repeat(2, max-content); }
|
||||
|
||||
@@ -40,11 +40,11 @@ Given A → B, B depends on A
|
||||
|
||||
```
|
||||
digraph Dependency {
|
||||
"." -> download_tracker
|
||||
"." -> download_itunes
|
||||
"." -> bundle_combine
|
||||
download_itunes -> index_app_names
|
||||
index_app_names -> index_categories
|
||||
index_app_names -> index_rank
|
||||
index_app_names -> html_index_domains
|
||||
index_categories -> html_categories
|
||||
index_categories -> html_index_apps
|
||||
@@ -55,7 +55,8 @@ digraph Dependency {
|
||||
html_index_apps -> html_ranking
|
||||
html_categories -> html_ranking
|
||||
html_index_domains -> html_root
|
||||
"." -> download_tracker
|
||||
index_categories -> index_rank
|
||||
index_rank -> html_group_compare
|
||||
}
|
||||
```
|
||||
[graphviz](http://www.webgraphviz.com/)
|
||||
@@ -47,21 +47,6 @@ def stat(col, title, ident, value, optional=None):
|
||||
|
||||
def gen_page(bundle_id, obj):
|
||||
|
||||
def round_num(num):
|
||||
return format(num, '.1f') # .rstrip('0').rstrip('.')
|
||||
|
||||
def as_pm(value):
|
||||
return round_num(value) + '/min'
|
||||
|
||||
def as_percent(value):
|
||||
return round_num(value * 100) + '%'
|
||||
|
||||
def seconds_to_time(seconds):
|
||||
seconds = int(seconds)
|
||||
minutes, seconds = divmod(seconds, 60)
|
||||
hours, minutes = divmod(minutes, 60)
|
||||
return '{:02d}:{:02d}:{:02d}'.format(hours, minutes, seconds)
|
||||
|
||||
name = index_app_names.get_name(bundle_id)
|
||||
gernes = index_categories.get_categories(bundle_id)
|
||||
obj['tracker'] = list(filter(lambda x: x[2], obj['subdom']))
|
||||
@@ -83,13 +68,13 @@ def gen_page(bundle_id, obj):
|
||||
</div>
|
||||
<div id="stats">
|
||||
{ stat(1, 'Number of recordings:', 'sum_rec', obj['sum_rec']) }
|
||||
{ stat(1, 'Average recording time:', 'avg_time', seconds_to_time(obj['avg_time'])) }
|
||||
{ stat(2, 'Cumulative recording time:', 'sum_time', seconds_to_time(obj['sum_time'])) }
|
||||
{ stat(1, 'Average number of requests:', 'avg_logs_pm', round_num(obj['avg_logs']), as_pm(obj['avg_logs_pm'])) }
|
||||
{ stat(2, 'Total number of requests:', 'sum_logs_pm', str(obj['sum_logs']), as_pm(obj['sum_logs_pm'])) }
|
||||
{ stat(1, 'Average recording time:', 'avg_time', HTML.seconds_to_time(obj['avg_time'])) }
|
||||
{ stat(2, 'Cumulative recording time:', 'sum_time', HTML.seconds_to_time(obj['sum_time'])) }
|
||||
{ stat(1, 'Average number of requests:', 'avg_logs_pm', HTML.fmt_round_num(obj['avg_logs']), HTML.fmt_as_pm(obj['avg_logs_pm'])) }
|
||||
{ stat(2, 'Total number of requests:', 'sum_logs_pm', str(obj['sum_logs']), HTML.fmt_as_pm(obj['sum_logs_pm'])) }
|
||||
{ stat(1, 'Number of domains:', 'pardom', len(obj['pardom'])) }
|
||||
{ stat(2, 'Number of subdomains:', 'subdom', len(obj['subdom'])) }
|
||||
{ stat(3, 'Tracker percentage:', 'tracker_percent', as_percent(obj['tracker_percent'])) }
|
||||
{ stat(3, 'Tracker percentage:', 'tracker_percent', HTML.fmt_as_percent(obj['tracker_percent'])) }
|
||||
</div>
|
||||
<h3>Connections</h3>
|
||||
<div>
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
import lib_common as mylib
|
||||
import lib_html as HTML
|
||||
import html_group_compare
|
||||
|
||||
|
||||
def write_overview_page(base_dir, category_tuples, title):
|
||||
@@ -12,7 +13,8 @@ def write_overview_page(base_dir, category_tuples, title):
|
||||
cluster[i].append(x)
|
||||
except KeyError:
|
||||
cluster[i] = [x]
|
||||
src = '<h2>{}</h2><div id="categories">'.format(title)
|
||||
src = HTML.h2_path_n_rank(title, [], 'compare/', 'Compare')
|
||||
src += '<div id="categories">'
|
||||
for i, arr in sorted(cluster.items()):
|
||||
mylib.sort_by_name(arr, 1)
|
||||
kind = 'Apps' if i == 6 else 'Games' if i == 7 else 'Other'
|
||||
@@ -21,6 +23,12 @@ def write_overview_page(base_dir, category_tuples, title):
|
||||
src += ''.join([HTML.a_category(*x) for x in arr]) + '</div>'
|
||||
HTML.write(base_dir, src + '</div>', title)
|
||||
|
||||
# make groupby compare html
|
||||
base_dir = mylib.path_add(base_dir, 'compare')
|
||||
html_group_compare.write_groupby_multi(
|
||||
base_dir, [('/category', 'All Categories')], 'Compare', 'Categories',
|
||||
[('category-6', 'Category: Apps'), ('category-7', 'Category: Games')])
|
||||
|
||||
|
||||
def process(affected=None, per_page=60):
|
||||
print('generating html: category-index ...')
|
||||
@@ -34,7 +42,7 @@ def process(affected=None, per_page=60):
|
||||
continue
|
||||
out_dir = mylib.path_add(base, cid)
|
||||
# full url since categories can have page 2, 3, etc.
|
||||
A = HTML.h2_path_n_rank(cname, [(parent, '/category/')], 'ranking/')
|
||||
A = HTML.h2_path_n_rank(cname, [('/category/', parent)], 'ranking/')
|
||||
Z = HTML.p_download_json('data.json', 'category-{}.json'.format(cid))
|
||||
_, a = HTML.write_app_pages(out_dir, json['apps'],
|
||||
cname, per_page, pre=A, post=Z)
|
||||
|
||||
136
src/html_group_compare.py
Executable file
136
src/html_group_compare.py
Executable file
@@ -0,0 +1,136 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import lib_common as mylib
|
||||
import lib_html as HTML
|
||||
import index_rank # fname_rank_list
|
||||
|
||||
|
||||
def make_groupby_table_html(json, struct):
|
||||
sep = ' class="sep"'
|
||||
par_tr = ''
|
||||
sub_tr = ''
|
||||
for par_col, cols in struct:
|
||||
par_tr += f'<th{sep} colspan="{len(cols)}">{par_col}</th>'
|
||||
for i, x in enumerate(cols):
|
||||
sub_tr += '<th{}>{}</th>'.format(sep if i == 0 else '', x[0])
|
||||
src = f'''
|
||||
<div class="xscroll">
|
||||
<table class="cluster alternate">
|
||||
<tr><th></th>{par_tr}</tr>
|
||||
<tr><th></th>{sub_tr}</tr>
|
||||
'''
|
||||
for group in json:
|
||||
ident = group[0]
|
||||
if isinstance(ident, list):
|
||||
ident = f'<a href="{ident[1]}">{ident[0]}</a>'
|
||||
src += f'<tr><td>{ident}</td>'
|
||||
for _, cols in struct:
|
||||
first = True
|
||||
for _, col_i, fn in cols:
|
||||
val = group[col_i]
|
||||
src += '<td{}>{}</td>'.format(sep if first else '', fn(val))
|
||||
first = False
|
||||
src += '</tr>\n'
|
||||
return src + '</table></div>\n'
|
||||
|
||||
|
||||
def prefill_groupby_table(cluster_file):
|
||||
return make_groupby_table_html(mylib.json_read(cluster_file), [
|
||||
['Apps', [
|
||||
(' ', 1, int)]],
|
||||
['Number of Recordings', [
|
||||
('avg', 2, HTML.fmt_round_num),
|
||||
('total', 3, int)]],
|
||||
['Number of Requests', [
|
||||
('avg', 4, HTML.fmt_round_num),
|
||||
('total', 5, int)]],
|
||||
['Recording Time', [
|
||||
('avg', 6, HTML.seconds_to_time),
|
||||
('total', 7, HTML.seconds_to_time)]],
|
||||
['Requests per Minute', [
|
||||
('min', 9, HTML.fmt_as_pm),
|
||||
('avg', 8, HTML.fmt_as_pm),
|
||||
('max', 10, HTML.fmt_as_pm)]],
|
||||
['Number of Domains', [
|
||||
('min', 12, int),
|
||||
('avg', 11, HTML.fmt_round_num),
|
||||
('max', 13, int)]],
|
||||
['Number of Subdomains', [
|
||||
('min', 15, int),
|
||||
('avg', 14, HTML.fmt_round_num),
|
||||
('max', 16, int)]],
|
||||
['Tracker Percentage', [
|
||||
('min', 18, HTML.fmt_as_percent),
|
||||
('avg', 17, HTML.fmt_as_percent),
|
||||
('max', 19, HTML.fmt_as_percent)]],
|
||||
])
|
||||
|
||||
|
||||
def gen_and_link_groupby(cluster_id, base, raw_fname='data'):
|
||||
json_file = index_rank.fname_rank_list('groupby', cluster_id)
|
||||
raw_fname += '.json'
|
||||
mylib.symlink(json_file, mylib.path_add(base, raw_fname))
|
||||
named_download = 'compare-{}.json'.format(cluster_id)
|
||||
return prefill_groupby_table(json_file) + HTML.p_download_json(
|
||||
raw_fname, named_download)
|
||||
|
||||
|
||||
def write_groupby_multi(base_dir, h2_paths, h2_title, page_title, groups_arr):
|
||||
mylib.mkdir(base_dir)
|
||||
src = '<h2>{}</h2>'.format(HTML.a_path(h2_paths, h2_title))
|
||||
for gid, name in groups_arr:
|
||||
raw_name = gid if len(groups_arr) > 1 else 'data'
|
||||
src += f'\n<h3>{name}</h3>'
|
||||
src += gen_and_link_groupby(gid, base_dir, raw_name)
|
||||
HTML.write(base_dir, src, title='Compare: ' + page_title)
|
||||
|
||||
|
||||
def write_groupby_single(base_dir, gid, name, parent):
|
||||
base = mylib.path_add(base_dir, gid)
|
||||
write_groupby_multi(base,
|
||||
[('/results/', 'Results'), ('/compare/', parent)],
|
||||
name, name, [(gid, '')])
|
||||
|
||||
|
||||
def process():
|
||||
print('generating html: group compare ...')
|
||||
print(' lists')
|
||||
base_custom = mylib.path_out('compare')
|
||||
title_custom = 'Compare'
|
||||
arr = []
|
||||
for list_id, json in mylib.enum_custom_lists('groupby_'):
|
||||
try:
|
||||
if json['hidden']:
|
||||
continue
|
||||
except KeyError:
|
||||
pass
|
||||
list_name = json['name']
|
||||
print(' ' + list_name)
|
||||
grp_count = len(json['groups'])
|
||||
app_count = sum([len(x['apps']) for x in json['groups']])
|
||||
try:
|
||||
desc = json['desc']
|
||||
except KeyError:
|
||||
desc = None
|
||||
arr.append((list_id, list_name, grp_count, app_count, desc))
|
||||
write_groupby_single(base_custom, list_id, list_name, title_custom)
|
||||
|
||||
print(' index page')
|
||||
mylib.sort_by_name(arr, 1)
|
||||
src = f'''
|
||||
<h2>{HTML.a_path([('/results/', 'Results')], title_custom)}</h2>
|
||||
<p class="squeeze">
|
||||
Comparison groups do not compare a single application against another.
|
||||
Instead comparison groups cluster multiple applications into a single, logical group.
|
||||
For example, paid-vs-free apps could be a comparison group.
|
||||
</p>
|
||||
<div>'''
|
||||
for x in arr:
|
||||
src += HTML.h4_a_title_sub_desc(
|
||||
x[0], x[1], f'has {x[2]} groups with {x[3]} apps', x[4])
|
||||
HTML.write(base_custom, src + '</div>', title=title_custom)
|
||||
print('')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
process()
|
||||
@@ -8,7 +8,7 @@ import index_app_names # get_sorted_app_names
|
||||
def process(per_page=60):
|
||||
print('generating html: app-index ...')
|
||||
title = 'Apps (A–Z)'
|
||||
header = HTML.h2_path_n_rank(title, [('Results', '/results/')], 'ranking/')
|
||||
header = HTML.h2_path_n_rank(title, [('/results/', 'Results')], 'ranking/')
|
||||
p, a = HTML.write_app_pages(mylib.path_out('index', 'apps'),
|
||||
index_app_names.get_sorted_app_names(),
|
||||
title, per_page=per_page, pre=header)
|
||||
|
||||
@@ -70,7 +70,7 @@ def gen_html_trinity(idx_dir, app_count, json, title, symlink):
|
||||
|
||||
def write_index(fname, title, button):
|
||||
HTML.write(idx_dir, '<h2>{}</h2>{}{}'.format(
|
||||
HTML.a_path([('Results', '/results/')], title),
|
||||
HTML.a_path([('/results/', 'Results')], title),
|
||||
dropdown_choose(button), duo_list(list1, list2)
|
||||
), title=title, fname=fname)
|
||||
|
||||
@@ -91,7 +91,7 @@ def gen_html_trinity(idx_dir, app_count, json, title, symlink):
|
||||
|
||||
def gen_lookup(html_dir, doms_dict, flag, title):
|
||||
HTML.write(html_dir, f'''
|
||||
<h2>{ HTML.a_path([('All Domains', '/index/domains/all/')],
|
||||
<h2>{ HTML.a_path([('/index/domains/all/', 'All Domains')],
|
||||
'<span id="name"></span>') }</h2>
|
||||
<p>Known Tracker: <b id="known">?</b></p>
|
||||
<p>Present in: <b id="num-apps">… applications</b></p>
|
||||
|
||||
@@ -20,7 +20,7 @@ def html_default_description():
|
||||
def html_table():
|
||||
return '''
|
||||
<div class="xscroll yscroll">
|
||||
<table id="rank-list" class="alternate"><tr><td>Loading …</td></tr></table>
|
||||
<table id="rank-list" class="alternate stick-first"><tr><td>Loading …</td></tr></table>
|
||||
</div>'''
|
||||
|
||||
|
||||
@@ -35,8 +35,8 @@ def html_script_chunk(fname, sort_col, sort_order):
|
||||
|
||||
def write_ranking_all(title, base_dir):
|
||||
# full urls since app index can have page 2, 3, etc.
|
||||
src = html_h2_path([('Results', '/results/'),
|
||||
('Apps (A–Z)', '/index/apps/')])
|
||||
src = html_h2_path([('/results/', 'Results'),
|
||||
('/index/apps/', 'Apps (A–Z)')])
|
||||
src += html_default_description()
|
||||
src += html_table()
|
||||
src += HTML.p_download_json('data.json', 'raw-apps.json')
|
||||
@@ -49,8 +49,8 @@ def write_ranking_all(title, base_dir):
|
||||
def write_ranking_category(cid, category_name):
|
||||
base = mylib.path_out('category', cid, 'ranking')
|
||||
# full urls since categories can have page 2, 3, etc.
|
||||
src = html_h2_path([('All Categories', '/category/'),
|
||||
(category_name, '/category/{}/'.format(cid))])
|
||||
src = html_h2_path([('/category/', 'All Categories'),
|
||||
('/category/{}/'.format(cid), category_name)])
|
||||
src += html_default_description()
|
||||
src += html_table()
|
||||
src += HTML.p_download_json('data.json',
|
||||
@@ -63,8 +63,8 @@ def write_ranking_category(cid, category_name):
|
||||
|
||||
def write_ranking_custom_lists(base_dir, list_id, list_name, parent_title):
|
||||
base = mylib.path_add(base_dir, list_id)
|
||||
src = html_h2_path([('Results', '/results/'),
|
||||
(parent_title, '/lists/')], list_name)
|
||||
src = html_h2_path([('/results/', 'Results'),
|
||||
('/lists/', parent_title)], list_name)
|
||||
src += html_table()
|
||||
src += HTML.p_download_json('data.json',
|
||||
'raw-list-{}.json'.format(list_id))
|
||||
@@ -88,22 +88,28 @@ def process():
|
||||
base_custom = mylib.path_out('lists')
|
||||
title_custom = 'Lists'
|
||||
arr = []
|
||||
for list_id, json in mylib.enum_custom_lists():
|
||||
for list_id, json in mylib.enum_custom_lists('list_'):
|
||||
list_name = json['name']
|
||||
arr.append((list_id, list_name, len(json['apps'])))
|
||||
try:
|
||||
desc = json['desc']
|
||||
except KeyError:
|
||||
desc = None
|
||||
arr.append((list_id, list_name, len(json['apps']), desc))
|
||||
write_ranking_custom_lists(base_custom, list_id, list_name,
|
||||
parent_title=title_custom)
|
||||
|
||||
print(' index page')
|
||||
mylib.sort_by_name(arr, 1)
|
||||
src = html_h2_path([('Results', '/results/')], title_custom)
|
||||
src = html_h2_path([('/results/', 'Results')], title_custom)
|
||||
src += '''
|
||||
<p class="squeeze">
|
||||
We present selected lists of apps that have been added to AppCheck.
|
||||
App lists compare individual applications against each other.
|
||||
These curated lists group together, at least in some aspect, similar applications.
|
||||
This could be all instant messengers, health related apps, or apps from a common developer or country.
|
||||
</p>
|
||||
<div class="found-in">'''
|
||||
for x in arr:
|
||||
src += '<p><a href="{}/">{}</a> <span>contains {} apps</span></p>\n'.format(*x)
|
||||
src += HTML.h4_a_title_sub_desc(x[0], x[1], f'contains {x[2]} apps', x[3])
|
||||
HTML.write(base_custom, src + '</div>', title=title_custom)
|
||||
print('')
|
||||
|
||||
|
||||
@@ -92,9 +92,12 @@ def gen_results(base_dir, c_apps, c_domains, title):
|
||||
<li>List of <a href="/category/">Categories</a></li>
|
||||
<li>List of <a href="/index/domains/all/">All Domains</a>,
|
||||
only <a href="/index/domains/tracker/">Trackers</a>,
|
||||
or <a href="/index/domains/highly-used/">Highly-used Domains</a> which appear in at least 5 apps but are not considered tracker <i>yet</i>.</li>
|
||||
or <a href="/index/domains/highly-used/">Highly-used Domains</a> <br>which appear in at least 5 apps but are not considered tracker <i>yet</i>.</li>
|
||||
</ul>
|
||||
<ul>
|
||||
<li>Compare <a href="/lists/">App Lists</a></li>
|
||||
<li>Compare <a href="/compare/">Group Lists</a></li>
|
||||
</ul>
|
||||
<p>Or compare similar application via custom comparison <a href="/lists/">Lists</a>.</p>
|
||||
'''.format(title, c_apps, c_domains, c_recs, c_logs), title=title)
|
||||
mylib.symlink(index_rank.fname_app_rank(),
|
||||
mylib.path_add(base_dir, 'rank.json')) # after HTML.write
|
||||
|
||||
@@ -4,6 +4,7 @@ import sys
|
||||
import lib_common as mylib
|
||||
import download_itunes # get_genres
|
||||
import index_app_names # get_name
|
||||
import index_rank # save_groupby_list
|
||||
|
||||
_dict_apps = None
|
||||
_dict_names = None
|
||||
@@ -96,9 +97,23 @@ def persist_individual_files():
|
||||
|
||||
index = sorted_reverse_index()
|
||||
make_dir_individuals()
|
||||
cat_groups = {}
|
||||
for cid, cname in _dict_names.items():
|
||||
# write individual
|
||||
mylib.json_write(fname_cat_individual(cid),
|
||||
{'meta': [cid, cname], 'apps': index[cid]})
|
||||
# prep groupby compare
|
||||
obj = {'name': cname, 'apps': [x for x, _ in index[cid]]}
|
||||
cat_g_id = int(int(cid) / 1000)
|
||||
try:
|
||||
cat_groups[cat_g_id].append(obj)
|
||||
except KeyError:
|
||||
cat_groups[cat_g_id] = [obj]
|
||||
|
||||
for key, vals in cat_groups.items():
|
||||
mylib.sort_by_name(vals, 'name')
|
||||
index_rank.save_groupby_list(
|
||||
f'category-{key}', f'Category {key}xxx', vals, hidden=True)
|
||||
|
||||
|
||||
def get_categories(bundle_id):
|
||||
|
||||
@@ -48,6 +48,39 @@ def json_to_list(json):
|
||||
]
|
||||
|
||||
|
||||
def group_multiple_apps(values_array):
|
||||
c = len(values_array)
|
||||
asum = [0] * 10
|
||||
amin = [float('inf')] * 10
|
||||
amax = [0] * 10
|
||||
for values in values_array:
|
||||
for i in range(10):
|
||||
asum[i] += values[i]
|
||||
amin[i] = min(amin[i], values[i])
|
||||
amax[i] = max(amax[i], values[i])
|
||||
|
||||
def flt(val):
|
||||
return int(val * 1000) / 1000
|
||||
return [
|
||||
# app-count
|
||||
c,
|
||||
# rec-count (avg, sum)
|
||||
flt(asum[0] / c), asum[0],
|
||||
# req-count (avg, sum)
|
||||
flt(asum[9] / c), asum[8],
|
||||
# rec-time (avg, sum)
|
||||
int(asum[1] / c), asum[2],
|
||||
# req-pm (avg, min, max)
|
||||
flt(asum[3] / c), flt(amin[3]), flt(amax[3]),
|
||||
# pardom-count (avg, min, max)
|
||||
flt(asum[5] / c), amin[5], amax[5],
|
||||
# subdom-count (avg, min, max)
|
||||
flt(asum[6] / c), amin[6], amax[6],
|
||||
# tracker-percent (avg, min, max)
|
||||
flt(asum[7] / c), flt(amin[7]), flt(amax[7]),
|
||||
]
|
||||
|
||||
|
||||
def update_summary_index(index, bundle_ids, deleteOnly=False):
|
||||
did_change = False
|
||||
if deleteOnly:
|
||||
@@ -102,18 +135,45 @@ def write_ranking_category_list(index, affected_ids):
|
||||
|
||||
def write_ranking_custom_lists(index, affected_ids):
|
||||
make_rank_list_dir('custom', reset=affected_ids == ['*'])
|
||||
for list_id, json in mylib.enum_custom_lists():
|
||||
for list_id, json in mylib.enum_custom_lists('list_'):
|
||||
ret = list(filter_by_list(index, json['apps'], affected_ids))
|
||||
if len(ret) == 0:
|
||||
continue
|
||||
mylib.json_write(fname_rank_list('custom', list_id), ret, pretty=False)
|
||||
|
||||
|
||||
def write_ranking_list(index, affected_ids):
|
||||
def write_ranking_groupby_lists(index, affected_ids):
|
||||
make_rank_list_dir('groupby', reset=affected_ids == ['*'])
|
||||
for listid, json in mylib.enum_custom_lists('groupby_'):
|
||||
ret = []
|
||||
for bid, values in index.items():
|
||||
ret.append([bid, index_app_names.get_name(bid)] + values)
|
||||
del(values[8:]) # prepare for write_rank_index
|
||||
anyone = affected_ids == ['*']
|
||||
for group in json['groups']:
|
||||
arr = []
|
||||
for x in index:
|
||||
if not anyone and x[0] in affected_ids:
|
||||
anyone = True
|
||||
if x[0] in group['apps']:
|
||||
arr.append(x[2:])
|
||||
if len(arr) > 0:
|
||||
ident = group['name']
|
||||
try:
|
||||
url = group['url']
|
||||
ident = [ident, url]
|
||||
except KeyError:
|
||||
pass
|
||||
ret.append([ident] + group_multiple_apps(arr))
|
||||
if len(ret) == 0 or not anyone:
|
||||
continue
|
||||
mylib.json_write(fname_rank_list('groupby', listid), ret, pretty=False)
|
||||
|
||||
|
||||
def write_ranking_list(index, affected_ids):
|
||||
# prepend bundle-id and app name
|
||||
ret = [[b, index_app_names.get_name(b)] + v for b, v in index.items()]
|
||||
|
||||
# TODO: return list of updated files
|
||||
print(' write group-by lists')
|
||||
write_ranking_groupby_lists(ret, affected_ids)
|
||||
|
||||
print(' write custom lists')
|
||||
# sort by %-tracker asc, #-pardom asc, avg-req-per-min asc
|
||||
@@ -166,6 +226,14 @@ def get_total_counts():
|
||||
return [0, 0]
|
||||
|
||||
|
||||
def save_groupby_list(gid, title, groups, hidden=False):
|
||||
base = mylib.path_data('_lists')
|
||||
mylib.mkdir(base)
|
||||
fname = mylib.path_add(base, f'groupby_{gid}.json')
|
||||
json = {'name': title, 'hidden': hidden, 'groups': groups}
|
||||
mylib.json_write(fname, json, pretty=False)
|
||||
|
||||
|
||||
def process(bundle_ids, deleteOnly=False):
|
||||
print('writing index: ranking ...')
|
||||
fname = fname_app_summary()
|
||||
@@ -177,6 +245,8 @@ def process(bundle_ids, deleteOnly=False):
|
||||
ids = mylib.appids_in_data(bundle_ids)
|
||||
if update_summary_index(index, ids, deleteOnly=deleteOnly):
|
||||
write_ranking_list(index, bundle_ids)
|
||||
for values in index.values():
|
||||
del(values[8:])
|
||||
write_rank_index(index)
|
||||
print('')
|
||||
|
||||
|
||||
@@ -247,10 +247,10 @@ def enum_newly_added():
|
||||
yield fname, os.path.basename(fname)[3:] # del prefix 'in_'
|
||||
|
||||
|
||||
def enum_custom_lists():
|
||||
for fname in glob.glob(path_data('_lists', 'list_*.json')):
|
||||
def enum_custom_lists(prefix):
|
||||
for fname in glob.glob(path_data('_lists', prefix + '*.json')):
|
||||
with open(fname, 'r') as fp:
|
||||
yield os.path.basename(fname)[5:-5], json.load(fp)
|
||||
yield os.path.basename(fname)[len(prefix):-5], json.load(fp)
|
||||
|
||||
|
||||
def enum_jsons(bundle_id):
|
||||
|
||||
@@ -23,9 +23,9 @@ def a_subdomain(x, inner=None, attr_str=''):
|
||||
return '<a{} href="/subdomain/#{}">{}</a>'.format(attr_str, x, inner or x)
|
||||
|
||||
|
||||
def h2_path_n_rank(title, path_parts, rank_href):
|
||||
return '<h2>{} <a class="snd floatr" href="{}">Ranking</a></h2>'.format(
|
||||
a_path(path_parts, title), rank_href)
|
||||
def h2_path_n_rank(title, path_parts, rank_href, rank_title='Ranking'):
|
||||
return '<h2>{} <a class="snd floatr" href="{}">{}</a></h2>'.format(
|
||||
a_path(path_parts, title), rank_href, rank_title)
|
||||
|
||||
|
||||
def p_download_json(href, download_name):
|
||||
@@ -65,7 +65,7 @@ def div(inner, attr=None):
|
||||
def a_path(parts, suffix):
|
||||
''' expects (name, url) tuples '''
|
||||
return ' / '.join(['<a href="{}">{}</a>'.format(url, title)
|
||||
for title, url in parts] + [suffix])
|
||||
for url, title in parts] + [suffix])
|
||||
|
||||
|
||||
# Simple constructs
|
||||
@@ -81,6 +81,36 @@ def date_utc(ctime):
|
||||
time.strftime('%Y-%m-%d, %H:%M', time.gmtime(ctime)))
|
||||
|
||||
|
||||
def seconds_to_time(seconds):
|
||||
seconds = int(seconds)
|
||||
minutes, seconds = divmod(seconds, 60)
|
||||
hours, minutes = divmod(minutes, 60)
|
||||
return '{:02d}:{:02d}:{:02d}'.format(hours, minutes, seconds)
|
||||
|
||||
|
||||
def fmt_round_num(num):
|
||||
return format(num, '.1f') # .rstrip('0').rstrip('.')
|
||||
|
||||
|
||||
def fmt_as_pm(value):
|
||||
return fmt_round_num(value) + '/min'
|
||||
|
||||
|
||||
def fmt_as_percent(value):
|
||||
return fmt_round_num(value * 100) + '%'
|
||||
|
||||
|
||||
def h4_a_title_sub_desc(href, title, subtitle, desc):
|
||||
if desc:
|
||||
desc = f'<p class="squeeze">{desc}</p>'
|
||||
return f'''
|
||||
<div class="title-sub">
|
||||
<h4><a href="{href}/">{title}</a></h4>
|
||||
<span>{subtitle}</span>
|
||||
{desc or ''}
|
||||
</div>'''
|
||||
|
||||
|
||||
# Higher level constructs
|
||||
|
||||
def pagination(current, total):
|
||||
|
||||
@@ -8,6 +8,7 @@ import download_itunes
|
||||
import download_tracker
|
||||
import html_bundle
|
||||
import html_categories
|
||||
import html_group_compare
|
||||
import html_index_apps
|
||||
import html_index_domains
|
||||
import html_ranking
|
||||
@@ -41,6 +42,7 @@ def rebuild_html(bundle_ids=None, cat_ids=None, inclIApp=True):
|
||||
else:
|
||||
print('no new bundle, not rebuilding index')
|
||||
html_ranking.process() # after html_categories & html_index_apps
|
||||
html_group_compare.process() # after index_rank
|
||||
app_count, dom_count = html_index_domains.process() # after index_domains
|
||||
html_root.process(app_count, dom_count, inclStatic=True)
|
||||
|
||||
|
||||
@@ -1,156 +1,166 @@
|
||||
<svg width="660pt" height="404pt" viewBox="0.00 0.00 660.00 404.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<svg width="611pt" height="404pt" viewBox="0.00 0.00 611.00 404.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<g id="graph1" class="graph" transform="scale(1 1) rotate(0) translate(4 400)">
|
||||
<title>Dependency</title>
|
||||
<polygon fill="white" stroke="white" points="-4,5 -4,-400 657,-400 657,5 -4,5"></polygon>
|
||||
<polygon fill="white" stroke="white" points="-4,5 -4,-400 608,-400 608,5 -4,5"></polygon>
|
||||
<!-- . -->
|
||||
<g id="node1" class="node"><title>.</title>
|
||||
<ellipse fill="none" stroke="black" cx="299" cy="-378" rx="27" ry="18"></ellipse>
|
||||
<text text-anchor="middle" x="299" y="-373.8" font-family="Times,serif" font-size="14.00">.</text>
|
||||
</g>
|
||||
<!-- download_itunes -->
|
||||
<g id="node3" class="node"><title>download_itunes</title>
|
||||
<ellipse fill="none" stroke="black" cx="299" cy="-306" rx="77.0235" ry="18"></ellipse>
|
||||
<text text-anchor="middle" x="299" y="-301.8" font-family="Times,serif" font-size="14.00">download_itunes</text>
|
||||
</g>
|
||||
<!-- .->download_itunes -->
|
||||
<g id="edge2" class="edge"><title>.->download_itunes</title>
|
||||
<path fill="none" stroke="black" d="M299,-359.697C299,-351.983 299,-342.712 299,-334.112"></path>
|
||||
<polygon fill="black" stroke="black" points="302.5,-334.104 299,-324.104 295.5,-334.104 302.5,-334.104"></polygon>
|
||||
</g>
|
||||
<!-- bundle_combine -->
|
||||
<g id="node5" class="node"><title>bundle_combine</title>
|
||||
<ellipse fill="none" stroke="black" cx="129" cy="-306" rx="75.1062" ry="18"></ellipse>
|
||||
<text text-anchor="middle" x="129" y="-301.8" font-family="Times,serif" font-size="14.00">bundle_combine</text>
|
||||
</g>
|
||||
<!-- .->bundle_combine -->
|
||||
<g id="edge4" class="edge"><title>.->bundle_combine</title>
|
||||
<path fill="none" stroke="black" d="M276.425,-367.705C250.531,-357.042 207.41,-339.287 174.475,-325.725"></path>
|
||||
<polygon fill="black" stroke="black" points="175.625,-322.413 165.045,-321.842 172.959,-328.886 175.625,-322.413"></polygon>
|
||||
<ellipse fill="none" stroke="black" cx="261" cy="-378" rx="27" ry="18"></ellipse>
|
||||
<text text-anchor="middle" x="261" y="-373.8" font-family="Times,serif" font-size="14.00">.</text>
|
||||
</g>
|
||||
<!-- download_tracker -->
|
||||
<g id="node30" class="node"><title>download_tracker</title>
|
||||
<ellipse fill="none" stroke="black" cx="475" cy="-306" rx="80.1284" ry="18"></ellipse>
|
||||
<text text-anchor="middle" x="475" y="-301.8" font-family="Times,serif" font-size="14.00">download_tracker</text>
|
||||
<g id="node3" class="node"><title>download_tracker</title>
|
||||
<ellipse fill="none" stroke="black" cx="85" cy="-306" rx="80.1284" ry="18"></ellipse>
|
||||
<text text-anchor="middle" x="85" y="-301.8" font-family="Times,serif" font-size="14.00">download_tracker</text>
|
||||
</g>
|
||||
<!-- .->download_tracker -->
|
||||
<g id="edge32" class="edge"><title>.->download_tracker</title>
|
||||
<path fill="none" stroke="black" d="M321.649,-367.992C348.38,-357.36 393.544,-339.397 427.955,-325.711"></path>
|
||||
<polygon fill="black" stroke="black" points="429.352,-328.922 437.351,-321.974 426.765,-322.418 429.352,-328.922"></polygon>
|
||||
<g id="edge2" class="edge"><title>.->download_tracker</title>
|
||||
<path fill="none" stroke="black" d="M238.351,-367.992C211.62,-357.36 166.456,-339.397 132.045,-325.711"></path>
|
||||
<polygon fill="black" stroke="black" points="133.235,-322.418 122.649,-321.974 130.648,-328.922 133.235,-322.418"></polygon>
|
||||
</g>
|
||||
<!-- download_itunes -->
|
||||
<g id="node5" class="node"><title>download_itunes</title>
|
||||
<ellipse fill="none" stroke="black" cx="261" cy="-306" rx="77.0235" ry="18"></ellipse>
|
||||
<text text-anchor="middle" x="261" y="-301.8" font-family="Times,serif" font-size="14.00">download_itunes</text>
|
||||
</g>
|
||||
<!-- .->download_itunes -->
|
||||
<g id="edge4" class="edge"><title>.->download_itunes</title>
|
||||
<path fill="none" stroke="black" d="M261,-359.697C261,-351.983 261,-342.712 261,-334.112"></path>
|
||||
<polygon fill="black" stroke="black" points="264.5,-334.104 261,-324.104 257.5,-334.104 264.5,-334.104"></polygon>
|
||||
</g>
|
||||
<!-- bundle_combine -->
|
||||
<g id="node7" class="node"><title>bundle_combine</title>
|
||||
<ellipse fill="none" stroke="black" cx="431" cy="-306" rx="75.1062" ry="18"></ellipse>
|
||||
<text text-anchor="middle" x="431" y="-301.8" font-family="Times,serif" font-size="14.00">bundle_combine</text>
|
||||
</g>
|
||||
<!-- .->bundle_combine -->
|
||||
<g id="edge6" class="edge"><title>.->bundle_combine</title>
|
||||
<path fill="none" stroke="black" d="M283.575,-367.705C309.469,-357.042 352.59,-339.287 385.525,-325.725"></path>
|
||||
<polygon fill="black" stroke="black" points="387.041,-328.886 394.955,-321.842 384.375,-322.413 387.041,-328.886"></polygon>
|
||||
</g>
|
||||
<!-- index_app_names -->
|
||||
<g id="node7" class="node"><title>index_app_names</title>
|
||||
<ellipse fill="none" stroke="black" cx="299" cy="-234" rx="80.1456" ry="18"></ellipse>
|
||||
<text text-anchor="middle" x="299" y="-229.8" font-family="Times,serif" font-size="14.00">index_app_names</text>
|
||||
<g id="node9" class="node"><title>index_app_names</title>
|
||||
<ellipse fill="none" stroke="black" cx="261" cy="-234" rx="80.1456" ry="18"></ellipse>
|
||||
<text text-anchor="middle" x="261" y="-229.8" font-family="Times,serif" font-size="14.00">index_app_names</text>
|
||||
</g>
|
||||
<!-- download_itunes->index_app_names -->
|
||||
<g id="edge6" class="edge"><title>download_itunes->index_app_names</title>
|
||||
<path fill="none" stroke="black" d="M299,-287.697C299,-279.983 299,-270.712 299,-262.112"></path>
|
||||
<polygon fill="black" stroke="black" points="302.5,-262.104 299,-252.104 295.5,-262.104 302.5,-262.104"></polygon>
|
||||
<g id="edge8" class="edge"><title>download_itunes->index_app_names</title>
|
||||
<path fill="none" stroke="black" d="M261,-287.697C261,-279.983 261,-270.712 261,-262.112"></path>
|
||||
<polygon fill="black" stroke="black" points="264.5,-262.104 261,-252.104 257.5,-262.104 264.5,-262.104"></polygon>
|
||||
</g>
|
||||
<!-- index_rank -->
|
||||
<g id="node11" class="node"><title>index_rank</title>
|
||||
<ellipse fill="none" stroke="black" cx="257" cy="-162" rx="54.219" ry="18"></ellipse>
|
||||
<text text-anchor="middle" x="257" y="-157.8" font-family="Times,serif" font-size="14.00">index_rank</text>
|
||||
<g id="node19" class="node"><title>index_rank</title>
|
||||
<ellipse fill="none" stroke="black" cx="390" cy="-90" rx="54.219" ry="18"></ellipse>
|
||||
<text text-anchor="middle" x="390" y="-85.8" font-family="Times,serif" font-size="14.00">index_rank</text>
|
||||
</g>
|
||||
<!-- bundle_combine->index_rank -->
|
||||
<g id="edge18" class="edge"><title>bundle_combine->index_rank</title>
|
||||
<path fill="none" stroke="black" d="M144.021,-288.252C160.084,-270.35 186.314,-241.143 209,-216 217.49,-206.59 226.843,-196.256 235.019,-187.232"></path>
|
||||
<polygon fill="black" stroke="black" points="237.729,-189.455 241.851,-179.695 232.542,-184.754 237.729,-189.455"></polygon>
|
||||
<path fill="none" stroke="black" d="M427.344,-287.832C425.16,-277.467 422.36,-263.987 420,-252 410.771,-205.131 400.734,-150.437 394.925,-118.376"></path>
|
||||
<polygon fill="black" stroke="black" points="398.348,-117.632 393.125,-108.414 391.459,-118.877 398.348,-117.632"></polygon>
|
||||
</g>
|
||||
<!-- index_domains -->
|
||||
<g id="node20" class="node"><title>index_domains</title>
|
||||
<ellipse fill="none" stroke="black" cx="92" cy="-234" rx="70.0665" ry="18"></ellipse>
|
||||
<text text-anchor="middle" x="92" y="-229.8" font-family="Times,serif" font-size="14.00">index_domains</text>
|
||||
<g id="node21" class="node"><title>index_domains</title>
|
||||
<ellipse fill="none" stroke="black" cx="499" cy="-234" rx="70.0665" ry="18"></ellipse>
|
||||
<text text-anchor="middle" x="499" y="-229.8" font-family="Times,serif" font-size="14.00">index_domains</text>
|
||||
</g>
|
||||
<!-- bundle_combine->index_domains -->
|
||||
<g id="edge20" class="edge"><title>bundle_combine->index_domains</title>
|
||||
<path fill="none" stroke="black" d="M120.043,-288.055C115.789,-280.007 110.608,-270.205 105.862,-261.226"></path>
|
||||
<polygon fill="black" stroke="black" points="108.899,-259.481 101.131,-252.275 102.71,-262.752 108.899,-259.481"></polygon>
|
||||
<path fill="none" stroke="black" d="M447.115,-288.411C455.544,-279.734 466.024,-268.946 475.354,-259.342"></path>
|
||||
<polygon fill="black" stroke="black" points="478.071,-261.568 482.528,-251.956 473.05,-256.69 478.071,-261.568"></polygon>
|
||||
</g>
|
||||
<!-- index_categories -->
|
||||
<g id="node9" class="node"><title>index_categories</title>
|
||||
<ellipse fill="none" stroke="black" cx="407" cy="-162" rx="76.1936" ry="18"></ellipse>
|
||||
<text text-anchor="middle" x="407" y="-157.8" font-family="Times,serif" font-size="14.00">index_categories</text>
|
||||
<g id="node11" class="node"><title>index_categories</title>
|
||||
<ellipse fill="none" stroke="black" cx="250" cy="-162" rx="76.1936" ry="18"></ellipse>
|
||||
<text text-anchor="middle" x="250" y="-157.8" font-family="Times,serif" font-size="14.00">index_categories</text>
|
||||
</g>
|
||||
<!-- index_app_names->index_categories -->
|
||||
<g id="edge8" class="edge"><title>index_app_names->index_categories</title>
|
||||
<path fill="none" stroke="black" d="M324.049,-216.765C338.77,-207.223 357.567,-195.04 373.545,-184.684"></path>
|
||||
<polygon fill="black" stroke="black" points="375.599,-187.523 382.087,-179.147 371.792,-181.649 375.599,-187.523"></polygon>
|
||||
</g>
|
||||
<!-- index_app_names->index_rank -->
|
||||
<g id="edge10" class="edge"><title>index_app_names->index_rank</title>
|
||||
<path fill="none" stroke="black" d="M288.833,-216.055C283.88,-207.801 277.821,-197.701 272.323,-188.538"></path>
|
||||
<polygon fill="black" stroke="black" points="275.22,-186.563 267.073,-179.789 269.217,-190.165 275.22,-186.563"></polygon>
|
||||
<g id="edge10" class="edge"><title>index_app_names->index_categories</title>
|
||||
<path fill="none" stroke="black" d="M258.281,-215.697C257.069,-207.983 255.612,-198.712 254.261,-190.112"></path>
|
||||
<polygon fill="black" stroke="black" points="257.698,-189.44 252.688,-180.104 250.783,-190.526 257.698,-189.44"></polygon>
|
||||
</g>
|
||||
<!-- html_index_domains -->
|
||||
<g id="node13" class="node"><title>html_index_domains</title>
|
||||
<ellipse fill="none" stroke="black" cx="92" cy="-162" rx="92.3709" ry="18"></ellipse>
|
||||
<text text-anchor="middle" x="92" y="-157.8" font-family="Times,serif" font-size="14.00">html_index_domains</text>
|
||||
<ellipse fill="none" stroke="black" cx="511" cy="-162" rx="92.3709" ry="18"></ellipse>
|
||||
<text text-anchor="middle" x="511" y="-157.8" font-family="Times,serif" font-size="14.00">html_index_domains</text>
|
||||
</g>
|
||||
<!-- index_app_names->html_index_domains -->
|
||||
<g id="edge12" class="edge"><title>index_app_names->html_index_domains</title>
|
||||
<path fill="none" stroke="black" d="M256.611,-218.666C224.5,-207.807 180.162,-192.813 145.592,-181.123"></path>
|
||||
<polygon fill="black" stroke="black" points="146.712,-177.807 136.118,-177.919 144.47,-184.438 146.712,-177.807"></polygon>
|
||||
<path fill="none" stroke="black" d="M309.203,-219.503C349.483,-208.225 407.134,-192.082 450.51,-179.937"></path>
|
||||
<polygon fill="black" stroke="black" points="451.685,-183.243 460.371,-177.176 449.798,-176.502 451.685,-183.243"></polygon>
|
||||
</g>
|
||||
<!-- html_categories -->
|
||||
<g id="node15" class="node"><title>html_categories</title>
|
||||
<ellipse fill="none" stroke="black" cx="407" cy="-90" rx="72.5712" ry="18"></ellipse>
|
||||
<text text-anchor="middle" x="407" y="-85.8" font-family="Times,serif" font-size="14.00">html_categories</text>
|
||||
<ellipse fill="none" stroke="black" cx="72" cy="-90" rx="72.5712" ry="18"></ellipse>
|
||||
<text text-anchor="middle" x="72" y="-85.8" font-family="Times,serif" font-size="14.00">html_categories</text>
|
||||
</g>
|
||||
<!-- index_categories->html_categories -->
|
||||
<g id="edge14" class="edge"><title>index_categories->html_categories</title>
|
||||
<path fill="none" stroke="black" d="M407,-143.697C407,-135.983 407,-126.712 407,-118.112"></path>
|
||||
<polygon fill="black" stroke="black" points="410.5,-118.104 407,-108.104 403.5,-118.104 410.5,-118.104"></polygon>
|
||||
<path fill="none" stroke="black" d="M212.251,-146.155C184.928,-135.41 147.83,-120.821 118.651,-109.346"></path>
|
||||
<polygon fill="black" stroke="black" points="119.672,-105.986 109.084,-105.584 117.11,-112.501 119.672,-105.986"></polygon>
|
||||
</g>
|
||||
<!-- html_index_apps -->
|
||||
<g id="node17" class="node"><title>html_index_apps</title>
|
||||
<ellipse fill="none" stroke="black" cx="575" cy="-90" rx="77.3345" ry="18"></ellipse>
|
||||
<text text-anchor="middle" x="575" y="-85.8" font-family="Times,serif" font-size="14.00">html_index_apps</text>
|
||||
<ellipse fill="none" stroke="black" cx="240" cy="-90" rx="77.3345" ry="18"></ellipse>
|
||||
<text text-anchor="middle" x="240" y="-85.8" font-family="Times,serif" font-size="14.00">html_index_apps</text>
|
||||
</g>
|
||||
<!-- index_categories->html_index_apps -->
|
||||
<g id="edge16" class="edge"><title>index_categories->html_index_apps</title>
|
||||
<path fill="none" stroke="black" d="M443.04,-145.983C468.396,-135.418 502.52,-121.2 529.715,-109.869"></path>
|
||||
<polygon fill="black" stroke="black" points="531.135,-113.069 539.019,-105.992 528.442,-106.607 531.135,-113.069"></polygon>
|
||||
<path fill="none" stroke="black" d="M247.528,-143.697C246.426,-135.983 245.102,-126.712 243.873,-118.112"></path>
|
||||
<polygon fill="black" stroke="black" points="247.323,-117.509 242.443,-108.104 240.393,-118.499 247.323,-117.509"></polygon>
|
||||
</g>
|
||||
<!-- html_bundle -->
|
||||
<g id="node22" class="node"><title>html_bundle</title>
|
||||
<ellipse fill="none" stroke="black" cx="256" cy="-90" rx="59.2871" ry="18"></ellipse>
|
||||
<text text-anchor="middle" x="256" y="-85.8" font-family="Times,serif" font-size="14.00">html_bundle</text>
|
||||
</g>
|
||||
<!-- index_rank->html_bundle -->
|
||||
<g id="edge22" class="edge"><title>index_rank->html_bundle</title>
|
||||
<path fill="none" stroke="black" d="M256.753,-143.697C256.643,-135.983 256.51,-126.712 256.387,-118.112"></path>
|
||||
<polygon fill="black" stroke="black" points="259.887,-118.053 256.244,-108.104 252.888,-118.153 259.887,-118.053"></polygon>
|
||||
<!-- index_categories->index_rank -->
|
||||
<g id="edge32" class="edge"><title>index_categories->index_rank</title>
|
||||
<path fill="none" stroke="black" d="M281.071,-145.465C302.076,-134.962 329.938,-121.031 352.226,-109.887"></path>
|
||||
<polygon fill="black" stroke="black" points="354.021,-112.903 361.4,-105.3 350.89,-106.642 354.021,-112.903"></polygon>
|
||||
</g>
|
||||
<!-- html_root -->
|
||||
<g id="node28" class="node"><title>html_root</title>
|
||||
<ellipse fill="none" stroke="black" cx="92" cy="-90" rx="49.1927" ry="18"></ellipse>
|
||||
<text text-anchor="middle" x="92" y="-85.8" font-family="Times,serif" font-size="14.00">html_root</text>
|
||||
<g id="node29" class="node"><title>html_root</title>
|
||||
<ellipse fill="none" stroke="black" cx="512" cy="-90" rx="49.1927" ry="18"></ellipse>
|
||||
<text text-anchor="middle" x="512" y="-85.8" font-family="Times,serif" font-size="14.00">html_root</text>
|
||||
</g>
|
||||
<!-- html_index_domains->html_root -->
|
||||
<g id="edge30" class="edge"><title>html_index_domains->html_root</title>
|
||||
<path fill="none" stroke="black" d="M92,-143.697C92,-135.983 92,-126.712 92,-118.112"></path>
|
||||
<polygon fill="black" stroke="black" points="95.5001,-118.104 92,-108.104 88.5001,-118.104 95.5001,-118.104"></polygon>
|
||||
<path fill="none" stroke="black" d="M511.247,-143.697C511.357,-135.983 511.49,-126.712 511.613,-118.112"></path>
|
||||
<polygon fill="black" stroke="black" points="515.112,-118.153 511.756,-108.104 508.113,-118.053 515.112,-118.153"></polygon>
|
||||
</g>
|
||||
<!-- html_ranking -->
|
||||
<g id="node25" class="node"><title>html_ranking</title>
|
||||
<ellipse fill="none" stroke="black" cx="491" cy="-18" rx="63.108" ry="18"></ellipse>
|
||||
<text text-anchor="middle" x="491" y="-13.8" font-family="Times,serif" font-size="14.00">html_ranking</text>
|
||||
<g id="node26" class="node"><title>html_ranking</title>
|
||||
<ellipse fill="none" stroke="black" cx="143" cy="-18" rx="63.108" ry="18"></ellipse>
|
||||
<text text-anchor="middle" x="143" y="-13.8" font-family="Times,serif" font-size="14.00">html_ranking</text>
|
||||
</g>
|
||||
<!-- html_categories->html_ranking -->
|
||||
<g id="edge28" class="edge"><title>html_categories->html_ranking</title>
|
||||
<path fill="none" stroke="black" d="M426.907,-72.411C437.865,-63.2789 451.629,-51.8093 463.59,-41.8418"></path>
|
||||
<polygon fill="black" stroke="black" points="466.084,-44.3191 471.526,-35.2285 461.603,-38.9416 466.084,-44.3191"></polygon>
|
||||
<path fill="none" stroke="black" d="M88.826,-72.411C97.845,-63.519 109.112,-52.4107 119.032,-42.6309"></path>
|
||||
<polygon fill="black" stroke="black" points="121.631,-44.9831 126.295,-35.4699 116.716,-39.9983 121.631,-44.9831"></polygon>
|
||||
</g>
|
||||
<!-- html_index_apps->html_ranking -->
|
||||
<g id="edge26" class="edge"><title>html_index_apps->html_ranking</title>
|
||||
<path fill="none" stroke="black" d="M555.093,-72.411C544.135,-63.2789 530.371,-51.8093 518.41,-41.8418"></path>
|
||||
<polygon fill="black" stroke="black" points="520.397,-38.9416 510.474,-35.2285 515.916,-44.3191 520.397,-38.9416"></polygon>
|
||||
<path fill="none" stroke="black" d="M217.502,-72.7646C204.433,-63.3328 187.786,-51.3197 173.544,-41.0422"></path>
|
||||
<polygon fill="black" stroke="black" points="175.198,-37.9197 165.041,-34.906 171.102,-43.596 175.198,-37.9197"></polygon>
|
||||
</g>
|
||||
<!-- html_bundle -->
|
||||
<g id="node23" class="node"><title>html_bundle</title>
|
||||
<ellipse fill="none" stroke="black" cx="526" cy="-18" rx="59.2871" ry="18"></ellipse>
|
||||
<text text-anchor="middle" x="526" y="-13.8" font-family="Times,serif" font-size="14.00">html_bundle</text>
|
||||
</g>
|
||||
<!-- index_rank->html_bundle -->
|
||||
<g id="edge22" class="edge"><title>index_rank->html_bundle</title>
|
||||
<path fill="none" stroke="black" d="M418.179,-74.496C438.33,-64.1241 465.685,-50.0446 487.798,-38.6626"></path>
|
||||
<polygon fill="black" stroke="black" points="489.629,-41.6569 496.918,-33.9684 486.425,-35.4329 489.629,-41.6569"></polygon>
|
||||
</g>
|
||||
<!-- html_group_compare -->
|
||||
<g id="node32" class="node"><title>html_group_compare</title>
|
||||
<ellipse fill="none" stroke="black" cx="354" cy="-18" rx="94.2613" ry="18"></ellipse>
|
||||
<text text-anchor="middle" x="354" y="-13.8" font-family="Times,serif" font-size="14.00">html_group_compare</text>
|
||||
</g>
|
||||
<!-- index_rank->html_group_compare -->
|
||||
<g id="edge34" class="edge"><title>index_rank->html_group_compare</title>
|
||||
<path fill="none" stroke="black" d="M381.285,-72.055C377.146,-64.0067 372.105,-54.2046 367.488,-45.2259"></path>
|
||||
<polygon fill="black" stroke="black" points="370.57,-43.5675 362.884,-36.2753 364.345,-46.7689 370.57,-43.5675"></polygon>
|
||||
</g>
|
||||
<!-- index_domains->html_index_domains -->
|
||||
<g id="edge24" class="edge"><title>index_domains->html_index_domains</title>
|
||||
<path fill="none" stroke="black" d="M92,-215.697C92,-207.983 92,-198.712 92,-190.112"></path>
|
||||
<polygon fill="black" stroke="black" points="95.5001,-190.104 92,-180.104 88.5001,-190.104 95.5001,-190.104"></polygon>
|
||||
<path fill="none" stroke="black" d="M501.966,-215.697C503.289,-207.983 504.878,-198.712 506.352,-190.112"></path>
|
||||
<polygon fill="black" stroke="black" points="509.828,-190.552 508.068,-180.104 502.928,-189.369 509.828,-190.552"></polygon>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 9.8 KiB After Width: | Height: | Size: 10 KiB |
@@ -3,9 +3,9 @@
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=0.75" />
|
||||
<script type="text/javascript" src="/static/script.js?4"></script>
|
||||
<script type="text/javascript" src="/static/script.js?5"></script>
|
||||
<title>#_TITLE_#AppCheck: Privacy Monitor</title>
|
||||
<link rel="stylesheet" type="text/css" href="/static/style.css?4">
|
||||
<link rel="stylesheet" type="text/css" href="/static/style.css?5">
|
||||
<link rel="stylesheet" type="text/css" href="/static/fonts/font.css">
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="/static/favicon/apple-touch-icon.png">
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="/static/favicon/favicon-32x32.png">
|
||||
|
||||
Reference in New Issue
Block a user