diff --git a/pypistats/plots/data_base.json b/pypistats/plots/data_base.json index 6ae56b4..69f6f12 100644 --- a/pypistats/plots/data_base.json +++ b/pypistats/plots/data_base.json @@ -1,104 +1,72 @@ { - "downloads":{ - "data":[ - { - "x":[ - "2017-05-01", - "2017-05-02", - "2017-05-03" - ], - "y":[ - "2", - "5", - "4" - ], - "name":"Downloads", - "type":"scatter", - "mode":"lines+markers", - "connectgaps":true, - "marker":{ - "symbol":"circle", - "line":{ - "color":"#444", - "width":1 - } - }, - "line":{ - "shape":"linear", - "smoothing":1, - "width":2 - } - } - ] - }, - "percentages":{ - "data":[ - { - "x":[ - "2017-05-01", - "2017-05-02", - "2017-05-03" - ], - "y":[ - "2", - "5", - "4" - ], - "text":[ - "2", - "5", - "4" - ], - "name":"Proportional downloads", - "hoverinfo": "x+text+name", - "type":"scatter", - "mode":"lines+markers", - "connectgaps":true, - "marker":{ - "symbol":"circle", - "line":{ - "color":"#444", - "width":1 - } - }, - "line":{ - "shape":"linear", - "smoothing":1, - "width":2 - } - } - ] - }, - "percentages_fill":{ - "data":[ - { - "x":[ - "2017-05-01", - "2017-05-02", - "2017-05-03" - ], - "y":[ - "2", - "5", - "4" - ], - "text":[ - "2", - "5", - "4" - ], - "hoverinfo": "x+text+name", - "name":"Downloads", - "type":"scatter", - "mode":"lines", - "connectgaps":false, - "line":{ - "shape":"linear", - "smoothing":1, - "width":2 - }, - "fill":"tonexty" - } - ] - } + "downloads": { + "data": [ + { + "x": [ + "2017-05-01", + "2017-05-02", + "2017-05-03" + ], + "y": [ + "2", + "5", + "4" + ], + "name": "Downloads", + "type": "scatter", + "mode": "lines+markers", + "connectgaps": true, + "marker": { + "symbol": "circle", + "line": { + "color": "#444", + "width": 1 + } + }, + "line": { + "shape": "linear", + "smoothing": 1, + "width": 2 + } + } + ] + }, + "percentages": { + "data": [ + { + "x": [ + "2017-05-01", + "2017-05-02", + "2017-05-03" + ], + "y": [ + "2", + "5", + "4" + ], + "text": [ + "2", + "5", + "4" + ], + "name": "Proportional downloads", + "hoverinfo": "x+text+name", + "type": "scatter", + "mode": "lines+markers", + "connectgaps": true, + "marker": { + "symbol": "circle", + "line": { + "color": "#444", + "width": 1 + } + }, + "line": { + "shape": "linear", + "smoothing": 1, + "width": 2 + } + } + ] + } } diff --git a/pypistats/plots/plot_base.json b/pypistats/plots/plot_base.json index 391342b..1103075 100644 --- a/pypistats/plots/plot_base.json +++ b/pypistats/plots/plot_base.json @@ -1,156 +1,212 @@ { - "downloads":{ - "layout":{ - "autosize":true, - "height":400, - "margin":{ - "r":100, - "t":40, - "autoexpand":true, - "b":80, - "l":100, - "pad":0 - }, - "paper_bgcolor":"#fff", - "plot_bgcolor":"rgba(175, 175, 175, 0.2)", - "showlegend":true, - "legend":{ - "orientation":"v", - "bgcolor":"#e7e7e7", - "xanchor":"left", - "yanchor":"middle", - "x":0, - "y":0.5 - }, - "title":"Downloads", - "xaxis":{ - "tickformat":"%m-%d", - "dtick":604800000, - "tick0":"2017-08-07", - "gridcolor":"#FFF", - "gridwidth":2, - "anchor":"y", - "domain":[ - 0, - 1 - ], - "title":"Date", - "titlefont":{ - "family":"'Geneva', Verdana, Geneva, sans-serif", - "size":16, - "color":"#7f7f7f" - }, - "showline":true, - "linecolor":"rgba(148, 148, 148, 1)", - "linewidth":2, - "tickangle":-45 - }, - "yaxis":{ - "hoverformat":",.0", - "tickformat":",.0", - "gridcolor":"#FFF", - "gridwidth":2, - "autotick":true, - "rangemode":"tozero", - "showline":true, - "title":"Downloads", - "ticksuffix":"", - "tickmode":"auto", - "linecolor":"rgba(148, 148, 148, 1)", - "linewidth":2 - } + "downloads": { + "layout": { + "autosize": true, + "height": 400, + "margin": { + "r": 100, + "t": 80, + "autoexpand": true, + "b": 80, + "l": 100, + "pad": 0 + }, + "paper_bgcolor": "#fff", + "plot_bgcolor": "rgba(175, 175, 175, 0.2)", + "showlegend": true, + "legend": { + "orientation": "v", + "bgcolor": "#e7e7e7", + "xanchor": "left", + "yanchor": "middle", + "x": 0, + "y": 0.5 + }, + "title": "Downloads", + "xaxis": { + "tickformat": "%m-%d", + "dtick": 604800000, + "tick0": "2017-08-07", + "gridcolor": "#FFF", + "gridwidth": 2, + "anchor": "y", + "domain": [ + 0, + 1 + ], + "title": "Date", + "titlefont": { + "family": "Geneva, Verdana, Geneva, sans-serif", + "size": 16, + "color": "#7f7f7f" }, - "config":{ - "displaylogo":false, - "modeBarButtonsToRemove":[ - "toImage", - "sendDataToCloud", - "zoom2d", - "pan2d", - "select2d", - "lasso2d", - "zoomIn2d", - "zoomOut2d", - "toggleSpikelines" - ] + "showline": true, + "linecolor": "rgba(148, 148, 148, 1)", + "linewidth": 2, + "tickangle": -45, + "rangeselector": { + "buttons": [ + { + "step": "day", + "stepmode": "backward", + "count": 31, + "label": "30d" + }, + { + "step": "day", + "stepmode": "backward", + "count": 61, + "label": "60d" + }, + { + "step": "day", + "stepmode": "backward", + "count": 91, + "label": "90d" + }, + { + "step": "day", + "stepmode": "backward", + "count": 181, + "label": "all" + } + ] } + }, + "yaxis": { + "hoverformat": ",.0", + "tickformat": ",.0", + "gridcolor": "#FFF", + "gridwidth": 2, + "autotick": true, + "rangemode": "tozero", + "showline": true, + "title": "Downloads", + "ticksuffix": "", + "tickmode": "auto", + "linecolor": "rgba(148, 148, 148, 1)", + "linewidth": 2, + "rangeselector": { + "buttons": [ + { + "step": "day", + "stepmode": "backward", + "count": 31, + "label": "30d" + }, + { + "step": "day", + "stepmode": "backward", + "count": 61, + "label": "60d" + }, + { + "step": "day", + "stepmode": "backward", + "count": 91, + "label": "90d" + }, + { + "step": "day", + "stepmode": "backward", + "count": 181, + "label": "all" + } + ] + } + } }, - "percentages":{ - "layout":{ - "autosize":true, - "height":400, - "margin":{ - "r":100, - "t":40, - "autoexpand":true, - "b":80, - "l":100, - "pad":0 - }, - "paper_bgcolor":"#fff", - "plot_bgcolor":"rgba(175, 175, 175, 0.2)", - "showlegend":true, - "legend":{ - "orientation":"v", - "bgcolor":"#e7e7e7", - "xanchor":"left", - "yanchor":"middle", - "x":0, - "y":0.5 - }, - "title":"Proportional Downloads", - "xaxis":{ - "tickformat":"%m-%d", - "dtick":604800000, - "tick0":"2017-08-07", - "gridcolor":"#FFF", - "gridwidth":2, - "anchor":"y", - "domain":[ - 0, - 1 - ], - "title":"Date", - "titlefont":{ - "family":"'Geneva', Verdana, Geneva, sans-serif", - "size":16, - "color":"#7f7f7f" - }, - "showline":true, - "linecolor":"rgba(148, 148, 148, 1)", - "linewidth":2, - "tickangle":-45 - }, - "yaxis":{ - "range":[ - 0, - 100 - ], - "dtick":20, - "gridcolor":"#FFF", - "gridwidth":2, - "autotick":false, - "showline":true, - "title":"Download Proportion", - "ticksuffix":"%", - "tickmode":"auto", - "linecolor":"rgba(148, 148, 148, 1)", - "linewidth":2 - } - }, - "config":{ - "displaylogo":false, - "modeBarButtonsToRemove":[ - "toImage", - "sendDataToCloud", - "zoom2d", - "pan2d", - "select2d", - "lasso2d", - "zoomIn2d", - "zoomOut2d", - "toggleSpikelines" - ] - } + "config": { + "displaylogo": false, + "modeBarButtonsToRemove": [ + "toImage", + "sendDataToCloud", + "zoom2d", + "pan2d", + "select2d", + "lasso2d", + "zoomIn2d", + "zoomOut2d", + "toggleSpikelines" + ] } + }, + "percentages": { + "layout": { + "autosize": true, + "height": 400, + "margin": { + "r": 100, + "t": 80, + "autoexpand": true, + "b": 80, + "l": 100, + "pad": 0 + }, + "paper_bgcolor": "#fff", + "plot_bgcolor": "rgba(175, 175, 175, 0.2)", + "showlegend": true, + "legend": { + "orientation": "v", + "bgcolor": "#e7e7e7", + "xanchor": "left", + "yanchor": "middle", + "x": 0, + "y": 0.5 + }, + "title": "Proportional Downloads", + "xaxis": { + "tickformat": "%m-%d", + "dtick": 604800000, + "tick0": "2017-08-07", + "gridcolor": "#FFF", + "gridwidth": 2, + "anchor": "y", + "domain": [ + 0, + 1 + ], + "title": "Date", + "titlefont": { + "family": "Geneva, Verdana, Geneva, sans-serif", + "size": 16, + "color": "#7f7f7f" + }, + "showline": true, + "linecolor": "rgba(148, 148, 148, 1)", + "linewidth": 2, + "tickangle": -45 + }, + "yaxis": { + "range": [ + 0, + 100 + ], + "dtick": 20, + "gridcolor": "#FFF", + "gridwidth": 2, + "autotick": false, + "showline": true, + "title": "Download Proportion", + "ticksuffix": "%", + "tickmode": "auto", + "linecolor": "rgba(148, 148, 148, 1)", + "linewidth": 2 + } + }, + "config": { + "displaylogo": false, + "modeBarButtonsToRemove": [ + "toImage", + "sendDataToCloud", + "zoom2d", + "pan2d", + "select2d", + "lasso2d", + "zoomIn2d", + "zoomOut2d", + "toggleSpikelines" + ] + } + } } diff --git a/pypistats/tasks/pypi.py b/pypistats/tasks/pypi.py index ff87425..85df905 100644 --- a/pypistats/tasks/pypi.py +++ b/pypistats/tasks/pypi.py @@ -415,7 +415,7 @@ def etl(): if __name__ == "__main__": - date = "2018-07-28" + date = "2018-10-13" env = "prod" print(date, env) print(get_daily_download_stats(env, date)) diff --git a/pypistats/views/api.py b/pypistats/views/api.py index 24241af..7eb4c90 100644 --- a/pypistats/views/api.py +++ b/pypistats/views/api.py @@ -14,7 +14,7 @@ from pypistats.models.download import RecentDownloadCount from pypistats.models.download import SystemDownloadCount -blueprint = Blueprint('api', __name__, url_prefix='/api') +blueprint = Blueprint("api", __name__, url_prefix="/api") @blueprint.route("/") @@ -27,7 +27,7 @@ def api(): def api_downloads_recent(package): """Get the recent downloads of a package.""" package = package.replace(".", "-") - category = request.args.get('period') + category = request.args.get("period") if category is None: downloads = RecentDownloadCount.query.\ filter_by(package=package).all() @@ -55,12 +55,12 @@ def api_downloads_recent(package): def api_downloads_overall(package): """Get the overall download time series of a package.""" package = package.replace(".", "-") - mirrors = request.args.get('mirrors') - if mirrors == 'true': + mirrors = request.args.get("mirrors") + if mirrors == "true": downloads = OverallDownloadCount.query.\ filter_by(package=package, category="with_mirrors").\ order_by(OverallDownloadCount.date).all() - elif mirrors == 'false': + elif mirrors == "false": downloads = OverallDownloadCount.query.\ filter_by(package=package, category="without_mirrors").\ order_by(OverallDownloadCount.date).all() diff --git a/pypistats/views/general.py b/pypistats/views/general.py index ff51787..0c82c3c 100644 --- a/pypistats/views/general.py +++ b/pypistats/views/general.py @@ -12,6 +12,7 @@ from flask import g from flask import json from flask import redirect from flask import render_template +from flask import request from flask_wtf import FlaskForm import requests from wtforms import StringField @@ -97,8 +98,17 @@ def package_page(package): """Render the package page.""" package = package.replace(".", "-") # Recent download stats + try: + # Take the min of the lookback and 180 + lookback = min(abs(int(request.args.get("lookback", 180))), 180) + except ValueError: + lookback = 180 + + start_date = str(datetime.date.today() - datetime.timedelta(lookback)) + recent_downloads = RecentDownloadCount.query.\ filter_by(package=package).all() + if len(recent_downloads) == 0: return redirect(f"/search/{package}") recent = {r: 0 for r in RECENT_CATEGORIES} @@ -125,8 +135,8 @@ def package_page(package): model_data = [] for model in MODELS: records = model.query.filter_by(package=package).\ - order_by(model.date, - model.category).all() + filter(model.date >= start_date).\ + order_by(model.date, model.category).all() if model == OverallDownloadCount: metrics = ["downloads"] @@ -144,6 +154,8 @@ def package_page(package): plots = [] for model in model_data: plot = deepcopy(current_app.config["PLOT_BASE"])[model["metric"]] + + # Set data data = [] for category, values in model["data"].items(): base = deepcopy(current_app.config["DATA_BASE"][model["metric"]]["data"][0]) @@ -154,12 +166,38 @@ def package_page(package): base["name"] = category.title() data.append(base) plot["data"] = data + + # Add titles if model["metric"] == "percentages": plot["layout"]["title"] = \ f"Daily Download Proportions of {package} package - {model['name'].title().replace('_', ' ')}" # noqa else: plot["layout"]["title"] = \ f"Daily Download Quantity of {package} package - {model['name'].title().replace('_', ' ')}" # noqa + + # Explicitly set range + plot["layout"]["xaxis"]["range"] = [str(records[0].date - datetime.timedelta(1)), str(datetime.date.today())] + + # Add range buttons + plot["layout"]["xaxis"]["rangeselector"] = {"buttons": []} + drange = (datetime.date.today() - records[0].date).days + for k in [30, 60, 90, 120, 9999]: + if k <= drange: + plot["layout"]["xaxis"]["rangeselector"]["buttons"].append({ + "step": "day", + "stepmode": "backward", + "count": k+1, + "label": f"{k}d" + }) + else: + plot["layout"]["xaxis"]["rangeselector"]["buttons"].append({ + "step": "day", + "stepmode": "backward", + "count": drange + 1, + "label": "all" + }) + break + plots.append(plot) return render_template(