[mod] oscar: /preferences , engines tab: report engine times

* display the median time instead of the average.
* add a "Reliability" column (sum up the metrics and the checker results).
* the "selected language", "SafeSearch", "Time range" values are displayed as "broken" when the checker tests fail.
This commit is contained in:
Alexandre Flament 2021-04-14 18:11:35 +02:00
parent c27fef1cde
commit 7cfd8d900a
34 changed files with 849 additions and 60 deletions

View file

@ -20,7 +20,6 @@ import searx.settings_loader
from os import environ from os import environ
from os.path import realpath, dirname, join, abspath, isfile from os.path import realpath, dirname, join, abspath, isfile
searx_dir = abspath(dirname(__file__)) searx_dir = abspath(dirname(__file__))
engine_dir = dirname(realpath(__file__)) engine_dir = dirname(realpath(__file__))
static_path = abspath(join(dirname(__file__), 'static')) static_path = abspath(join(dirname(__file__), 'static'))

View file

@ -5,6 +5,7 @@ import types
import functools import functools
import itertools import itertools
from time import time from time import time
from timeit import default_timer
from urllib.parse import urlparse from urllib.parse import urlparse
import re import re
@ -386,7 +387,7 @@ class Checker:
params = self.processor.get_params(search_query, engineref_category) params = self.processor.get_params(search_query, engineref_category)
if params is not None: if params is not None:
counter_inc('engine', search_query.engineref_list[0].name, 'search', 'count', 'sent') counter_inc('engine', search_query.engineref_list[0].name, 'search', 'count', 'sent')
self.processor.search(search_query.query, params, result_container, time(), 5) self.processor.search(search_query.query, params, result_container, default_timer(), 5)
return result_container return result_container
def get_result_container_tests(self, test_name: str, search_query: SearchQuery) -> ResultContainerTests: def get_result_container_tests(self, test_name: str, search_query: SearchQuery) -> ResultContainerTests:

View file

@ -923,12 +923,78 @@ input.cursor-text {
padding: 0.5rem 1rem; padding: 0.5rem 1rem;
margin: 0rem 0 0 2rem; margin: 0rem 0 0 2rem;
border: 1px solid #ddd; border: 1px solid #ddd;
box-shadow: 2px 2px 2px 0px rgba(0, 0, 0, 0.1);
background: white; background: white;
font-size: 14px; font-size: 14px;
font-weight: normal; font-weight: normal;
z-index: 1000000; z-index: 1000000;
} }
td:hover .engine-tooltip,
th:hover .engine-tooltip, th:hover .engine-tooltip,
.engine-tooltip:hover { .engine-tooltip:hover {
display: inline-block; display: inline-block;
} }
/* stacked-bar-chart */
.stacked-bar-chart {
margin: 0;
padding: 0 0.125rem 0 3rem;
width: 100%;
width: -moz-available;
width: -webkit-fill-available;
width: fill;
flex-direction: row;
flex-wrap: nowrap;
flex-grow: 1;
align-items: center;
display: inline-flex;
}
.stacked-bar-chart-value {
width: 3rem;
display: inline-block;
position: absolute;
padding: 0 0.5rem;
text-align: right;
}
.stacked-bar-chart-base {
display: flex;
flex-shrink: 0;
flex-grow: 0;
flex-basis: unset;
}
.stacked-bar-chart-median {
display: flex;
flex-shrink: 0;
flex-grow: 0;
flex-basis: unset;
background: #000000;
border: 1px solid rgba(0, 0, 0, 0.9);
padding: 0.3rem 0;
}
.stacked-bar-chart-rate80 {
display: flex;
flex-shrink: 0;
flex-grow: 0;
flex-basis: unset;
background: transparent;
border: 1px solid rgba(0, 0, 0, 0.3);
padding: 0.3rem 0;
}
.stacked-bar-chart-rate95 {
display: flex;
flex-shrink: 0;
flex-grow: 0;
flex-basis: unset;
background: transparent;
border-bottom: 1px dotted rgba(0, 0, 0, 0.5);
padding: 0;
}
.stacked-bar-chart-rate100 {
display: flex;
flex-shrink: 0;
flex-grow: 0;
flex-basis: unset;
background: transparent;
border-left: 1px solid rgba(0, 0, 0, 0.9);
padding: 0.4rem 0;
width: 1px;
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -896,15 +896,81 @@ input.cursor-text {
padding: 0.5rem 1rem; padding: 0.5rem 1rem;
margin: 0rem 0 0 2rem; margin: 0rem 0 0 2rem;
border: 1px solid #ddd; border: 1px solid #ddd;
box-shadow: 2px 2px 2px 0px rgba(0, 0, 0, 0.1);
background: white; background: white;
font-size: 14px; font-size: 14px;
font-weight: normal; font-weight: normal;
z-index: 1000000; z-index: 1000000;
} }
td:hover .engine-tooltip,
th:hover .engine-tooltip, th:hover .engine-tooltip,
.engine-tooltip:hover { .engine-tooltip:hover {
display: inline-block; display: inline-block;
} }
/* stacked-bar-chart */
.stacked-bar-chart {
margin: 0;
padding: 0 0.125rem 0 3rem;
width: 100%;
width: -moz-available;
width: -webkit-fill-available;
width: fill;
flex-direction: row;
flex-wrap: nowrap;
flex-grow: 1;
align-items: center;
display: inline-flex;
}
.stacked-bar-chart-value {
width: 3rem;
display: inline-block;
position: absolute;
padding: 0 0.5rem;
text-align: right;
}
.stacked-bar-chart-base {
display: flex;
flex-shrink: 0;
flex-grow: 0;
flex-basis: unset;
}
.stacked-bar-chart-median {
display: flex;
flex-shrink: 0;
flex-grow: 0;
flex-basis: unset;
background: #d5d8d7;
border: 1px solid rgba(213, 216, 215, 0.9);
padding: 0.3rem 0;
}
.stacked-bar-chart-rate80 {
display: flex;
flex-shrink: 0;
flex-grow: 0;
flex-basis: unset;
background: transparent;
border: 1px solid rgba(213, 216, 215, 0.3);
padding: 0.3rem 0;
}
.stacked-bar-chart-rate95 {
display: flex;
flex-shrink: 0;
flex-grow: 0;
flex-basis: unset;
background: transparent;
border-bottom: 1px dotted rgba(213, 216, 215, 0.5);
padding: 0;
}
.stacked-bar-chart-rate100 {
display: flex;
flex-shrink: 0;
flex-grow: 0;
flex-basis: unset;
background: transparent;
border-left: 1px solid rgba(213, 216, 215, 0.9);
padding: 0.4rem 0;
width: 1px;
}
/*Global*/ /*Global*/
body { body {
background: #1d1f21 none !important; background: #1d1f21 none !important;

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -688,6 +688,71 @@ input[type=checkbox]:not(:checked) + .label_hide_if_checked + .label_hide_if_not
z-index: 1000000; z-index: 1000000;
} }
th:hover .engine-tooltip, th:hover .engine-tooltip,
td:hover .engine-tooltip,
.engine-tooltip:hover { .engine-tooltip:hover {
display: inline-block; display: inline-block;
} }
/* stacked-bar-chart */
.stacked-bar-chart {
margin: 0;
padding: 0 0.125rem 0 3rem;
width: 100%;
width: -moz-available;
width: -webkit-fill-available;
width: fill;
flex-direction: row;
flex-wrap: nowrap;
flex-grow: 1;
align-items: center;
display: inline-flex;
}
.stacked-bar-chart-value {
width: 3rem;
display: inline-block;
position: absolute;
padding: 0 0.5rem;
text-align: right;
}
.stacked-bar-chart-base {
display: flex;
flex-shrink: 0;
flex-grow: 0;
flex-basis: unset;
}
.stacked-bar-chart-median {
display: flex;
flex-shrink: 0;
flex-grow: 0;
flex-basis: unset;
background: #000000;
border: 1px solid rgba(0, 0, 0, 0.9);
padding: 0.3rem 0;
}
.stacked-bar-chart-rate80 {
display: flex;
flex-shrink: 0;
flex-grow: 0;
flex-basis: unset;
background: transparent;
border: 1px solid rgba(0, 0, 0, 0.3);
padding: 0.3rem 0;
}
.stacked-bar-chart-rate95 {
display: flex;
flex-shrink: 0;
flex-grow: 0;
flex-basis: unset;
background: transparent;
border-bottom: 1px dotted rgba(0, 0, 0, 0.5);
padding: 0;
}
.stacked-bar-chart-rate100 {
display: flex;
flex-shrink: 0;
flex-grow: 0;
flex-basis: unset;
background: transparent;
border-left: 1px solid rgba(0, 0, 0, 0.9);
padding: 0.4rem 0;
width: 1px;
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -1,4 +1,7 @@
@import "../logicodev/variables.less"; @import "../logicodev/variables.less";
@stacked-bar-chart: rgb(213, 216, 215, 1);
@import "../logicodev/footer.less"; @import "../logicodev/footer.less";
@import "../logicodev/checkbox.less"; @import "../logicodev/checkbox.less";
@import "../logicodev/onoff.less"; @import "../logicodev/onoff.less";

View file

@ -20,12 +20,72 @@ input.cursor-text {
padding: 0.5rem 1rem; padding: 0.5rem 1rem;
margin: 0rem 0 0 2rem; margin: 0rem 0 0 2rem;
border: 1px solid #ddd; border: 1px solid #ddd;
box-shadow: 2px 2px 2px 0px rgba(0,0,0,0.1);
background: white; background: white;
font-size: 14px; font-size: 14px;
font-weight: normal; font-weight: normal;
z-index: 1000000; z-index: 1000000;
} }
th:hover .engine-tooltip, .engine-tooltip:hover { td:hover .engine-tooltip, th:hover .engine-tooltip, .engine-tooltip:hover {
display: inline-block; display: inline-block;
} }
/* stacked-bar-chart */
.stacked-bar-chart {
margin: 0;
padding: 0 0.125rem 0 3rem;
width: 100%;
width: -moz-available;
width: -webkit-fill-available;
width: fill;
flex-direction: row;
flex-wrap: nowrap;
flex-grow: 1;
align-items: center;
display: inline-flex;
}
.stacked-bar-chart-value {
width: 3rem;
display: inline-block;
position: absolute;
padding: 0 0.5rem;
text-align: right;
}
.stacked-bar-chart-base {
display:flex;
flex-shrink: 0;
flex-grow: 0;
flex-basis: unset;
}
.stacked-bar-chart-median {
.stacked-bar-chart-base();
background: @stacked-bar-chart;
border: 1px solid fade(@stacked-bar-chart, 90%);
padding: 0.3rem 0;
}
.stacked-bar-chart-rate80 {
.stacked-bar-chart-base();
background: transparent;
border: 1px solid fade(@stacked-bar-chart, 30%);
padding: 0.3rem 0;
}
.stacked-bar-chart-rate95 {
.stacked-bar-chart-base();
background: transparent;
border-bottom: 1px dotted fade(@stacked-bar-chart, 50%);
padding: 0;
}
.stacked-bar-chart-rate100 {
.stacked-bar-chart-base();
background: transparent;
border-left: 1px solid fade(@stacked-bar-chart, 90%);
padding: 0.4rem 0;
width: 1px;
}

