##// END OF EJS Templates
Thread status field instead of bumpable and archived fields (per BB-73)
neko259 -
r1414:cbf56940 default
parent child Browse files
Show More
@@ -56,9 +56,9 b' class ThreadAdmin(admin.ModelAdmin):'
56 def op(self, obj: Thread):
56 def op(self, obj: Thread):
57 return obj.get_opening_post_id()
57 return obj.get_opening_post_id()
58
58
59 list_display = ('id', 'op', 'title', 'reply_count', 'archived', 'ip',
59 list_display = ('id', 'op', 'title', 'reply_count', 'status', 'ip',
60 'display_tags')
60 'display_tags')
61 list_filter = ('bump_time', 'archived', 'bumpable')
61 list_filter = ('bump_time', 'status')
62 search_fields = ('id', 'title')
62 search_fields = ('id', 'title')
63 filter_horizontal = ('tags',)
63 filter_horizontal = ('tags',)
64
64
@@ -238,7 +238,7 b' class PostForm(NeboardForm):'
238 for thread_id in threads_id_list:
238 for thread_id in threads_id_list:
239 try:
239 try:
240 thread = Post.objects.get(id=int(thread_id))
240 thread = Post.objects.get(id=int(thread_id))
241 if not thread.is_opening() or thread.get_thread().archived:
241 if not thread.is_opening() or thread.get_thread().is_archived():
242 raise ObjectDoesNotExist()
242 raise ObjectDoesNotExist()
243 threads.append(thread)
243 threads.append(thread)
244 except (ObjectDoesNotExist, ValueError):
244 except (ObjectDoesNotExist, ValueError):
@@ -27,8 +27,8 b' class PostImageManager(models.Manager):'
27
27
28 return post_image
28 return post_image
29
29
30 def get_random_images(self, count, include_archived=False, tags=None):
30 def get_random_images(self, count, tags=None):
31 images = self.filter(post_images__thread__archived=include_archived)
31 images = self
32 if tags is not None:
32 if tags is not None:
33 images = images.filter(post_images__threads__tags__in=tags)
33 images = images.filter(post_images__threads__tags__in=tags)
34 return images.order_by('?')[:count]
34 return images.order_by('?')[:count]
@@ -178,7 +178,7 b' class Post(models.Model, Viewable):'
178 thread = self.get_thread()
178 thread = self.get_thread()
179
179
180 css_classes = [CSS_CLS_POST]
180 css_classes = [CSS_CLS_POST]
181 if thread.archived:
181 if thread.is_archived():
182 css_classes.append(CSS_CLS_ARCHIVE_POST)
182 css_classes.append(CSS_CLS_ARCHIVE_POST)
183 elif not thread.can_bump():
183 elif not thread.can_bump():
184 css_classes.append(CSS_CLS_DEAD_POST)
184 css_classes.append(CSS_CLS_DEAD_POST)
@@ -283,7 +283,7 b' class Post(models.Model, Viewable):'
283 for thread in self.get_threads().all():
283 for thread in self.get_threads().all():
284 thread.last_edit_time = self.last_edit_time
284 thread.last_edit_time = self.last_edit_time
285
285
286 thread.save(update_fields=['last_edit_time', 'bumpable'])
286 thread.save(update_fields=['last_edit_time', 'status'])
287
287
288 super().save(force_insert, force_update, using, update_fields)
288 super().save(force_insert, force_update, using, update_fields)
289
289
@@ -335,7 +335,7 b' class Post(models.Model, Viewable):'
335 thread.update_bump_status()
335 thread.update_bump_status()
336
336
337 thread.last_edit_time = self.last_edit_time
337 thread.last_edit_time = self.last_edit_time
338 thread.save(update_fields=['last_edit_time', 'bumpable'])
338 thread.save(update_fields=['last_edit_time', 'status'])
339 self.threads.add(opening_post.get_thread())
339 self.threads.add(opening_post.get_thread())
340
340
341 def get_tripcode(self):
341 def get_tripcode(self):
@@ -5,6 +5,7 b' from django.db.models import Count'
5 from django.core.urlresolvers import reverse
5 from django.core.urlresolvers import reverse
6
6
7 from boards.models.base import Viewable
7 from boards.models.base import Viewable
8 from boards.models.thread import STATUS_ACTIVE, STATUS_BUMPLIMIT, STATUS_ARCHIVE
8 from boards.utils import cached_result
9 from boards.utils import cached_result
9 import boards
10 import boards
10
11
@@ -61,22 +62,20 b' class Tag(models.Model, Viewable):'
61
62
62 return self.get_thread_count() == 0
63 return self.get_thread_count() == 0
63
64
64 def get_thread_count(self, archived=None, bumpable=None) -> int:
65 def get_thread_count(self, status=None) -> int:
65 threads = self.get_threads()
66 threads = self.get_threads()
66 if archived is not None:
67 if status is not None:
67 threads = threads.filter(archived=archived)
68 threads = threads.filter(status=status)
68 if bumpable is not None:
69 threads = threads.filter(bumpable=bumpable)
70 return threads.count()
69 return threads.count()
71
70
72 def get_active_thread_count(self) -> int:
71 def get_active_thread_count(self) -> int:
73 return self.get_thread_count(archived=False, bumpable=True)
72 return self.get_thread_count(status=STATUS_ACTIVE)
74
73
75 def get_bumplimit_thread_count(self) -> int:
74 def get_bumplimit_thread_count(self) -> int:
76 return self.get_thread_count(archived=False, bumpable=False)
75 return self.get_thread_count(status=STATUS_BUMPLIMIT)
77
76
78 def get_archived_thread_count(self) -> int:
77 def get_archived_thread_count(self) -> int:
79 return self.get_thread_count(archived=True)
78 return self.get_thread_count(status=STATUS_ARCHIVE)
80
79
81 def get_absolute_url(self):
80 def get_absolute_url(self):
82 return reverse('tag', kwargs={'tag_name': self.name})
81 return reverse('tag', kwargs={'tag_name': self.name})
@@ -106,11 +105,11 b' class Tag(models.Model, Viewable):'
106 def get_description(self):
105 def get_description(self):
107 return self.description
106 return self.description
108
107
109 def get_random_image_post(self, archived=False):
108 def get_random_image_post(self, status=False):
110 posts = boards.models.Post.objects.annotate(images_count=Count(
109 posts = boards.models.Post.objects.annotate(images_count=Count(
111 'images')).filter(images_count__gt=0, threads__tags__in=[self])
110 'images')).filter(images_count__gt=0, threads__tags__in=[self])
112 if archived is not None:
111 if status is not None:
113 posts = posts.filter(thread__archived=archived)
112 posts = posts.filter(thread__status=status)
114 return posts.order_by('?').first()
113 return posts.order_by('?').first()
115
114
116 def get_first_letter(self):
115 def get_first_letter(self):
@@ -5,6 +5,10 b' from django.db.models import Count, Sum,'
5 from django.utils import timezone
5 from django.utils import timezone
6 from django.db import models
6 from django.db import models
7
7
8 STATUS_ACTIVE = 'active'
9 STATUS_BUMPLIMIT = 'bumplimit'
10 STATUS_ARCHIVE = 'archived'
11
8 from boards import settings
12 from boards import settings
9 import boards
13 import boards
10 from boards.utils import cached_result, datetime_to_epoch
14 from boards.utils import cached_result, datetime_to_epoch
@@ -33,7 +37,7 b' class ThreadManager(models.Manager):'
33 archive or delete the old ones.
37 archive or delete the old ones.
34 """
38 """
35
39
36 threads = Thread.objects.filter(archived=False).order_by('-bump_time')
40 threads = Thread.objects.exclude(status=STATUS_ARCHIVE).order_by('-bump_time')
37 thread_count = threads.count()
41 thread_count = threads.count()
38
42
39 max_thread_count = settings.get_int('Messages', 'MaxThreadCount')
43 max_thread_count = settings.get_int('Messages', 'MaxThreadCount')
@@ -50,11 +54,10 b' class ThreadManager(models.Manager):'
50 logger.info('Processed %d old threads' % num_threads_to_delete)
54 logger.info('Processed %d old threads' % num_threads_to_delete)
51
55
52 def _archive_thread(self, thread):
56 def _archive_thread(self, thread):
53 thread.archived = True
57 thread.status = STATUS_ARCHIVE
54 thread.bumpable = False
55 thread.last_edit_time = timezone.now()
58 thread.last_edit_time = timezone.now()
56 thread.update_posts_time()
59 thread.update_posts_time()
57 thread.save(update_fields=['archived', 'last_edit_time', 'bumpable'])
60 thread.save(update_fields=['last_edit_time', 'status'])
58
61
59 def get_new_posts(self, datas):
62 def get_new_posts(self, datas):
60 query = None
63 query = None
@@ -90,9 +93,8 b' class Thread(models.Model):'
90 tags = models.ManyToManyField('Tag', related_name='thread_tags')
93 tags = models.ManyToManyField('Tag', related_name='thread_tags')
91 bump_time = models.DateTimeField(db_index=True)
94 bump_time = models.DateTimeField(db_index=True)
92 last_edit_time = models.DateTimeField()
95 last_edit_time = models.DateTimeField()
93 archived = models.BooleanField(default=False)
94 bumpable = models.BooleanField(default=True)
95 max_posts = models.IntegerField(default=get_thread_max_posts)
96 max_posts = models.IntegerField(default=get_thread_max_posts)
97 status = models.CharField(max_length=50, default=STATUS_ACTIVE)
96
98
97 def get_tags(self) -> QuerySet:
99 def get_tags(self) -> QuerySet:
98 """
100 """
@@ -118,7 +120,7 b' class Thread(models.Model):'
118
120
119 def update_bump_status(self, exclude_posts=None):
121 def update_bump_status(self, exclude_posts=None):
120 if self.has_post_limit() and self.get_reply_count() >= self.max_posts:
122 if self.has_post_limit() and self.get_reply_count() >= self.max_posts:
121 self.bumpable = False
123 self.status = STATUS_BUMPLIMIT
122 self.update_posts_time(exclude_posts=exclude_posts)
124 self.update_posts_time(exclude_posts=exclude_posts)
123
125
124 def _get_cache_key(self):
126 def _get_cache_key(self):
@@ -138,7 +140,7 b' class Thread(models.Model):'
138 Checks if the thread can be bumped by replying to it.
140 Checks if the thread can be bumped by replying to it.
139 """
141 """
140
142
141 return self.bumpable and not self.is_archived()
143 return self.get_status() == STATUS_ACTIVE
142
144
143 def get_last_replies(self) -> QuerySet:
145 def get_last_replies(self) -> QuerySet:
144 """
146 """
@@ -255,4 +257,7 b' class Thread(models.Model):'
255 return self.get_replies().filter(id__gt=post_id)
257 return self.get_replies().filter(id__gt=post_id)
256
258
257 def is_archived(self):
259 def is_archived(self):
258 return self.archived
260 return self.get_status() == STATUS_ARCHIVE
261
262 def get_status(self):
263 return self.status
@@ -3,6 +3,7 b' from django.core.urlresolvers import rev'
3 from django.shortcuts import get_object_or_404
3 from django.shortcuts import get_object_or_404
4 from boards.models import Post, Tag, Thread
4 from boards.models import Post, Tag, Thread
5 from boards import settings
5 from boards import settings
6 from boards.models.thread import STATUS_ARCHIVE
6
7
7 __author__ = 'nekorin'
8 __author__ = 'nekorin'
8
9
@@ -18,7 +19,7 b' class AllThreadsFeed(Feed):'
18 description_template = 'boards/rss/post.html'
19 description_template = 'boards/rss/post.html'
19
20
20 def items(self):
21 def items(self):
21 return Thread.objects.filter(archived=False).order_by('-id')[:MAX_ITEMS]
22 return Thread.objects.exclude(status=STATUS_ARCHIVE).order_by('-id')[:MAX_ITEMS]
22
23
23 def item_title(self, item):
24 def item_title(self, item):
24 return item.get_opening_post().title
25 return item.get_opening_post().title
@@ -36,7 +37,7 b' class TagThreadsFeed(Feed):'
36 description_template = 'boards/rss/post.html'
37 description_template = 'boards/rss/post.html'
37
38
38 def items(self, obj):
39 def items(self, obj):
39 return obj.get_threads().filter(archived=False).order_by('-id')[:MAX_ITEMS]
40 return obj.get_threads().exclude(status=STATUS_ARCHIVE).order_by('-id')[:MAX_ITEMS]
40
41
41 def get_object(self, request, tag_name):
42 def get_object(self, request, tag_name):
42 return get_object_or_404(Tag, name=tag_name)
43 return get_object_or_404(Tag, name=tag_name)
@@ -21,14 +21,14 b''
21 and this is an opening post (thread death time) or a post for popup
21 and this is an opening post (thread death time) or a post for popup
22 (we don't see OP here so we show the death time in the post itself).
22 (we don't see OP here so we show the death time in the post itself).
23 {% endcomment %}
23 {% endcomment %}
24 {% if thread.archived %}
24 {% if thread.is_archived %}
25 {% if is_opening %}
25 {% if is_opening %}
26 <time datetime="{{ thread.bump_time|date:'c' }}">{{ thread.bump_time }}</time>
26 <time datetime="{{ thread.bump_time|date:'c' }}">{{ thread.bump_time }}</time>
27 {% endif %}
27 {% endif %}
28 {% endif %}
28 {% endif %}
29 {% if is_opening %}
29 {% if is_opening %}
30 {% if need_open_link %}
30 {% if need_open_link %}
31 {% if thread.archived %}
31 {% if thread.is_archived %}
32 <a class="link" href="{% url 'thread' post.id %}">{% trans "Open" %}</a>
32 <a class="link" href="{% url 'thread' post.id %}">{% trans "Open" %}</a>
33 {% else %}
33 {% else %}
34 <a class="link" href="{% url 'thread' post.id %}#form">{% trans "Reply" %}</a>
34 <a class="link" href="{% url 'thread' post.id %}#form">{% trans "Reply" %}</a>
@@ -41,7 +41,7 b''
41 {% endwith %}
41 {% endwith %}
42 {% endif %}
42 {% endif %}
43 {% endif %}
43 {% endif %}
44 {% if reply_link and not thread.archived %}
44 {% if reply_link and not thread.is_archived %}
45 <a href="#form" onclick="addQuickReply('{{ post.id }}'); return false;">{% trans 'Reply' %}</a>
45 <a href="#form" onclick="addQuickReply('{{ post.id }}'); return false;">{% trans 'Reply' %}</a>
46 {% endif %}
46 {% endif %}
47
47
@@ -38,7 +38,7 b''
38 {% endfor %}
38 {% endfor %}
39 </div>
39 </div>
40
40
41 {% if not thread.archived %}
41 {% if not thread.is_archived %}
42 <div class="post-form-w">
42 <div class="post-form-w">
43 <script src="{% static 'js/panel.js' %}"></script>
43 <script src="{% static 'js/panel.js' %}"></script>
44 <div class="form-title">{% trans "Reply to thread" %} #{{ opening_post.id }}<span class="reply-to-message"> {% trans "to message " %} #<span id="reply-to-message-id"></span></span></div>
44 <div class="form-title">{% trans "Reply to thread" %} #{{ opening_post.id }}<span class="reply-to-message"> {% trans "to message " %} #<span id="reply-to-message-id"></span></span></div>
@@ -31,6 +31,7 b' class ApiTest(TestCase):'
31 req = MockRequest()
31 req = MockRequest()
32 req.POST['thread'] = opening_post.id
32 req.POST['thread'] = opening_post.id
33 req.POST['uids'] = ' '.join(uids)
33 req.POST['uids'] = ' '.join(uids)
34 req.user = None
34 # Check the timestamp before post was added
35 # Check the timestamp before post was added
35 response = api.api_get_threaddiff(req)
36 response = api.api_get_threaddiff(req)
36 diff = simplejson.loads(response.content)
37 diff = simplejson.loads(response.content)
@@ -3,6 +3,7 b' from django.test import TestCase'
3
3
4 from boards import settings
4 from boards import settings
5 from boards.models import Tag, Post, Thread
5 from boards.models import Tag, Post, Thread
6 from boards.models.thread import STATUS_ARCHIVE
6
7
7
8
8 class PostTests(TestCase):
9 class PostTests(TestCase):
@@ -96,7 +97,7 b' class PostTests(TestCase):'
96 self._create_post()
97 self._create_post()
97
98
98 self.assertEqual(settings.get_int('Messages', 'MaxThreadCount'),
99 self.assertEqual(settings.get_int('Messages', 'MaxThreadCount'),
99 len(Thread.objects.filter(archived=False)))
100 len(Thread.objects.exclude(status=STATUS_ARCHIVE)))
100
101
101 def test_pages(self):
102 def test_pages(self):
102 """Test that the thread list is properly split into pages"""
103 """Test that the thread list is properly split into pages"""
@@ -104,9 +105,9 b' class PostTests(TestCase):'
104 for i in range(settings.get_int('Messages', 'MaxThreadCount')):
105 for i in range(settings.get_int('Messages', 'MaxThreadCount')):
105 self._create_post()
106 self._create_post()
106
107
107 all_threads = Thread.objects.filter(archived=False)
108 all_threads = Thread.objects.exclude(status=STATUS_ARCHIVE)
108
109
109 paginator = Paginator(Thread.objects.filter(archived=False),
110 paginator = Paginator(Thread.objects.exclude(status=STATUS_ARCHIVE),
110 settings.get_int('View', 'ThreadsPerPage'))
111 settings.get_int('View', 'ThreadsPerPage'))
111 posts_in_second_page = paginator.page(2).object_list
112 posts_in_second_page = paginator.page(2).object_list
112 first_post = posts_in_second_page[0]
113 first_post = posts_in_second_page[0]
@@ -12,6 +12,7 b' from boards.abstracts.settingsmanager im'
12
12
13 from boards.forms import PostForm, PlainErrorList
13 from boards.forms import PostForm, PlainErrorList
14 from boards.models import Post, Thread, Tag
14 from boards.models import Post, Thread, Tag
15 from boards.models.thread import STATUS_ARCHIVE
15 from boards.utils import datetime_to_epoch
16 from boards.utils import datetime_to_epoch
16 from boards.views.thread import ThreadView
17 from boards.views.thread import ThreadView
17 from boards.models.user import Notification
18 from boards.models.user import Notification
@@ -136,9 +137,9 b' def api_get_threads(request, count):'
136 tag_name = request.GET[PARAMETER_TAG]
137 tag_name = request.GET[PARAMETER_TAG]
137 if tag_name is not None:
138 if tag_name is not None:
138 tag = get_object_or_404(Tag, name=tag_name)
139 tag = get_object_or_404(Tag, name=tag_name)
139 threads = tag.get_threads().filter(archived=False)
140 threads = tag.get_threads().exclude(status=STATUS_ARCHIVE)
140 else:
141 else:
141 threads = Thread.objects.filter(archived=False)
142 threads = Thread.objects.exclude(status=STATUS_ARCHIVE)
142
143
143 if PARAMETER_OFFSET in request.GET:
144 if PARAMETER_OFFSET in request.GET:
144 offset = request.GET[PARAMETER_OFFSET]
145 offset = request.GET[PARAMETER_OFFSET]
@@ -155,8 +156,7 b' def api_get_threads(request, count):'
155
156
156 # TODO Add tags, replies and images count
157 # TODO Add tags, replies and images count
157 post_data = opening_post.get_post_data(include_last_update=True)
158 post_data = opening_post.get_post_data(include_last_update=True)
158 post_data['bumpable'] = thread.can_bump()
159 post_data['status'] = thread.get_status()
159 post_data['archived'] = thread.archived
160
160
161 opening_posts.append(post_data)
161 opening_posts.append(post_data)
162
162
@@ -97,7 +97,7 b' class ThreadView(BaseBoardView, PostMixi'
97
97
98 return redirect('thread', post_id) # FIXME Different for different modes
98 return redirect('thread', post_id) # FIXME Different for different modes
99
99
100 if not opening_post.get_thread().archived:
100 if not opening_post.get_thread().is_archived():
101 form = PostForm(request.POST, request.FILES,
101 form = PostForm(request.POST, request.FILES,
102 error_class=PlainErrorList)
102 error_class=PlainErrorList)
103 form.session = request.session
103 form.session = request.session
General Comments 0
You need to be logged in to leave comments. Login now