Skip to content

Commit 2d5a299

Browse files
authored
Merge pull request #214 from honeylogic-io/button-trending-configurable
feat: add button to sort trending by popular weekly, monthly, yearly
2 parents 23dfdc2 + 78dd18e commit 2d5a299

File tree

9 files changed

+137
-13
lines changed

9 files changed

+137
-13
lines changed

django_wtf/conftest.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,20 @@
11
# pylint: disable=redefined-outer-name
22
import pytest
33
import responses
4+
from pytest_factoryboy import register
45

6+
from django_wtf.core.factories import (
7+
RepositoryFactory,
8+
RepositoryStarsFactory,
9+
ValidRepositoryFactory,
10+
)
511
from django_wtf.users.models import User
612
from django_wtf.users.tests.factories import UserFactory
713

14+
register(RepositoryFactory)
15+
register(ValidRepositoryFactory)
16+
register(RepositoryStarsFactory)
17+
818

919
@pytest.fixture(autouse=True)
1020
def media_storage(settings, tmpdir): # pylint: disable=redefined-outer-name

django_wtf/core/factories.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,11 @@ def categories(self, create, extracted, **kwargs):
4949
self.categories.add(category)
5050

5151

52+
class ValidRepositoryFactory(RepositoryFactory):
53+
type = RepositoryType.APP
54+
stars = 100
55+
56+
5257
class RepositoryStarsFactory(DjangoModelFactory):
5358
class Meta:
5459
model = RepositoryStars

django_wtf/core/queries.py

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,16 @@
88

99
one_week_ago = datetime.today().date() - timedelta(days=7)
1010

11+
cache_time = 60 * 60 * 24
12+
# TODO: Make this dynamic
13+
# cache_time = 1
1114

12-
@cached_as(RepositoryStars, Constance, timeout=60 * 60 * 24)
13-
def trending_repositories(**filters):
15+
16+
@cached_as(RepositoryStars, Constance, timeout=cache_time)
17+
def trending_repositories(days_since, **filters):
1418
trending = []
1519
for repo in Repository.valid.filter(stars__gte=20, **filters):
16-
stars_in_the_last_week = repo.stars_since(
17-
timedelta(days=config.DAYS_SINCE_TRENDING)
18-
)
20+
stars_in_the_last_week = repo.stars_since(timedelta(days=days_since))
1921
if stars_in_the_last_week > 0:
2022
setattr(repo, "stars_lately", stars_in_the_last_week)
2123
setattr(repo, "stars_quota", repo.stars_lately / repo.stars) # type: ignore

