Create goal with privacy

This commit is contained in:
Mouse Reeve 2021-01-16 11:34:19 -08:00
parent 739b6e19e2
commit 112b9f9332
11 changed files with 161 additions and 16 deletions

View file

@ -192,4 +192,4 @@ class ShelfForm(CustomForm):
class GoalForm(CustomForm):
class Meta:
model = models.AnnualGoal
fields = ['user', 'year', 'goal']
fields = ['user', 'year', 'goal', 'privacy']

View file

@ -1,4 +1,4 @@
# Generated by Django 3.0.7 on 2021-01-15 23:01
# Generated by Django 3.0.7 on 2021-01-16 18:43
import bookwyrm.models.fields
from django.conf import settings
@ -20,9 +20,10 @@ class Migration(migrations.Migration):
('created_date', models.DateTimeField(auto_now_add=True)),
('updated_date', models.DateTimeField(auto_now=True)),
('remote_id', bookwyrm.models.fields.RemoteIdField(max_length=255, null=True, validators=[bookwyrm.models.fields.validate_remote_id])),
('goal', bookwyrm.models.fields.IntegerField()),
('goal', models.IntegerField()),
('year', models.IntegerField(default=2021)),
('user', bookwyrm.models.fields.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to=settings.AUTH_USER_MODEL)),
('privacy', models.CharField(choices=[('public', 'Public'), ('unlisted', 'Unlisted'), ('followers', 'Followers'), ('direct', 'Direct')], default='public', max_length=255)),
('user', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to=settings.AUTH_USER_MODEL)),
],
options={
'unique_together': {('user', 'year')},

View file

@ -19,7 +19,7 @@ from bookwyrm.utils import regex
from .base_model import OrderedCollectionPageMixin
from .base_model import ActivitypubMixin, BookWyrmModel
from .federated_server import FederatedServer
from . import fields
from . import fields, Review
class User(OrderedCollectionPageMixin, AbstractUser):
@ -224,9 +224,14 @@ class KeyPair(ActivitypubMixin, BookWyrmModel):
class AnnualGoal(BookWyrmModel):
''' set a goal for how many books you read in a year '''
user = fields.ForeignKey('User', on_delete=models.PROTECT)
goal = fields.IntegerField()
user = models.ForeignKey('User', on_delete=models.PROTECT)
goal = models.IntegerField()
year = models.IntegerField(default=timezone.now().year)
privacy = models.CharField(
max_length=255,
default='public',
choices=fields.PrivacyLevels.choices
)
class Meta:
''' unqiueness constraint '''
@ -243,6 +248,23 @@ class AnnualGoal(BookWyrmModel):
finish_date__year__gte=self.year
).order_by('finish_date').all()
@property
def ratings(self):
''' ratings for books read this year '''
book_ids = [r.book.id for r in self.books]
reviews = Review.objects.filter(
user=self.user,
book__in=book_ids,
)
return {r.book.id: r.rating for r in reviews}
@property
def progress_percent(self):
return int(float(self.book_count / self.goal) * 100)
@property
def book_count(self):
''' how many books you've read this year '''

View file

@ -71,7 +71,8 @@
{% if goal %}
<section class="section">
<div class="block">
{{ goal }} hi
<h3 class="title is-4">{{ goal.year }} Reading Goal</h3>
{% include 'snippets/goal_progress.html' with goal=goal %}
</div>
</section>
{% endif %}

View file

@ -0,0 +1,43 @@
{% extends 'layout.html' %}
{% block content %}
<section class="block">
<h1 class="title">{{ year }} Reading Progress</h1>
{% if not goal and user == request.user %}
{% now 'Y' as year %}
<article class="card">
<header class="card-header">
<h2 class="card-header-title has-background-primary has-text-white">
<span class="icon icon-book is-size-3 mr-2" aria-hidden="true"></span> {{ year }} reading goal
</h2>
</header>
<section class="card-content content">
<p>Set a goal for how many books you'll finish reading in {{ year }}, and track your progress throughout the year.</p>
{% include 'snippets/goal_form.html' %}
</section>
</article>
{% elif not goal and user != request.user %}
<p>{{ user.display_name }} hasn't set a reading goal for {{ year }}.</p>
{% else %}
{% include 'snippets/goal_progress.html' with goal=goal %}
{% endif %}
</section>
{% if goal.books %}
<section>
<h2 class="title">{% if goal.user == request.user %}Your{% else %}{{ goal.user.display_name }}'s{% endif %} {{ year }} Books</h2>
<div class="columns is-multiline">
{% for book in goal.books %}
<div class="column is-narrow">
<div class="box">
<a href="{{ book.book.local_path }}">
{% include 'snippets/discover/small-book.html' with book=book.book rating=goal.ratings %}
</a>
</div>
</div>
{% endfor %}
</div>
</section>
{% endif %}
{% endblock %}

