Make review views dynamic and add import script (#1444)

This commit is contained in:
Greg Kaleka
2024-11-21 07:55:02 -05:00
committed by GitHub
parent 6a64dce691
commit 15698ee478
13 changed files with 723 additions and 274 deletions

View File

@@ -56,6 +56,7 @@ INSTALLED_APPS = [
"django.contrib.messages",
"django.contrib.staticfiles",
"django.contrib.sites",
"django.contrib.postgres",
]
# Third-party apps

View File

@@ -72,7 +72,12 @@ from users.views import (
)
from versions.api import ImportVersionsView, VersionViewSet
from versions.feeds import AtomVersionFeed, RSSVersionFeed
from versions.views import InProgressReleaseNotesView, VersionDetail
from versions.views import (
InProgressReleaseNotesView,
PastReviewListView,
ScheduledReviewListView,
VersionDetail,
)
router = routers.SimpleRouter()
@@ -259,7 +264,7 @@ urlpatterns = (
),
path(
"review/past/",
TemplateView.as_view(template_name="review/past_reviews.html"),
PastReviewListView.as_view(),
name="review-past",
),
path(
@@ -269,7 +274,7 @@ urlpatterns = (
),
path(
"review/upcoming/",
TemplateView.as_view(template_name="review/upcoming_reviews.html"),
ScheduledReviewListView.as_view(),
name="review-upcoming",
),
path(

View File

@@ -98,10 +98,12 @@ make the columns go to 1
Learn how new libraries are added
</p>
<div class="grid grid-cols-1 gap-4 mt-6 text-lg pl-4">
<div class="w-auto ml-6 mr-2"><a class="list-item text-sky-600 dark:text-sky-300 hover:text-orange dark:hover:text-orange" href="/doc/formal-reviews/index.html">Introduction</a></div>
<div class="w-auto ml-6 mr-2"><a class="list-item text-sky-600 dark:text-sky-300 hover:text-orange dark:hover:text-orange" href="/doc/formal-reviews/submissions.html">Submission Process</a></div>
<div class="w-auto ml-6 mr-2"><a class="list-item text-sky-600 dark:text-sky-300 hover:text-orange dark:hover:text-orange" href="/doc/formal-reviews/writing-reviews.html">Write a Review</a></div>
<div class="w-auto ml-6 mr-2"><a class="list-item text-sky-600 dark:text-sky-300 hover:text-orange dark:hover:text-orange" href="/doc/formal-reviews/managing-reviews.html">Manage Reviews</a></div>
{# <div class="w-auto ml-6 mr-2"><a class="list-item text-sky-600 dark:text-sky-300 hover:text-orange dark:hover:text-orange" href="/doc/formal-reviews/index.html">Introduction</a></div>#}
<div class="w-auto ml-6 mr-2"><a class="list-item text-sky-600 dark:text-sky-300 hover:text-orange dark:hover:text-orange" href="{% url 'review-process' %}">Submission Process</a></div>
<div class="w-auto ml-6 mr-2"><a class="list-item text-sky-600 dark:text-sky-300 hover:text-orange dark:hover:text-orange" href="{% url 'review-past' %}">Past Review Results and Milestones</a></div>
<div class="w-auto ml-6 mr-2"><a class="list-item text-sky-600 dark:text-sky-300 hover:text-orange dark:hover:text-orange" href="{% url 'review-upcoming' %}">Upcoming Reviews</a></div>
{# <div class="w-auto ml-6 mr-2"><a class="list-item text-sky-600 dark:text-sky-300 hover:text-orange dark:hover:text-orange" href="/doc/formal-reviews/writing-reviews.html">Write a Review</a></div>#}
{# <div class="w-auto ml-6 mr-2"><a class="list-item text-sky-600 dark:text-sky-300 hover:text-orange dark:hover:text-orange" href="/doc/formal-reviews/managing-reviews.html">Manage Reviews</a></div>#}
</div>
</div>

View File

@@ -25,11 +25,12 @@
</div>
</div>
</a>
{% if av_avatar_type == "wide" %}
{% if av_avatar_type == "wide" or av_avatar_type == "with_name" %}
<div class="flex flex-col leading-tight min-w-[15rem]">
<div class="text-black dark:text-white">
{{ av_name }}
</div>
{% if av_avatar_type == "wide" %}
<div
class="
text-sm
@@ -46,6 +47,7 @@
>
{{ av_contributor_label }}
</div>
{% endif %}
</div>
{% endif %}
</div>

View File

@@ -1,98 +1,62 @@
{% extends "base.html" %}
{% load static %}
{% load static avatar_tags %}
{% block subnav %}
<div class="py-8 px-4 space-y-4 text-sm uppercase border-b md:py-2 md:px-0 md:space-x-10 border-slate">
<a href="{% url 'review-process' %}" class="block md:inline">Review Process</a>
<a href="{% url 'review-request' %}" class="block md:inline">Submit Review</a>
<a href="{% url 'review-upcoming' %}" class="block md:inline">Upcoming Reviews</a>
<a href="{% url 'review-past' %}" class="block md:inline md:py-1 md:border-b text-orange md:border-orange">Past Reviews</a>
</div>
{% endblock %}
{% block title %}Past Reviews{% endblock %}
{% block content %}
<div class="py-0 px-3 mb-3 md:py-6 md:px-0">
<div class="py-16 md:w-full">
<h1 class="text-4xl text-center">Upcoming Reviews</h1>
<p class="mt-0 text-center">
We are proud of the past reviews and community members who worked on them.
</p>
<div class="p-6 mt-11 w-full rounded bg-charcoal">
<div class="hidden w-full md:flex">
<div class="p-3 w-1/5">Submnission</div>
<div class="p-3 w-1/5">Submitter</div>
<div class="p-3 w-1/5">Review Manager</div>
<div class="p-3 w-1/5">Review Dates</div>
<div class="p-3 w-1/5">Result</div>
</div>
<div class="py-6 border-b md:flex md:py-0 md:border-b-0 border-slate">
<div class="p-3 text-left md:w-1/5 md:border md:border-slate"><span class="font-bold md:hidden">Submission:</span> Lambda 2</div>
<div class="p-3 text-left md:w-1/5 md:border md:border-slate"><span class="font-bold md:hidden">Submitter:</span> Peter Dimov</div>
<div class="p-3 text-left md:w-1/5 md:border md:border-slate"><span class="font-bold md:hidden">Review Manager:</span> Joel de Guzman</div>
<div class="p-3 text-left md:w-1/5 md:border md:border-slate"><span class="font-bold md:hidden">Review Dates:</span> March 22, 2021 - March 31, 2021</div>
<div class="p-3 text-left md:w-1/5 md:border md:border-slate"><span class="font-bold md:hidden">Result:</span> <a href="#" class="text-orange">Pending</a></div>
</div>
<div class="py-6 border-b md:flex md:py-0 md:border-b-0 border-slate">
<div class="p-3 text-left md:w-1/5 md:border md:border-slate"><span class="font-bold md:hidden">Submission:</span> Lambda 2</div>
<div class="p-3 text-left md:w-1/5 md:border md:border-slate"><span class="font-bold md:hidden">Submitter:</span> Peter Dimov</div>
<div class="p-3 text-left md:w-1/5 md:border md:border-slate"><span class="font-bold md:hidden">Review Manager:</span> Joel de Guzman</div>
<div class="p-3 text-left md:w-1/5 md:border md:border-slate"><span class="font-bold md:hidden">Review Dates:</span> March 22, 2021 - March 31, 2021</div>
<div class="p-3 text-left md:w-1/5 md:border md:border-slate"><span class="font-bold md:hidden">Result:</span> <a href="#" class="text-orange">Pending</a></div>
</div>
<div class="py-6 border-b md:flex md:py-0 md:border-b-0 border-slate">
<div class="p-3 text-left md:w-1/5 md:border md:border-slate"><span class="font-bold md:hidden">Submission:</span> Lambda 2</div>
<div class="p-3 text-left md:w-1/5 md:border md:border-slate"><span class="font-bold md:hidden">Submitter:</span> Peter Dimov</div>
<div class="p-3 text-left md:w-1/5 md:border md:border-slate"><span class="font-bold md:hidden">Review Manager:</span> Joel de Guzman</div>
<div class="p-3 text-left md:w-1/5 md:border md:border-slate"><span class="font-bold md:hidden">Review Dates:</span> March 22, 2021 - March 31, 2021</div>
<div class="p-3 text-left md:w-1/5 md:border md:border-slate"><span class="font-bold md:hidden">Result:</span> <a href="#" class="text-orange">Pending</a></div>
</div>
<div class="py-6 border-b md:flex md:py-0 md:border-b-0 border-slate">
<div class="p-3 text-left md:w-1/5 md:border md:border-slate"><span class="font-bold md:hidden">Submission:</span> Lambda 2</div>
<div class="p-3 text-left md:w-1/5 md:border md:border-slate"><span class="font-bold md:hidden">Submitter:</span> Peter Dimov</div>
<div class="p-3 text-left md:w-1/5 md:border md:border-slate"><span class="font-bold md:hidden">Review Manager:</span> Joel de Guzman</div>
<div class="p-3 text-left md:w-1/5 md:border md:border-slate"><span class="font-bold md:hidden">Review Dates:</span> March 22, 2021 - March 31, 2021</div>
<div class="p-3 text-left md:w-1/5 md:border md:border-slate"><span class="font-bold md:hidden">Result:</span> <a href="#" class="text-orange">Pending</a></div>
</div>
<div class="py-6 border-b md:flex md:py-0 md:border-b-0 border-slate">
<div class="p-3 text-left md:w-1/5 md:border md:border-slate"><span class="font-bold md:hidden">Submission:</span> Lambda 2</div>
<div class="p-3 text-left md:w-1/5 md:border md:border-slate"><span class="font-bold md:hidden">Submitter:</span> Peter Dimov</div>
<div class="p-3 text-left md:w-1/5 md:border md:border-slate"><span class="font-bold md:hidden">Review Manager:</span> Joel de Guzman</div>
<div class="p-3 text-left md:w-1/5 md:border md:border-slate"><span class="font-bold md:hidden">Review Dates:</span> March 22, 2021 - March 31, 2021</div>
<div class="p-3 text-left md:w-1/5 md:border md:border-slate"><span class="font-bold md:hidden">Result:</span> <a href="#" class="text-orange">Pending</a></div>
</div>
<div class="py-6 border-b md:flex md:py-0 md:border-b-0 border-slate">
<div class="p-3 text-left md:w-1/5 md:border md:border-slate"><span class="font-bold md:hidden">Submission:</span> Lambda 2</div>
<div class="p-3 text-left md:w-1/5 md:border md:border-slate"><span class="font-bold md:hidden">Submitter:</span> Peter Dimov</div>
<div class="p-3 text-left md:w-1/5 md:border md:border-slate"><span class="font-bold md:hidden">Review Manager:</span> Joel de Guzman</div>
<div class="p-3 text-left md:w-1/5 md:border md:border-slate"><span class="font-bold md:hidden">Review Dates:</span> March 22, 2021 - March 31, 2021</div>
<div class="p-3 text-left md:w-1/5 md:border md:border-slate"><span class="font-bold md:hidden">Result:</span> <a href="#" class="text-orange">Pending</a></div>
</div>
<div class="py-6 border-b md:flex md:py-0 md:border-b-0 border-slate">
<div class="p-3 text-left md:w-1/5 md:border md:border-slate"><span class="font-bold md:hidden">Submission:</span> Lambda 2</div>
<div class="p-3 text-left md:w-1/5 md:border md:border-slate"><span class="font-bold md:hidden">Submitter:</span> Peter Dimov</div>
<div class="p-3 text-left md:w-1/5 md:border md:border-slate"><span class="font-bold md:hidden">Review Manager:</span> Joel de Guzman</div>
<div class="p-3 text-left md:w-1/5 md:border md:border-slate"><span class="font-bold md:hidden">Review Dates:</span> March 22, 2021 - March 31, 2021</div>
<div class="p-3 text-left md:w-1/5 md:border md:border-slate"><span class="font-bold md:hidden">Result:</span> <a href="#" class="text-orange">Pending</a></div>
</div>
<div class="py-6 border-b md:flex md:py-0 md:border-b-0 border-slate">
<div class="p-3 text-left md:w-1/5 md:border md:border-slate"><span class="font-bold md:hidden">Submission:</span> Lambda 2</div>
<div class="p-3 text-left md:w-1/5 md:border md:border-slate"><span class="font-bold md:hidden">Submitter:</span> Peter Dimov</div>
<div class="p-3 text-left md:w-1/5 md:border md:border-slate"><span class="font-bold md:hidden">Review Manager:</span> Joel de Guzman</div>
<div class="p-3 text-left md:w-1/5 md:border md:border-slate"><span class="font-bold md:hidden">Review Dates:</span> March 22, 2021 - March 31, 2021</div>
<div class="p-3 text-left md:w-1/5 md:border md:border-slate"><span class="font-bold md:hidden">Result:</span> <a href="#" class="text-orange">Pending</a></div>
</div>
</div>
</div>
<h1 class="text-xl md:text-3xl">Past Review Results and Milestones</h1>
<section class="content my-4 !whitespace-normal">
<table class="p-0 m-0 max-w-full text-sm text-left border-0 border-separate table-auto dark:text-white border-spacing-0 text-slate dark:bg-charcoal">
<thead>
<tr>
<th scope="col" class="p-3 text-base bg-gray-100 border border-r-0 border-b-0 border-gray-400 dark:border-slate dark:bg-charcoal w-1/5">Submission</th>
<th scope="col" class="p-3 text-base bg-gray-100 border border-r-0 border-b-0 border-gray-400 dark:border-slate dark:bg-charcoal w-1/5">Submitter</th>
<th scope="col" class="p-3 text-base bg-gray-100 border border-r-0 border-b-0 border-gray-400 dark:border-slate dark:bg-charcoal w-1/5">Review Manager</th>
<th scope="col" class="p-3 text-base bg-gray-100 border border-r-0 border-b-0 border-gray-400 dark:border-slate dark:bg-charcoal w-1/5">Review Dates</th>
<th scope="col" class="p-3 text-base bg-gray-100 border border-b-0 border-gray-400 dark:border-slate dark:bg-charcoal w-1/5">Result</th>
</tr>
</thead>
<tbody>
{% for review in object_list %}
<tr>
<td class="p-3 break-words border border-r-0 {% if not forloop.last %}border-b-0 {% endif %}border-gray-400 dark:border-slate">
<span class="font-bold md:hidden">Submission:</span> {{ review.submission }}
</td>
<td class="p-3 border border-r-0 {% if not forloop.last %}border-b-0 {% endif %}border-gray-400 dark:border-slate">
<span class="font-bold md:hidden">Submitter:</span>
{% for submitter in review.submitters.all %}
{% avatar user=submitter avatar_type="with_name" %}{% if not forloop.last %}<br/>{% endif %}
{% empty %}
{{ review.submitter_raw|default:"-" }}
{% endfor %}
</td>
<td class="p-3 border border-r-0 {% if not forloop.last %}border-b-0 {% endif %}border-gray-400 dark:border-slate">
<span class="font-bold md:hidden">Review Manager:</span>
{% if review.review_manager %}
{% avatar user=review.review_manager avatar_type="with_name" %}
{% else %}
{{ review.review_manager_raw|default:"-" }}
{% endif %}
</td>
<td class="p-3 border border-r-0 {% if not forloop.last %}border-b-0 {% endif %}border-gray-400 dark:border-slate">
<span class="font-bold md:hidden">Review Dates:</span> {{ review.review_dates }}
</td>
<td class="p-3 border {% if not forloop.last %}border-b-0 {% endif %}border-gray-400 dark:border-slate">
<span class="font-bold md:hidden">Result:</span>
{% for result in review.results.all %}
<p class="p-1">
{% if not result.is_most_recent %}<s>{% endif %}
<a href="{{ result.announcement_link }}" class="block items-center py-1 px-2 rounded cursor-pointer hover:bg-gray-100 dark:hover:bg-slate text-sky-600 dark:text-sky-300 hover:text-orange dark:hover:text-orange">{{ result.short_description }}</a>
{% if not result.is_most_recent %}</s>{% endif %}
</p>
{% endfor %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
</section>
</div>
{% endblock %}

View File

@@ -2,129 +2,125 @@
{% load static %}
{% block subnav %}
<div class="py-8 px-4 space-y-4 text-sm uppercase border-b md:py-2 md:px-0 md:space-x-10 border-slate">
<a href="{% url 'review-process' %}" class="block md:inline md:py-1 md:border-b text-orange md:border-orange">Review Process</a>
<a href="{% url 'review-request' %}" class="block md:inline">Submit Review</a>
<a href="{% url 'review-upcoming' %}" class="block md:inline">Upcoming Reviews</a>
<a href="{% url 'review-past' %}" class="block md:inline">Past Reviews</a>
</div>
{% endblock %}
{% block content %}
<div class="py-0 px-3 mb-3 md:py-6 md:px-0">
<div class="container">
<section class="max-w-4xl mx-auto px-4">
<h2 class="mt-4 mb-0">Boost Library Submission Process</h2>
<p>This page describes the process a library developer goes through to get a library accepted into Boost.</p>
<h2 id="steps-for-getting-a-library-accepted-by-boost">Steps for Getting a Library Accepted by Boost</h2>
<div class="items-center mb-6 space-x-3 md:flex md:mb-0 md:space-x-6">
<div class="py-16 space-y-11 md:w-1/2">
<h1 class="text-4xl">Formal Reviews are Vital</h1>
<p>
Boost libraries are selected for their relevance, high quality, and fitness for purpose. The review
process ensures that only the best libraries with committed maintainers become part of the collection.
Volunteers who write reviews are performing a vital service for the community. They are heroes!
</p>
<div class="mt-16">
<a href="#" class="py-3 px-6 uppercase rounded-md border text-orange border-slate">Submit a review <small> > </small></a>
</div>
</div>
<div id="reviewProcessAnim" class="md:w-1/2"></div>
</div>
<h3 id="learn-about-boost">Learn about Boost</h3>
<p>Follow posts on the <a href="https://lists.boost.org/mailman/listinfo.cgi/boost">Boost developers' mailing list</a> for a while, or look through the <a href="https://lists.boost.org/Archives/boost/">message archives</a>. Explore this website. Learn the <a href="../contributor-guide/requirements/library-requirements.html">Library Requirements</a>. Read the rest of this page to learn about the process. Search the web to get an idea of the commitment required to get a library into Boost.</p>
<h2 class="block my-16 text-4xl text-center">How It Works</h2>
<p>There is a culture associated with Boost, aimed at encouraging high quality libraries by a process of discussion and refinement. Some libraries get past community review in less than two years from first concept, but most take longer, sometimes a lot longer. Five to ten years to get a library past review and into Boost is not unheard of, and you should prepare yourself for the personal investment required.</p>
<div class="py-11 px-4 space-y-8 border-t md:flex border-slate">
<div class="md:w-1/4">
<img class="mx-auto" src="{% static 'img/icons/graphic_determine-interest.svg' %}" alt="Determine Interest" />
</div>
<div class="space-y-6 md:w-3/4">
<h3 class="text-2xl">1. Determine Interest</h3>
<p>
Make sure your library is suitable for the collection by proposing it to the Boost community in the forum.
Existing authors will either endorse the library or explain why it is not appropriate or what might be
needed to make it a good fit.
</p>
</div>
</div>
<h3 id="determine-interest">Determine Interest</h3>
<p>While participation in reviews for other submissions is not a prerequisite for submitting a library to Boost, it is highly recommended; it will acquaint you with the process and the emotional demands of a formal review. There's nothing that quite deflates the ego like having brilliant members of the C++ community critiquing your work, but, alas, it's worth it!</p>
<div class="py-11 px-4 space-y-8 border-t md:flex border-slate">
<div class="md:w-1/4">
<img class="mx-auto" src="{% static 'img/icons/graphic_post-and-request-manager.svg' %}" alt="Post and Request a Manager" />
</div>
<div class="space-y-6 md:w-3/4">
<h3 class="text-2xl">2. Post and Request a Manager</h3>
<p>
The Review Manager is the overseer for the review process of a library. To get the library on the schedule,
a suitable manager should volunteer. Post your library to the mailing list to find a volunteer manager.
</p>
</div>
</div>
<p>Potential library submitters should be careful to research the prior art before beginning to design a new library. Unfortunately, now and then folks arrive at Boost with a new library into which they have invested many hours, only to find that Boost already has that functionality, and sometimes has had it for years. Candidates should also research libraries being developed by others intended for Boost - if you have an itch to scratch, often so have had others and collaboration developing their library is usually considerably more efficient than going at it alone.</p>
<div class="py-11 px-4 space-y-8 border-t md:flex border-slate">
<div class="md:w-1/4">
<img class="mx-auto" src="{% static 'img/icons/graphic_prepare-for-review.svg' %}" alt="Prepare for Review" />
</div>
<div class="space-y-6 md:w-3/4">
<h3 class="text-2xl">3. Prepare for Review</h3>
<p>
The time between the library endorsement and the beginning of the review on the schedule is a great
opportunity to make sure that the library <a href="#" class="text-orange">meets the requirements</a>,
including documentation and build scripts. Before your formal review begins, double-, triple-, and
quadruple-check your library. Verify that every code example works, that all unit tests pass on at least
two compilers on at least two major operating systems, and run your documentation through a spelling and
grammar checker.
</p>
</div>
</div>
<p>Potential library submitters should also be careful to publicize, canvas for, and gauge interest in their library, ideally before beginning it, but certainly before submitting it for review. Even a superbly designed library can fail review if there isn't enough interest in the subject matter; We can only review libraries with enough appeal to form a viable peer review. Ensuring that enough people are interested in your potential library goes a long way to ensure that.</p>
<div class="py-11 px-4 space-y-8 border-t md:flex border-slate">
<div class="md:w-1/4">
<img class="mx-auto" src="{% static 'img/icons/graphic_review-period.svg' %}" alt="Review Period" />
</div>
<div class="space-y-6 md:w-3/4">
<h3 class="text-2xl">4. Review Period</h3>
<p>
For the two weeks after the review starts, engaged community members from the mailing list ask questions,
make comments, and often get into arguments about your library. Finally when they are done, they write
their summary containing their opinion of your library and, most importantly, whether to accept or reject it.
</p>
</div>
</div>
<p>There are many places to publicize and canvas for a library. The <a href="https://lists.boost.org/mailman/listinfo.cgi/boost">Boost developers' mailing list</a> ought to be your first stop in gauging interest in a possible new C++ library. Be prepared to pivot your design and focus until your proposed library finds traction. Other places useful for gauging interest in a library might be <a href="https://www.reddit.com/r/cpp/">Reddit/r/cpp</a>.</p>
<div class="py-11 px-4 space-y-8 border-t md:flex border-slate">
<div class="md:w-1/4">
<img class="mx-auto" src="{% static 'img/icons/graphic_accepted-or-rejected.svg' %}" alt="Accepted or Rejected" />
</div>
<div class="space-y-6 md:w-3/4">
<h3 class="text-2xl">4. Accepted or Rejected</h3>
<p>
After the scheduled time has passed, the review manager retreats for a while to contemplate the results
and issue a verdict on whether or not the library will be added to Boost.
</p>
</div>
</div>
<p>A message to the Boost developers mailing list might be as simple as "Is there any interest in a library which solves Traveling Salesperson problems in linear time?"</p>
<div class="py-5 px-3 my-4 rounded md:px-11 md:my-16 bg-charcoal">
<div class="flex justify-between mb-6">
<span class="inline py-1 px-3 w-auto text-lg uppercase rounded text-green bg-green/10">Recent reviews</span>
<span class="inline px-2 w-auto uppercase text-orange">See All ></span>
</div>
<div class="py-11 sm:space-y-11 md:flex md:space-y-0 md:space-x-6 md:divide-x-2 divide-slate">
<div class="pr-2 pb-6 border-b md:pl-6 md:border-0 last:border-0 border-slate">
<h3 class="pb-3 text-2xl">Library Name Here</h3>
<p class="py-5 text-lg">About the library.. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue. Maecenas faucibus mollis interdum. Nullam quis risus eget urna mollis ornare vel eu leo. Donec id elit non mi porta gravida at eget metus. Donec id elit non mi porta gravida at eget metus. Sed posuere consectetur est at lobortis.</p>
<div class="mt-3"><span class="mr-5">Authors</span> <img src="{% static 'img/fpo/user.png' %}" alt="user" class="inline" /></div>
</div>
<div class="pr-2 pb-6 border-b md:pl-6 md:border-0 last:border-0 border-slate">
<h3 class="pb-3 text-2xl">Library Name Here</h3>
<p class="py-5 text-lg">About the library.. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue. Maecenas faucibus mollis interdum. Nullam quis risus eget urna mollis ornare vel eu leo. Donec id elit non mi porta gravida at eget metus. Donec id elit non mi porta gravida at eget metus. Sed posuere consectetur est at lobortis.</p>
<div class="mt-3"><span class="mr-5">Authors</span> <img src="{% static 'img/fpo/user.png' %}" alt="user" class="inline" /></div>
</div>
<div class="pr-2 pb-6 border-b md:pl-6 md:border-0 last:border-0 border-slate">
<h3 class="pb-3 text-2xl">Library Name Here</h3>
<p class="py-5 text-lg">About the library.. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla vitae elit libero, a pharetra augue. Maecenas faucibus mollis interdum. Nullam quis risus eget urna mollis ornare vel eu leo. Donec id elit non mi porta gravida at eget metus. Donec id elit non mi porta gravida at eget metus. Sed posuere consectetur est at lobortis.</p>
<div class="mt-3"><span class="mr-5">Authors</span> <img src="{% static 'img/fpo/user.png' %}" alt="user" class="inline" /></div>
</div>
</div>
</div>
<p>A bit of further description or snippet of code may be helpful. By the way, the preferred format for messages on the mailing list is plain text; not rich text, HTML, etc.</p>
</div>
<p>Avoid posting lengthy descriptions, documentation, or code to the mailing list, and, please, no attachments. The best place to provide lengthy material is via. a web link. Project hosting services such as sourceforge, github, google code, and bitbucket serve well for this purpose.</p>
<h3 id="start-development">Start Development</h3>
<p>If response to an initial query indicates interest, then by all means make your library publicly available if you haven't already done so.</p>
<p>Please commit your code to a version control system such as Git, and make your documentation available in HTML format on a public website such as Github. An issue tracker such as the one provided by Github is also highly recommended.</p>
<p>Your library should contain material as if it were on the boost.org web site. The closer your library reflects the final directory structure and format of the web site, the better. This makes it possible for reviewers to simply copy your code into the Boost distribution for testing.</p>
<p>Please verify that your library compiles and runs under at least two compilers. This flushes out obvious portability problems.</p>
<p>It is recommended that you release your code under <a href="../user-guide/bsl.html">the Boost Software License</a>.</p>
<h3 id="refinement">Refinement</h3>
<p>Discuss, refine, rewrite. Repeat until satisfied.</p>
<p>The exact details of this process varies a lot. Usually it is public, on the mailing list, but frequently discussion happens in private emails. For some libraries the process is over quickly, but for others it goes on for months. It's often challenging, and sometimes veers into completely unexpected directions.</p>
<p>The <a href="https://lists.boost.org/Archives/boost/">mailing list archives</a> of past messages is one way to see how this process worked for other Boost libraries.</p>
<p>Alternatively, follow the status links in the previously submitted libraries listed in <a href="review-results.html#pastreviewresults">Past Review Results and Milestones</a>.</p>
<h3 id="getting-seconded-for-review">Getting Seconded for Review</h3>
<p>When you feel that your library is ready for entry into Boost, you need to find at least one member (but preferably several) of the Boost community who is willing to publicly endorse your library for entry into Boost. A simple method of achieving this is to post to the <a href="https://lists.boost.org/mailman/listinfo.cgi/boost">Boost developers' mailing list</a> a short description of your library, links to its github and documentation, and a request for endorsements.</p>
<p>It is expected that those who endorse a library for review will have performed at least a cursory check of the library's suitability for Boost in terms of documentation, fit with the rest of Boost and usefulness. A public endorsement of a library for review means that from an initial glance, they believe that the library has a reasonable chance to be accepted during a formal review. The expectation is that these endorsers will themselves review of the library during formal review period, though this is not binding.</p>
<p>Once you have a list of people who have publicly endorsed your library for review, email the <a href="https://lists.boost.org/mailman/listinfo.cgi/boost">Boost developers' mailing list</a> to request that your library be added to the <a href="review-results.html#currentscheule">Current Schedule</a> where the following information will be shown:</p>
<ul>
<li>Submission (Library name)</li>
<li>Submitter (author or authors)</li>
<li>Review Manager</li>
<li>Review Dates (start and end dates of the review period)</li>
<li>Links to the status of the review, given as announcements.</li>
</ul>
<h3 id="seek-a-review-manager">Seek a Review Manager</h3>
<p>In order to schedule a formal review, the author must find a capable volunteer to manage the review. This should be someone with knowledge of the library domain, and experience with the review process. See <a href="managing-reviews.html">Managing Reviews</a> for the responsibilities of the review manager.</p>
<p>Authors can find community members interested in managing reviews through discussion of the library on the developer list. If no one steps forward to volunteer to manage the review, it is appropriate to contact an experienced Boost member who showed interest in the library. Be considerate that managing a review is a serious commitment; for this reason, it's better to contact the member off-list.</p>
<p>If you cannot find a review manager after three weeks using the means above, and your submission is targeting eventual standardization, there is a list of Boost regulars who are also WG21 committee members who have volunteered to act as review managers in such cases. Try them in the order listed. They are: Zach Laine, Micheal Caisse, Matt Calabrese, Edward Diener, Louis Dionne, Vinnie Falco, Glen Fernandes, and David Sankel.</p>
<p>Once a potential review manager has been identified, contact the <a href="managing-reviews.html#reviewwizards">Review Wizards</a> for approval. The wizards approve review managers based on their level of participation in the Boost community.</p>
<p>The review wizards will coordinate with both the author and review manager to schedule a date convenient for both.</p>
<h3 id="formal-review">Formal Review</h3>
<p>Before your formal review begins, double-, triple-, and quadruple-check your library. Verify that every code example works, that all unit tests pass on at least two compilers on at least two major operating systems, and run your documentation through a spelling and grammar checker.</p>
<p>Please do not modify your library on its master branch during a review. Instead, modify a separate develop branch in response to feedback and reviews. For bigger ticket items of work, open issues on your issue tracker so interested people can track the fixing of specific issues raised.</p>
<p>The review manager will consider all the reviews made by members of the community and arrive at a decision on whether your library is rejected, conditionally accepted or unconditionally accepted. They will post a report summarizing the decision publicly. If conditions are attached to acceptance, you will need to implement those conditions or else undergo an additional formal review.</p>
<h3 id="fast-track-reviews">Fast Track Reviews</h3>
<p>To qualify for a fast track review:</p>
<ul>
<li>The component must be small.</li>
<li>The technique must be already in use in Boost libraries and the new component provides a common implementation.</li>
<li>A full Boost-conformant implementation is available in the sandbox.</li>
<li>The review wizard determines that the proposal qualifies for fast track review.</li>
</ul>
<h4 id="fast-track-procedure">Fast Track Procedure</h4>
<ol>
<li>The Boost review wizard posts a review announcement to the main Boost developer's list. The fast track review period will normally last for 5 days. No two fast-track reviews will run in parallel. Fast track reviews may run during full reviews, though generally, this is to be avoided.</li>
<li>After the review period ends, the submitter will post a review summary containing proposed changes to the reviewed implementation.</li>
<li>The review wizard will accept or reject the proposed library and proposed changes.</li>
<li>After applying the proposed changes, the component is checked into the repository like any other library.</li>
</ol>
<h3 id="mini-reviews">Mini-Reviews</h3>
<p>It is possible that in the review process some issues might need to be fixed as a <em>requirement</em> for acceptance. If a review does result in conditions on acceptance, the review manager may request a <em>Mini-Review</em>, at a later date, to determine if the conditions have been met. The Mini-Review is usually conducted by the same review manager.</p>
<h3 id="boost-website-posting">Boost Website Posting</h3>
<p>Once an accepted library is ready for inclusion on the Boost web site, the submitter is typically given Boost repository write access, and expected to check-in and maintain the library there. Contact the moderators if you need write access or direct use of the repository isn't possible for you.</p>
<h3 id="people-page">People Page</h3>
<p>If the boost.org web site doesn't already have your capsule biography and picture (optional, with not-too-serious pictures preferred!), please send them to the Boost webmaster. It is up to you as to whether or not the biography includes your email address or other contact information. The preferred picture format is .jpg, but other common formats are acceptable. The preferred image size is 500x375 but the webmaster has photo editing software and can do the image preparation if necessary.</p>
<h3 id="lifecycle">Lifecycle</h3>
<p>Libraries are software; they lose their value over time if not maintained. Postings on the Boost developers or users mailing lists can alert you to potential maintenance needs; please plan to maintain your library over time. If you no longer can or wish to maintain your library, please post a message on the Boost developers mailing list asking for a new maintainer to volunteer and then spend the time to help them take over.</p>
<p>Orphaned libraries will be put in the care of a maintenance team, pending a search for a new maintainer.</p>
<h3 id="library-maintainers-rights-and-responsibilities">Library Maintainer's Rights and Responsibilities</h3>
<p>By submitting a library to Boost, you accept responsibility for maintaining your library, or finding a qualified volunteer to serve as maintainer. You must be willing to put your library and documentation under a Boost-compatible license.</p>
<p>You will be expected to respond to reasonable bug reports and questions on time and to participate as needed in discussions of your library on the Boost mailing lists.</p>
<p>You are free to change your library in any way you wish, and you are encouraged to actively make improvements. However, peer review is an important part of the Boost process and as such you are also encouraged to get feedback from the Boost community before making substantial changes to the interface of an accepted library.</p>
<p>If at some point you no longer wish to serve as maintainer of your library, it is your responsibility to make this known to the Boost community, and to find another individual to take your place.</p>
<p>Libraries which have been abandoned will be put in care of a maintenance team.</p>
</section>
</div>
{% endblock %}

View File

@@ -1,71 +1,75 @@
{% extends "base.html" %}
{% load static %}
{% load static avatar_tags %}
{% block subnav %}
<div class="py-8 px-4 space-y-4 text-sm uppercase border-b md:py-2 md:px-0 md:space-x-10 border-slate">
<a href="{% url 'review-process' %}" class="block md:inline">Review Process</a>
<a href="{% url 'review-request' %}" class="block md:inline">Submit Review</a>
<a href="{% url 'review-upcoming' %}" class="block md:inline md:py-1 md:border-b text-orange md:border-orange">Upcoming Reviews</a>
<a href="{% url 'review-past' %}" class="block md:inline">Past Reviews</a>
</div>
{% endblock %}
{% block title %}Upcoming Reviews{% endblock %}
{% block content %}
<div class="py-0 px-3 mb-3 md:py-6 md:px-0">
<div class="py-16 space-y-11 md:w-full">
<h1 class="text-4xl text-center">Upcoming Reviews</h1>
<div class="p-6 w-full rounded bg-charcoal">
<div class="hidden w-full md:flex">
<div class="p-3 w-1/5">Submnission</div>
<div class="p-3 w-1/5">Submitter</div>
<div class="p-3 w-1/5">Link</div>
<div class="p-3 w-1/5">Review Manager</div>
<div class="p-3 w-1/5">Review Dates</div>
</div>
<div class="py-6 border-b md:flex md:py-0 md:border-b-0 border-slate">
<div class="p-3 text-left md:w-1/5 md:border md:border-slate"><span class="font-bold md:hidden">Submission:</span> Text (mini-review)</div>
<div class="p-3 text-left md:w-1/5 md:border md:border-slate"><span class="font-bold md:hidden">Submitter:</span> Zach Laine</div>
<div class="p-3 text-left md:w-1/5 md:border md:border-slate"><span class="font-bold md:hidden">Link:</span> <a href="#" class="text-orange">Github Documentation</a></div>
<div class="p-3 text-left md:w-1/5 md:border md:border-slate"><span class="font-bold md:hidden">Review Manager:</span> Needed!</div>
<div class="p-3 text-left md:w-1/5 md:border md:border-slate"><span class="font-bold md:hidden">Review Dates:</span> March 22, 2021 - March 31, 2021</div>
</div>
<div class="py-6 border-b md:flex md:py-0 md:border-b-0 border-slate">
<div class="p-3 text-left md:w-1/5 md:border md:border-slate"><span class="font-bold md:hidden">Submission:</span> Text (mini-review)</div>
<div class="p-3 text-left md:w-1/5 md:border md:border-slate"><span class="font-bold md:hidden">Submitter:</span> Zach Laine</div>
<div class="p-3 text-left md:w-1/5 md:border md:border-slate"><span class="font-bold md:hidden">Link:</span> <a href="#" class="text-orange">Github Documentation</a></div>
<div class="p-3 text-left md:w-1/5 md:border md:border-slate"><span class="font-bold md:hidden">Review Manager:</span> Needed!</div>
<div class="p-3 text-left md:w-1/5 md:border md:border-slate"><span class="font-bold md:hidden">Review Dates:</span> March 22, 2021 - March 31, 2021</div>
</div>
<div class="py-6 border-b md:flex md:py-0 md:border-b-0 border-slate">
<div class="p-3 text-left md:w-1/5 md:border md:border-slate"><span class="font-bold md:hidden">Submission:</span> Text (mini-review)</div>
<div class="p-3 text-left md:w-1/5 md:border md:border-slate"><span class="font-bold md:hidden">Submitter:</span> Zach Laine</div>
<div class="p-3 text-left md:w-1/5 md:border md:border-slate"><span class="font-bold md:hidden">Link:</span> <a href="#" class="text-orange">Github Documentation</a></div>
<div class="p-3 text-left md:w-1/5 md:border md:border-slate"><span class="font-bold md:hidden">Review Manager:</span> Needed!</div>
<div class="p-3 text-left md:w-1/5 md:border md:border-slate"><span class="font-bold md:hidden">Review Dates:</span> March 22, 2021 - March 31, 2021</div>
</div>
<div class="py-6 border-b md:flex md:py-0 md:border-b-0 border-slate">
<div class="p-3 text-left md:w-1/5 md:border md:border-slate"><span class="font-bold md:hidden">Submission:</span> Text (mini-review)</div>
<div class="p-3 text-left md:w-1/5 md:border md:border-slate"><span class="font-bold md:hidden">Submitter:</span> Zach Laine</div>
<div class="p-3 text-left md:w-1/5 md:border md:border-slate"><span class="font-bold md:hidden">Link:</span> <a href="#" class="text-orange">Github Documentation</a></div>
<div class="p-3 text-left md:w-1/5 md:border md:border-slate"><span class="font-bold md:hidden">Review Manager:</span> Needed!</div>
<div class="p-3 text-left md:w-1/5 md:border md:border-slate"><span class="font-bold md:hidden">Review Dates:</span> March 22, 2021 - March 31, 2021</div>
</div>
</div>
</div>
<h1 class="text-xl md:text-3xl">Upcoming Reviews</h1>
<section class="content my-4 !whitespace-normal">
<table class="p-0 m-0 w-full text-sm text-left border-0 border-separate table-auto dark:text-white border-spacing-0 text-slate dark:bg-charcoal">
<thead>
<tr>
<th scope="col" class="p-3 text-base bg-gray-100 border border-r-0 border-b-0 border-gray-400 dark:border-slate dark:bg-charcoal w-1/5">Submission</th>
<th scope="col" class="p-3 text-base bg-gray-100 border border-r-0 border-b-0 border-gray-400 dark:border-slate dark:bg-charcoal w-1/5">Submitter</th>
<th scope="col" class="p-3 text-base bg-gray-100 border border-r-0 border-b-0 border-gray-400 dark:border-slate dark:bg-charcoal w-1/5">Link</th>
<th scope="col" class="p-3 text-base bg-gray-100 border border-r-0 border-b-0 border-gray-400 dark:border-slate dark:bg-charcoal w-1/5">Review Manager</th>
<th scope="col" class="p-3 text-base bg-gray-100 border border-b-0 border-gray-400 dark:border-slate dark:bg-charcoal w-1/5">Review Dates</th>
</tr>
</thead>
<tbody>
{% for review in object_list %}
<tr>
<td class="p-3 border border-r-0 {% if not forloop.last %}border-b-0 {% endif %}border-gray-400 dark:border-slate">
<span class="font-bold md:hidden">Submission:</span> {{ review.submission }}
</td>
<td class="p-3 border border-r-0 {% if not forloop.last %}border-b-0 {% endif %}border-gray-400 dark:border-slate">
<span class="font-bold md:hidden">Submitter:</span>
{% for submitter in review.submitters.all %}
{% avatar user=submitter avatar_type="with_name" %}{% if not forloop.last %}<br/>{% endif %}
{% empty %}
{{ review.submitter_raw|default:"-" }}
{% endfor %}
</td>
<td class="p-3 border border-r-0 {% if not forloop.last %}border-b-0 {% endif %}border-gray-400 dark:border-slate">
<span class="font-bold md:hidden">Link:</span>
<p class="p-1 pt-0">
<a href="{{ review.github_link }}" class="block items-center py-1 px-2 rounded cursor-pointer hover:bg-gray-100 dark:hover:bg-slate text-sky-600 dark:text-sky-300 hover:text-orange dark:hover:text-orange">
<i class="float-left mt-1 mr-1 fab fa-github"></i>
Github
</a>
</p>
<p class="p-1">
<a href="{{ review.documentation_link }}" class="block items-center py-1 px-2 rounded cursor-pointer hover:bg-gray-100 dark:hover:bg-slate text-sky-600 dark:text-sky-300 hover:text-orange dark:hover:text-orange">
<i class="float-left mt-1 mr-1 fa fa-book"></i>
Documentation
</a>
</p>
</td>
<td class="p-3 border border-r-0 {% if not forloop.last %}border-b-0 {% endif %}border-gray-400 dark:border-slate">
<span class="font-bold md:hidden">Review Manager:</span>
{% if review.review_manager %}
{% avatar user=review.review_manager avatar_type="with_name" %}
{% else %}
{{ review.review_manager_raw|default:"-" }}
{% endif %}
</td>
<td class="p-3 border {% if not forloop.last %}border-b-0 {% endif %}border-gray-400 dark:border-slate">
<span class="font-bold md:hidden">Review Dates:</span> {{ review.review_dates }}
</td>
</tr>
{% endfor %}
</tbody>
</table>
</section>
<div class="flex space-x-11">
<div class="w-1/2">
<h3>Overview</h3>
<p>
Reviews are scheduled when the review wizards approve a review manager and agree with the manager and
author on dates. See <a href="#" class="text-orange">Review Process</a> for more information.
author on dates. See <a href="{% url 'review-process' %}" class="cursor-pointer text-sky-600 dark:text-sky-300 hover:text-orange dark:hover:text-orange">Review Process</a> for more information.
</p>
<p>
In addition to upcoming reviews, the schedule includes recent reviews already completed; that helps track
@@ -79,8 +83,8 @@
In order for a review to proceed, a Boost member must volunteer to manage the review. This should be someone
with experience with the review process and knowledge of the library's domain. Reviewers have been celebrated
and recognized for contributing to some of the greatest revolutionary programs. Be a leader and make an impact
within the Boost community! Contact Mateusz Loskot (<a href="#" class="text-orange">mateusz@loskot.net</a>) or
John Phillips (<a href="#" class="text-orange">johnphillipsithaca@gmail.com</a>) to become a reviewer.
within the Boost community! Contact Mateusz Loskot (<a href="mailto:mateusz@loskot.net" class="cursor-pointer text-sky-600 dark:text-sky-300 hover:text-orange dark:hover:text-orange">mateusz@loskot.net</a>) or
John Phillips (<a href="mailto:johnphillipsithaca@gmail.com" class="cursor-pointer text-sky-600 dark:text-sky-300 hover:text-orange dark:hover:text-orange">johnphillipsithaca@gmail.com</a>) to become a reviewer.
</p>
</div>

View File

@@ -1,6 +1,7 @@
from django.contrib import admin
from django.http import HttpResponseRedirect
from django.db.models.query import QuerySet
from django.http import HttpRequest, HttpResponseRedirect
from django.urls import path
from . import models
@@ -53,3 +54,32 @@ class VersionAdmin(admin.ModelAdmin):
""",
)
return HttpResponseRedirect("../")
class ResultInline(admin.StackedInline):
model = models.ReviewResult
autocomplete_fields = ("review",)
verbose_name = "Result"
verbose_name_plural = "Results"
extra = 0
@admin.register(models.Review)
class ReviewAdmin(admin.ModelAdmin):
list_display = ["submission", "review_dates", "get_results"]
search_fields = ["submission"]
inlines = [ResultInline]
def get_results(self, obj):
return " | ".join(obj.results.values_list("short_description", flat=True))
def get_queryset(self, request: HttpRequest) -> QuerySet:
return super().get_queryset(request).prefetch_related("results")
@admin.register(models.ReviewResult)
class ReviewResultAdmin(admin.ModelAdmin):
list_display = ["review", "short_description"]
def get_queryset(self, request: HttpRequest) -> QuerySet:
return super().get_queryset(request).select_related("review")

View File

@@ -0,0 +1,269 @@
from bs4 import BeautifulSoup
import djclick as click
import requests
from django.contrib.auth import get_user_model
from django.db import transaction
from django.db.models import Q
from libraries.utils import generate_fake_email
from versions.models import Review, ReviewResult
User = get_user_model()
@click.command()
@click.option(
"--dry-run", is_flag=True, help="Parse the data but don't save to database"
)
@click.option(
"--dry-run-users", is_flag=True, help="Save reviews, but don't link users"
)
def command(dry_run, dry_run_users):
"""Import Boost library reviews from boost.org table data"""
click.echo("Starting review import from boost.org")
url = "https://www.boost.org/community/review_schedule.html"
response = requests.get(url)
soup = BeautifulSoup(response.text, "html.parser")
# Parse both tables
scheduled_review_table = soup.find("table", summary="Formal Review Schedule")
past_review_table = soup.find("table", summary="Review Results")
if not scheduled_review_table or not past_review_table:
click.secho("Could not find review tables in page content", fg="red", err=True)
return
upcoming_reviews = _parse_table(scheduled_review_table)
past_reviews = _parse_table(past_review_table, past_results=True)
click.echo(
f"Found {len(upcoming_reviews)} upcoming and {len(past_reviews)} past reviews"
)
if dry_run:
click.echo("Dry run - no changes made")
return
reviews_created = results_created = 0
# Import everything in a transaction
with transaction.atomic():
# Create or update past reviews
for review_data, results in past_reviews:
review = Review.objects.create(**review_data)
reviews_created += 1
for result in results:
ReviewResult.objects.create(review=review, **result)
results_created += 1
# Create or update upcoming reviews
for review_data, _ in upcoming_reviews:
Review.objects.update_or_create(
submission=review_data["submission"],
submitter_raw=review_data["submitter_raw"],
defaults=review_data,
)
reviews_created += 1
click.secho("\nFinished importing reviews", fg="green")
click.secho(
f"Created {reviews_created} reviews and {results_created} results", fg="green"
)
users_linked = 0
managers_linked = 0
click.echo("\nAttempting to parse users\n")
# Link users in separate transaction
with transaction.atomic():
for review in Review.objects.all():
# Handle submitters
submitter_names = _parse_users_from_raw_names(review.submitter_raw)
for first_name, last_name in submitter_names:
if dry_run_users:
click.echo(
"Would link submitter: "
f"{first_name} {last_name} to {review.submission}"
)
else:
user = _get_user_from_name(first_name, last_name)
if user:
review.submitters.add(user)
users_linked += 1
click.echo(f"Linked submitter {user} to {review.submission}")
# Handle review manager
if (
review.review_manager_raw
and review.review_manager_raw
!= Review._meta.get_field("review_manager_raw").default
):
manager_names = _parse_users_from_raw_names(review.review_manager_raw)
if manager_names:
first_name, last_name = manager_names[0]
if dry_run_users:
click.echo(
"Would set manager: "
f"{first_name} {last_name} for {review.submission}"
)
else:
user = _get_user_from_name(first_name, last_name)
if user:
review.review_manager = user
review.save()
managers_linked += 1
click.echo(f"Linked manager {user} to {review.submission}")
click.secho(
f"\nLinked {users_linked} submitters and {managers_linked} managers",
fg="green",
)
click.secho("\nDone!", fg="green")
def _parse_table(table, past_results=False):
"""Parse a review table and return review data"""
rows = table.find_all("tr")[1:] # Skip header row
reviews = []
for row in rows:
cells = row.find_all("td")
if not cells or not cells[0].text.strip():
continue
review_data = {
"submission": cells[0].text.strip(),
"submitter_raw": cells[1].text.strip(),
"review_manager_raw": cells[2 if past_results else 3].text.strip(),
"review_dates": cells[3 if past_results else 4].text.strip(),
"github_link": "",
"documentation_link": "",
}
# Handle links for upcoming reviews
if not past_results:
links = cells[2].find_all("a")
for link in links:
if "github" in link.text.lower():
review_data["github_link"] = link.get("href", "")
elif "documentation" in link.text.lower():
review_data["documentation_link"] = link.get("href", "")
# Handle results for past reviews
results_data = []
if past_results:
result_cell = cells[4]
# First handle any linked results
for element in result_cell.contents:
if element.name == "del":
link = element.find("a")
if link:
results_data.append(
{
"short_description": link.text.strip(),
"announcement_link": link.get("href", ""),
"is_most_recent": False,
}
)
elif element.name == "a":
results_data.append(
{
"short_description": element.text.strip(),
"announcement_link": element.get("href", ""),
"is_most_recent": True,
}
)
# If no results were found, use the text content of the cell
if not results_data and (description := result_cell.text.strip()):
results_data.append({"short_description": description})
reviews.append((review_data, results_data))
return reviews
def _parse_users_from_raw_names(raw_name_string: str) -> list[tuple[str, str]]:
"""
Parse a raw name string into a list of (first_name, last_name) tuples.
Marked as private since this is a fairly narrow, clunky solution optimized for
the names seen in the actual boost.org table.
Handles inputs like:
"John Doe"
"John Doe & Jane Smith"
"John Doe and Jane Smith"
"John Doe, Jane Smith"
"John Doe, Jane Smith, Joaquin M López Muñoz"
Returns a list like:
[("John", "Doe"), ("Jane", "Smith"), ("Joaquin M López", "Muñoz")]
"""
# Clean up the string - normalize whitespace and separators
cleaned = (
raw_name_string.replace("\n", " & ")
.replace("\t", " ")
.replace(" and ", " & ")
.replace(",", " & ")
.replace("ª", "") # special character observed
.replace("OvermindDL1", "") # replaced review manager observed
)
# Collapse multiple `&` separators
while " & & " in cleaned:
cleaned = cleaned.replace(" & & ", " & ")
# Collapse multiple spaces
cleaned = " ".join(cleaned.split())
# Split on & and clean up each name
names = [name.strip() for name in cleaned.split("&")]
parsed_names = []
for name in names:
if not name:
continue
# Try to split into first and last name
parts = name.split()
if len(parts) == 1:
# Just one name, treat as first name
parsed_names.append((parts[0], ""))
else:
# Assume last word is last name, rest is first name
parsed_names.append((" ".join(parts[:-1]), parts[-1]))
return parsed_names
def _get_user_from_name(first_name, last_name):
matching_users = User.objects.filter(
Q(first_name__iexact=first_name, last_name__iexact=last_name)
| Q(
first_name__unaccent__iexact=first_name,
last_name__unaccent__iexact=last_name,
)
| Q(display_name__unaccent__iexact=f"{first_name} {last_name}")
| Q(display_name__iexact=f"{first_name} {last_name}")
)
if count := matching_users.count() == 1:
return matching_users.first()
elif count:
click.secho(
f"Found multiple users with the same name: {first_name} {last_name}",
fg="red",
)
return None
# No existing user by this name; create a fake "stub" user
fake_email = generate_fake_email(f"{first_name} {last_name}")
if user := User.objects.filter(email=fake_email).first():
return user
return User.objects.create_stub_user(
fake_email.lower(), first_name=first_name, last_name=last_name
)

View File

@@ -0,0 +1,82 @@
# Generated by Django 4.2.16 on 2024-11-13 16:12
from django.conf import settings
from django.contrib.postgres.operations import UnaccentExtension
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
("versions", "0011_version_full_release"),
]
operations = [
UnaccentExtension(),
migrations.CreateModel(
name="Review",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("submission", models.CharField()),
("submitter_raw", models.CharField()),
("review_manager_raw", models.CharField(blank=True, default="Needed!")),
("review_dates", models.CharField()),
("github_link", models.URLField(blank=True, default="")),
("documentation_link", models.URLField(blank=True, default="")),
(
"review_manager",
models.ForeignKey(
default=None,
null=True,
on_delete=django.db.models.deletion.SET_NULL,
related_name="managed_reviews",
to=settings.AUTH_USER_MODEL,
),
),
(
"submitters",
models.ManyToManyField(
related_name="submitted_reviews", to=settings.AUTH_USER_MODEL
),
),
],
),
migrations.CreateModel(
name="ReviewResult",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("short_description", models.CharField()),
("is_most_recent", models.BooleanField(default=True)),
("announcement_link", models.URLField(blank=True, default="")),
(
"review",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="results",
to="versions.review",
),
),
],
options={
"verbose_name_plural": "review results",
},
),
]

View File

@@ -1,4 +1,5 @@
import re
from django.contrib.auth import get_user_model
from django.db import models
from django.urls import reverse
from django.utils.functional import cached_property
@@ -6,6 +7,8 @@ from django.utils.text import slugify
from .managers import VersionManager, VersionFileManager
User = get_user_model()
class Version(models.Model):
name = models.CharField(
@@ -151,3 +154,53 @@ class VersionFile(models.Model):
display_name = models.CharField(max_length=256, blank=True, null=True)
objects = VersionFileManager()
# TODO: should this go in a new `reviews` app?
class Review(models.Model):
submission = models.CharField()
# TODO: drop raw fields once users have been linked
submitter_raw = models.CharField()
review_manager_raw = models.CharField(blank=True, default="Needed!")
submitters = models.ManyToManyField(User, related_name="submitted_reviews")
review_manager = models.ForeignKey(
User,
related_name="managed_reviews",
null=True,
default=None,
on_delete=models.SET_NULL,
)
review_dates = models.CharField()
github_link = models.URLField(blank=True, default="")
documentation_link = models.URLField(blank=True, default="")
def __str__(self) -> str:
return self.submission
def __repr__(self) -> str:
return f"<Review: {self} ({self.pk})>"
class ReviewResult(models.Model):
review = models.ForeignKey(Review, related_name="results", on_delete=models.CASCADE)
short_description = models.CharField()
is_most_recent = models.BooleanField(default=True)
announcement_link = models.URLField(blank=True, default="")
class Meta:
verbose_name_plural = "review results"
def __str__(self) -> str:
return self.short_description
def __repr__(self) -> str:
return f"<ReviewResult: {self} ({self.pk})>"
def save(self, *args, **kwargs):
"""Ensure only one status is most recent per review."""
if self.is_most_recent:
sibling_results = ReviewResult.objects.filter(review=self.review).exclude(
pk=self.pk
)
sibling_results.update(is_most_recent=False)
super().save(*args, **kwargs)

View File

@@ -113,3 +113,19 @@ def test_stripped_boost_url_slug(slug, expected, version):
def test_get_absolute_url(version):
expected_url = f"/releases/{version.slug}/"
assert version.get_absolute_url() == expected_url
def test_review_results():
review = baker.make("versions.Review")
pending_result = baker.make(
"versions.ReviewResult", review=review, short_description="Pending"
)
assert pending_result.is_most_recent
accepted_result = baker.make(
"versions.ReviewResult", review=review, short_description="Accepted"
)
assert accepted_result.is_most_recent
pending_result.refresh_from_db()
assert not pending_result.is_most_recent

View File

@@ -1,9 +1,10 @@
from django.db.models.query import QuerySet
import structlog
from itertools import groupby
from operator import attrgetter
from django.db.models import Q, Count
from django.views.generic import DetailView, TemplateView
from django.views.generic import DetailView, TemplateView, ListView
from django.views.generic.edit import FormMixin
from django.shortcuts import redirect, get_object_or_404
from django.contrib import messages
@@ -21,7 +22,7 @@ from libraries.utils import (
determine_selected_boost_version,
library_doc_latest_transform,
)
from versions.models import Version
from versions.models import Review, Version
logger = structlog.get_logger(__name__)
@@ -173,3 +174,27 @@ class InProgressReleaseNotesView(TemplateView):
return rendered_content.content_html
except RenderedContent.DoesNotExist:
return
class PastReviewListView(ListView):
model = Review
template_name = "review/past_reviews.html"
def get_queryset(self) -> QuerySet[Review]:
qs = super().get_queryset()
return (
qs.filter(results__isnull=False)
.distinct()
.select_related("review_manager")
.prefetch_related("results", "submitters")
.order_by("id")
)
class ScheduledReviewListView(ListView):
model = Review
template_name = "review/upcoming_reviews.html"
def get_queryset(self) -> QuerySet[Review]:
qs = super().get_queryset()
return qs.exclude(results__isnull=False).distinct()