add bookmark model

This commit is contained in:
Zhao-Linux 2022-11-18 02:17:45 +07:00
parent 03db2db899
commit d0e4d9512c
14 changed files with 223 additions and 16 deletions

View file

@ -60,6 +60,7 @@ from judge.views import (
user, user,
volunteer, volunteer,
pagevote, pagevote,
bookmark,
widgets, widgets,
internal, internal,
) )
@ -448,6 +449,8 @@ urlpatterns = [
), ),
url(r"^pagevotes/upvote/$", pagevote.upvote_page, name="pagevote_upvote"), url(r"^pagevotes/upvote/$", pagevote.upvote_page, name="pagevote_upvote"),
url(r"^pagevotes/downvote/$", pagevote.downvote_page, name="pagevote_downvote"), url(r"^pagevotes/downvote/$", pagevote.downvote_page, name="pagevote_downvote"),
url(r"^bookmarks/dobookmark/$", bookmark.dobookmark_page, name="dobookmark"),
url(r"^bookmarks/undobookmark/$", bookmark.undobookmark_page, name="undobookmark"),
url(r"^comments/upvote/$", comment.upvote_comment, name="comment_upvote"), url(r"^comments/upvote/$", comment.upvote_comment, name="comment_upvote"),
url(r"^comments/downvote/$", comment.downvote_comment, name="comment_downvote"), url(r"^comments/downvote/$", comment.downvote_comment, name="comment_downvote"),
url(r"^comments/hide/$", comment.comment_hide, name="comment_hide"), url(r"^comments/hide/$", comment.comment_hide, name="comment_hide"),

View file

@ -0,0 +1,38 @@
# Generated by Django 3.2.16 on 2022-11-17 17:13
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('judge', '0137_auto_20221116_2201'),
]
operations = [
migrations.CreateModel(
name='BookMark',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('page', models.CharField(db_index=True, max_length=30, verbose_name='associated page')),
],
options={
'verbose_name': 'bookmark',
'verbose_name_plural': 'bookmarks',
},
),
migrations.CreateModel(
name='MakeBookMark',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('bookmark', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='bookmark', to='judge.bookmark')),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='user_bookmark', to='judge.profile')),
],
options={
'verbose_name': 'make bookmark',
'verbose_name_plural': 'make bookmarks',
'unique_together': {('user', 'bookmark')},
},
),
]

View file

