##// END OF EJS Templates
Added thread metadata (replies, images, gets) to the threads list.
neko259 -
r30:0d6932e0 default
parent child Browse files
Show More
@@ -1,145 +1,173 b''
1 1 import os
2 2 from django.db import models
3 3 from django.utils import timezone
4 4 import time
5 5
6 6 from neboard import settings
7 7
8 8 import thumbs
9 9
10 10 NO_PARENT = -1
11 11 NO_IP = '0.0.0.0'
12 12 UNKNOWN_UA = ''
13 13
14 14
15 15 def update_image_filename(instance, filename):
16 16 """Get unique image filename"""
17 17
18 18 path = 'images/'
19 19 new_name = str(int(time.mktime(time.gmtime()))) + '_' + filename
20 20 return os.path.join(path, new_name)
21 21
22 22
23 23 class PostManager(models.Manager):
24 24 def create_post(self, title, text, image=None, parent_id=NO_PARENT,
25 25 ip=NO_IP, tags=None):
26 26 post = self.create(title=title,
27 27 text=text,
28 28 pub_time=timezone.now(),
29 29 parent=parent_id,
30 30 image=image,
31 31 poster_ip=ip,
32 32 poster_user_agent=UNKNOWN_UA,
33 33 last_edit_time=timezone.now())
34 34
35 35 if tags:
36 36 for tag in tags:
37 37 post.tags.add(tag)
38 38
39 39 if parent_id != NO_PARENT:
40 40 parent = self.get(id=parent_id)
41 41 parent.last_edit_time=timezone.now()
42 42 parent.save()
43 43 else:
44 44 self._delete_old_threads()
45 45
46 46 return post
47 47
48 48 def delete_post(self, post):
49 49 children = self.filter(parent=post.id)
50 50 for child in children:
51 51 self.delete_post(child)
52 52 post.delete()
53 53
54 54 def delete_posts_by_ip(self, ip):
55 55 posts = self.filter(poster_ip=ip)
56 56 for post in posts:
57 57 self.delete_post(post)
58 58
59 59 def get_threads(self, tag=None):
60 60 if tag:
61 61 threads = self.filter(parent=NO_PARENT, tags=tag)
62 62 else:
63 63 threads = self.filter(parent=NO_PARENT)
64 64 threads = list(threads.order_by('-last_edit_time'))
65 65
66 66 return threads
67 67
68 68 def get_thread(self, opening_post_id):
69 69 opening_post = self.get(id=opening_post_id)
70 70 replies = self.filter(parent=opening_post_id)
71 71
72 72 thread = [opening_post]
73 73 thread.extend(replies)
74 74
75 75 return thread
76 76
77 77 def exists(self, post_id):
78 78 posts = self.filter(id=post_id)
79 79
80 80 return len(posts) > 0
81 81
82 82 def _delete_old_threads(self):
83 83 """
84 84 Preserves maximum thread count. If there are too many threads,
85 85 delete the old ones.
86 86 """
87 87
88 88 # TODO Try to find a better way to get the active thread count.
89 89
90 90 # TODO Move old threads to the archive instead of deleting them.
91 91 # Maybe make some 'old' field in the model to indicate the thread
92 92 # must not be shown and be able for replying.
93 93
94 94 threads = self.get_threads()
95 95 thread_count = len(threads)
96 96
97 97 if thread_count > settings.MAX_THREAD_COUNT:
98 98 num_threads_to_delete = thread_count - settings.MAX_THREAD_COUNT
99 99 old_threads = threads[-num_threads_to_delete:]
100 100
101 101 for thread in old_threads:
102 102 self.delete_post(thread)
103 103
104 104
105 105 class Tag(models.Model):
106 106 """
107 107 A tag is a text node assigned to the post. The tag serves as a board
108 108 section. There can be multiple tags for each message
109 109 """
110 110
111 111 name = models.CharField(max_length=100)
112 112 # TODO Connect the tag to its posts to check the number of threads for
113 113 # the tag.
114 114
115 115
116 116 class Post(models.Model):
117 117 """A post is a message."""
118 118
119 119 objects = PostManager()
120 120
121 121 title = models.CharField(max_length=50)
122 122 pub_time = models.DateTimeField()
123 123 text = models.TextField()
124 124 image = thumbs.ImageWithThumbsField(upload_to=update_image_filename,
125 125 blank=True, sizes=((200, 150),))
126 126 poster_ip = models.IPAddressField()
127 127 poster_user_agent = models.TextField()
128 128 parent = models.BigIntegerField()
129 129 tags = models.ManyToManyField(Tag)
130 130 last_edit_time = models.DateTimeField()
131 131
132 132 def __unicode__(self):
133 133 return self.title + ' (' + self.text + ')'
134 134
135 def _get_replies(self):
136 return Post.objects.filter(parent=self.id)
137
138 def get_reply_count(self):
139 return len(self._get_replies())
140
141 def get_images_count(self):
142 images_count = 1 if self.image else 0
143 for reply in self._get_replies():
144 if reply.image:
145 images_count += 1
146
147 return images_count
148
149 def get_gets_count(self):
150 gets_count = 1 if self.is_get() else 0
151 for reply in self._get_replies():
152 if reply.is_get():
153 gets_count += 1
154
155 return gets_count
156
157 def is_get(self):
158 """If the post has pretty id (1, 1000, 77777), than it is called GET"""
159
160 # TODO Make a better algorithm for describing gets
161 return self.id == 1 or self.id % 10 == 0
162
135 163
136 164 class Admin(models.Model):
137 165 """
138 166 Model for admin users
139 167 """
140 168 name = models.CharField(max_length=100)
141 169 password = models.CharField(max_length=100)
142 170
143 171 def __unicode__(self):
144 172 return self.name + '/' + '*' * len(self.password)
145 173
@@ -1,71 +1,79 b''
1 1 html {
2 2 background: #444;
3 3 color: #ffffff;
4 4 }
5 5
6 6 #admin_panel {
7 7 background: #FF0000;
8 8 color: #00FF00
9 9 }
10 10
11 11 .title {
12 12 font-weight: bold;
13 13 color: #ffcc00;
14 14 }
15 15
16 16 .post-form {
17 17 text-align: left;
18 18 display: table;
19 19 border-radius: 5px;
20 20 padding: 5px;
21 21 margin: 5px;
22 22 background: #334;
23 23 }
24 24
25 25 .form-row {
26 26 display: table-row;
27 27 }
28 28
29 29 .form-input {
30 30 display: table-cell;
31 31 }
32 32
33 33 .link {
34 color: #829dba;
34 color: #afdcec;
35 35 }
36 36
37 37 .link:hover {
38 color: #ba9f82;
38 color: #fff380;
39 39 }
40 40
41 41 .post_id {
42 42 color: #ffffff;
43 43 }
44 44
45 45 .block {
46 46 display: inline-block;
47 47 vertical-align: top;
48 48 }
49 49
50 50 .tag {
51 51 color: #b4cfec;
52 52 }
53 53
54 54 .tag:hover {
55 55 color: #d0edb4;
56 56 }
57 57
58 58 .post_id {
59 59 color: #fff380;
60 60 }
61 61
62 62 .post {
63 63 background: #333;
64 64 margin: 5px;
65 65 padding: 10px;
66 66 border-radius: 5px;
67 67 }
68 68
69 69 .form-title {
70 70 font-weight: bolder;
71 }
72
73 .metadata {
74 background: #666;
75 padding: 10px;
76 border-radius: 5px;
77 float: left;
78 margin-top: 5px;
71 79 } No newline at end of file
@@ -1,44 +1,60 b''
1 1 # SOME DESCRIPTIVE TITLE.
2 2 # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
3 3 # This file is distributed under the same license as the PACKAGE package.
4 4 # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
5 5 #
6 #, fuzzy
7 6 msgid ""
8 7 msgstr ""
9 8 "Project-Id-Version: PACKAGE VERSION\n"
10 9 "Report-Msgid-Bugs-To: \n"
11 "POT-Creation-Date: 2013-04-05 20:55+0300\n"
10 "POT-Creation-Date: 2013-04-11 21:26+0300\n"
12 11 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
13 12 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
14 13 "Language-Team: LANGUAGE <LL@li.org>\n"
15 "Language: \n"
14 "Language: ru\n"
16 15 "MIME-Version: 1.0\n"
17 16 "Content-Type: text/plain; charset=UTF-8\n"
18 17 "Content-Transfer-Encoding: 8bit\n"
19 18 "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
20 19 "%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
21 20
21 #: templates/posting_general.html:22
22 msgid "View"
23 msgstr "ΠŸΡ€ΠΎΡΠΌΠΎΡ‚Ρ€"
24
25 #: templates/posting_general.html:25
26 msgid "replies"
27 msgstr "ΠΎΡ‚Π²Π΅Ρ‚ΠΎΠ²"
28
29 #: templates/posting_general.html:26
30 msgid "images"
31 msgstr "ΠΈΠ·ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠΉ"
32
33 #: templates/posting_general.html:29 templates/posting_general.html.py:61
34 #: templates/thread.html:23
35 msgid "Tags"
36 msgstr "Π’Π΅Π³ΠΈ"
37
38 #: templates/posting_general.html:46
39 msgid "Create new thread"
40 msgstr "Π‘ΠΎΠ·Π΄Π°Ρ‚ΡŒ Π½ΠΎΠ²Ρ‹ΠΉ Ρ‚Ρ€Π΅Π΄"
41
42 #: templates/posting_general.html:49 templates/thread.html:42
22 43 msgid "Title"
23 44 msgstr "Π—Π°Π³ΠΎΠ»ΠΎΠ²ΠΎΠΊ"
24 45
46 #: templates/posting_general.html:53 templates/thread.html:46
25 47 msgid "Text"
26 48 msgstr "ВСкст"
27 49
50 #: templates/posting_general.html:57 templates/thread.html:50
28 51 msgid "Image"
29 52 msgstr "ΠšΠ°Ρ€Ρ‚ΠΈΠ½ΠΊΠ°"
30 53
54 #: templates/posting_general.html:64 templates/thread.html:53
31 55 msgid "Post"
32 56 msgstr "ΠžΡ‚ΠΏΡ€Π°Π²ΠΈΡ‚ΡŒ"
33 57
34 msgid "Tags"
35 msgstr "Π’Π΅Π³ΠΈ"
36
37 msgid "View"
38 msgstr "ΠŸΡ€ΠΎΡΠΌΠΎΡ‚Ρ€"
39
40 msgid "Create new thread"
41 msgstr "Π‘ΠΎΠ·Π΄Π°Ρ‚ΡŒ Π½ΠΎΠ²Ρ‹ΠΉ Ρ‚Ρ€Π΅Π΄"
42
58 #: templates/thread.html:39
43 59 msgid "Reply to the thread"
44 msgstr "ΠžΡ‚Π²Π΅Ρ‚ΠΈΡ‚ΡŒ Π² Ρ‚Ρ€Π΅Π΄" No newline at end of file
60 msgstr "ΠžΡ‚Π²Π΅Ρ‚ΠΈΡ‚ΡŒ Π² Ρ‚Ρ€Π΅Π΄"
@@ -1,63 +1,68 b''
1 1 {% extends "base.html" %}
2 2
3 3 {% load i18n %}
4 4
5 5 {% block content %}
6 6
7 7 {% if threads %}
8 8 {% for thread in threads %}
9 9 <div class="post">
10 10 {% if thread.image %}
11 11 <div class="block">
12 12 <a href="{{ thread.image.url }}"><img
13 13 src="{{ thread.image.url_200x150 }}" />
14 14 </a>
15 15 </div>
16 16 {% endif %}
17 17 <div class="block">
18 18 <span class="title">{{ thread.title }}</span>
19 19 <span class="post_id">(#{{ thread.id }})</span>
20 20 [{{ thread.pub_time }}]
21 <a class="link" href="/thread/{{ thread.id }}/">
22 [{% trans "View" %}]</a><br />
21 [<a class="link" href="/thread/{{ thread.id }}/">{% trans "View" %}</a>]<br />
23 22 {{ thread.text|truncatechars:300 }}<br />
24 {% if thread.tags %}
25 <span class="tags">{% trans 'Tags' %}:
26 {% for tag in thread.tags.all %}
27 <a class="tag" href="/tag/{{ tag.name }}">
28 {{ tag.name }}</a>
29 {% endfor %}
30 </span>
31 {% endif %}
23 <div class="metadata">
24 ({{ thread.get_reply_count }} {% trans 'replies' %},
25 {{ thread.get_images_count }} {% trans 'images' %},
26 {{ thread.get_gets_count }} {% trans 'gets' %})
27 <br />
28 {% if thread.tags.all %}
29 <span class="tags">{% trans 'Tags' %}:
30 {% for tag in thread.tags.all %}
31 <a class="tag" href="/tag/{{ tag.name }}">
32 {{ tag.name }}</a>
33 {% endfor %}
34 </span>
35 {% endif %}
36 </div>
32 37 </div>
33 38 </div>
34 39 {% endfor %}
35 40 {% else %}
36 41 No threads found.
37 42 <hr />
38 43 {% endif %}
39 44
40 45 <div class="post-form">
41 46 <span class="form-title">{% trans "Create new thread" %}</span>
42 47 <form enctype="multipart/form-data" method="post">{% csrf_token %}
43 48 <div class="form-row">
44 49 <div class="form-input">{% trans 'Title' %}</div>
45 50 <div class="form-input">{{ form.title }}</div>
46 51 </div>
47 52 <div class="form-row">
48 53 <div class="form-input">{% trans 'Text' %}</div>
49 54 <div class="form-input">{{ form.text }}</div>
50 55 </div>
51 56 <div class="form-row">
52 57 <div class="form-input">{% trans 'Image' %}</div>
53 58 <div class="form-input">{{ form.image }}</div>
54 59 </div>
55 60 <div class="form-row">
56 61 <div class="form-input">{% trans 'Tags' %}</div>
57 62 <div class="form-input">{{ form.tags }}</div>
58 63 </div>
59 64 <input type="submit" value="{% trans 'Post' %}" />
60 65 </form>
61 66 </div>
62 67
63 68 {% endblock %} No newline at end of file
@@ -1,57 +1,61 b''
1 1 {% extends "base.html" %}
2 2
3 3 {% load i18n %}
4 4
5 5 {% block content %}
6 6
7 7 {% if posts %}
8 8 {% for post in posts %}
9 <a name="{{ post.id }}"></a>
9 10 <div class="post">
10 11 {% if post.image %}
11 12 <div class="block">
12 13 <a href="{{ post.image.url }}"><img
13 14 src="{{ post.image.url_200x150 }}" />
14 15 </a>
15 16 </div>
16 17 {% endif %}
17 18 <div class="block">
18 19 <span class="title">{{ post.title }}</span>
19 <span class="post_id">(#{{ post.id }})</span>
20 <a class="post_id" href="#{{ post.id }}">
21 (#{{ post.id }})</a>
20 22 [{{ post.pub_time }}]<br />
21 23 {{ post.text }}<br />
22 {% ifnotequal post.tags.all|length 0 %}
23 <span class="tags">{% trans 'Tags' %}:
24 {% for tag in post.tags.all %}
25 <a class="tag" href="/tag/{{ tag.name }}">
26 {{ tag.name }}</a>
27 {% endfor %}
28 </span>
29 {% endifnotequal %}
24 {% if post.tags.all %}
25 <div class="metadata">
26 <span class="tags">{% trans 'Tags' %}:
27 {% for tag in post.tags.all %}
28 <a class="tag" href="/tag/{{ tag.name }}">
29 {{ tag.name }}</a>
30 {% endfor %}
31 </span>
32 </div>
33 {% endif %}
30 34 </div>
31 35 </div>
32 36 {% endfor %}
33 37 {% else %}
34 38 No threads found.
35 39 <hr />
36 40 {% endif %}
37 41
38 42 <div class="post-form">
39 43 <span class="form-title">{% trans "Reply to the thread" %}</span>
40 44 <form enctype="multipart/form-data" method="post">{% csrf_token %}
41 45 <div class="form-row">
42 46 <div class="form-input">{% trans 'Title' %}</div>
43 47 <div class="form-input">{{ form.title }}</div>
44 48 </div>
45 49 <div class="form-row">
46 50 <div class="form-input">{% trans 'Text' %}</div>
47 51 <div class="form-input">{{ form.text }}</div>
48 52 </div>
49 53 <div class="form-row">
50 54 <div class="form-input">{% trans 'Image' %}</div>
51 55 <div class="form-input">{{ form.image }}</div>
52 56 </div>
53 57 <input type="submit" value="{% trans 'Post' %}" />
54 58 </form>
55 59 </div>
56 60
57 61 {% endblock %} No newline at end of file
General Comments 0
You need to be logged in to leave comments. Login now