Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add button to sort trending by popular weekly, monthly, yearly #214

Merged
merged 1 commit into from
Mar 7, 2024
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
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
Loading