Merge branch 'main' into list-not-loading
This commit is contained in:
34
bookwyrm/migrations/0123_alter_user_preferred_language.py
Normal file
34
bookwyrm/migrations/0123_alter_user_preferred_language.py
Normal file
@ -0,0 +1,34 @@
|
||||
# Generated by Django 3.2.5 on 2022-01-04 22:31
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("bookwyrm", "0122_alter_annualgoal_year"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name="user",
|
||||
name="preferred_language",
|
||||
field=models.CharField(
|
||||
blank=True,
|
||||
choices=[
|
||||
("en-us", "English"),
|
||||
("de-de", "Deutsch (German)"),
|
||||
("es-es", "Español (Spanish)"),
|
||||
("gl-es", "Galego (Galician)"),
|
||||
("fr-fr", "Français (French)"),
|
||||
("lt-lt", "Lietuvių (Lithuanian)"),
|
||||
("pt-br", "Português do Brasil (Brazilian Portuguese)"),
|
||||
("pt-pt", "Português Europeu (European Portuguese)"),
|
||||
("zh-hans", "简体中文 (Simplified Chinese)"),
|
||||
("zh-hant", "繁體中文 (Traditional Chinese)"),
|
||||
],
|
||||
max_length=255,
|
||||
null=True,
|
||||
),
|
||||
),
|
||||
]
|
@ -168,7 +168,8 @@ LANGUAGES = [
|
||||
("gl-es", _("Galego (Galician)")),
|
||||
("fr-fr", _("Français (French)")),
|
||||
("lt-lt", _("Lietuvių (Lithuanian)")),
|
||||
("pt-br", _("Português - Brasil (Brazilian Portuguese)")),
|
||||
("pt-br", _("Português do Brasil (Brazilian Portuguese)")),
|
||||
("pt-pt", _("Português Europeu (European Portuguese)")),
|
||||
("zh-hans", _("简体中文 (Simplified Chinese)")),
|
||||
("zh-hant", _("繁體中文 (Traditional Chinese)")),
|
||||
]
|
||||
|
@ -3,7 +3,7 @@ import math
|
||||
import logging
|
||||
from django.dispatch import receiver
|
||||
from django.db import transaction
|
||||
from django.db.models import signals, Count, Q
|
||||
from django.db.models import signals, Count, Q, Case, When, IntegerField
|
||||
|
||||
from bookwyrm import models
|
||||
from bookwyrm.redis_store import RedisStore, r
|
||||
@ -85,24 +85,17 @@ class SuggestedUsers(RedisStore):
|
||||
def get_suggestions(self, user, local=False):
|
||||
"""get suggestions"""
|
||||
values = self.get_store(self.store_id(user), withscores=True)
|
||||
results = []
|
||||
annotations = [
|
||||
When(pk=int(pk), then=self.get_counts_from_rank(score)["mutuals"])
|
||||
for (pk, score) in values
|
||||
]
|
||||
# annotate users with mutuals and shared book counts
|
||||
for user_id, rank in values:
|
||||
counts = self.get_counts_from_rank(rank)
|
||||
try:
|
||||
user = models.User.objects.get(
|
||||
id=user_id, is_active=True, bookwyrm_user=True
|
||||
)
|
||||
except models.User.DoesNotExist as err:
|
||||
# if this happens, the suggestions are janked way up
|
||||
logger.exception(err)
|
||||
continue
|
||||
user.mutuals = counts["mutuals"]
|
||||
if (local and user.local) or not local:
|
||||
results.append(user)
|
||||
if len(results) >= 5:
|
||||
break
|
||||
return results
|
||||
users = models.User.objects.filter(
|
||||
is_active=True, bookwyrm_user=True, id__in=[pk for (pk, _) in values]
|
||||
).annotate(mutuals=Case(*annotations, output_field=IntegerField(), default=0))
|
||||
if local:
|
||||
users = users.filter(local=True)
|
||||
return users[:5]
|
||||
|
||||
|
||||
def get_annotated_users(viewer, *args, **kwargs):
|
||||
|
@ -2,8 +2,5 @@
|
||||
{% load i18n %}
|
||||
|
||||
{% block searchresults %}
|
||||
<h2 class="title is-5">
|
||||
{% trans "Add new members!" %}
|
||||
</h2>
|
||||
{% include 'groups/suggested_users.html' with suggested_users=suggested_users %}
|
||||
{% endblock %}
|
||||
|
@ -5,11 +5,11 @@
|
||||
<div class="column is-two-thirds">
|
||||
<input type="hidden" name="user" value="{{ request.user.id }}" />
|
||||
<div class="field">
|
||||
<label class="label" for="id_name">{% trans "Group Name:" %}</label>
|
||||
<label class="label" for="group_form_id_name">{% trans "Group Name:" %}</label>
|
||||
{{ group_form.name }}
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="label" for="id_description">{% trans "Group Description:" %}</label>
|
||||
<label class="label" for="group_form_id_description">{% trans "Group Description:" %}</label>
|
||||
{{ group_form.description }}
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,37 +1,41 @@
|
||||
{% extends 'groups/layout.html' %}
|
||||
{% load i18n %}
|
||||
{% load bookwyrm_tags %}
|
||||
{% load bookwyrm_group_tags %}
|
||||
{% load markdown %}
|
||||
|
||||
{% block panel %}
|
||||
|
||||
<div class="columns mt-3">
|
||||
<section class="column is-three-quarters">
|
||||
|
||||
{% if group.user == request.user %}
|
||||
<div class="block">
|
||||
<form class="field has-addons" method="get" action="{% url 'group-find-users' group.id %}">
|
||||
<div class="control">
|
||||
<input type="text" name="user_query" value="{{ request.GET.user_query }}" class="input" placeholder="{% trans 'Search to add a user' %}" aria-label="{% trans 'Search to add a user' %}">
|
||||
</div>
|
||||
<div class="control">
|
||||
<button class="button" type="submit">
|
||||
<span class="icon icon-search" title="{% trans 'Search' %}">
|
||||
<span class="is-sr-only">{% trans "Search" %}</span>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% block searchresults %}
|
||||
{% endblock %}
|
||||
<div class="mb-2">
|
||||
{% include "groups/members.html" with group=group %}
|
||||
</div>
|
||||
|
||||
<h2 class="title is-5">Lists</h2>
|
||||
</section>
|
||||
</div>
|
||||
<header class="columns content is-mobile">
|
||||
<div class="column">
|
||||
<h2 class="title is-5">{% trans "Lists" %}</h2>
|
||||
<p class="subtitle is-6">
|
||||
{% trans "Members of this group can create group-curated lists." %}
|
||||
</p>
|
||||
</div>
|
||||
{% if request.user.is_authenticated and group|is_member:request.user %}
|
||||
<div class="column is-narrow is-flex">
|
||||
{% trans "Create List" as button_text %}
|
||||
{% include 'snippets/toggle/open_button.html' with controls_text="create_list" icon_with_text="plus" text=button_text focus="create_list_header" %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</header>
|
||||
{% if request.user.is_authenticated and group|is_member:request.user %}
|
||||
<div class="block">
|
||||
{% include 'lists/create_form.html' with controls_text="create_list" curation_group=group %}
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="columns mt-3">
|
||||
<section class="column is-three-quarters">
|
||||
{% if not lists %}
|
||||
<p>{% trans "This group has no lists" %}</p>
|
||||
{% else %}
|
||||
@ -45,19 +49,17 @@
|
||||
<a href="{{ list.local_path }}">{{ list.name }}</a> <span class="subtitle">{% include 'snippets/privacy-icons.html' with item=list %}</span>
|
||||
</h4>
|
||||
</header>
|
||||
|
||||
{% with list_books=list.listitem_set.all|slice:5 %}
|
||||
{% if list_books %}
|
||||
<div class="card-image columns is-mobile is-gapless is-clipped">
|
||||
{% for book in list_books %}
|
||||
<a class="column is-cover" href="{{ book.book.local_path }}">
|
||||
{% include 'snippets/book_cover.html' with book=book.book cover_class='is-h-s' size='small' aria='show' %}
|
||||
</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if list_books %}
|
||||
<div class="card-image columns is-mobile is-gapless is-clipped">
|
||||
{% for book in list_books %}
|
||||
<a class="column is-cover" href="{{ book.book.local_path }}">
|
||||
{% include 'snippets/book_cover.html' with book=book.book cover_class='is-h-s' size='small' aria='show' %}
|
||||
</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
|
||||
<div class="card-content is-flex-grow-0">
|
||||
<div class="is-clipped" {% if list.description %}title="{{ list.description }}"{% endif %}>
|
||||
{% if list.description %}
|
||||
@ -74,9 +76,8 @@
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
{% endif %}
|
||||
{% include "snippets/pagination.html" with page=items %}
|
||||
</section>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
|
@ -1,5 +1,6 @@
|
||||
{% extends 'layout.html' %}
|
||||
{% load i18n %}
|
||||
{% load bookwyrm_group_tags %}
|
||||
|
||||
{% block title %}{{ group.name }}{% endblock %}
|
||||
|
||||
@ -11,12 +12,12 @@
|
||||
{% include 'groups/created_text.html' with group=group %}
|
||||
</p>
|
||||
</div>
|
||||
{% if request.user == group.user %}
|
||||
<div class="column is-narrow is-flex">
|
||||
{% if request.user == group.user %}
|
||||
{% trans "Edit group" as button_text %}
|
||||
{% include 'snippets/toggle/open_button.html' with text=button_text icon_with_text="pencil" controls_text="edit_group" focus="edit_group_header" %}
|
||||
{% endif %}
|
||||
{% trans "Edit group" as button_text %}
|
||||
{% include 'snippets/toggle/open_button.html' with text=button_text icon_with_text="pencil" controls_text="edit_group" focus="edit_group_header" %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</header>
|
||||
|
||||
<div class="block content">
|
||||
|
@ -5,8 +5,22 @@
|
||||
{% load bookwyrm_group_tags %}
|
||||
|
||||
<h2 class="title is-5">Group Members</h2>
|
||||
<p class="subtitle is-6">{% trans "Members can add and remove books on a group's book lists" %}</p>
|
||||
|
||||
{% if group.user == request.user %}
|
||||
<div class="block">
|
||||
<form class="field has-addons" method="get" action="{% url 'group-find-users' group.id %}">
|
||||
<div class="control">
|
||||
<input type="text" name="user_query" value="{{ request.GET.user_query }}" class="input" placeholder="{% trans 'Search to add a user' %}" aria-label="{% trans 'Search to add a user' %}">
|
||||
</div>
|
||||
<div class="control">
|
||||
<button class="button" type="submit">
|
||||
<span class="icon icon-search" title="{% trans 'Search' %}">
|
||||
<span class="is-sr-only">{% trans "Search" %}</span>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if group.user != request.user and group|is_member:request.user %}
|
||||
<form action="{% url 'remove-group-member' %}" method="POST" class="my-4">
|
||||
{% csrf_token %}
|
||||
|
@ -3,6 +3,9 @@
|
||||
{% load humanize %}
|
||||
|
||||
{% if suggested_users %}
|
||||
<h2 class="title is-5">
|
||||
{% trans "Add new members!" %}
|
||||
</h2>
|
||||
<div class="column is-flex is-flex-grow-0">
|
||||
{% for user in suggested_users %}
|
||||
<div class="box has-text-centered is-shadowless has-background-white-bis m-2">
|
||||
|
@ -7,6 +7,6 @@
|
||||
|
||||
{% block form %}
|
||||
<form name="create-list" method="post" action="{% url 'lists' %}">
|
||||
{% include 'lists/form.html' %}
|
||||
{% include 'lists/form.html' with curation_group=group %}
|
||||
</form>
|
||||
{% endblock %}
|
||||
|
@ -25,7 +25,7 @@
|
||||
value="closed"
|
||||
aria-described-by="id_curation_closed_help"
|
||||
id="id_curation_closed"
|
||||
{% if not list or list.curation == 'closed' %} checked{% endif %}
|
||||
{% if not curation_group.exists or not list or list.curation == 'closed' %}checked{% endif %}
|
||||
>
|
||||
<label for="id_curation_closed">
|
||||
{% trans "Closed" %}
|
||||
@ -76,7 +76,7 @@
|
||||
value="group"
|
||||
aria-described-by="id_curation_group_help"
|
||||
id="id_curation_group"
|
||||
{% if list.curation == 'group' %}checked{% endif %}
|
||||
{% if curation_group.id or list.curation == 'group' %}checked{% endif %}
|
||||
>
|
||||
<label for="id_curation_group">
|
||||
{% trans "Group" %}
|
||||
@ -85,15 +85,15 @@
|
||||
{% trans "Group members can add to and remove from this list" %}
|
||||
</p>
|
||||
|
||||
<fieldset class="{% if list.curation != 'group' %}is-hidden{% endif %}" id="list_group_selector">
|
||||
<fieldset class="{% if list.curation != 'group' and not curation_group %}is-hidden{% endif %}" id="list_group_selector">
|
||||
{% if user.memberships.exists %}
|
||||
<label class="label" for="id_group" id="group">{% trans "Select Group" %}</label>
|
||||
<div class="field has-addons">
|
||||
<div class="select control">
|
||||
<select name="group" id="id_group">
|
||||
<option value="" disabled {% if not list.group %} selected{% endif %}>{% trans "Select a group" %}</option>
|
||||
<option value="" disabled {% if not list.group and not curation_group %} selected{% endif %}>{% trans "Select a group" %}</option>
|
||||
{% for membership in user.memberships.all %}
|
||||
<option value="{{ membership.group.id }}" {% if list.group.id == membership.group.id %} selected{% endif %}>{{ membership.group.name }}</option>
|
||||
<option value="{{ membership.group.id }}" {% if list.group.id == membership.group.id or curation_group.id == membership.group.id %} selected{% endif %}>{{ membership.group.name }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
|
@ -34,7 +34,8 @@ class Group(View):
|
||||
data = {
|
||||
"group": group,
|
||||
"lists": lists,
|
||||
"group_form": forms.GroupForm(instance=group),
|
||||
"group_form": forms.GroupForm(instance=group, auto_id="group_form_id_%s"),
|
||||
"list_form": forms.ListForm(),
|
||||
"path": "/group",
|
||||
}
|
||||
return TemplateResponse(request, "groups/group.html", data)
|
||||
@ -121,6 +122,11 @@ class FindUsers(View):
|
||||
"""basic profile info"""
|
||||
user_query = request.GET.get("user_query")
|
||||
group = get_object_or_404(models.Group, id=group_id)
|
||||
lists = (
|
||||
models.List.privacy_filter(request.user)
|
||||
.filter(group=group)
|
||||
.order_by("-updated_date")
|
||||
)
|
||||
|
||||
if not group:
|
||||
return HttpResponseBadRequest()
|
||||
@ -142,7 +148,7 @@ class FindUsers(View):
|
||||
.filter(similarity__gt=0.5, local=True)
|
||||
.order_by("-similarity")[:5]
|
||||
)
|
||||
data = {"no_results": not user_results}
|
||||
no_results = not user_results
|
||||
|
||||
if user_results.count() < 5:
|
||||
user_results = list(user_results) + suggested_users.get_suggestions(
|
||||
@ -151,8 +157,11 @@ class FindUsers(View):
|
||||
|
||||
data = {
|
||||
"suggested_users": user_results,
|
||||
"no_results": no_results,
|
||||
"group": group,
|
||||
"group_form": forms.GroupForm(instance=group),
|
||||
"lists": lists,
|
||||
"group_form": forms.GroupForm(instance=group, auto_id="group_form_id_%s"),
|
||||
"list_form": forms.ListForm(),
|
||||
"user_query": user_query,
|
||||
"requestor_is_manager": request.user == group.user,
|
||||
}
|
||||
|
Reference in New Issue
Block a user