View file

@ -14,3 +14,5 @@
@light-green: #01D7D4; @light-green: #01D7D4;
@orange: #FFA92F; @orange: #FFA92F;
@dark-red: #c9432f; @dark-red: #c9432f;
@stacked-bar-chart: rgb(0, 0, 0);

View file

@ -1,3 +1,5 @@
@import "variables.less";
@import "footer.less"; @import "footer.less";
@import "checkbox.less"; @import "checkbox.less";

View file

@ -14,6 +14,66 @@
z-index: 1000000; z-index: 1000000;
} }
th:hover .engine-tooltip, .engine-tooltip:hover { th:hover .engine-tooltip, td:hover .engine-tooltip, .engine-tooltip:hover {
display: inline-block; display: inline-block;
} }
/* stacked-bar-chart */
.stacked-bar-chart {
margin: 0;
padding: 0 0.125rem 0 3rem;
width: 100%;
width: -moz-available;
width: -webkit-fill-available;
width: fill;
flex-direction: row;
flex-wrap: nowrap;
flex-grow: 1;
align-items: center;
display: inline-flex;
}
.stacked-bar-chart-value {
width: 3rem;
display: inline-block;
position: absolute;
padding: 0 0.5rem;
text-align: right;
}
.stacked-bar-chart-base {
display:flex;
flex-shrink: 0;
flex-grow: 0;
flex-basis: unset;
}
.stacked-bar-chart-median {
.stacked-bar-chart-base();
background: @stacked-bar-chart;
border: 1px solid fade(@stacked-bar-chart, 90%);
padding: 0.3rem 0;
}
.stacked-bar-chart-rate80 {
.stacked-bar-chart-base();
background: transparent;
border: 1px solid fade(@stacked-bar-chart, 30%);
padding: 0.3rem 0;
}
.stacked-bar-chart-rate95 {
.stacked-bar-chart-base();
background: transparent;
border-bottom: 1px dotted fade(@stacked-bar-chart, 50%);
padding: 0;
}
.stacked-bar-chart-rate100 {
.stacked-bar-chart-base();
background: transparent;
border-left: 1px solid fade(@stacked-bar-chart, 90%);
padding: 0.4rem 0;
width: 1px;
}

View file

@ -0,0 +1 @@
@stacked-bar-chart: rgb(0, 0, 0);

View file

@ -1,4 +1,4 @@
/*! searx | 23-03-2021 | */ /*! searx | 21-04-2021 | */
/* /*
* searx, A privacy-respecting, hackable metasearch engine * searx, A privacy-respecting, hackable metasearch engine
* *
@ -692,6 +692,12 @@ html.js .show_if_nojs {
.danger { .danger {
background-color: #fae1e1; background-color: #fae1e1;
} }
.warning {
background: #faf5e1;
}
.success {
background: #e3fae1;
}
.badge { .badge {
display: inline-block; display: inline-block;
color: #fff; color: #fff;
@ -1147,6 +1153,69 @@ select:focus {
transform: rotate(360deg); transform: rotate(360deg);
} }
} }
/* -- stacked bar chart -- */
.stacked-bar-chart {
margin: 0;
padding: 0 0.125rem 0 4rem;
width: 100%;
width: -moz-available;
width: -webkit-fill-available;
width: fill;
flex-direction: row;
flex-wrap: nowrap;
align-items: center;
display: inline-flex;
}
.stacked-bar-chart-value {
width: 3rem;
display: inline-block;
position: absolute;
padding: 0 0.5rem;
text-align: right;
}
.stacked-bar-chart-base {
display: flex;
flex-shrink: 0;
flex-grow: 0;
flex-basis: unset;
}
.stacked-bar-chart-median {
display: flex;
flex-shrink: 0;
flex-grow: 0;
flex-basis: unset;
background: #000000;
border: 1px solid rgba(0, 0, 0, 0.9);
padding: 0.3rem 0;
}
.stacked-bar-chart-rate80 {
display: flex;
flex-shrink: 0;
flex-grow: 0;
flex-basis: unset;
background: transparent;
border: 1px solid rgba(0, 0, 0, 0.3);
padding: 0.3rem 0;
}
.stacked-bar-chart-rate95 {
display: flex;
flex-shrink: 0;
flex-grow: 0;
flex-basis: unset;
background: transparent;
border-bottom: 1px dotted rgba(0, 0, 0, 0.5);
padding: 0;
}
.stacked-bar-chart-rate100 {
display: flex;
flex-shrink: 0;
flex-grow: 0;
flex-basis: unset;
background: transparent;
border-left: 1px solid rgba(0, 0, 0, 0.9);
padding: 0.4rem 0;
width: 1px;
}
/*! Autocomplete.js v2.6.3 | license MIT | (c) 2017, Baptiste Donaux | http://autocomplete-js.com */ /*! Autocomplete.js v2.6.3 | license MIT | (c) 2017, Baptiste Donaux | http://autocomplete-js.com */
.autocomplete { .autocomplete {
position: absolute; position: absolute;
@ -1435,8 +1504,10 @@ select:focus {
font-size: 14px; font-size: 14px;
font-weight: normal; font-weight: normal;
z-index: 1000000; z-index: 1000000;
text-align: left;
} }
#main_preferences th:hover .engine-tooltip, #main_preferences th:hover .engine-tooltip,
#main_preferences td:hover .engine-tooltip,
#main_preferences .engine-tooltip:hover { #main_preferences .engine-tooltip:hover {
display: inline-block; display: inline-block;
} }

