Library Version Based CPP versions and descriptions (#1422)

This commit is contained in:
daveoconnor
2024-11-12 12:49:16 -08:00
committed by GitHub
parent 34ffcf91cb
commit 1d31bef1e4
15 changed files with 164 additions and 84 deletions

View File

@@ -195,7 +195,7 @@ class LibraryUpdater:
Retrieve the full list of library data for Boost libraries from their Github
repos.
Included libraries are rrtrieved from the list of modules in .gitmodules in the
Included libraries are retrieved from the list of modules in .gitmodules in the
main Boost repo. The libraries.json file is retrieved from each module and
parsed to get the library metadata. Most libraries.json files contain info
about individual libraries, but a few such as "system", "functional", etc.
@@ -259,7 +259,6 @@ class LibraryUpdater:
"name": library_data["name"],
"github_url": library_data["github_url"],
"description": library_data["description"],
"cpp_standard_minimum": library_data["cxxstd"],
"data": library_data,
},
)

View File

@@ -0,0 +1,42 @@
# Generated by Django 4.2.16 on 2024-11-08 23:03
from django.db import migrations, models
import django.db.migrations.operations.special
def populate_cpp_standard_minimum_and_description(apps, schema_editor):
# populate cpp minimum and description from the values in the 'data' columns
LibraryVersion = apps.get_model("libraries", "LibraryVersion")
for library_version in LibraryVersion.objects.all():
updated = False
if library_version.data.get("cxxstd"):
library_version.cpp_standard_minimum = library_version.data["cxxstd"]
updated = True
if library_version.data.get("description"):
library_version.description = library_version.data["description"]
updated = True
if updated:
library_version.save()
class Migration(migrations.Migration):
replaces = [('libraries', '0026_libraryversion_cpp_standard_minimum'), ('libraries', '0027_populate_cpp_standard_minimum'), ('libraries', '0028_libraryversion_description'), ('libraries', '0029_populate_library_version_description')]
dependencies = [
('libraries', '0025_libraryversion_deletions_and_more'),
]
operations = [
migrations.AddField(
model_name="libraryversion",
name="cpp_standard_minimum",
field=models.CharField(blank=True, max_length=50, null=True),
),
migrations.AddField(
model_name='libraryversion',
name='description',
field=models.TextField(blank=True, help_text='The description of the library.', null=True),
),
migrations.RunPython(populate_cpp_standard_minimum_and_description, migrations.RunPython.noop)
]

View File

