Compare group of apps

This commit is contained in:
relikd
2020-10-13 19:46:13 +02:00
parent 215ff82d65
commit 76b22281f5
16 changed files with 429 additions and 156 deletions

View File

@@ -128,6 +128,12 @@ footer .links {
.pagination a { margin: .5em; padding: .2em; } .pagination a { margin: .5em; padding: .2em; }
.pagination a.active { border: 1pt solid black; border-radius: .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; } #categories h3 { margin-top: 2em; }
@@ -136,7 +142,6 @@ footer .links {
#dom-top10>div { margin: .4em; } #dom-top10>div { margin: .4em; }
#dom-top10 a, #dom-toc a { word-wrap: break-word; } #dom-top10 a, #dom-toc a { word-wrap: break-word; }
#dom-toc span { display: table; } #dom-toc span { display: table; }
.found-in span, .snd { color: #586472; font-size: .85em; }
.fillbar { .fillbar {
display: block; display: block;
background: #DDD; background: #DDD;
@@ -159,8 +164,6 @@ footer .links {
/* app bundle */ /* app bundle */
.squeeze { max-width: 700px; } .squeeze { max-width: 700px; }
h2.title { margin-bottom: 0; }
p.subtitle { margin-top: .2em; }
.mg_lr { margin: 0 .4em; } .mg_lr { margin: 0 .4em; }
.mg_top { margin-top: 2em; } .mg_top { margin-top: 2em; }
.right { text-align: right; } .right { text-align: right; }
@@ -172,9 +175,11 @@ p.subtitle { margin-top: .2em; }
.xscroll { overflow-x: scroll; max-width: 100%; } .xscroll { overflow-x: scroll; max-width: 100%; }
.yscroll { overflow-y: scroll; max-height: 80vh; } .yscroll { overflow-y: scroll; max-height: 80vh; }
.floatr, #main-nav { float: right; } .floatr, #main-nav { float: right; }
table { border-collapse: collapse; }
table.alternate td, table.alternate th { padding: .5em; } table.alternate td, table.alternate th { padding: .5em; }
table.alternate tr:nth-child(even) { background: #DDD; } table.alternate tr:nth-child(even) { background: #DDD; }
table.alternate tr:nth-child(odd) { background: #F9F9F9; } 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 { margin-bottom: 2em; }*/
#meta .icons { 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 th:not(:first-child) { min-width: 12ex; }
#rank-list img { width: 25px; height: 25px; vertical-align: bottom; } #rank-list img { width: 25px; height: 25px; vertical-align: bottom; }
#rank-list td { text-align: center; min-width: max-content; } #rank-list td { text-align: center; }
#rank-list td:nth-child(1) { #rank-list td:nth-child(1) { text-align: right; white-space: nowrap; }
position: sticky; left: 0;
background: inherit;
text-align: right;
}
#rank-list td:nth-child(2) { text-align: left; } #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 */ /* responsive */
@media(max-width: 900px) { @media(max-width: 900px) {
#stats { grid-template-columns: repeat(2, max-content); } #stats { grid-template-columns: repeat(2, max-content); }

View File

@@ -40,11 +40,11 @@ Given A → B, B depends on A
``` ```
digraph Dependency { digraph Dependency {
"." -> download_tracker
"." -> download_itunes "." -> download_itunes
"." -> bundle_combine "." -> bundle_combine
download_itunes -> index_app_names download_itunes -> index_app_names
index_app_names -> index_categories index_app_names -> index_categories
index_app_names -> index_rank
index_app_names -> html_index_domains index_app_names -> html_index_domains
index_categories -> html_categories index_categories -> html_categories
index_categories -> html_index_apps index_categories -> html_index_apps
@@ -55,7 +55,8 @@ digraph Dependency {
html_index_apps -> html_ranking html_index_apps -> html_ranking
html_categories -> html_ranking html_categories -> html_ranking
html_index_domains -> html_root html_index_domains -> html_root
"." -> download_tracker index_categories -> index_rank
index_rank -> html_group_compare
} }
``` ```
[graphviz](http://www.webgraphviz.com/) [graphviz](http://www.webgraphviz.com/)

View File

@@ -47,21 +47,6 @@ def stat(col, title, ident, value, optional=None):
def gen_page(bundle_id, obj): 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) name = index_app_names.get_name(bundle_id)
gernes = index_categories.get_categories(bundle_id) gernes = index_categories.get_categories(bundle_id)
obj['tracker'] = list(filter(lambda x: x[2], obj['subdom'])) obj['tracker'] = list(filter(lambda x: x[2], obj['subdom']))
@@ -83,13 +68,13 @@ def gen_page(bundle_id, obj):
</div> </div>
<div id="stats"> <div id="stats">
{ stat(1, 'Number of recordings:', 'sum_rec', obj['sum_rec']) } { stat(1, 'Number of recordings:', 'sum_rec', obj['sum_rec']) }
{ stat(1, 'Average recording time:', 'avg_time', seconds_to_time(obj['avg_time'])) } { stat(1, 'Average recording time:', 'avg_time', HTML.seconds_to_time(obj['avg_time'])) }
{ stat(2, 'Cumulative recording time:', 'sum_time', seconds_to_time(obj['sum_time'])) } { stat(2, 'Cumulative recording time:', 'sum_time', HTML.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(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']), as_pm(obj['sum_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(1, 'Number of domains:', 'pardom', len(obj['pardom'])) }
{ stat(2, 'Number of subdomains:', 'subdom', len(obj['subdom'])) } { 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> </div>
<h3>Connections</h3> <h3>Connections</h3>
<div> <div>

View File

@@ -2,6 +2,7 @@
import lib_common as mylib import lib_common as mylib
import lib_html as HTML import lib_html as HTML
import html_group_compare
def write_overview_page(base_dir, category_tuples, title): 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) cluster[i].append(x)
except KeyError: except KeyError:
cluster[i] = [x] 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()): for i, arr in sorted(cluster.items()):
mylib.sort_by_name(arr, 1) mylib.sort_by_name(arr, 1)
kind = 'Apps' if i == 6 else 'Games' if i == 7 else 'Other' 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>' src += ''.join([HTML.a_category(*x) for x in arr]) + '</div>'
HTML.write(base_dir, src + '</div>', title) 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): def process(affected=None, per_page=60):
print('generating html: category-index ...') print('generating html: category-index ...')
@@ -34,7 +42,7 @@ def process(affected=None, per_page=60):
continue continue
out_dir = mylib.path_add(base, cid) out_dir = mylib.path_add(base, cid)
# full url since categories can have page 2, 3, etc. # 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)) Z = HTML.p_download_json('data.json', 'category-{}.json'.format(cid))
_, a = HTML.write_app_pages(out_dir, json['apps'], _, a = HTML.write_app_pages(out_dir, json['apps'],
cname, per_page, pre=A, post=Z) cname, per_page, pre=A, post=Z)

136
src/html_group_compare.py Executable file
View 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()

View File

@@ -8,7 +8,7 @@ import index_app_names # get_sorted_app_names
def process(per_page=60): def process(per_page=60):
print('generating html: app-index ...') print('generating html: app-index ...')
title = 'Apps (AZ)' title = 'Apps (AZ)'
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'), p, a = HTML.write_app_pages(mylib.path_out('index', 'apps'),
index_app_names.get_sorted_app_names(), index_app_names.get_sorted_app_names(),
title, per_page=per_page, pre=header) title, per_page=per_page, pre=header)

View File

@@ -70,7 +70,7 @@ def gen_html_trinity(idx_dir, app_count, json, title, symlink):
def write_index(fname, title, button): def write_index(fname, title, button):
HTML.write(idx_dir, '<h2>{}</h2>{}{}'.format( 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) dropdown_choose(button), duo_list(list1, list2)
), title=title, fname=fname) ), 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): def gen_lookup(html_dir, doms_dict, flag, title):
HTML.write(html_dir, f''' 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> '<span id="name"></span>') }</h2>
<p>Known Tracker: <b id="known">?</b></p> <p>Known Tracker: <b id="known">?</b></p>
<p>Present in: <b id="num-apps">… applications</b></p> <p>Present in: <b id="num-apps">… applications</b></p>

View File

@@ -20,7 +20,7 @@ def html_default_description():
def html_table(): def html_table():
return ''' return '''
<div class="xscroll yscroll"> <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>''' </div>'''
@@ -35,8 +35,8 @@ def html_script_chunk(fname, sort_col, sort_order):
def write_ranking_all(title, base_dir): def write_ranking_all(title, base_dir):
# full urls since app index can have page 2, 3, etc. # full urls since app index can have page 2, 3, etc.
src = html_h2_path([('Results', '/results/'), src = html_h2_path([('/results/', 'Results'),
('Apps (AZ)', '/index/apps/')]) ('/index/apps/', 'Apps (AZ)')])
src += html_default_description() src += html_default_description()
src += html_table() src += html_table()
src += HTML.p_download_json('data.json', 'raw-apps.json') 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): def write_ranking_category(cid, category_name):
base = mylib.path_out('category', cid, 'ranking') base = mylib.path_out('category', cid, 'ranking')
# full urls since categories can have page 2, 3, etc. # full urls since categories can have page 2, 3, etc.
src = html_h2_path([('All Categories', '/category/'), src = html_h2_path([('/category/', 'All Categories'),
(category_name, '/category/{}/'.format(cid))]) ('/category/{}/'.format(cid), category_name)])
src += html_default_description() src += html_default_description()
src += html_table() src += html_table()
src += HTML.p_download_json('data.json', 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): def write_ranking_custom_lists(base_dir, list_id, list_name, parent_title):
base = mylib.path_add(base_dir, list_id) base = mylib.path_add(base_dir, list_id)
src = html_h2_path([('Results', '/results/'), src = html_h2_path([('/results/', 'Results'),
(parent_title, '/lists/')], list_name) ('/lists/', parent_title)], list_name)
src += html_table() src += html_table()
src += HTML.p_download_json('data.json', src += HTML.p_download_json('data.json',
'raw-list-{}.json'.format(list_id)) 'raw-list-{}.json'.format(list_id))
@@ -88,22 +88,28 @@ def process():
base_custom = mylib.path_out('lists') base_custom = mylib.path_out('lists')
title_custom = 'Lists' title_custom = 'Lists'
arr = [] arr = []
for list_id, json in mylib.enum_custom_lists(): for list_id, json in mylib.enum_custom_lists('list_'):
list_name = json['name'] 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, write_ranking_custom_lists(base_custom, list_id, list_name,
parent_title=title_custom) parent_title=title_custom)
print(' index page') print(' index page')
mylib.sort_by_name(arr, 1) mylib.sort_by_name(arr, 1)
src = html_h2_path([('Results', '/results/')], title_custom) src = html_h2_path([('/results/', 'Results')], title_custom)
src += ''' src += '''
<p class="squeeze"> <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> </p>
<div class="found-in">''' <div class="found-in">'''
for x in arr: 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) HTML.write(base_custom, src + '</div>', title=title_custom)
print('') print('')

View File

@@ -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="/category/">Categories</a></li>
<li>List of <a href="/index/domains/all/">All Domains</a>, <li>List of <a href="/index/domains/all/">All Domains</a>,
only <a href="/index/domains/tracker/">Trackers</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> </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) '''.format(title, c_apps, c_domains, c_recs, c_logs), title=title)
mylib.symlink(index_rank.fname_app_rank(), mylib.symlink(index_rank.fname_app_rank(),
mylib.path_add(base_dir, 'rank.json')) # after HTML.write mylib.path_add(base_dir, 'rank.json')) # after HTML.write

View File

@@ -4,6 +4,7 @@ import sys
import lib_common as mylib import lib_common as mylib
import download_itunes # get_genres import download_itunes # get_genres
import index_app_names # get_name import index_app_names # get_name
import index_rank # save_groupby_list
_dict_apps = None _dict_apps = None
_dict_names = None _dict_names = None
@@ -96,9 +97,23 @@ def persist_individual_files():
index = sorted_reverse_index() index = sorted_reverse_index()
make_dir_individuals() make_dir_individuals()
cat_groups = {}
for cid, cname in _dict_names.items(): for cid, cname in _dict_names.items():
# write individual
mylib.json_write(fname_cat_individual(cid), mylib.json_write(fname_cat_individual(cid),
{'meta': [cid, cname], 'apps': index[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): def get_categories(bundle_id):

View File

@@ -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): def update_summary_index(index, bundle_ids, deleteOnly=False):
did_change = False did_change = False
if deleteOnly: if deleteOnly:
@@ -102,18 +135,45 @@ def write_ranking_category_list(index, affected_ids):
def write_ranking_custom_lists(index, affected_ids): def write_ranking_custom_lists(index, affected_ids):
make_rank_list_dir('custom', reset=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)) ret = list(filter_by_list(index, json['apps'], affected_ids))
if len(ret) == 0: if len(ret) == 0:
continue continue
mylib.json_write(fname_rank_list('custom', list_id), ret, pretty=False) mylib.json_write(fname_rank_list('custom', list_id), ret, pretty=False)
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 = []
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): def write_ranking_list(index, affected_ids):
ret = [] # prepend bundle-id and app name
for bid, values in index.items(): ret = [[b, index_app_names.get_name(b)] + v for b, v in index.items()]
ret.append([bid, index_app_names.get_name(bid)] + values)
del(values[8:]) # prepare for write_rank_index # TODO: return list of updated files
print(' write group-by lists')
write_ranking_groupby_lists(ret, affected_ids)
print(' write custom lists') print(' write custom lists')
# sort by %-tracker asc, #-pardom asc, avg-req-per-min asc # sort by %-tracker asc, #-pardom asc, avg-req-per-min asc
@@ -166,6 +226,14 @@ def get_total_counts():
return [0, 0] 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): def process(bundle_ids, deleteOnly=False):
print('writing index: ranking ...') print('writing index: ranking ...')
fname = fname_app_summary() fname = fname_app_summary()
@@ -177,6 +245,8 @@ def process(bundle_ids, deleteOnly=False):
ids = mylib.appids_in_data(bundle_ids) ids = mylib.appids_in_data(bundle_ids)
if update_summary_index(index, ids, deleteOnly=deleteOnly): if update_summary_index(index, ids, deleteOnly=deleteOnly):
write_ranking_list(index, bundle_ids) write_ranking_list(index, bundle_ids)
for values in index.values():
del(values[8:])
write_rank_index(index) write_rank_index(index)
print('') print('')

View File

@@ -247,10 +247,10 @@ def enum_newly_added():
yield fname, os.path.basename(fname)[3:] # del prefix 'in_' yield fname, os.path.basename(fname)[3:] # del prefix 'in_'
def enum_custom_lists(): def enum_custom_lists(prefix):
for fname in glob.glob(path_data('_lists', 'list_*.json')): for fname in glob.glob(path_data('_lists', prefix + '*.json')):
with open(fname, 'r') as fp: 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): def enum_jsons(bundle_id):

View File

@@ -23,9 +23,9 @@ def a_subdomain(x, inner=None, attr_str=''):
return '<a{} href="/subdomain/#{}">{}</a>'.format(attr_str, x, inner or x) return '<a{} href="/subdomain/#{}">{}</a>'.format(attr_str, x, inner or x)
def h2_path_n_rank(title, path_parts, rank_href): def h2_path_n_rank(title, path_parts, rank_href, rank_title='Ranking'):
return '<h2>{} <a class="snd floatr" href="{}">Ranking</a></h2>'.format( return '<h2>{} <a class="snd floatr" href="{}">{}</a></h2>'.format(
a_path(path_parts, title), rank_href) a_path(path_parts, title), rank_href, rank_title)
def p_download_json(href, download_name): def p_download_json(href, download_name):
@@ -65,7 +65,7 @@ def div(inner, attr=None):
def a_path(parts, suffix): def a_path(parts, suffix):
''' expects (name, url) tuples ''' ''' expects (name, url) tuples '''
return ' / '.join(['<a href="{}">{}</a>'.format(url, title) return ' / '.join(['<a href="{}">{}</a>'.format(url, title)
for title, url in parts] + [suffix]) for url, title in parts] + [suffix])
# Simple constructs # Simple constructs
@@ -81,6 +81,36 @@ def date_utc(ctime):
time.strftime('%Y-%m-%d, %H:%M', time.gmtime(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 # Higher level constructs
def pagination(current, total): def pagination(current, total):

View File

@@ -8,6 +8,7 @@ import download_itunes
import download_tracker import download_tracker
import html_bundle import html_bundle
import html_categories import html_categories
import html_group_compare
import html_index_apps import html_index_apps
import html_index_domains import html_index_domains
import html_ranking import html_ranking
@@ -41,6 +42,7 @@ def rebuild_html(bundle_ids=None, cat_ids=None, inclIApp=True):
else: else:
print('no new bundle, not rebuilding index') print('no new bundle, not rebuilding index')
html_ranking.process() # after html_categories & html_index_apps 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 app_count, dom_count = html_index_domains.process() # after index_domains
html_root.process(app_count, dom_count, inclStatic=True) html_root.process(app_count, dom_count, inclStatic=True)

View File

@@ -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)"> <g id="graph1" class="graph" transform="scale(1 1) rotate(0) translate(4 400)">
<title>Dependency</title> <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> <g id="node1" class="node"><title>.</title>
<ellipse fill="none" stroke="black" cx="299" cy="-378" rx="27" ry="18"></ellipse> <ellipse fill="none" stroke="black" cx="261" 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> <text text-anchor="middle" x="261" 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>
<!-- .&#45;&gt;download_itunes -->
<g id="edge2" class="edge"><title>.-&gt;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>
<!-- .&#45;&gt;bundle_combine -->
<g id="edge4" class="edge"><title>.-&gt;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>
</g> </g>
<!-- download_tracker --> <!-- download_tracker -->
<g id="node30" class="node"><title>download_tracker</title> <g id="node3" class="node"><title>download_tracker</title>
<ellipse fill="none" stroke="black" cx="475" cy="-306" rx="80.1284" ry="18"></ellipse> <ellipse fill="none" stroke="black" cx="85" 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> <text text-anchor="middle" x="85" y="-301.8" font-family="Times,serif" font-size="14.00">download_tracker</text>
</g> </g>
<!-- .&#45;&gt;download_tracker --> <!-- .&#45;&gt;download_tracker -->
<g id="edge32" class="edge"><title>.-&gt;download_tracker</title> <g id="edge2" class="edge"><title>.-&gt;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> <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="429.352,-328.922 437.351,-321.974 426.765,-322.418 429.352,-328.922"></polygon> <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>
<!-- .&#45;&gt;download_itunes -->
<g id="edge4" class="edge"><title>.-&gt;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>
<!-- .&#45;&gt;bundle_combine -->
<g id="edge6" class="edge"><title>.-&gt;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> </g>
<!-- index_app_names --> <!-- index_app_names -->
<g id="node7" class="node"><title>index_app_names</title> <g id="node9" class="node"><title>index_app_names</title>
<ellipse fill="none" stroke="black" cx="299" cy="-234" rx="80.1456" ry="18"></ellipse> <ellipse fill="none" stroke="black" cx="261" 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> <text text-anchor="middle" x="261" y="-229.8" font-family="Times,serif" font-size="14.00">index_app_names</text>
</g> </g>
<!-- download_itunes&#45;&gt;index_app_names --> <!-- download_itunes&#45;&gt;index_app_names -->
<g id="edge6" class="edge"><title>download_itunes-&gt;index_app_names</title> <g id="edge8" class="edge"><title>download_itunes-&gt;index_app_names</title>
<path fill="none" stroke="black" d="M299,-287.697C299,-279.983 299,-270.712 299,-262.112"></path> <path fill="none" stroke="black" d="M261,-287.697C261,-279.983 261,-270.712 261,-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> <polygon fill="black" stroke="black" points="264.5,-262.104 261,-252.104 257.5,-262.104 264.5,-262.104"></polygon>
</g> </g>
<!-- index_rank --> <!-- index_rank -->
<g id="node11" class="node"><title>index_rank</title> <g id="node19" class="node"><title>index_rank</title>
<ellipse fill="none" stroke="black" cx="257" cy="-162" rx="54.219" ry="18"></ellipse> <ellipse fill="none" stroke="black" cx="390" cy="-90" 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> <text text-anchor="middle" x="390" y="-85.8" font-family="Times,serif" font-size="14.00">index_rank</text>
</g> </g>
<!-- bundle_combine&#45;&gt;index_rank --> <!-- bundle_combine&#45;&gt;index_rank -->
<g id="edge18" class="edge"><title>bundle_combine-&gt;index_rank</title> <g id="edge18" class="edge"><title>bundle_combine-&gt;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> <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="237.729,-189.455 241.851,-179.695 232.542,-184.754 237.729,-189.455"></polygon> <polygon fill="black" stroke="black" points="398.348,-117.632 393.125,-108.414 391.459,-118.877 398.348,-117.632"></polygon>
</g> </g>
<!-- index_domains --> <!-- index_domains -->
<g id="node20" class="node"><title>index_domains</title> <g id="node21" class="node"><title>index_domains</title>
<ellipse fill="none" stroke="black" cx="92" cy="-234" rx="70.0665" ry="18"></ellipse> <ellipse fill="none" stroke="black" cx="499" 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> <text text-anchor="middle" x="499" y="-229.8" font-family="Times,serif" font-size="14.00">index_domains</text>
</g> </g>
<!-- bundle_combine&#45;&gt;index_domains --> <!-- bundle_combine&#45;&gt;index_domains -->
<g id="edge20" class="edge"><title>bundle_combine-&gt;index_domains</title> <g id="edge20" class="edge"><title>bundle_combine-&gt;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> <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="108.899,-259.481 101.131,-252.275 102.71,-262.752 108.899,-259.481"></polygon> <polygon fill="black" stroke="black" points="478.071,-261.568 482.528,-251.956 473.05,-256.69 478.071,-261.568"></polygon>
</g> </g>
<!-- index_categories --> <!-- index_categories -->
<g id="node9" class="node"><title>index_categories</title> <g id="node11" class="node"><title>index_categories</title>
<ellipse fill="none" stroke="black" cx="407" cy="-162" rx="76.1936" ry="18"></ellipse> <ellipse fill="none" stroke="black" cx="250" 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> <text text-anchor="middle" x="250" y="-157.8" font-family="Times,serif" font-size="14.00">index_categories</text>
</g> </g>
<!-- index_app_names&#45;&gt;index_categories --> <!-- index_app_names&#45;&gt;index_categories -->
<g id="edge8" class="edge"><title>index_app_names-&gt;index_categories</title> <g id="edge10" class="edge"><title>index_app_names-&gt;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> <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="375.599,-187.523 382.087,-179.147 371.792,-181.649 375.599,-187.523"></polygon> <polygon fill="black" stroke="black" points="257.698,-189.44 252.688,-180.104 250.783,-190.526 257.698,-189.44"></polygon>
</g>
<!-- index_app_names&#45;&gt;index_rank -->
<g id="edge10" class="edge"><title>index_app_names-&gt;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> </g>
<!-- html_index_domains --> <!-- html_index_domains -->
<g id="node13" class="node"><title>html_index_domains</title> <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> <ellipse fill="none" stroke="black" cx="511" 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> <text text-anchor="middle" x="511" y="-157.8" font-family="Times,serif" font-size="14.00">html_index_domains</text>
</g> </g>
<!-- index_app_names&#45;&gt;html_index_domains --> <!-- index_app_names&#45;&gt;html_index_domains -->
<g id="edge12" class="edge"><title>index_app_names-&gt;html_index_domains</title> <g id="edge12" class="edge"><title>index_app_names-&gt;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> <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="146.712,-177.807 136.118,-177.919 144.47,-184.438 146.712,-177.807"></polygon> <polygon fill="black" stroke="black" points="451.685,-183.243 460.371,-177.176 449.798,-176.502 451.685,-183.243"></polygon>
</g> </g>
<!-- html_categories --> <!-- html_categories -->
<g id="node15" class="node"><title>html_categories</title> <g id="node15" class="node"><title>html_categories</title>
<ellipse fill="none" stroke="black" cx="407" cy="-90" rx="72.5712" ry="18"></ellipse> <ellipse fill="none" stroke="black" cx="72" 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> <text text-anchor="middle" x="72" y="-85.8" font-family="Times,serif" font-size="14.00">html_categories</text>
</g> </g>
<!-- index_categories&#45;&gt;html_categories --> <!-- index_categories&#45;&gt;html_categories -->
<g id="edge14" class="edge"><title>index_categories-&gt;html_categories</title> <g id="edge14" class="edge"><title>index_categories-&gt;html_categories</title>
<path fill="none" stroke="black" d="M407,-143.697C407,-135.983 407,-126.712 407,-118.112"></path> <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="410.5,-118.104 407,-108.104 403.5,-118.104 410.5,-118.104"></polygon> <polygon fill="black" stroke="black" points="119.672,-105.986 109.084,-105.584 117.11,-112.501 119.672,-105.986"></polygon>
</g> </g>
<!-- html_index_apps --> <!-- html_index_apps -->
<g id="node17" class="node"><title>html_index_apps</title> <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> <ellipse fill="none" stroke="black" cx="240" 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> <text text-anchor="middle" x="240" y="-85.8" font-family="Times,serif" font-size="14.00">html_index_apps</text>
</g> </g>
<!-- index_categories&#45;&gt;html_index_apps --> <!-- index_categories&#45;&gt;html_index_apps -->
<g id="edge16" class="edge"><title>index_categories-&gt;html_index_apps</title> <g id="edge16" class="edge"><title>index_categories-&gt;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> <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="531.135,-113.069 539.019,-105.992 528.442,-106.607 531.135,-113.069"></polygon> <polygon fill="black" stroke="black" points="247.323,-117.509 242.443,-108.104 240.393,-118.499 247.323,-117.509"></polygon>
</g> </g>
<!-- html_bundle --> <!-- index_categories&#45;&gt;index_rank -->
<g id="node22" class="node"><title>html_bundle</title> <g id="edge32" class="edge"><title>index_categories-&gt;index_rank</title>
<ellipse fill="none" stroke="black" cx="256" cy="-90" rx="59.2871" ry="18"></ellipse> <path fill="none" stroke="black" d="M281.071,-145.465C302.076,-134.962 329.938,-121.031 352.226,-109.887"></path>
<text text-anchor="middle" x="256" y="-85.8" font-family="Times,serif" font-size="14.00">html_bundle</text> <polygon fill="black" stroke="black" points="354.021,-112.903 361.4,-105.3 350.89,-106.642 354.021,-112.903"></polygon>
</g>
<!-- index_rank&#45;&gt;html_bundle -->
<g id="edge22" class="edge"><title>index_rank-&gt;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>
</g> </g>
<!-- html_root --> <!-- html_root -->
<g id="node28" class="node"><title>html_root</title> <g id="node29" class="node"><title>html_root</title>
<ellipse fill="none" stroke="black" cx="92" cy="-90" rx="49.1927" ry="18"></ellipse> <ellipse fill="none" stroke="black" cx="512" 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> <text text-anchor="middle" x="512" y="-85.8" font-family="Times,serif" font-size="14.00">html_root</text>
</g> </g>
<!-- html_index_domains&#45;&gt;html_root --> <!-- html_index_domains&#45;&gt;html_root -->
<g id="edge30" class="edge"><title>html_index_domains-&gt;html_root</title> <g id="edge30" class="edge"><title>html_index_domains-&gt;html_root</title>
<path fill="none" stroke="black" d="M92,-143.697C92,-135.983 92,-126.712 92,-118.112"></path> <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="95.5001,-118.104 92,-108.104 88.5001,-118.104 95.5001,-118.104"></polygon> <polygon fill="black" stroke="black" points="515.112,-118.153 511.756,-108.104 508.113,-118.053 515.112,-118.153"></polygon>
</g> </g>
<!-- html_ranking --> <!-- html_ranking -->
<g id="node25" class="node"><title>html_ranking</title> <g id="node26" class="node"><title>html_ranking</title>
<ellipse fill="none" stroke="black" cx="491" cy="-18" rx="63.108" ry="18"></ellipse> <ellipse fill="none" stroke="black" cx="143" 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> <text text-anchor="middle" x="143" y="-13.8" font-family="Times,serif" font-size="14.00">html_ranking</text>
</g> </g>
<!-- html_categories&#45;&gt;html_ranking --> <!-- html_categories&#45;&gt;html_ranking -->
<g id="edge28" class="edge"><title>html_categories-&gt;html_ranking</title> <g id="edge28" class="edge"><title>html_categories-&gt;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> <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="466.084,-44.3191 471.526,-35.2285 461.603,-38.9416 466.084,-44.3191"></polygon> <polygon fill="black" stroke="black" points="121.631,-44.9831 126.295,-35.4699 116.716,-39.9983 121.631,-44.9831"></polygon>
</g> </g>
<!-- html_index_apps&#45;&gt;html_ranking --> <!-- html_index_apps&#45;&gt;html_ranking -->
<g id="edge26" class="edge"><title>html_index_apps-&gt;html_ranking</title> <g id="edge26" class="edge"><title>html_index_apps-&gt;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> <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="520.397,-38.9416 510.474,-35.2285 515.916,-44.3191 520.397,-38.9416"></polygon> <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&#45;&gt;html_bundle -->
<g id="edge22" class="edge"><title>index_rank-&gt;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&#45;&gt;html_group_compare -->
<g id="edge34" class="edge"><title>index_rank-&gt;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> </g>
<!-- index_domains&#45;&gt;html_index_domains --> <!-- index_domains&#45;&gt;html_index_domains -->
<g id="edge24" class="edge"><title>index_domains-&gt;html_index_domains</title> <g id="edge24" class="edge"><title>index_domains-&gt;html_index_domains</title>
<path fill="none" stroke="black" d="M92,-215.697C92,-207.983 92,-198.712 92,-190.112"></path> <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="95.5001,-190.104 92,-180.104 88.5001,-190.104 95.5001,-190.104"></polygon> <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>
</g> </g>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 9.8 KiB

After

Width:  |  Height:  |  Size: 10 KiB

View File

@@ -3,9 +3,9 @@
<head> <head>
<meta charset="utf-8"/> <meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=0.75" /> <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> <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="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="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"> <link rel="icon" type="image/png" sizes="32x32" href="/static/favicon/favicon-32x32.png">