File diff suppressed because one or more lines are too long

View file

@ -1,4 +1,4 @@
/*! searx | 23-03-2021 | */ /*! searx | 21-04-2021 | */
/* /*
* searx, A privacy-respecting, hackable metasearch engine * searx, A privacy-respecting, hackable metasearch engine
* *
@ -692,6 +692,12 @@ html.js .show_if_nojs {
.danger { .danger {
background-color: #fae1e1; background-color: #fae1e1;
} }
.warning {
background: #faf5e1;
}
.success {
background: #e3fae1;
}
.badge { .badge {
display: inline-block; display: inline-block;
color: #fff; color: #fff;
@ -1147,6 +1153,69 @@ select:focus {
transform: rotate(360deg); transform: rotate(360deg);
} }
} }
/* -- stacked bar chart -- */
.stacked-bar-chart {
margin: 0;
padding: 0 0.125rem 0 4rem;
width: 100%;
width: -moz-available;
width: -webkit-fill-available;
width: fill;
flex-direction: row;
flex-wrap: nowrap;
align-items: center;
display: inline-flex;
}
.stacked-bar-chart-value {
width: 3rem;
display: inline-block;
position: absolute;
padding: 0 0.5rem;
text-align: right;
}
.stacked-bar-chart-base {
display: flex;
flex-shrink: 0;
flex-grow: 0;
flex-basis: unset;
}
.stacked-bar-chart-median {
display: flex;
flex-shrink: 0;
flex-grow: 0;
flex-basis: unset;
background: #000000;
border: 1px solid rgba(0, 0, 0, 0.9);
padding: 0.3rem 0;
}
.stacked-bar-chart-rate80 {
display: flex;
flex-shrink: 0;
flex-grow: 0;
flex-basis: unset;
background: transparent;
border: 1px solid rgba(0, 0, 0, 0.3);
padding: 0.3rem 0;
}
.stacked-bar-chart-rate95 {
display: flex;
flex-shrink: 0;
flex-grow: 0;
flex-basis: unset;
background: transparent;
border-bottom: 1px dotted rgba(0, 0, 0, 0.5);
padding: 0;
}
.stacked-bar-chart-rate100 {
display: flex;
flex-shrink: 0;
flex-grow: 0;
flex-basis: unset;
background: transparent;
border-left: 1px solid rgba(0, 0, 0, 0.9);
padding: 0.4rem 0;
width: 1px;
}
/*! Autocomplete.js v2.6.3 | license MIT | (c) 2017, Baptiste Donaux | http://autocomplete-js.com */ /*! Autocomplete.js v2.6.3 | license MIT | (c) 2017, Baptiste Donaux | http://autocomplete-js.com */
.autocomplete { .autocomplete {
position: absolute; position: absolute;
@ -1435,8 +1504,10 @@ select:focus {
font-size: 14px; font-size: 14px;
font-weight: normal; font-weight: normal;
z-index: 1000000; z-index: 1000000;
text-align: left;
} }
#main_preferences th:hover .engine-tooltip, #main_preferences th:hover .engine-tooltip,
#main_preferences td:hover .engine-tooltip,
#main_preferences .engine-tooltip:hover { #main_preferences .engine-tooltip:hover {
display: inline-block; display: inline-block;
} }

File diff suppressed because one or more lines are too long

View file

@ -1,4 +1,4 @@
/*! simple/searx.min.js | 23-03-2021 | */ /*! simple/searx.min.js | 21-04-2021 | */
(function(t,e){"use strict";var a=e.currentScript||function(){var t=e.getElementsByTagName("script");return t[t.length-1]}();t.searx={touch:"ontouchstart"in t||t.DocumentTouch&&document instanceof DocumentTouch||false,method:a.getAttribute("data-method"),autocompleter:a.getAttribute("data-autocompleter")==="true",search_on_category_select:a.getAttribute("data-search-on-category-select")==="true",infinite_scroll:a.getAttribute("data-infinite-scroll")==="true",static_path:a.getAttribute("data-static-path"),translations:JSON.parse(a.getAttribute("data-translations"))};e.getElementsByTagName("html")[0].className=t.searx.touch?"js touch":"js"})(window,document); (function(t,e){"use strict";var a=e.currentScript||function(){var t=e.getElementsByTagName("script");return t[t.length-1]}();t.searx={touch:"ontouchstart"in t||t.DocumentTouch&&document instanceof DocumentTouch||false,method:a.getAttribute("data-method"),autocompleter:a.getAttribute("data-autocompleter")==="true",search_on_category_select:a.getAttribute("data-search-on-category-select")==="true",infinite_scroll:a.getAttribute("data-infinite-scroll")==="true",static_path:a.getAttribute("data-static-path"),translations:JSON.parse(a.getAttribute("data-translations"))};e.getElementsByTagName("html")[0].className=t.searx.touch?"js touch":"js"})(window,document);
//# sourceMappingURL=searx.head.min.js.map //# sourceMappingURL=searx.head.min.js.map

View file