@@ -150,7 +150,7 @@ class Library(models.Model):
)
description = models.TextField(
blank=True, null=True, help_text="The description of the library."
)
) # holds the most recent version's description
github_url = models.URLField(
max_length=500,
blank=True,
@@ -160,7 +160,9 @@ class Library(models.Model):
versions = models.ManyToManyField(
"versions.Version", through="libraries.LibraryVersion", related_name="libraries"
)
cpp_standard_minimum = models.CharField(max_length=50, blank=True, null=True)
cpp_standard_minimum = models.CharField(
max_length=50, blank=True, null=True
) # deprecated for LibraryVersion.cpp_standard_minimum
active_development = models.BooleanField(default=True, db_index=True)
categories = models.ManyToManyField(Category, related_name="libraries")
@@ -291,21 +293,6 @@ class Library(models.Model):
# If no content was found for any of the files
return None
def get_cpp_standard_minimum_display(self):
"""Returns the display name for the C++ standard, or the value if not found.
Source of values is
https://docs.cppalliance.org/user-guide/prev/library_metadata.html"""
display_names = {
"98": "C++98",
"03": "C++03",
"11": "C++11",
"14": "C++14",
"17": "C++17",
"20": "C++20",
}
return display_names.get(self.cpp_standard_minimum, self.cpp_standard_minimum)
def github_properties(self):
"""Returns the owner and repo name for the library"""
if not self.github_url:
@@ -381,6 +368,9 @@ class LibraryVersion(models.Model):
null=True,
help_text="The path to the docs for this library version.",
)
description = models.TextField(
blank=True, null=True, help_text="The description of the library."
)
data = models.JSONField(
default=dict, help_text="Contains the libraries.json for this library-version"
)
@@ -388,6 +378,7 @@ class LibraryVersion(models.Model):
insertions = models.IntegerField(default=0)
deletions = models.IntegerField(default=0)
files_changed = models.IntegerField(default=0)
cpp_standard_minimum = models.CharField(max_length=50, blank=True, null=True)
def __str__(self):
return f"{self.library.name} ({self.version.name})"
@@ -406,6 +397,21 @@ class LibraryVersion(models.Model):
def get_release_notes(self):
return get_release_notes_for_library_version(self)
def get_cpp_standard_minimum_display(self):
"""Returns the display name for the C++ standard, or the value if not found.
Source of values is
https://docs.cppalliance.org/user-guide/prev/library_metadata.html"""
display_names = {
"98": "C++98",
"03": "C++03",
"11": "C++11",
"14": "C++14",
"17": "C++17",
"20": "C++20",
}
return display_names.get(self.cpp_standard_minimum, self.cpp_standard_minimum)
class Issue(models.Model):
"""

View File

@@ -6,14 +6,14 @@ from libraries.models import CommitAuthor
from mailing_list.models import EmailData
def test_get_cpp_standard_minimum_display(library):
library.cpp_standard_minimum = "11"
library.save()
assert library.get_cpp_standard_minimum_display() == "C++11"
def test_get_cpp_standard_minimum_display(library_version):
library_version.cpp_standard_minimum = "11"
library_version.save()
assert library_version.get_cpp_standard_minimum_display() == "C++11"
library.cpp_standard_minimum = "42"
library.save()
assert library.get_cpp_standard_minimum_display() == "42"
library_version.cpp_standard_minimum = "42"
library_version.save()
assert library_version.get_cpp_standard_minimum_display() == "42"
def test_github_properties(library):

View File

@@ -125,9 +125,9 @@ def test_library_list_by_category(
library_version.library.categories.add(category)
res = tp.get(url)
tp.response_200(res)
assert "library_list" in res.context
assert "category" in res.context["library_list"][0]
assert "libraries" in res.context["library_list"][0]
assert "library_versions_by_category" in res.context
assert "category" in res.context["library_versions_by_category"][0]
assert "library_version_list" in res.context["library_versions_by_category"][0]
# Create a new library version that is not in the selected version
existing_category = library_version.library.categories.first()
@@ -137,8 +137,12 @@ def test_library_list_by_category(
baker.make("libraries.LibraryVersion", version=new_version, library=new_lib)
res = tp.get(f"/libraries/by-category/?version={library_version.version.slug}")
tp.response_200(res)
assert existing_category in [x["category"] for x in res.context["library_list"]]
assert new_category not in [x["category"] for x in res.context["library_list"]]
assert existing_category in [
x["category"] for x in res.context["library_versions_by_category"]
]
assert new_category not in [
x["category"] for x in res.context["library_versions_by_category"]
]
def test_library_detail(library_version, tp):

View File

@@ -112,7 +112,7 @@ class LibraryList(VersionAlertMixin, ListView):
"version_str": version_str,
"categories": Category.objects.none(),
"versions": Version.objects.none(),
"library_list": Library.objects.none(),
"library_version_list": LibraryVersion.objects.none(),
}
)
return context
@@ -124,7 +124,12 @@ class LibraryList(VersionAlertMixin, ListView):
context["categories"] = self.get_categories(context["version"])
context["versions"] = self.get_versions()
context["version_str"] = version_str
context["library_list"] = self.get_queryset()
context["library_version_list"] = LibraryVersion.objects.filter(
version__slug=version_str
if version_str != LATEST_RELEASE_URL_PATH_STR
else version.slug
).prefetch_related("authors", "library", "library__categories")
context["url_params"] = build_view_query_params_from_request(self.request)
return context
@@ -158,6 +163,7 @@ class LibraryList(VersionAlertMixin, ListView):
# Manually exclude the master and develop branches.
versions = versions.exclude(name__in=["develop", "master", "head"])
versions.prefetch_related("library_version")
return versions
def dispatch(self, request, *args, **kwargs):
@@ -194,24 +200,43 @@ class LibraryListByCategory(LibraryList):
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context["library_list"] = self.get_results_by_category(
context["library_versions_by_category"] = self.get_results_by_category(
version=context.get("version")
)
return context
def get_results_by_category(self, version: Version | None):
queryset = super().get_queryset()
filter_kwargs = {"libraries__versions__name": version} if version else {}
# Define filter kwargs based on whether version is provided
version_filter = {"version__name": version} if version else {}
category_filter = {"libraries__versions__name": version} if version else {}
library_versions_qs = LibraryVersion.objects.filter(**version_filter)
categories = (
Category.objects.filter(**filter_kwargs).distinct().order_by("name")
Category.objects.filter(**category_filter)
.distinct()
.order_by("name")
.prefetch_related(
Prefetch(
"libraries__library_version",
queryset=library_versions_qs,
to_attr="prefetched_library_versions",
)
)
)
categories = categories.prefetch_related(
Prefetch("libraries", queryset=queryset, to_attr="prefetched_libraries")
)
results_by_category = [
{"category": category, "libraries": category.prefetched_libraries}
for category in categories
]
results_by_category = []
for category in categories:
library_versions = []
for library in category.libraries.all():
prefetched_versions = getattr(
library, "prefetched_library_versions", []
)
library_versions.extend(prefetched_versions)
results_by_category.append(
{"category": category, "library_version_list": library_versions}
)
return results_by_category
@@ -247,6 +272,7 @@ class LibraryDetail(FormMixin, VersionAlertMixin, DetailView):
library_version = LibraryVersion.objects.get(
library=self.get_object(), version=context["version"]
)
context["library_version"] = library_version
context["documentation_url"] = get_documentation_url(
library_version, context["version_str"] == LATEST_RELEASE_URL_PATH_STR
)

View File

@@ -1,25 +1,25 @@
<tr class="border-0 md:border border-gray-200/10 border-dotted md:border-t-0 md:border-r-0 md:border-l-0 md:border-b-1 hover:bg-gray-100 dark:hover:bg-gray-700 transition-all duration-200 ease-in-out cursor-pointer"
onclick="window.location='{% if version_str != LATEST_RELEASE_URL_PATH_STR %}{% url 'library-detail-by-version' slug=library.slug version_slug=version.slug %}{% else %}{% url 'library-detail' slug=library.slug %}{% endif %}'">
onclick="window.location='{% if version_str != LATEST_RELEASE_URL_PATH_STR %}{% url 'library-detail-by-version' slug=library_version.library.slug version_slug=version.slug %}{% else %}{% url 'library-detail' slug=library_version.library.slug %}{% endif %}'">
<td class="py-2 align-top md:w-1/5">
<a class="mr-1 font-bold capitalize text-sky-600 dark:text-sky-300 hover:text-orange dark:hover:text-orange"
href="
{% if version_str != LATEST_RELEASE_URL_PATH_STR %}
{% url 'library-detail-by-version' slug=library.slug version_slug=version.slug %}
{% url 'library-detail-by-version' slug=library_version.library.slug version_slug=version.slug %}
{% else %}
{% url 'library-detail' slug=library.slug %}
{% url 'library-detail' slug=library_version.library.slug %}
{% endif %}"
>{{ library.name }}</a>
>{{ library_version.library.name }}</a>
</td>
<td class="py-2 align-top w-12">
<span class="flex justify-center py-0 px-2 text-sm font-bold text-gray-600 rounded-full border dark:text-gray-300 bg-green/40 border-green/60 dark:bg-green/40 tabular-nums"
title="{% if library.cpp_standard_minimum and library.cpp_standard_minimum != 'None' %}C++ Version {{ library.cpp_standard_minimum }} or Later{% else %}C++ Version 03 or Later{% endif %}">
title="{% if library_version.cpp_standard_minimum and library_version.cpp_standard_minimum != 'None' %}C++ Version {{ library_version.cpp_standard_minimum }} or Later{% else %}C++ Version 03 or Later{% endif %}">
<span class="text-white dark:text-gray-800 tracking-wide scale-x-75 origin-left w-6 text-nowrap whitespace-nowrap">C++</span>
{% if library.cpp_standard_minimum and library.cpp_standard_minimum != 'None' %}{{ library.cpp_standard_minimum }}{% else %}03{% endif %}
{% if library_version.cpp_standard_minimum and library_version.cpp_standard_minimum != 'None' %}{{ library_version.cpp_standard_minimum }}{% else %}03{% endif %}
</span>
</td>
<td class="hidden md:block py-2 pl-3 align-top">{{ library.description }}</td>
<td class="hidden md:block py-2 pl-3 align-top">{{ library_version.description|default:"No description provide for this version." }}</td>
</tr>
<tr class="block md:hidden">
<td>{{ library.description }}</td>
<td>{{ library_version.description|default:"No description provide for this version." }}</td>
</tr>

View File

@@ -1,25 +1,25 @@
<tr class="border-0 md:border border-gray-200/10 border-dotted md:border-t-0 md:border-r-0 md:border-l-0 md:border-b-1 hover:bg-gray-100 dark:hover:bg-gray-700 transition-all duration-300 ease-in-out cursor-pointer"
onclick="window.location='{% if version %}{% url 'library-detail-by-version' slug=library.slug version_slug=version.slug %}{% else %}{% url 'library-detail' slug=library.slug %}{% endif %}'">
onclick="window.location='{% if version %}{% url 'library-detail-by-version' slug=library_version.library.slug version_slug=version.slug %}{% else %}{% url 'library-detail' slug=library_version.library.slug %}{% endif %}'">
<td class="py-2 align-top md:w-1/5">
<a class="mr-1 pl-1 font-bold capitalize text-sky-600 dark:text-sky-300 hover:text-orange dark:hover:text-orange"
href="
{% if version_str != LATEST_RELEASE_URL_PATH_STR %}
{% url 'library-detail-by-version' slug=library.slug version_slug=version.slug %}
{% url 'library-detail-by-version' slug=library_version.library.slug version_slug=version.slug %}
{% else %}
{% url 'library-detail' slug=library.slug %}
{% url 'library-detail' slug=library_version.library.slug %}
{% endif %}"
>{{ library.name }}</a>
>{{ library_version.library.name }}</a>
</td>
<td class="py-2 px-2 align-top w-12">
<span class="flex justify-center py-0 px-2 text-sm font-bold text-gray-600 rounded-full border dark:text-gray-300 bg-green/40 border-green/60 dark:bg-green/40 tabular-nums"
title="{% if library.cpp_standard_minimum and library.cpp_standard_minimum != 'None' %}C++ Version {{ library.cpp_standard_minimum }} or Later{% else %}C++ Version 03 or Later{% endif %}">
title="{% if library_version.cpp_standard_minimum and library_version.cpp_standard_minimum != 'None' %}C++ Version {{ library_version.cpp_standard_minimum }} or Later{% else %}C++ Version 03 or Later{% endif %}">
<span class="text-white dark:text-gray-800 tracking-wide scale-x-75 origin-left w-6 text-nowrap whitespace-nowrap">C++</span>
{% if library.cpp_standard_minimum and library.cpp_standard_minimum != 'None' %}{{ library.cpp_standard_minimum }}{% else %}03{% endif %}
{% if library_version.cpp_standard_minimum and library_version.cpp_standard_minimum != 'None' %}{{ library_version.cpp_standard_minimum }}{% else %}03{% endif %}
</span>
</td>
<td class="hidden md:block py-2 pl-3 align-top">{{ library.description }}</td>
<td class="hidden md:block py-2 pl-3 align-top">{{ library_version.description|default:"No description provide for this version." }}</td>
</tr>
<tr class="block md:hidden pl-1">
<td>{{ library.description }}</td>
<td>{{ library_version.description|default:"No description provide for this version." }}</td>
</tr>

View File

@@ -2,17 +2,17 @@
{% load date_filters %}
<div class="relative content-between p-3 bg-white md:rounded-lg md:shadow-lg md:p-5 dark:bg-charcoal hover:bg-gray-100 dark:hover:bg-gray-700 transition-all duration-200 ease-in-out cursor-pointer"
onclick="window.location='{% if version_str != LATEST_RELEASE_URL_PATH_STR %}{% url 'library-detail-by-version' slug=library.slug version_slug=version.slug %}{% else %}{% url 'library-detail' slug=library.slug %}{% endif %}'">
onclick="window.location='{% if version_str != LATEST_RELEASE_URL_PATH_STR %}{% url 'library-detail-by-version' slug=library_version.library.slug version_slug=version.slug %}{% else %}{% url 'library-detail' slug=library_version.library.slug %}{% endif %}'">
<div class="">
<h3 class="pb-2 text-xl md:text-2xl capitalize border-b border-gray-700">
<a class="link-header" href="
{% if version_str != LATEST_RELEASE_URL_PATH_STR %}
{% url 'library-detail-by-version' slug=library.slug version_slug=version.slug %}
{% url 'library-detail-by-version' slug=library_version.library.slug version_slug=version.slug %}
{% else %}
{% url 'library-detail' slug=library.slug %}
{% url 'library-detail' slug=library_version.library.slug %}
{% endif %}"
>{{ library.name }}</a>
{% for author in library.authors.all %}
>{{ library_version.library.name }}</a>
{% for author in library_version.library.authors.all %}
{% if author.image %}
<img src="{{ author.image.url }}" class="inline float-right rounded w-[30px] ml-1" alt="{{ author.get_display_name }}" />
{% endif %}
@@ -21,19 +21,19 @@ onclick="window.location='{% if version_str != LATEST_RELEASE_URL_PATH_STR %}{%
</div>
<div class="mb-3 pb-3">
<p class="mb-3 text-gray-700 dark:text-gray-300">{{ library.description }}</p>
<p class="mb-3 text-gray-700 dark:text-gray-300">{{ library_version.description|default:"No description provide for this version." }}</p>
</div>
<div class="text-sm flex py-3 bottom-3 absolute w-full md:w-11/12">
<div >
<span class="flex justify-center py-0 px-2 text-sm font-bold text-gray-600 rounded-full border dark:text-gray-300 bg-green/40 border-green/60 dark:bg-green/40 tabular-nums"
title="C++ Version {% if library.cpp_standard_minimum and library.cpp_standard_minimum != 'None' %}{{ library.cpp_standard_minimum }}{% else %}03{% endif %} or Later">
title="C++ Version {% if library_version.cpp_standard_minimum and library_version.cpp_standard_minimum != 'None' %}{{ library_version.cpp_standard_minimum }}{% else %}03{% endif %} or Later">
<span class="text-white dark:text-gray-800 tracking-wide scale-x-75 origin-left w-6 text-nowrap whitespace-nowrap">C++</span>
{% if library.cpp_standard_minimum and library.cpp_standard_minimum != 'None' %}{{ library.cpp_standard_minimum }}{% else %}03{% endif %}
{% if library_version.cpp_standard_minimum and library_version.cpp_standard_minimum != 'None' %}{{ library_version.cpp_standard_minimum }}{% else %}03{% endif %}
</span>
</div>
{#<div class="w-1/6 tracking-wider text-charcoal dark:text-white/60">{% if library.first_boost_version %}{{ library.first_boost_version.release_date|years_since }} yrs{% endif %}</div>#}
{# <div class="w-1/6 tracking-wider text-charcoal dark:text-white/60">{% if library_version.library.first_boost_version %}{{ library_version.library.first_boost_version.release_date|years_since }} yrs{% endif %}</div>#}
<div class="w-5/6 text-right mr-2 font-bold capitalize text-sky-600 dark:text-sky-300">
{% for c in library.categories.all %}
{% for c in library_version.library.categories.all %}
<a href="{% url 'libraries' %}?category={{ c.slug }}{% if version_str != LATEST_RELEASE_URL_PATH_STR %}&version={{ version.slug }}{% endif %}" class="hover:text-orange">{{ c.name }}</a>{% if not forloop.last %}, {% endif %}{% endfor %}
</div>
</div>

View File

@@ -15,12 +15,12 @@
{# Libraries list #}
<div class="space-y-3">
{% for result in library_list %}
{% for result in library_versions_by_category %}
<div class="relative content-between p-3 w-full bg-white md:rounded-lg md:shadow-lg md:p-5 dark:bg-charcoal">
<h5 class="mb-2 pb-2 text-xl md:text-2xl leading-tight text-orange border-b border-gray-300 dark:border-slate">{{ result.category }}</h5>
<table class="table-auto w-full">
<tbody>
{% for library in result.libraries %}
{% for library_version in result.library_version_list %}
{% include "libraries/_library_category_list_item.html" %}
{% empty %}
<p class="text-gray-600 dark:text-gray-400">No libraries in this category yet.</p>

View File

@@ -2,7 +2,7 @@
{% load i18n static avatar_tags %}
{% block title %}{{ object.display_name }} ({{ version.display_name }}){% endblock %}
{% block description %}{% trans object.description %}{% endblock %}
{% block description %}{% if library_version.description %}{% trans library_version.description %}{% endif %}{% endblock %}
{% block author %}{{ author_tag }}{% endblock %}
{% block content %}
@@ -46,12 +46,9 @@
<div class="pb-4 pt-1">
<span
class="py-0 px-2 text-sm font-bold text-gray-600 rounded-full border dark:text-gray-300 bg-green/40 border-green/60 dark:bg-green/40"
title="C++ Version
{% if library.cpp_standard_minimum and library.cpp_standard_minimum != 'None' %}{{ library.cpp_standard_minimum }}{% else %}03{% endif %} or Later">
{% if library.cpp_standard_minimum and library.cpp_standard_minimum != 'None' %}
title="C++ Version {% if library_version.cpp_standard_minimum %}{{ library_version.cpp_standard_minimum }}{% else %}03{% endif %} or Later">
<span class="scale-x-75 text-white dark:text-gray-800 tracking-wide origin-left w-6 inline-block whitespace-nowrap">C++</span>
{{ library.cpp_standard_minimum }}{% else %}03{% endif %}
{% if library_version.cpp_standard_minimum %}{{ library_version.cpp_standard_minimum }}{% else %}03{% endif %}
</span>
<span class="float-right">{% if object.first_boost_version %}Added in Boost
{{ object.first_boost_version.display_name }}{% endif %}</span>
@@ -67,7 +64,7 @@
{% endfor %}
</div>
<p class="pt-4 mt-4 whitespace-normal text-lg border-t border-gray-700">{{ object.description }}</p>
<p class="pt-4 mt-4 whitespace-normal text-lg border-t border-gray-700">{{ library_version.description|default:"No description provide for this version." }}</p>
</div>
<div class="-ml-2 min-h-16">

View File

@@ -20,7 +20,7 @@
{% endif %}
<table class="table-auto w-full">
<tbody>
{% for library in library_list %}
{% for library_version in library_version_list %}
{% include "libraries/_library_flat_list_item.html" %}
{% endfor %}
</tbody>

View File

@@ -15,7 +15,7 @@
{# Libraries list #}
<div class="grid grid-cols-1 gap-4 mb-5 md:grid-cols-2 lg:grid-cols-3">
{% for library in library_list %}
{% for library_version in library_version_list %}
{% include "libraries/_library_list_item.html" %}
{% endfor %}
</div>

View File

@@ -5,6 +5,6 @@
</a>
</h4>
<p class="pt-0">
{{ library_version.library.description }}
{{ library_version.description }}
</p>
</div>

View File

@@ -244,6 +244,7 @@ def skip_library_version(library_slug, version_slug):
@app.task
def import_library_versions(version_name, token=None, version_type="tag"):
"""For a specific version, imports all LibraryVersions using GitHub data"""
# todo: this needs to be refactored and tests added
try:
version = Version.objects.get(name=version_name)
except Version.DoesNotExist:
@@ -348,12 +349,17 @@ def import_library_versions(version_name, token=None, version_type="tag"):
defaults={
"name": lib_data.get("name"),
"description": lib_data.get("description"),
"cpp_standard_minimum": lib_data.get("cxxstd"),
"data": lib_data,
},
)
library_version, _ = LibraryVersion.objects.update_or_create(
version=version, library=library, defaults={"data": lib_data}
version=version,
library=library,
defaults={
"data": lib_data,
"cpp_standard_minimum": lib_data.get("cxxstd"),
"description": lib_data.get("description"),
},
)
if not library.github_url:
github_data = client.get_repo(repo_slug=library_name)