Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions pypistats/templates/package.html
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,8 @@ <h1>{{ package }}</h1>
<br>
Downloads last month:
{{ "{:,.0f}".format(recent['month']) }}
<br>
<input type="checkbox" id="smoothing" {{ 'checked' if use_smoothing else ''}} onclick="return setSmoothing();"> 7-day smoothing
</p>
<script>

Expand Down Expand Up @@ -106,5 +108,14 @@ <h1>{{ package }}</h1>
}
};
})();

function setSmoothing() {
if (document.getElementById('smoothing').checked) {
window.location='{{ url_for('general.package_page', package=package) }}?smooth=true';
} else {
window.location='{{ url_for('general.package_page', package=package) }}';
}
return false;
}
</script>
{% endblock %}
48 changes: 43 additions & 5 deletions pypistats/views/general.py
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ def package_page(package):
recent[r.category] = r.downloads

# PyPI metadata
metadata = None
metadata = dict()
if package != "__all__":
try:
metadata = requests.get(f"https://pypi.python.org/pypi/{package}/json", timeout=5).json()
Expand All @@ -171,6 +171,7 @@ def package_page(package):

# Get data from db
model_data = []
use_smoothing = request.args.get("smooth") is not None
for model in MODELS:
records = (
model.query.filter_by(package=package)
Expand All @@ -191,7 +192,11 @@ def package_page(package):

for metric in metrics:
model_data.append(
{"metric": metric, "name": model.__tablename__, "data": data_function[metric](records, category_key)}
{
"metric": metric,
"name": model.__tablename__,
"data": data_function[metric](records, category_key=category_key, use_smoothing=use_smoothing),
}
)

# Build the plots
Expand Down Expand Up @@ -240,7 +245,29 @@ def package_page(package):

plots.append(plot)

return render_template("package.html", package=package, plots=plots, metadata=metadata, recent=recent, user=g.user)
return render_template(
"package.html",
package=package,
plots=plots,
metadata=metadata,
recent=recent,
user=g.user,
use_smoothing=use_smoothing,
)


def smooth_data(data, window=7):
# Ensure data is sorted by date
data["x"], data["y"] = zip(*[(x, y) for x, y in sorted(zip(data["x"], data["y"]), key=lambda pair: pair[0])])
# Smooth data on a rolling window
smoothed_data = deepcopy(data)
smoothed_data["y"] = list(smoothed_data["y"])
for i in range(len(data["y"])):
window_start = max(0, i - window // 2)
window_end = min(len(data["y"]), i + window // 2 + 1)
window_data = data["y"][window_start:window_end]
smoothed_data["y"][i] = sum(window_data) / len(window_data)
return smoothed_data


def python_minor_key(version):
Expand All @@ -252,7 +279,7 @@ def python_minor_key(version):
return key


def get_download_data(records, category_key=None):
def get_download_data(records, category_key=None, use_smoothing=False):
"""Organize the data for the absolute plots."""
data = defaultdict(lambda: {"x": [], "y": []})

Expand Down Expand Up @@ -301,10 +328,16 @@ def get_download_data(records, category_key=None):
if category not in date_categories:
data[category]["x"].append(str(records[-1].date))
data[category]["y"].append(0)

if use_smoothing:
# Smooth data using a 7-day window
for category in all_categories:
data[category] = smooth_data(data[category])

return data


def get_proportion_data(records, category_key=None):
def get_proportion_data(records, category_key=None, use_smoothing=False):
"""Organize the data for the fill plots."""
data = defaultdict(lambda: {"x": [], "y": [], "text": []})

Expand Down Expand Up @@ -349,6 +382,11 @@ def get_proportion_data(records, category_key=None):
data[category]["y"].append(value)
data[category]["text"].append("{0:.2f}%".format(value) + " = {:,}".format(date_categories[category]))

if use_smoothing:
# Smooth data using a 7-day window
for category in all_categories:
data[category] = smooth_data(data[category])

return data


Expand Down
3 changes: 3 additions & 0 deletions tests/test_functional.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,9 @@ def test_package_metadata_and_charts(client, date):
def test_pypi_unavailable(client, date, pypi_request):
pypi_request.side_effect = requests.Timeout("PyPI unavailable")
assert "No metadata found." in assert_package_page(client, date, 10, 30, 30)
response = client.get("/packages/sample-package?smooth=true")
assert response.status_code == 200
assert "No metadata found." in response.get_data(as_text=True)


def test_all_packages_page(client, pypi_request):
Expand Down
Loading