@ -1,4 +1,4 @@
/*! simple/searx.min.js | 23-03-2021 | */ /*! simple/searx.min.js | 21-04-2021 | */
window.searx=function(t,a){"use strict";if(t.Element){(function(e){e.matches=e.matches||e.matchesSelector||e.webkitMatchesSelector||e.msMatchesSelector||function(e){var t=this,n=(t.parentNode||t.document).querySelectorAll(e),i=-1;while(n[++i]&&n[i]!=t);return!!n[i]}})(Element.prototype)}function o(e,t,n){try{e.call(t,n)}catch(e){console.log(e)}}var s=window.searx||{};s.on=function(i,e,r,t){t=t||false;if(typeof i!=="string"){i.addEventListener(e,r,t)}else{a.addEventListener(e,function(e){var t=e.target||e.srcElement,n=false;while(t&&t.matches&&t!==a&&!(n=t.matches(i)))t=t.parentElement;if(n)o(r,t,e)},t)}};s.ready=function(e){if(document.readyState!="loading"){e.call(t)}else{t.addEventListener("DOMContentLoaded",e.bind(t))}};s.http=function(e,t,n){var i=new XMLHttpRequest,r=function(){},a=function(){},o={then:function(e){r=e;return o},catch:function(e){a=e;return o}};try{i.open(e,t,true);i.onload=function(){if(i.status==200){r(i.response,i.responseType)}else{a(Error(i.statusText))}};i.onerror=function(){a(Error("Network Error"))};i.onabort=function(){a(Error("Transaction is aborted"))};i.send()}catch(e){a(e)}return o};s.loadStyle=function(e){var t=s.static_path+e,n="style_"+e.replace(".","_"),i=a.getElementById(n);if(i===null){i=a.createElement("link");i.setAttribute("id",n);i.setAttribute("rel","stylesheet");i.setAttribute("type","text/css");i.setAttribute("href",t);a.body.appendChild(i)}};s.loadScript=function(e,t){var n=s.static_path+e,i="script_"+e.replace(".","_"),r=a.getElementById(i);if(r===null){r=a.createElement("script");r.setAttribute("id",i);r.setAttribute("src",n);r.onload=t;r.onerror=function(){r.setAttribute("error","1")};a.body.appendChild(r)}else if(!r.hasAttribute("error")){try{t.apply(r,[])}catch(e){console.log(e)}}else{console.log("callback not executed : script '"+n+"' not loaded.")}};s.insertBefore=function(e,t){element.parentNode.insertBefore(e,t)};s.insertAfter=function(e,t){t.parentNode.insertBefore(e,t.nextSibling)};s.on(".close","click",function(e){var t=e.target||e.srcElement;this.parentNode.classList.add("invisible")});return s}(window,document);(function(e){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=e()}else if(typeof define==="function"&&define.amd){define([],e)}else{var t;if(typeof window!=="undefined"){t=window}else if(typeof global!=="undefined"){t=global}else if(typeof self!=="undefined"){t=self}else{t=this}t.AutoComplete=e()}})(function(){var e,t,n;return function a(o,s,l){function u(n,e){if(!s[n]){if(!o[n]){var t=typeof require=="function"&&require;if(!e&&t)return t(n,!0);if(c)return c(n,!0);var i=new Error("Cannot find module '"+n+"'");throw i.code="MODULE_NOT_FOUND",i}var r=s[n]={exports:{}};o[n][0].call(r.exports,function(e){var t=o[n][1][e];return u(t?t:e)},r,r.exports,a,o,s,l)}return s[n].exports}var c=typeof require=="function"&&require;for(var e=0;e<l.length;e++)u(l[e]);return u}({1:[function(e,t,n){ window.searx=function(t,a){"use strict";if(t.Element){(function(e){e.matches=e.matches||e.matchesSelector||e.webkitMatchesSelector||e.msMatchesSelector||function(e){var t=this,n=(t.parentNode||t.document).querySelectorAll(e),i=-1;while(n[++i]&&n[i]!=t);return!!n[i]}})(Element.prototype)}function o(e,t,n){try{e.call(t,n)}catch(e){console.log(e)}}var s=window.searx||{};s.on=function(i,e,r,t){t=t||false;if(typeof i!=="string"){i.addEventListener(e,r,t)}else{a.addEventListener(e,function(e){var t=e.target||e.srcElement,n=false;while(t&&t.matches&&t!==a&&!(n=t.matches(i)))t=t.parentElement;if(n)o(r,t,e)},t)}};s.ready=function(e){if(document.readyState!="loading"){e.call(t)}else{t.addEventListener("DOMContentLoaded",e.bind(t))}};s.http=function(e,t,n){var i=new XMLHttpRequest,r=function(){},a=function(){},o={then:function(e){r=e;return o},catch:function(e){a=e;return o}};try{i.open(e,t,true);i.onload=function(){if(i.status==200){r(i.response,i.responseType)}else{a(Error(i.statusText))}};i.onerror=function(){a(Error("Network Error"))};i.onabort=function(){a(Error("Transaction is aborted"))};i.send()}catch(e){a(e)}return o};s.loadStyle=function(e){var t=s.static_path+e,n="style_"+e.replace(".","_"),i=a.getElementById(n);if(i===null){i=a.createElement("link");i.setAttribute("id",n);i.setAttribute("rel","stylesheet");i.setAttribute("type","text/css");i.setAttribute("href",t);a.body.appendChild(i)}};s.loadScript=function(e,t){var n=s.static_path+e,i="script_"+e.replace(".","_"),r=a.getElementById(i);if(r===null){r=a.createElement("script");r.setAttribute("id",i);r.setAttribute("src",n);r.onload=t;r.onerror=function(){r.setAttribute("error","1")};a.body.appendChild(r)}else if(!r.hasAttribute("error")){try{t.apply(r,[])}catch(e){console.log(e)}}else{console.log("callback not executed : script '"+n+"' not loaded.")}};s.insertBefore=function(e,t){element.parentNode.insertBefore(e,t)};s.insertAfter=function(e,t){t.parentNode.insertBefore(e,t.nextSibling)};s.on(".close","click",function(e){var t=e.target||e.srcElement;this.parentNode.classList.add("invisible")});return s}(window,document);(function(e){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=e()}else if(typeof define==="function"&&define.amd){define([],e)}else{var t;if(typeof window!=="undefined"){t=window}else if(typeof global!=="undefined"){t=global}else if(typeof self!=="undefined"){t=self}else{t=this}t.AutoComplete=e()}})(function(){var e,t,n;return function a(o,s,l){function u(n,e){if(!s[n]){if(!o[n]){var t=typeof require=="function"&&require;if(!e&&t)return t(n,!0);if(c)return c(n,!0);var i=new Error("Cannot find module '"+n+"'");throw i.code="MODULE_NOT_FOUND",i}var r=s[n]={exports:{}};o[n][0].call(r.exports,function(e){var t=o[n][1][e];return u(t?t:e)},r,r.exports,a,o,s,l)}return s[n].exports}var c=typeof require=="function"&&require;for(var e=0;e<l.length;e++)u(l[e]);return u}({1:[function(e,t,n){
/* /*

View file

@ -19,6 +19,9 @@
@color-warning: #dbba34; @color-warning: #dbba34;
@color-warning-background: lighten(@color-warning, 40%); @color-warning-background: lighten(@color-warning, 40%);
@color-success: #42db34;
@color-success-background: lighten(@color-success, 40%);
/// General /// General
@color-font: #444; @color-font: #444;

View file

@ -105,9 +105,10 @@
font-size: 14px; font-size: 14px;
font-weight: normal; font-weight: normal;
z-index: 1000000; z-index: 1000000;
text-align: left;
} }
th:hover .engine-tooltip, .engine-tooltip:hover { th:hover .engine-tooltip, td:hover .engine-tooltip, .engine-tooltip:hover {
display: inline-block; display: inline-block;
} }

View file

@ -4,6 +4,8 @@
* To convert "style.less" to "style.css" run: $make styles * To convert "style.less" to "style.css" run: $make styles
*/ */
@stacked-bar-chart: rgb(0, 0, 0);
@import "normalize.less"; @import "normalize.less";
@import "definitions.less"; @import "definitions.less";

View file

@ -36,6 +36,14 @@ html.js .show_if_nojs {
background-color: @color-error-background; background-color: @color-error-background;
} }
.warning {
background: @color-warning-background;
}
.success {
background: @color-success-background;
}
.badge { .badge {
display: inline-block; display: inline-block;
color: #fff; color: #fff;
@ -465,4 +473,62 @@ select {
-webkit-transform: rotate(360deg); -webkit-transform: rotate(360deg);
transform: rotate(360deg); transform: rotate(360deg);
} }
} }
/* -- stacked bar chart -- */
.stacked-bar-chart {
margin: 0;
padding: 0 0.125rem 0 4rem;
width: 100%;
width: -moz-available;
width: -webkit-fill-available;
width: fill;
flex-direction: row;
flex-wrap: nowrap;
align-items: center;
display: inline-flex;
}
.stacked-bar-chart-value {
width: 3rem;
display: inline-block;
position: absolute;
padding: 0 0.5rem;
text-align: right;
}
.stacked-bar-chart-base {
display:flex;
flex-shrink: 0;
flex-grow: 0;
flex-basis: unset;
}
.stacked-bar-chart-median {
.stacked-bar-chart-base();
background: @stacked-bar-chart;
border: 1px solid fade(@stacked-bar-chart, 90%);
padding: 0.3rem 0;
}
.stacked-bar-chart-rate80 {
.stacked-bar-chart-base();
background: transparent;
border: 1px solid fade(@stacked-bar-chart, 30%);
padding: 0.3rem 0;
}
.stacked-bar-chart-rate95 {
.stacked-bar-chart-base();
background: transparent;
border-bottom: 1px dotted fade(@stacked-bar-chart, 50%);
padding: 0;
}
.stacked-bar-chart-rate100 {
.stacked-bar-chart-base();
background: transparent;
border-left: 1px solid fade(@stacked-bar-chart, 90%);
padding: 0.4rem 0;
width: 1px;
}

View file

@ -134,13 +134,11 @@ custom-select{% if rtl %}-rtl{% endif %}
{%- endmacro %} {%- endmacro %}
{% macro support_toggle(supports) -%} {% macro support_toggle(supports) -%}
{%- if supports -%} {%- if supports == '?' -%}
<span class="label label-success"> <span class="glyphicon glyphicon-exclamation-sign" aria-hidden="true" title="{{- _('broken') -}}"></span>{{- "" -}}
{{- _("supported") -}} {%- elif supports -%}
</span> <span class="glyphicon glyphicon-ok" aria-hidden="true" title="{{- _('supported') -}}"></span>{{- "" -}}
{%- else -%} {%- else -%}
<span class="label label-danger"> <span aria-hidden="true" title="{{- _('not supported') -}}"></span>{{- "" -}}
{{- _("not supported") -}}
</span>
{%- endif -%} {%- endif -%}
{%- endmacro %} {%- endmacro %}

View file

@ -1,16 +1,92 @@
{% from 'oscar/macros.html' import preferences_item_header, preferences_item_header_rtl, preferences_item_footer, preferences_item_footer_rtl, checkbox_toggle, support_toggle, custom_select_class %} {% from 'oscar/macros.html' import preferences_item_header, preferences_item_header_rtl, preferences_item_footer, preferences_item_footer_rtl, checkbox_toggle, support_toggle, custom_select_class %}
{% extends "oscar/base.html" %} {% extends "oscar/base.html" %}
{% macro engine_about(search_engine, id) -%} {%- macro engine_about(search_engine, id) -%}
{% if search_engine.about is defined %} {% if search_engine.about is defined or stats[search_engine.name]['result_count'] > 0 %}
{% set about = search_engine.about %} {% set about = search_engine.about %}
<div class="engine-tooltip" role="tooltip" id="{{ id }}">{{- "" -}} <div class="engine-tooltip" role="tooltip" id="{{ id }}">{{- "" -}}
<h5><a href="{{about.website}}" rel="noreferrer">{{about.website}}</a></h5> {% if search_engine.about is defined %}
{%- if about.wikidata_id -%}<p><a href="https://www.wikidata.org/wiki/{{about.wikidata_id}}" rel="noreferrer">wikidata.org/wiki/{{about.wikidata_id}}</a></p>{%- endif -%} <h5><a href="{{about.website}}" rel="noreferrer">{{about.website}}</a></h5>
{%- if about.wikidata_id -%}<p><a href="https://www.wikidata.org/wiki/{{about.wikidata_id}}" rel="noreferrer">wikidata.org/wiki/{{about.wikidata_id}}</a></p>{%- endif -%}
{% endif %}
{%- if search_engine.enable_http %}<p>{{ icon('exclamation-sign', 'No HTTPS') }}{{ _('No HTTPS')}}</p>{% endif -%} {%- if search_engine.enable_http %}<p>{{ icon('exclamation-sign', 'No HTTPS') }}{{ _('No HTTPS')}}</p>{% endif -%}
{%- if stats[search_engine.name]['result_count'] -%}
<p>{{ _('Number of results') }}: {{ stats[search_engine.name]['result_count'] }} ( {{ _('Avg.') }} )</p>{{- "" -}}
{%- endif -%}
</div> </div>
{%- endif -%} {%- endif -%}
{%- endmacro %} {%- endmacro %}
{% block title %}{{ _('preferences') }} - {% endblock %}
{%- macro engine_time(engine_name, css_align_class) -%}
<td class="{{ css_align_class }} {{ 'danger' if stats[engine_name]['warn_time'] else '' }}">
{%- if stats[engine_name].time != None -%}
<span aria-labelledby="{{engine_name}}_time">
{%- if stats[engine_name]['warn_time'] -%}
{{icon('exclamation-sign')}}
{%- endif -%}
{{- stats[engine_name].time -}}
</span>{{- "" -}}
<div class="engine-tooltip text-left" role="tooltip" id="{{engine_name}}_time">{{- "" -}}
<p>{{ _('Median') }}: {{ stats[engine_name].time }}</p>{{- "" -}}
<p>{{ _('P80') }}: {{ stats[engine_name].rate80 }}</p>{{- "" -}}
<p>{{ _('P95') }}: {{ stats[engine_name].rate95 }}</p>{{- "" -}}
</div>
{%- endif -%}
</td>
{%- endmacro -%}
{%- macro engine_time(engine_name, css_align_class) -%}
<td class="{{ label }}" style="padding: 2px">{{- "" -}}
{%- if stats[engine_name].time != None -%}
<span class="stacked-bar-chart-value">{{- stats[engine_name].time -}}</span>{{- "" -}}
<span class="stacked-bar-chart" aria-labelledby="{{engine_name}}_chart" aria-hidden="true">{{- "" -}}
<span style="width: calc(max(2px, 100%*{{ (stats[engine_name].time / max_rate95)|round(3) }}))" class="stacked-bar-chart-median"></span>{{- "" -}}
<span style="width: calc(100%*{{ ((stats[engine_name].rate80 - stats[engine_name].time) / max_rate95)|round(3) }})" class="stacked-bar-chart-rate80"></span>{{- "" -}}
<span style="width: calc(100%*{{ ((stats[engine_name].rate95 - stats[engine_name].rate80) / max_rate95)|round(3) }})" class="stacked-bar-chart-rate95"></span>{{- "" -}}
<span class="stacked-bar-chart-rate100"></span>{{- "" -}}
</span>{{- "" -}}
<div class="engine-tooltip text-left" role="tooltip" id="{{engine_name}}_graph">{{- "" -}}
<p>{{ _('Median') }}: {{ stats[engine_name].time }}</p>{{- "" -}}
<p>{{ _('P80') }}: {{ stats[engine_name].rate80 }}</p>{{- "" -}}
<p>{{ _('P95') }}: {{ stats[engine_name].rate95 }}</p>{{- "" -}}
</div>
{%- endif -%}
</td>
{%- endmacro -%}
{%- macro engine_reliability(engine_name, css_align_class) -%}
{% set r = reliabilities.get(engine_name, {}).get('reliablity', None) %}
{% set checker_result = reliabilities.get(engine_name, {}).get('checker', []) %}
{% set errors = reliabilities.get(engine_name, {}).get('errors', []) %}
{% if r != None %}
{% if r <= 50 %}{% set label = 'danger' %}
{% elif r < 80 %}{% set label = 'warning' %}
{% elif r < 90 %}{% set label = 'default' %}
{% else %}{% set label = 'success' %}
{% endif %}
{% else %}
{% set r = '' %}
{% endif %}
{% if checker_result or errors %}
<td class="{{ css_align_class }} {{ label }}">{{- "" -}}
<span aria-labelledby="{{engine_name}}_reliablity">
{%- if reliabilities[engine_name].checker %}{{ icon('exclamation-sign', 'The checker fails on the some tests') }}{% endif %} {{ r -}}
</span>{{- "" -}}
<div class="engine-tooltip text-left" role="tooltip" id="{{engine_name}}_reliablity">
{%- if checker_result -%}
<p>{{ _("Failed checker test(s): ") }} {{ ', '.join(checker_result) }}</p>
{%- endif -%}
{%- for error in errors -%}
<p>{{ error }} </p>{{- "" -}}
{%- endfor -%}
</div>{{- "" -}}
</td>
{%- else -%}
<td class="{{ css_align_class }} {{ label }}"><span>{{ r }}</span></td>
{%- endif -%}
{%- endmacro -%}
{%- block title %}{{ _('preferences') }} - {% endblock -%}
{% block content %} {% block content %}
<div> <div>
@ -182,7 +258,6 @@
</fieldset> </fieldset>
</div> </div>
<div class="tab-pane active_if_nojs" id="tab_engine"> <div class="tab-pane active_if_nojs" id="tab_engine">
<!-- Nav tabs --> <!-- Nav tabs -->
<ul class="nav nav-tabs nav-justified hide_if_nojs" role="tablist"> <ul class="nav nav-tabs nav-justified hide_if_nojs" role="tablist">
{% for categ in all_categories %} {% for categ in all_categories %}
@ -217,14 +292,16 @@
<th scope="col">{{ _("Allow") }}</th> <th scope="col">{{ _("Allow") }}</th>
<th scope="col">{{ _("Engine name") }}</th> <th scope="col">{{ _("Engine name") }}</th>
<th scope="col">{{ _("Shortcut") }}</th> <th scope="col">{{ _("Shortcut") }}</th>
<th scope="col">{{ _("Selected language") }}</th> <th scope="col" style="width: 10rem">{{ _("Selected language") }}</th>
<th scope="col">{{ _("SafeSearch") }}</th> <th scope="col" style="width: 10rem">{{ _("SafeSearch") }}</th>
<th scope="col">{{ _("Time range") }}</th> <th scope="col" style="width: 10rem">{{ _("Time range") }}</th>
<th scope="col">{{ _("Avg. time") }}</th> <th scope="col">{{ _("Response time") }}</th>
<th scope="col">{{ _("Max time") }}</th> <th scope="col" class="text-right" style="width: 7rem">{{ _("Max time") }}</th>
<th scope="col" class="text-right" style="width: 7rem">{{ _("Reliablity") }}</th>
{% else %} {% else %}
<th scope="col" class="text-right">{{ _("Max time") }}</th> <th scope="col">{{ _("Reliablity") }}</th>
<th scope="col" class="text-right">{{ _("Avg. time") }}</th> <th scope="col">{{ _("Max time") }}</th>
<th scope="col" class="text-right">{{ _("Response time") }}</th>
<th scope="col" class="text-right">{{ _("Time range") }}</th> <th scope="col" class="text-right">{{ _("Time range") }}</th>
<th scope="col" class="text-right">{{ _("SafeSearch") }}</th> <th scope="col" class="text-right">{{ _("SafeSearch") }}</th>
<th scope="col" class="text-right">{{ _("Selected language") }}</th> <th scope="col" class="text-right">{{ _("Selected language") }}</th>
@ -246,17 +323,19 @@
{{- engine_about(search_engine, 'tooltip_' + categ + '_' + search_engine.name) -}} {{- engine_about(search_engine, 'tooltip_' + categ + '_' + search_engine.name) -}}
</th> </th>
<td class="name">{{ shortcuts[search_engine.name] }}</td> <td class="name">{{ shortcuts[search_engine.name] }}</td>
<td>{{ support_toggle(stats[search_engine.name].supports_selected_language) }}</td> <td>{{ support_toggle(supports[search_engine.name]['supports_selected_language']) }}</td>
<td>{{ support_toggle(search_engine.safesearch==True) }}</td> <td>{{ support_toggle(supports[search_engine.name]['safesearch']) }}</td>
<td>{{ support_toggle(search_engine.time_range_support==True) }}</td> <td>{{ support_toggle(supports[search_engine.name]['time_range_support']) }}</td>
<td class="{{ 'danger' if stats[search_engine.name]['warn_time'] else '' }}">{% if stats[search_engine.name]['warn_time'] %}{{ icon('exclamation-sign')}} {% endif %}{{ 'N/A' if stats[search_engine.name].time==None else stats[search_engine.name].time }}</td> {{ engine_time(search_engine.name, 'text-right') }}
<td class="{{ 'danger' if stats[search_engine.name]['warn_timeout'] else '' }}">{% if stats[search_engine.name]['warn_timeout'] %}{{ icon('exclamation-sign') }} {% endif %}{{ search_engine.timeout }}</td> <td class="text-right {{ 'danger' if stats[search_engine.name]['warn_timeout'] else '' }}">{% if stats[search_engine.name]['warn_timeout'] %}{{ icon('exclamation-sign') }} {% endif %}{{ search_engine.timeout }}</td>
{{ engine_reliability(search_engine.name, 'text-right ') }}
{% else %} {% else %}
<td class="{{ 'danger' if stats[search_engine.name]['warn_timeout'] else '' }}">{{ search_engine.timeout }}{% if stats[search_engine.name]['warn_time'] %} {{ icon('exclamation-sign')}}{% endif %}</td> {{ engine_reliability(search_engine.name, 'text-left') }}
<td class="{{ 'danger' if stats[search_engine.name]['warn_time'] else '' }}">{{ 'N/A' if stats[search_engine.name].time==None else stats[search_engine.name].time }}{% if stats[search_engine.name]['warn_time'] %} {{ icon('exclamation-sign')}}{% endif %}</td> <td class="text-left {{ 'danger' if stats[search_engine.name]['warn_timeout'] else '' }}">{{ search_engine.timeout }}{% if stats[search_engine.name]['warn_time'] %} {{ icon('exclamation-sign')}}{% endif %}</td>
<td>{{ support_toggle(search_engine.time_range_support==True) }}</td> {{ engine_time(search_engine.name, 'text-left') }}
<td>{{ support_toggle(search_engine.safesearch==True) }}</td> <td>{{ support_toggle(supports[search_engine.name]['time_range_support']) }}</td>
<td>{{ support_toggle(stats[search_engine.name].supports_selected_language) }}</td> <td>{{ support_toggle(supports[search_engine.name]['safesearch']) }}</td>
<td>{{ support_toggle(supports[search_engine.name]['supports_selected_language']) }}</td>
<td>{{ shortcuts[search_engine.name] }}</td> <td>{{ shortcuts[search_engine.name] }}</td>
<th scope="row"><span>{% if search_engine.enable_http %}{{ icon('exclamation-sign', 'No HTTPS') }}{% endif %}{{ search_engine.name }}</span>{{ engine_about(search_engine) }}</th> <th scope="row"><span>{% if search_engine.enable_http %}{{ icon('exclamation-sign', 'No HTTPS') }}{% endif %}{{ search_engine.name }}</span>{{ engine_about(search_engine) }}</th>
<td class="onoff-checkbox"> <td class="onoff-checkbox">

View file

@ -1,4 +1,16 @@
{% extends "oscar/base.html" %} {% extends "oscar/base.html" %}
{% block styles %}
<link rel="stylesheet" href="{{ url_for('static', filename='css/charts.min.css') }}" type="text/css" />
<style>
#engine-times {
--labels-size: 20rem;
}
#engine-times th {
text-align: right;
}
</style>
{% endblock %}
{% block title %}{{ _('stats') }} - {% endblock %} {% block title %}{{ _('stats') }} - {% endblock %}
{% block content %} {% block content %}
<div class="container-fluid"> <div class="container-fluid">

View file

@ -79,7 +79,11 @@
{%- macro checkbox(name, checked, readonly, disabled) -%} {%- macro checkbox(name, checked, readonly, disabled) -%}
<div class="checkbox">{{- '' -}} <div class="checkbox">{{- '' -}}
<input type="checkbox" value="None" id="{{ name }}" name="{{ name }}" {% if checked %}checked{% endif %}{% if readonly %} readonly="readonly" {% endif %}{% if disabled %} disabled="disabled" {% endif %}/>{{- '' -}} {%- if checked == '?' -%}
<label for="{{ name }}"></label>{{- '' -}} {{ icon_small('warning') }}
{%- else -%}
<input type="checkbox" value="None" id="{{ name }}" name="{{ name }}" {% if checked %}checked{% endif %}{% if readonly %} readonly="readonly" {% endif %}{% if disabled %} disabled="disabled" {% endif %}/>{{- '' -}}
<label for="{{ name }}"></label>{{- '' -}}
{%- endif -%}
</div> </div>
{%- endmacro -%} {%- endmacro -%}

View file

@ -29,6 +29,58 @@
{%- endif -%} {%- endif -%}
{%- endmacro %} {%- endmacro %}
{%- macro engine_time(engine_name) -%}
<td class="{{ label }}" style="padding: 2px; width: 13rem;">{{- "" -}}
{%- if stats[engine_name].time != None -%}
<span class="stacked-bar-chart-value">{{- stats[engine_name].time -}}</span>{{- "" -}}
<span class="stacked-bar-chart" aria-labelledby="{{engine_name}}_chart" aria-hidden="true">{{- "" -}}
<span style="width: calc(max(2px, 100%*{{ (stats[engine_name].time / max_rate95)|round(3) }}))" class="stacked-bar-chart-median"></span>{{- "" -}}
<span style="width: calc(100%*{{ ((stats[engine_name].rate80 - stats[engine_name].time) / max_rate95)|round(3) }})" class="stacked-bar-chart-rate80"></span>{{- "" -}}
<span style="width: calc(100%*{{ ((stats[engine_name].rate95 - stats[engine_name].rate80) / max_rate95)|round(3) }})" class="stacked-bar-chart-rate95"></span>{{- "" -}}
<span class="stacked-bar-chart-rate100"></span>{{- "" -}}
</span>{{- "" -}}
<div class="engine-tooltip text-left" role="tooltip" id="{{engine_name}}_graph">{{- "" -}}
<p>{{ _('Median') }}: {{ stats[engine_name].time }}</p>{{- "" -}}
<p>{{ _('P80') }}: {{ stats[engine_name].rate80 }}</p>{{- "" -}}
<p>{{ _('P95') }}: {{ stats[engine_name].rate95 }}</p>{{- "" -}}
</div>
{%- endif -%}
</td>
{%- endmacro -%}
{%- macro engine_reliability(engine_name) -%}
{% set r = reliabilities.get(engine_name, {}).get('reliablity', None) %}
{% set checker_result = reliabilities.get(engine_name, {}).get('checker', []) %}
{% set errors = reliabilities.get(engine_name, {}).get('errors', []) %}
{% if r != None %}
{% if r <= 50 %}{% set label = 'danger' %}
{% elif r < 80 %}{% set label = 'warning' %}
{% elif r < 90 %}{% set label = '' %}
{% else %}{% set label = 'success' %}
{% endif %}
{% else %}
{% set r = '' %}
{% endif %}
{% if checker_result or errors %}
<td class="{{ label }}">{{- "" -}}
<span aria-labelledby="{{engine_name}}_reliablity">
{%- if reliabilities[engine_name].checker %}{{ icon('warning', 'The checker fails on the some tests') }}{% endif %} {{ r -}}
</span>{{- "" -}}
<div class="engine-tooltip" style="right: 12rem;" role="tooltip" id="{{engine_name}}_reliablity">
{%- if checker_result -%}
<p>{{ _("The checker fails on this tests: ") }} {{ ', '.join(checker_result) }}</p>
{%- endif -%}
{%- if errors %}<p>{{ _('Errors:') }}</p>{% endif -%}
{%- for error in errors -%}
<p>{{ error }} </p>{{- "" -}}
{%- endfor -%}
</div>{{- "" -}}
</td>
{%- else -%}
<td class="{{ css_align_class }} {{ label }}"><span>{{ r }}</span></td>
{%- endif -%}
{%- endmacro -%}
{% block head %} {% endblock %} {% block head %} {% endblock %}
{% block content %} {% block content %}
@ -123,8 +175,9 @@
<th>{{ _("Supports selected language") }}</th> <th>{{ _("Supports selected language") }}</th>
<th>{{ _("SafeSearch") }}</th> <th>{{ _("SafeSearch") }}</th>
<th>{{ _("Time range") }}</th> <th>{{ _("Time range") }}</th>
<th>{{ _("Avg. time") }}</th> <th>{{ _("Response time") }}</th>
<th>{{ _("Max time") }}</th> <th>{{ _("Max time") }}</th>
<th>{{ _("Reliablity") }}</th>
</tr> </tr>
{% for search_engine in engines_by_category[categ] %} {% for search_engine in engines_by_category[categ] %}
@ -134,11 +187,12 @@
<td class="engine_checkbox">{{ checkbox_onoff(engine_id, (search_engine.name, categ) in disabled_engines) }}</td> <td class="engine_checkbox">{{ checkbox_onoff(engine_id, (search_engine.name, categ) in disabled_engines) }}</td>
<th class="name">{% if search_engine.enable_http %}{{ icon('warning', 'No HTTPS') }}{% endif %} {{ search_engine.name }} {{ engine_about(search_engine) }}</th> <th class="name">{% if search_engine.enable_http %}{{ icon('warning', 'No HTTPS') }}{% endif %} {{ search_engine.name }} {{ engine_about(search_engine) }}</th>
<td class="shortcut">{{ shortcuts[search_engine.name] }}</td> <td class="shortcut">{{ shortcuts[search_engine.name] }}</td>
<td>{{ checkbox(engine_id + '_supported_languages', current_language == 'all' or current_language in search_engine.supported_languages or current_language.split('-')[0] in search_engine.supported_languages, true, true) }}</td> <td>{{ checkbox(engine_id + '_supported_languages', supports[search_engine.name]['supports_selected_language'], true, true) }}</td>
<td>{{ checkbox(engine_id + '_safesearch', search_engine.safesearch==True, true, true) }}</td> <td>{{ checkbox(engine_id + '_safesearch', supports[search_engine.name]['safesearch'], true, true) }}</td>
<td>{{ checkbox(engine_id + '_time_range_support', search_engine.time_range_support==True, true, true) }}</td> <td>{{ checkbox(engine_id + '_time_range_support', supports[search_engine.name]['time_range_support'], true, true) }}</td>
<td class="{{ 'danger' if stats[search_engine.name]['warn_time'] else '' }}">{{ 'N/A' if stats[search_engine.name].time==None else stats[search_engine.name].time }}</td> {{ engine_time(search_engine.name) }}
<td class="{{ 'danger' if stats[search_engine.name]['warn_timeout'] else '' }}">{{ search_engine.timeout }}</td> <td class="{{ 'danger' if stats[search_engine.name]['warn_timeout'] else '' }}">{{ search_engine.timeout }}</td>
{{ engine_reliability(search_engine.name) }}
</tr> </tr>
{% endif %} {% endif %}
{% endfor %} {% endfor %}

View file

@ -93,7 +93,7 @@ from searx.preferences import Preferences, ValidationException, LANGUAGE_CODES
from searx.answerers import answerers from searx.answerers import answerers
from searx.network import stream as http_stream from searx.network import stream as http_stream
from searx.answerers import ask from searx.answerers import ask
from searx.metrics import get_engines_stats, get_engine_errors, histogram from searx.metrics import get_engines_stats, get_engine_errors, histogram, counter
# serve pages with HTTP/1.1 # serve pages with HTTP/1.1
from werkzeug.serving import WSGIRequestHandler from werkzeug.serving import WSGIRequestHandler
@ -170,6 +170,31 @@ _category_names = (gettext('files'),
gettext('onions'), gettext('onions'),
gettext('science')) gettext('science'))
#
exception_classname_to_label = {
"searx.exceptions.SearxEngineCaptchaException": gettext("CAPTCHA"),
"searx.exceptions.SearxEngineTooManyRequestsException": gettext("too many requests"),
"searx.exceptions.SearxEngineAccessDeniedException": gettext("access denied"),
"searx.exceptions.SearxEngineAPIException": gettext("server API error"),
"httpx.TimeoutException": gettext("HTTP timeout"),
"httpx.ConnectTimeout": gettext("HTTP timeout"),
"httpx.ReadTimeout": gettext("HTTP timeout"),
"httpx.WriteTimeout": gettext("HTTP timeout"),
"httpx.HTTPStatusError": gettext("HTTP error"),
"httpx.ConnectError": gettext("HTTP connection error"),
"httpx.RemoteProtocolError": gettext("HTTP protocol error"),
"httpx.LocalProtocolError": gettext("HTTP protocol error"),
"httpx.ProtocolError": gettext("HTTP protocol error"),
"httpx.ReadError": gettext("network error"),
"httpx.WriteError": gettext("network error"),
"httpx.ProxyError": gettext("proxy error"),
"searx.exceptions.SearxEngineXPathException": gettext("parsing error"),
"KeyError": gettext("parsing error"),
"json.decoder.JSONDecodeError": gettext("parsing error"),
"lxml.etree.ParserError": gettext("parsing error"),
None: gettext("unexpected crash"),
}
_flask_babel_get_translations = flask_babel.get_translations _flask_babel_get_translations = flask_babel.get_translations
@ -855,26 +880,101 @@ def preferences():
engines_by_category = {} engines_by_category = {}
for c in categories: for c in categories:
engines_by_category[c] = [e for e in categories[c] if e.name in filtered_engines] engines_by_category[c] = [e for e in categories[c] if e.name in filtered_engines]
# sort the engines alphabetically since the order in settings.yml is meaningless.
list.sort(engines_by_category[c], key=lambda e: e.name)
# get first element [0], the engine time, # get first element [0], the engine time,
# and then the second element [1] : the time (the first one is the label) # and then the second element [1] : the time (the first one is the label)
stats = {} stats = {}
max_rate95 = 0
for _, e in filtered_engines.items(): for _, e in filtered_engines.items():
h = histogram('engine', e.name, 'time', 'total') h = histogram('engine', e.name, 'time', 'total')
median = round(h.percentage(50), 1) if h.count > 0 else None median = round(h.percentage(50), 1) if h.count > 0 else None
rate80 = round(h.percentage(80), 1) if h.count > 0 else None
rate95 = round(h.percentage(95), 1) if h.count > 0 else None
max_rate95 = max(max_rate95, rate95 or 0)
result_count_sum = histogram('engine', e.name, 'result', 'count').sum
successful_count = counter('engine', e.name, 'search', 'count', 'successful')
result_count = int(result_count_sum / float(successful_count)) if successful_count else 0
stats[e.name] = { stats[e.name] = {
'time': median if median else None, 'time': median if median else None,
'warn_time': median is not None and median > settings['outgoing']['request_timeout'], 'rate80': rate80 if rate80 else None,
'rate95': rate95 if rate95 else None,
'warn_timeout': e.timeout > settings['outgoing']['request_timeout'], 'warn_timeout': e.timeout > settings['outgoing']['request_timeout'],
'supports_selected_language': _is_selected_language_supported(e, request.preferences) 'supports_selected_language': _is_selected_language_supported(e, request.preferences),
'result_count': result_count,
} }
# end of stats # end of stats
# reliabilities
reliabilities = {}
engine_errors = get_engine_errors(filtered_engines)
checker_results = checker_get_result()
checker_results = checker_results['engines'] \
if checker_results['status'] == 'ok' and 'engines' in checker_results else {}
for _, e in filtered_engines.items():
checker_result = checker_results.get(e.name, {})
checker_success = checker_result.get('success', True)
errors = engine_errors.get(e.name) or []
if counter('engine', e.name, 'search', 'count', 'sent') == 0:
# no request
reliablity = None
elif checker_success and not errors:
reliablity = 100
elif 'simple' in checker_result.get('errors', {}):
# the basic (simple) test doesn't work: the engine is broken accoding to the checker
# even if there is no exception
reliablity = 0
else:
reliablity = 100 - sum([error['percentage'] for error in errors if not error.get('secondary')])
reliabilities[e.name] = {
'reliablity': reliablity,
'errors': [],
'checker': checker_results.get(e.name, {}).get('errors', {}).keys(),
}
# keep the order of the list checker_results[e.name]['errors'] and deduplicate.
# the first element has the highest percentage rate.
reliabilities_errors = []
for error in errors:
error_user_message = None
if error.get('secondary') or 'exception_classname' not in error:
continue
error_user_message = exception_classname_to_label.get(error.get('exception_classname'))
if not error:
error_user_message = exception_classname_to_label[None]
if error_user_message not in reliabilities_errors:
reliabilities_errors.append(error_user_message)
reliabilities[e.name]['errors'] = reliabilities_errors
# supports
supports = {}
for _, e in filtered_engines.items():
supports_selected_language = _is_selected_language_supported(e, request.preferences)
safesearch = e.safesearch
time_range_support = e.time_range_support
for checker_test_name in checker_results.get(e.name, {}).get('errors', {}):
if supports_selected_language and checker_test_name.startswith('lang_'):
supports_selected_language = '?'
elif safesearch and checker_test_name == 'safesearch':
safesearch = '?'
elif time_range_support and checker_test_name == 'time_range':
time_range_support = '?'
supports[e.name] = {
'supports_selected_language': supports_selected_language,
'safesearch': safesearch,
'time_range_support': time_range_support,
}
#
locked_preferences = list() locked_preferences = list()
if 'preferences' in settings and 'lock' in settings['preferences']: if 'preferences' in settings and 'lock' in settings['preferences']:
locked_preferences = settings['preferences']['lock'] locked_preferences = settings['preferences']['lock']
#
return render('preferences.html', return render('preferences.html',
selected_categories=get_selected_categories(request.preferences, request.form), selected_categories=get_selected_categories(request.preferences, request.form),
all_categories=_get_ordered_categories(), all_categories=_get_ordered_categories(),
@ -883,6 +983,9 @@ def preferences():
image_proxy=image_proxy, image_proxy=image_proxy,
engines_by_category=engines_by_category, engines_by_category=engines_by_category,
stats=stats, stats=stats,
max_rate95=max_rate95,
reliabilities=reliabilities,
supports=supports,
answerers=[{'info': a.self_info(), 'keywords': a.keywords} for a in answerers], answerers=[{'info': a.self_info(), 'keywords': a.keywords} for a in answerers],
disabled_engines=disabled_engines, disabled_engines=disabled_engines,
autocomplete_backends=autocomplete_backends, autocomplete_backends=autocomplete_backends,