@ -55,6 +55,7 @@ from judge.models.submission import (
from judge.models.ticket import Ticket, TicketMessage from judge.models.ticket import Ticket, TicketMessage
from judge.models.volunteer import VolunteerProblemVote from judge.models.volunteer import VolunteerProblemVote
from judge.models.pagevote import PageVote, PageVoteVoter from judge.models.pagevote import PageVote, PageVoteVoter
from judge.models.bookmark import BookMark, MakeBookMark
revisions.register(Profile, exclude=["points", "last_access", "ip", "rating"]) revisions.register(Profile, exclude=["points", "last_access", "ip", "rating"])
revisions.register(Problem, follow=["language_limits"]) revisions.register(Problem, follow=["language_limits"])
@ -79,4 +80,5 @@ revisions.register(ContestParticipation)
revisions.register(Rating) revisions.register(Rating)
revisions.register(PageVoteVoter) revisions.register(PageVoteVoter)
revisions.register(VolunteerProblemVote) revisions.register(VolunteerProblemVote)
revisions.register(MakeBookMark)
del revisions del revisions

38
judge/models/bookmark.py Normal file
View file

@ -0,0 +1,38 @@
from django.db import models
from django.db.models import CASCADE
from django.utils.translation import gettext_lazy as _
from judge.models import Profile
__all__ = ["BookMark"]
class BookMark(models.Model):
page = models.CharField(
max_length=30,
verbose_name=_("associated page"),
db_index=True,
)
def get_bookmark(self, user):
userqueryset = MakeBookMark.objects.filter(bookmark=self, user=user)
if userqueryset.exists():
return True
else:
return False
class Meta:
verbose_name = _("bookmark")
verbose_name_plural = _("bookmarks")
def __str__(self):
return f"bookmark for {self.page}"
class MakeBookMark(models.Model):
bookmark = models.ForeignKey(BookMark, related_name="bookmark", on_delete=CASCADE)
user = models.ForeignKey(Profile, related_name="user_bookmark", on_delete=CASCADE, db_index=True)
class Meta:
unique_together = ["user", "bookmark"]
verbose_name = _("make bookmark")
verbose_name_plural = _("make bookmarks")

View file

@ -8,6 +8,7 @@ from django.views.generic import ListView
from judge.comments import CommentedDetailView from judge.comments import CommentedDetailView
from judge.views.pagevote import PageVoteDetailView, PageVoteListView from judge.views.pagevote import PageVoteDetailView, PageVoteListView
from judge.views.bookmark import BookMarkDetailView, BookMarkListView
from judge.models import ( from judge.models import (
BlogPost, BlogPost,
Comment, Comment,
@ -93,7 +94,7 @@ class FeedView(ListView):
return context return context
class PostList(FeedView, PageVoteListView): class PostList(FeedView, PageVoteListView, BookMarkListView):
model = BlogPost model = BlogPost
paginate_by = 10 paginate_by = 10
context_object_name = "posts" context_object_name = "posts"
@ -194,7 +195,7 @@ class CommentFeed(FeedView):
return context return context
class PostView(TitleMixin, CommentedDetailView, PageVoteDetailView): class PostView(TitleMixin, CommentedDetailView, PageVoteDetailView, BookMarkDetailView):
model = BlogPost model = BlogPost
pk_url_kwarg = "id" pk_url_kwarg = "id"
context_object_name = "post" context_object_name = "post"

87
judge/views/bookmark.py Normal file
View file

@ -0,0 +1,87 @@
from django.contrib.auth.decorators import login_required
from django.db import IntegrityError
from django.db.models import F
from django.http import (
Http404,
HttpResponse,
HttpResponseBadRequest,
HttpResponseForbidden,
)
from django.utils.translation import gettext as _
from judge.models.bookmark import BookMark, MakeBookMark
from django.views.generic.base import TemplateResponseMixin
from django.views.generic.detail import SingleObjectMixin
from judge.dblock import LockModel
from django.views.generic import View, ListView
__all__ = [
"dobookmark_page",
"undobookmark_page",
"BookMarkDetailView",
]
@login_required
def bookmark_page(request, delta):
if request.method != "POST":
return HttpResponseForbidden()
if "id" not in request.POST:
return HttpResponseBadRequest()
try:
bookmark_id = int(request.POST["id"])
bookmark_page = BookMark.objects.filter(id=bookmark_id)
except ValueError:
return HttpResponseBadRequest()
else:
if not bookmark_page.exists():
raise Http404()
if delta == 0:
bookmarklist = MakeBookMark.objects.filter(bookmark=bookmark_page.first(), user=request.profile)
if not bookmarklist.exists():
newbookmark = MakeBookMark(
bookmark=bookmark_page.first(),
user=request.profile,
)
newbookmark.save()
else:
bookmarklist = MakeBookMark.objects.filter(bookmark=bookmark_page.first(), user=request.profile)
if bookmarklist.exists():
bookmarklist.delete()
return HttpResponse("success", content_type="text/plain")
def dobookmark_page(request):
return bookmark_page(request, 0)
def undobookmark_page(request):
return bookmark_page(request, 1)
class BookMarkDetailView(TemplateResponseMixin, SingleObjectMixin, View):
def get_context_data(self, **kwargs):
context = super(BookMarkDetailView, self).get_context_data(**kwargs)
queryset = BookMark.objects.filter(page=self.get_comment_page())
if queryset.exists() == False:
bookmark = BookMark(page=self.get_comment_page(),)
bookmark.save()
context["bookmark"] = queryset.first()
return context
class BookMarkListView(ListView):
def get_context_data(self, **kwargs):
context = super(BookMarkListView, self).get_context_data(**kwargs)
for item in context["object_list"]:
bookmark, _ = BookMark.objects.get_or_create(
page=self.get_comment_page(item)
)
setattr(item, "bookmark", bookmark)
return context

View file

@ -83,6 +83,7 @@ from judge.utils.views import (
) )
from judge.widgets import HeavyPreviewPageDownWidget from judge.widgets import HeavyPreviewPageDownWidget
from judge.views.pagevote import PageVoteDetailView from judge.views.pagevote import PageVoteDetailView
from judge.views.bookmark import BookMarkDetailView
__all__ = [ __all__ = [
@ -382,7 +383,7 @@ class ContestMixin(object):
) )
class ContestDetail(ContestMixin, TitleMixin, CommentedDetailView, PageVoteDetailView): class ContestDetail(ContestMixin, TitleMixin, CommentedDetailView, PageVoteDetailView, BookMarkDetailView):
template_name = "contest/contest.html" template_name = "contest/contest.html"
def get_comment_page(self): def get_comment_page(self):

View file

@ -19,6 +19,8 @@ from django.views.generic import View, ListView
__all__ = [ __all__ = [
"upvote_page", "upvote_page",
"downvote_page", "downvote_page",
"PageVoteDetailView",
"PageVoteListView",
] ]
@ -97,14 +99,6 @@ class PageVoteDetailView(TemplateResponseMixin, SingleObjectMixin, View):
raise NotImplementedError() raise NotImplementedError()
return self.pagevote_page return self.pagevote_page
# def get(self, request, *args, **kwargs):
# self.object = self.get_object()
# return self.render_to_response(
# self.get_context_data(
# object=self.object,
# )
# )
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super(PageVoteDetailView, self).get_context_data(**kwargs) context = super(PageVoteDetailView, self).get_context_data(**kwargs)
queryset = PageVote.objects.filter(page=self.get_comment_page()) queryset = PageVote.objects.filter(page=self.get_comment_page())

View file

@ -87,6 +87,7 @@ from judge.utils.views import (
) )
from judge.ml.collab_filter import CollabFilter from judge.ml.collab_filter import CollabFilter
from judge.views.pagevote import PageVoteDetailView, PageVoteListView from judge.views.pagevote import PageVoteDetailView, PageVoteListView
from judge.views.bookmark import BookMarkDetailView, BookMarkListView
def get_contest_problem(problem, profile): def get_contest_problem(problem, profile):
@ -178,6 +179,7 @@ class ProblemSolution(
TitleMixin, TitleMixin,
CommentedDetailView, CommentedDetailView,
PageVoteDetailView, PageVoteDetailView,
BookMarkDetailView,
): ):
context_object_name = "problem" context_object_name = "problem"
template_name = "problem/editorial.html" template_name = "problem/editorial.html"
@ -243,7 +245,7 @@ class ProblemRaw(
class ProblemDetail( class ProblemDetail(
ProblemMixin, SolvedProblemMixin, CommentedDetailView, PageVoteDetailView ProblemMixin, SolvedProblemMixin, CommentedDetailView, PageVoteDetailView, BookMarkDetailView
): ):
context_object_name = "problem" context_object_name = "problem"
template_name = "problem/problem.html" template_name = "problem/problem.html"
@ -813,7 +815,7 @@ class ProblemList(QueryStringSortMixin, TitleMixin, SolvedProblemMixin, ListView
return HttpResponseRedirect(request.get_full_path()) return HttpResponseRedirect(request.get_full_path())
class ProblemFeed(ProblemList, PageVoteListView): class ProblemFeed(ProblemList, PageVoteListView, BookMarkListView):
model = Problem model = Problem
context_object_name = "problems" context_object_name = "problems"
template_name = "problem/feed.html" template_name = "problem/feed.html"

View file

@ -25,9 +25,12 @@
</span> </span>
{% endif %} {% endif %}
<span class="actionbar-block"> <span class="actionbar-block">
<span class="actionbar-button"> <span id="bookmark-button-{{bookmark.id}}"
class="bookmark-button actionbar-button {% if bookmark.get_bookmark(request.profile) == True %} bookmarked {% endif %}"
onclick="javascript:bookmark({{ bookmark.id }})"
>
<i class="fa fa-bookmark-o" style="font-size: large;"></i> <i class="fa fa-bookmark-o" style="font-size: large;"></i>
<span class="actionbar-text">{{_("Bookmark")}}</span> <span class="actionbar-text">{{_("Bookmark")}} {{ bookmark.id }} </span>
</span> </span>
</span> </span>
<span class="actionbar-block"> <span class="actionbar-block">

View file

@ -39,6 +39,10 @@
.actionbar-text { .actionbar-text {
padding-left: 0.4em; padding-left: 0.4em;
} }
.bookmarked {
color: rgb(180, 180, 7);
}
@media (max-width: 799px) { @media (max-width: 799px) {
.actionbar-text { .actionbar-text {
display: none; display: none;

View file

@ -20,6 +20,39 @@
}); });
} }
function ajax_bookmark(url, id, on_success) {
return $.ajax({
url: url,
type: 'POST',
data: {
id: id
},
success: function (data, textStatus, jqXHR) {
if (typeof on_success !== 'undefined')
on_success();
},
error: function (data, textStatus, jqXHR) {
alert('Could not bookmark: ' + data.responseText);
}
});
}
window.bookmark = function(id) {
var $bookmark = $('#bookmark-button-' + id);
if ($bookmark.hasClass('bookmarked')) {
ajax_bookmark('{{ url('undobookmark') }}', id, function () {
$bookmark.removeClass('bookmarked');
});
} else {
ajax_bookmark('{{ url('dobookmark') }}', id, function () {
if ($bookmark.hasClass('bookmarked'))
$bookmark.removeClass('bookmarked');
$bookmark.addClass('bookmarked');
});
}
}
var get_$votes = function (id) { var get_$votes = function (id) {
var $post = $('#page-vote-' + id); var $post = $('#page-vote-' + id);
return { return {
@ -30,7 +63,6 @@
window.pagevote_upvote = function (id) { window.pagevote_upvote = function (id) {
var $votes = get_$votes(id); var $votes = get_$votes(id);
console.log($votes.upvote, $votes.downvote);
if ($votes.upvote.hasClass('voted')) { if ($votes.upvote.hasClass('voted')) {
ajax_vote('{{ url('pagevote_downvote') }}', id, -1, function () { ajax_vote('{{ url('pagevote_downvote') }}', id, -1, function () {
$votes.upvote.removeClass('voted'); $votes.upvote.removeClass('voted');

View file

@ -43,6 +43,7 @@
{% endcache %} {% endcache %}
</div> </div>
{% set pagevote = post.pagevote %} {% set pagevote = post.pagevote %}
{% set bookmark = post.bookmark %}
{% set hide_actionbar_comment = True %} {% set hide_actionbar_comment = True %}
{% set include_hr = True %} {% set include_hr = True %}
{% include "actionbar/list.html" %} {% include "actionbar/list.html" %}

View file

@ -64,6 +64,7 @@
{% set include_hr = True %} {% set include_hr = True %}
{% set hide_actionbar_comment = True %} {% set hide_actionbar_comment = True %}
{% set pagevote = problem.pagevote %} {% set pagevote = problem.pagevote %}
{% set bookmark = post.bookmark %}
{% include "actionbar/list.html" %} {% include "actionbar/list.html" %}
{% if feed_type=='volunteer' and request.user.has_perm('judge.suggest_problem_changes') %} {% if feed_type=='volunteer' and request.user.has_perm('judge.suggest_problem_changes') %}