commit 2e474bd7e77d67708c91fce9257ee39cff828082 Author: Karsten Loesing karsten.loesing@gmx.net Date: Tue Sep 9 10:23:48 2014 +0200
Allow multiple transports to be displayed at once.
Implements a feature requested by asn in #12432. --- website/rserve/graphs.R | 91 +++++++++++++++----- .../metrics/web/graphs/GraphParameterChecker.java | 8 +- website/web/WEB-INF/users.jsp | 21 +++-- 3 files changed, 87 insertions(+), 33 deletions(-)
diff --git a/website/rserve/graphs.R b/website/rserve/graphs.R index 68a8b7d..d546127 100644 --- a/website/rserve/graphs.R +++ b/website/rserve/graphs.R @@ -795,19 +795,56 @@ plot_userstats <- function(start, end, node, variable, value, events, u$node == 'relay', ] title <- "Directly connecting users\n" } + u <- aggregate(list(lower = u$lower, upper = u$upper, + users = u$clients), + by = list(date = as.Date(u$date, "%Y-%m-%d"), + value = u$country), + FUN = sum) } else if (variable == 'transport') { - if (value != '!<*>') { - u <- u[u$transport == value & u$node == 'bridge', ] - title <- paste("Bridge users using transport ", value, "\n", - sep = "") + if ('!<OR>' %in% value) { + n <- u[u$transport != '' & u$transport != '<OR>' & + u$node == 'bridge', ] + n <- aggregate(list(lower = n$lower, upper = n$upper, + clients = n$clients), + by = list(date = n$date), + FUN = sum) + u <- rbind(u, data.frame(date = n$date, node = 'bridge', + country = '', transport = '!<OR>', + version = '', lower = n$lower, + upper = n$upper, clients = n$clients, + frac = NA)) + } + if (length(value) > 1) { + u <- u[u$transport %in% value & u$node == 'bridge', ] + u <- aggregate(list(lower = u$lower, upper = u$upper, + users = u$clients), + by = list(date = as.Date(u$date, "%Y-%m-%d"), + value = u$transport), + FUN = sum) + title <- paste("Bridge users by transport\n") } else { - u <- u[u$transport != '' & u$transport != '<OR>' & - u$node == 'bridge', ] - title <- paste("Bridge users using any pluggable transport\n") + u <- u[u$transport == value & u$node == 'bridge', ] + u <- aggregate(list(lower = u$lower, upper = u$upper, + users = u$clients), + by = list(date = as.Date(u$date, "%Y-%m-%d"), + value = u$transport), + FUN = sum) + title <- paste("Bridge users using ", + ifelse(value == '<??>', 'unknown pluggable transport(s)', + ifelse(value == '<OR>', 'default OR protocol', + ifelse(value == '!<OR>', 'any pluggable transport', + ifelse(value == 'fte', 'FTE', + ifelse(value == 'websocket', 'Flash proxy/websocket', + paste('transport', value)))))), "\n", sep = "") } } else if (variable == 'version') { - u <- u[u$version== value & u$node == 'bridge', ] + u <- u[u$version == value & u$node == 'bridge', ] title <- paste("Bridge users using IP", value, "\n", sep = "") + u <- aggregate(list(lower = u$lower, upper = u$upper, + users = u$clients), + by = list(date = as.Date(u$date, "%Y-%m-%d"), + value = u$version), + FUN = sum) } else { if (value != 'all') { u <- u[u$country == value & u$node == 'bridge', ] @@ -818,26 +855,28 @@ plot_userstats <- function(start, end, node, variable, value, events, u$node == 'bridge', ] title <- "Bridge users\n" } + u <- aggregate(list(lower = u$lower, upper = u$upper, + users = u$clients), + by = list(date = as.Date(u$date, "%Y-%m-%d"), + value = u$country), + FUN = sum) } - u <- aggregate(list(lower = u$lower, upper = u$upper, - users = u$clients), - by = list(date = as.Date(u$date, "%Y-%m-%d")), FUN = sum) - dates <- seq(from = as.Date(start, "%Y-%m-%d"), - to = as.Date(end, "%Y-%m-%d"), by="1 day") - missing <- setdiff(dates, u$date) - if (length(missing) > 0) { - u <- rbind(u, - data.frame(date = as.Date(missing, origin = "1970-01-01"), - users = NA, lower = NA, upper = NA)) - } + u <- merge(x = u, all.y = TRUE, y = data.frame(expand.grid( + date = seq(from = as.Date(start, "%Y-%m-%d"), + to = as.Date(end, "%Y-%m-%d"), by="1 day"), + value = ifelse(value == 'all', '', value)))) formatter <- function(x, ...) { format(x, scientific = FALSE, ...) } date_breaks <- date_breaks( as.numeric(max(u$date) - min(u$date))) max_y <- ifelse(length(na.omit(u$users)) == 0, 0, max(u$users, na.rm = TRUE)) - plot <- ggplot(u, aes(x = date, y = users)) + if (length(value) > 1) { + plot <- ggplot(u, aes(x = date, y = users, colour = value)) + } else { + plot <- ggplot(u, aes(x = date, y = users)) + } if (length(na.omit(u$users)) > 0 & events != "off" & - variable == 'country' & value != "all") { + variable == 'country' & length(value) == 1 && value != "all") { upturns <- u[u$users > u$upper, c("date", "users")] downturns <- u[u$users <= u$lower, c("date", "users")] if (events == "on") { @@ -865,6 +904,16 @@ plot_userstats <- function(start, end, node, variable, value, events, scale_y_continuous(name = "", limits = c(0, max_y), formatter = formatter) + opts(title = title) + if (length(value) > 1) { + plot <- plot + + scale_colour_hue(name = "", breaks = value, + labels = ifelse(value == '<??>', 'Unknown PT', + ifelse(value == '<OR>', 'Default OR protocol', + ifelse(value == '!<OR>', 'Any PT', + ifelse(value == 'fte', 'FTE', + ifelse(value == 'websocket', 'Flash proxy/websocket', + value)))))) + } ggsave(filename = path, width = 8, height = 5, dpi = 72) }
diff --git a/website/src/org/torproject/metrics/web/graphs/GraphParameterChecker.java b/website/src/org/torproject/metrics/web/graphs/GraphParameterChecker.java index ab77943..223a289 100644 --- a/website/src/org/torproject/metrics/web/graphs/GraphParameterChecker.java +++ b/website/src/org/torproject/metrics/web/graphs/GraphParameterChecker.java @@ -59,7 +59,7 @@ public class GraphParameterChecker { this.knownParameterValues.put("source", "all,siv,moria,torperf"); this.knownParameterValues.put("filesize", "50kb,1mb,5mb"); this.knownParameterValues.put("transport", - "obfs2,obfs3,websocket,fte,meek,scramblesuit,<OR>,<??>,!<*>"); + "obfs2,obfs3,websocket,fte,meek,scramblesuit,<OR>,<??>,!<OR>"); this.knownParameterValues.put("version", "v4,v6"); this.knownParameterValues.put("p", "100,99,98,97,95,91,90,80,75,70," + "60,50,40,30,25,20,10,9,5,3,2,1,0"); @@ -161,6 +161,9 @@ public class GraphParameterChecker { List<String> knownCountries = Arrays.asList( this.knownParameterValues.get("country").split(",")); if (countryParameters != null) { + if (countryParameters.length != 1) { + return null; + } for (String country : countryParameters) { if (country == null || country.length() == 0 || !knownCountries.contains(country)) { @@ -265,6 +268,9 @@ public class GraphParameterChecker { List<String> knownVersions = Arrays.asList( this.knownParameterValues.get("version").split(",")); if (versionParameters != null) { + if (versionParameters.length != 1) { + return null; + } for (String version : versionParameters) { if (version == null || version.length() == 0 || !knownVersions.contains(version)) { diff --git a/website/web/WEB-INF/users.jsp b/website/web/WEB-INF/users.jsp index 400fd31..04eeaf9 100644 --- a/website/web/WEB-INF/users.jsp +++ b/website/web/WEB-INF/users.jsp @@ -232,17 +232,16 @@ Bridge users by transport: <input type="text" name="end" size="10" value="<c:choose><c:when test="${fn:length(userstats_bridge_transport_end) == 0}">${default_end_date}</c:when><c:otherwise>${userstats_bridge_transport_end[0]}</c:otherwise></c:choose>"> </p><p> - Source: <select name="transport"> - <option value="<OR>"<c:if test="${userstats_bridge_transport_transport[0] eq '<OR>'}"> selected</c:if>>Default OR protocol</option> - <option value="obfs2"<c:if test="${userstats_bridge_transport_transport[0] eq 'obfs2'}"> selected</c:if>>obfs2</option> - <option value="obfs3"<c:if test="${userstats_bridge_transport_transport[0] eq 'obfs3'}"> selected</c:if>>obfs3</option> - <option value="websocket"<c:if test="${userstats_bridge_transport_transport[0] eq 'websocket'}"> selected</c:if>>Flash proxy/websocket</option> - <option value="fte"<c:if test="${userstats_bridge_transport_transport[0] eq 'fte'}"> selected</c:if>>FTE</option> - <option value="meek"<c:if test="${userstats_bridge_transport_transport[0] eq 'meek'}"> selected</c:if>>meek</option> - <option value="scramblesuit"<c:if test="${userstats_bridge_transport_transport[0] eq 'scramblesuit'}"> selected</c:if>>scramblesuit</option> - <option value="<??>"<c:if test="${userstats_bridge_transport_transport[0] eq '<??>'}"> selected</c:if>>Unknown transport</option> - <option value="!<*>"<c:if test="${userstats_bridge_transport_transport[0] eq '!<*>'}"> selected</c:if>>Any non-OR transport</option> - </select> + <label>Source: </label> + <input type="checkbox" name="transport" value="<OR>"<c:if test="${fn:length(userstats_bridge_transport_transport) == 0 or fn:contains(fn:join(userstats_bridge_transport_transport, ','), '<OR>')}"> checked</c:if>> Default OR protocol + <input type="checkbox" name="transport" value="obfs2"<c:if test="${fn:length(userstats_bridge_transport_transport) > 0 and fn:contains(fn:join(userstats_bridge_transport_transport, ','), 'obfs2')}"> checked</c:if>> obfs2 + <input type="checkbox" name="transport" value="obfs3"<c:if test="${fn:length(userstats_bridge_transport_transport) > 0 and fn:contains(fn:join(userstats_bridge_transport_transport, ','), 'obfs3')}"> checked</c:if>> obfs3 + <input type="checkbox" name="transport" value="websocket"<c:if test="${fn:length(userstats_bridge_transport_transport) > 0 and fn:contains(fn:join(userstats_bridge_transport_transport, ','), 'websocket')}"> checked</c:if>> Flash proxy/websocket + <input type="checkbox" name="transport" value="fte"<c:if test="${fn:length(userstats_bridge_transport_transport) > 0 and fn:contains(fn:join(userstats_bridge_transport_transport, ','), 'fte')}"> checked</c:if>> FTE + <input type="checkbox" name="transport" value="meek"<c:if test="${fn:length(userstats_bridge_transport_transport) > 0 and fn:contains(fn:join(userstats_bridge_transport_transport, ','), 'meek')}"> checked</c:if>> meek + <input type="checkbox" name="transport" value="scramblesuit"<c:if test="${fn:length(userstats_bridge_transport_transport) > 0 and fn:contains(fn:join(userstats_bridge_transport_transport, ','), 'scramblesuit')}"> checked</c:if>> scramblesuit + <input type="checkbox" name="transport" value="<??>"<c:if test="${fn:length(userstats_bridge_transport_transport) > 0 and fn:contains(fn:join(userstats_bridge_transport_transport, ','), '<??>')}"> checked</c:if>> Unknown pluggable transport(s) + <input type="checkbox" name="transport" value="!<OR>"<c:if test="${fn:length(userstats_bridge_transport_transport) > 0 and fn:contains(fn:join(userstats_bridge_transport_transport, ','), '!<OR>')}"> checked</c:if>> Any pluggable transport </p><p> <input class="submit" type="submit" value="Update graph"> </p>
tor-commits@lists.torproject.org