View file

@ -1,7 +1,9 @@
{% load bookwyrm_tags %}
{% if book %}
{% include 'snippets/book_cover.html' with book=book %}
{% if ratings %}
{% include 'snippets/stars.html' with rating=ratings|dict_key:book.id %}
{% endif %}
<h3 class="title is-6"><a href="/book/{{ book.id }}">{{ book.title }}</a></h3>
{% if book.authors %}

View file

@ -1,4 +1,4 @@
<form method="post" name="goal" action="/goal/{{ year }}">
<form method="post" name="goal" action="{{ request.user.local_path }}/goal/{{ year }}">
{% csrf_token %}
<input type="hidden" name="year" value="{{ year }}">
<input type="hidden" name="user" value="{{ request.user.id }}">
@ -15,16 +15,15 @@
</div>
<div class="column">
<label for="post_status" class="label">
<input type="checkbox" name="post-status" id="post_status" class="checkbox" checked>
Post to feed
<label class="label"><p class="mb-2">Goal privacy:</p>
{% include 'snippets/privacy_select.html' with no_label=True %}
</label>
<div class="field">
{% include 'snippets/privacy_select.html' %}
</div>
</div>
</div>
<label for="post_status" class="label">
<input type="checkbox" name="post-status" id="post_status" class="checkbox" checked>
Post to feed
</label>
<p>
<button type="submit" class="button is-link">Set goal</button>

View file

@ -0,0 +1,10 @@
<p>
{% if goal.progress_percent >= 100 %}
Success!
{% elif goal.progress_percent %}
{{ goal.progress_percent }}% complete!
{% endif %}
{% if goal.user == request.user %}You've{% else %}{{ goal.user.display_name }} has{% endif %} read {% if request.path != goal.local_path %}<a href="{{ goal.local_path }}">{% endif %}{{ goal.book_count }} of {{ goal.goal }} books{% if request.path != goal.local_path %}</a>{% endif %}.
</p>
<progress class="progress is-large" value="{{ goal.book_count }}" max="{{ goal.goal }}">{{ goal.progress_percent }}%</progress>

View file

@ -75,6 +75,9 @@ urlpatterns = [
re_path(r'%s/following(.json)?/?$' % user_path, views.Following.as_view()),
re_path(r'^edit-profile/?$', views.EditUser.as_view()),
# reading goals
re_path(r'%s/goal/(?P<year>\d{4})/?$' % user_path, views.Goal.as_view()),
# statuses
re_path(r'%s(.json)?/?$' % status_path, views.Status.as_view()),
re_path(r'%s/activity/?$' % status_path, views.Status.as_view()),

View file

@ -7,6 +7,7 @@ from .direct_message import DirectMessage
from .error import not_found_page, server_error_page
from .follow import follow, unfollow
from .follow import accept_follow_request, delete_follow_request, handle_accept
from .goal import Goal
from .import_data import Import, ImportStatus
from .interaction import Favorite, Unfavorite, Boost, Unboost
from .invite import ManageInvites, Invite

63
bookwyrm/views/goal.py Normal file
View file

@ -0,0 +1,63 @@
''' non-interactive pages '''
from django.contrib.auth.decorators import login_required
from django.http import HttpResponseNotFound
from django.shortcuts import redirect
from django.template.response import TemplateResponse
from django.utils.decorators import method_decorator
from django.views import View
from bookwyrm import forms, models
from .helpers import get_user_from_username
# pylint: disable= no-self-use
@method_decorator(login_required, name='dispatch')
class Goal(View):
''' track books for the year '''
def get(self, request, username, year):
''' reading goal page '''
user = get_user_from_username(username)
year = int(year)
goal = models.AnnualGoal.objects.filter(
year=year, user=user
).first()
if not goal and user != request.user:
return redirect('/')
if goal and user != request.user:
if goal.privacy == 'direct' or \
(goal.privacy == 'followers' and not follower):
return HttpResponseNotFound()
data = {
'title': '%s\'s %d Reading' % (user.display_name, year),
'goal_form': forms.GoalForm(instance=goal),
'goal': goal,
'user': user,
'year': year,
}
return TemplateResponse(request, 'goal.html', data)
def post(self, request, username, year):
''' update or create an annual goal '''
user = get_user_from_username(username)
if user != request.user:
return HttpResponseNotFound()
year = int(year)
goal = models.AnnualGoal.objects.filter(
year=year, user=request.user
).first()
form = forms.GoalForm(request.POST, instance=goal)
if not form.is_valid():
data = {
'title': '%s\'s %d Reading' % (goal.user.display_name, year),
'goal_form': form,
'goal': goal,
'year': year,
}
return TemplateResponse(request, 'goal.html', data)
form.save()
return redirect(request.headers.get('Referer', '/'))