Skip to content

Commit

Permalink
feat: add button to sort trending by popular weekly, monthly, yearly
Browse files Browse the repository at this point in the history
  • Loading branch information
danihodovic committed Mar 7, 2024
1 parent 23dfdc2 commit 78dd18e
Show file tree
Hide file tree
Showing 9 changed files with 137 additions and 13 deletions.
10 changes: 10 additions & 0 deletions django_wtf/conftest.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,20 @@
# pylint: disable=redefined-outer-name
import pytest
import responses
from pytest_factoryboy import register

from django_wtf.core.factories import (
RepositoryFactory,
RepositoryStarsFactory,
ValidRepositoryFactory,
)
from django_wtf.users.models import User
from django_wtf.users.tests.factories import UserFactory

register(RepositoryFactory)
register(ValidRepositoryFactory)
register(RepositoryStarsFactory)


@pytest.fixture(autouse=True)
def media_storage(settings, tmpdir): # pylint: disable=redefined-outer-name
Expand Down
5 changes: 5 additions & 0 deletions django_wtf/core/factories.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,11 @@ def categories(self, create, extracted, **kwargs):
self.categories.add(category)


class ValidRepositoryFactory(RepositoryFactory):
type = RepositoryType.APP
stars = 100


class RepositoryStarsFactory(DjangoModelFactory):
class Meta:
model = RepositoryStars
Expand Down
12 changes: 7 additions & 5 deletions django_wtf/core/queries.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,16 @@

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

cache_time = 60 * 60 * 24
# TODO: Make this dynamic
# cache_time = 1

@cached_as(RepositoryStars, Constance, timeout=60 * 60 * 24)
def trending_repositories(**filters):

@cached_as(RepositoryStars, Constance, timeout=cache_time)
def trending_repositories(days_since, **filters):
trending = []
for repo in Repository.valid.filter(stars__gte=20, **filters):
stars_in_the_last_week = repo.stars_since(
timedelta(days=config.DAYS_SINCE_TRENDING)
)
stars_in_the_last_week = repo.stars_since(timedelta(days=days_since))
if stars_in_the_last_week > 0:
setattr(repo, "stars_lately", stars_in_the_last_week)
setattr(repo, "stars_quota", repo.stars_lately / repo.stars) # type: ignore
Expand Down
2 changes: 1 addition & 1 deletion django_wtf/core/views/index_view.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ class IndexView(MetadataMixin, TemplateView):
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context["categories"] = self.categories_ordered_by_total_repositories()
context["trending_apps"] = trending_repositories()[0:5]
context["trending_apps"] = trending_repositories(days_since=14)[0:5]
context["trending_developers"] = trending_profiles()[0:5]
context["social_news"] = SocialNews.objects.filter(
created_at__gt=one_week_ago
Expand Down
30 changes: 30 additions & 0 deletions django_wtf/core/views/test_trending_repositories_view.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
from datetime import datetime, timedelta

import pytest
from django.urls import reverse

pytestmark = pytest.mark.django_db


def test_trending_by_week(
valid_repository_factory, repository_stars_factory, user_client
):
now = datetime.now()
seven_days_ago = now - timedelta(days=7)
url = reverse("core:trending-repositories")
more_popular = valid_repository_factory(stars=32)
less_popular = valid_repository_factory(stars=40)
repository_stars_factory(
repository=more_popular, created_at=seven_days_ago, stars=2
)
repository_stars_factory(
repository=less_popular, created_at=seven_days_ago, stars=10
)

res = user_client.get(url + "?trending=7")
assert res.context["object_list"][0] == more_popular
assert res.context["object_list"][1] == less_popular
assert res.context["object_list"][0].stars_lately == 30
assert res.context["object_list"][0].stars_quota == 0.9375
assert res.context["object_list"][1].stars_lately == 30
assert res.context["object_list"][1].stars_quota == 0.75
27 changes: 26 additions & 1 deletion django_wtf/core/views/trending_repositories_view.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from collections import OrderedDict

from constance import config
from django.views.generic import ListView
from meta.views import MetadataMixin
Expand All @@ -10,9 +12,32 @@ class TrendingRepositoriesView(MetadataMixin, ListView):
title = "Trending Django projects"
description = "Trending Django projects in the past week"
template_name = "core/trending_repositories.html"
periods = OrderedDict(
[
(7, "7 days"),
(14, "14 days"),
(30, "30 days"),
(90, "3 months"),
(365, "1 year"),
]
)

def get_queryset(self):
return trending_repositories()
trending = trending_repositories(self.get_period_value())
return trending

def get_period_value(self):
period_q = self.request.GET.get("trending", 14)
valid_value = period_q in [str(k) for k in self.periods.keys()]
return int(period_q) if valid_value else 14

def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context["periods"] = self.periods.items()
current_period_label = self.periods.get(self.get_period_value())
context["current_period"] = self.get_period_value()
context["current_period_label"] = current_period_label
return context

def get_meta_description(self, context=None):
return f"Trending Django projects in the past {config.DAYS_SINCE_TRENDING} days"
18 changes: 14 additions & 4 deletions django_wtf/templates/core/trending_repositories.html
Original file line number Diff line number Diff line change
@@ -1,19 +1,29 @@
{% extends "core/base.html" %}
{% load humanize user_agents wtf_tags %}
{% block content %}
<div class="flex w-full flex-col items-center self-center p-4 lg:w-7/12 lg:p-6">
<div class="mb-10 mt-3 flex flex-wrap gap-2 self-start text-sm lg:text-lg">
<div class="flex flex-col items-center self-center w-full p-4 lg:w-10/12 lg:p-6">
<div class="flex flex-wrap self-start mt-3 mb-10 text-sm gap-2 lg:text-lg">
<div class="badge badge-lg badge-primary">Trending repositories</div>
{% if request|is_mobile or request|is_tablet %}
<div class="badge badge-lg badge-primary">Repositories with the most stars</div>
{% else %}
<div class="badge badge-lg badge-primary">
Repositories with proportionally largest increase in stars for the last
{{ config.DAYS_SINCE_TRENDING | apnumber }} days
{{ current_period | apnumber }} days
</div>
{% endif %}
</div>
<table class="mb-5 table w-full leading-6">
<details class="dropdown">
<summary class="m-1 btn">Trending: {{ current_period_label }}</summary>
<ul class="p-2 shadow menu dropdown-content z-[1] bg-base-100 rounded-box w-52">
{% for period in periods %}
<li>
<a href="{% url "core:trending-repositories" %}?trending={{ period.0 }}">{{ period.1 }}</a>
</li>
{% endfor %}
</ul>
</details>
<table class="table w-full mb-5 leading-6">
<thead>
<tr>
<th class="hidden xl:block">Rank</th>
Expand Down
45 changes: 43 additions & 2 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ types-Markdown = "^3.3.27"
pyupgrade = "^3.15.0"
django-upgrade = "^1.15.0"
djlint = "^1.34.1"
pytest-factoryboy = "^2.7.0"

[build-system]
requires = ["poetry>=0.12"]
Expand Down

0 comments on commit 78dd18e

Please sign in to comment.