django_wtf/core/views/index_view.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ class IndexView(MetadataMixin, TemplateView):
3636
def get_context_data(self, **kwargs):
3737
context = super().get_context_data(**kwargs)
3838
context["categories"] = self.categories_ordered_by_total_repositories()
39-
context["trending_apps"] = trending_repositories()[0:5]
39+
context["trending_apps"] = trending_repositories(days_since=14)[0:5]
4040
context["trending_developers"] = trending_profiles()[0:5]
4141
context["social_news"] = SocialNews.objects.filter(
4242
created_at__gt=one_week_ago
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
from datetime import datetime, timedelta
2+
3+
import pytest
4+
from django.urls import reverse
5+
6+
pytestmark = pytest.mark.django_db
7+
8+
9+
def test_trending_by_week(
10+
valid_repository_factory, repository_stars_factory, user_client
11+
):
12+
now = datetime.now()
13+
seven_days_ago = now - timedelta(days=7)
14+
url = reverse("core:trending-repositories")
15+
more_popular = valid_repository_factory(stars=32)
16+
less_popular = valid_repository_factory(stars=40)
17+
repository_stars_factory(
18+
repository=more_popular, created_at=seven_days_ago, stars=2
19+
)
20+
repository_stars_factory(
21+
repository=less_popular, created_at=seven_days_ago, stars=10
22+
)
23+
24+
res = user_client.get(url + "?trending=7")
25+
assert res.context["object_list"][0] == more_popular
26+
assert res.context["object_list"][1] == less_popular
27+
assert res.context["object_list"][0].stars_lately == 30
28+
assert res.context["object_list"][0].stars_quota == 0.9375
29+
assert res.context["object_list"][1].stars_lately == 30
30+
assert res.context["object_list"][1].stars_quota == 0.75

django_wtf/core/views/trending_repositories_view.py

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
from collections import OrderedDict
2+
13
from constance import config
24
from django.views.generic import ListView
35
from meta.views import MetadataMixin
@@ -10,9 +12,32 @@ class TrendingRepositoriesView(MetadataMixin, ListView):
1012
title = "Trending Django projects"
1113
description = "Trending Django projects in the past week"
1214
template_name = "core/trending_repositories.html"
15+
periods = OrderedDict(
16+
[
17+
(7, "7 days"),
18+
(14, "14 days"),
19+
(30, "30 days"),
20+
(90, "3 months"),
21+
(365, "1 year"),
22+
]
23+
)
1324

1425
def get_queryset(self):
15-
return trending_repositories()
26+
trending = trending_repositories(self.get_period_value())
27+
return trending
28+
29+
def get_period_value(self):
30+
period_q = self.request.GET.get("trending", 14)
31+
valid_value = period_q in [str(k) for k in self.periods.keys()]
32+
return int(period_q) if valid_value else 14
33+
34+
def get_context_data(self, **kwargs):
35+
context = super().get_context_data(**kwargs)
36+
context["periods"] = self.periods.items()
37+
current_period_label = self.periods.get(self.get_period_value())
38+
context["current_period"] = self.get_period_value()
39+
context["current_period_label"] = current_period_label
40+
return context
1641

1742
def get_meta_description(self, context=None):
1843
return f"Trending Django projects in the past {config.DAYS_SINCE_TRENDING} days"

django_wtf/templates/core/trending_repositories.html

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,29 @@
11
{% extends "core/base.html" %}
22
{% load humanize user_agents wtf_tags %}
33
{% block content %}
4-
<div class="flex w-full flex-col items-center self-center p-4 lg:w-7/12 lg:p-6">
5-
<div class="mb-10 mt-3 flex flex-wrap gap-2 self-start text-sm lg:text-lg">
4+
<div class="flex flex-col items-center self-center w-full p-4 lg:w-10/12 lg:p-6">
5+
<div class="flex flex-wrap self-start mt-3 mb-10 text-sm gap-2 lg:text-lg">
66
<div class="badge badge-lg badge-primary">Trending repositories</div>
77
{% if request|is_mobile or request|is_tablet %}
88
<div class="badge badge-lg badge-primary">Repositories with the most stars</div>
99
{% else %}
1010
<div class="badge badge-lg badge-primary">
1111
Repositories with proportionally largest increase in stars for the last
12-
{{ config.DAYS_SINCE_TRENDING | apnumber }} days
12+
{{ current_period | apnumber }} days
1313
</div>
1414
{% endif %}
1515
</div>
16-
<table class="mb-5 table w-full leading-6">
16+
<details class="dropdown">
17+
<summary class="m-1 btn">Trending: {{ current_period_label }}</summary>
18+
<ul class="p-2 shadow menu dropdown-content z-[1] bg-base-100 rounded-box w-52">
19+
{% for period in periods %}
20+
<li>
21+
<a href="{% url "core:trending-repositories" %}?trending={{ period.0 }}">{{ period.1 }}</a>
22+
</li>
23+
{% endfor %}
24+
</ul>
25+
</details>
26+
<table class="table w-full mb-5 leading-6">
1727
<thead>
1828
<tr>
1929
<th class="hidden xl:block">Rank</th>

poetry.lock

Lines changed: 43 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ types-Markdown = "^3.3.27"
7878
pyupgrade = "^3.15.0"
7979
django-upgrade = "^1.15.0"
8080
djlint = "^1.34.1"
81+
pytest-factoryboy = "^2.7.0"
8182

8283
[build-system]
8384
requires = ["poetry>=0.12"]

0 commit comments

Comments
 (0)