##// END OF EJS Templates
Added fetch sources to fetch external information into threads as posts
neko259 -
r1968:81b9b636 default
parent child Browse files
Show More
@@ -0,0 +1,27 b''
1 # -*- coding: utf-8 -*-
2 # Generated by Django 1.11 on 2017-11-21 11:29
3 from __future__ import unicode_literals
4
5 from django.db import migrations, models
6 import django.db.models.deletion
7
8
9 class Migration(migrations.Migration):
10
11 dependencies = [
12 ('boards', '0066_auto_20171025_1148'),
13 ]
14
15 operations = [
16 migrations.CreateModel(
17 name='ThreadSource',
18 fields=[
19 ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
20 ('name', models.TextField()),
21 ('timestamp', models.DateTimeField()),
22 ('source', models.TextField()),
23 ('source_type', models.CharField(choices=[('RSS', 'RSS')], max_length=100)),
24 ('thread', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='boards.Thread')),
25 ],
26 ),
27 ]
@@ -0,0 +1,66 b''
1 import feedparser
2 import logging
3
4 from time import mktime
5 from datetime import datetime
6
7 from django.db import models, transaction
8 from django.utils.dateparse import parse_datetime
9 from django.utils.timezone import utc
10 from django.utils import timezone
11 from boards.models import Post
12 from boards.models.post import TITLE_MAX_LENGTH
13
14
15 SOURCE_TYPE_MAX_LENGTH = 100
16 SOURCE_TYPE_RSS = 'RSS'
17 TYPE_CHOICES = (
18 (SOURCE_TYPE_RSS, SOURCE_TYPE_RSS),
19 )
20
21
22 class ThreadSource(models.Model):
23 class Meta:
24 app_label = 'boards'
25
26 name = models.TextField()
27 thread = models.ForeignKey('Thread')
28 timestamp = models.DateTimeField()
29 source = models.TextField()
30 source_type = models.CharField(max_length=SOURCE_TYPE_MAX_LENGTH,
31 choices=TYPE_CHOICES)
32
33 def __str__(self):
34 return self.name
35
36 @transaction.atomic
37 def fetch_latest_posts(self):
38 """Creates new posts with the info fetched since the timestamp."""
39 logger = logging.getLogger('boards.source')
40
41 if self.thread.is_archived():
42 logger.error('The thread {} is archived, please try another one'.format(self.thread))
43 else:
44 start_timestamp = timezone.localtime(self.timestamp)
45 last_timestamp = start_timestamp
46 if self.thread.is_bumplimit():
47 logger.warn('The thread {} has reached its bumplimit, please create a new one'.format(self.thread))
48 if self.source_type == SOURCE_TYPE_RSS:
49 feed = feedparser.parse(self.source)
50 items = sorted(feed.entries, key=lambda entry: entry.published_parsed)
51 for item in items:
52 title = item.title[:TITLE_MAX_LENGTH]
53 timestamp = datetime.fromtimestamp(mktime(item.published_parsed), tz=utc)
54 if not timestamp:
55 logger.error('Invalid timestamp {} for {}'.format(item.published, title))
56 else:
57 if timestamp > last_timestamp:
58 last_timestamp = timestamp
59
60 if timestamp > start_timestamp:
61 Post.objects.create_post(title=title, text=item.description, thread=self.thread, file_urls=[item.link])
62 logger.info('Fetched item {} from {} into thread {}'.format(
63 title, self.name, self.thread))
64 self.timestamp = last_timestamp
65 self.save(update_fields=['timestamp'])
66
@@ -6,6 +6,7 b' from django.utils.translation import uge'
6 from django.core.urlresolvers import reverse
6 from django.core.urlresolvers import reverse
7 from boards.models import Post, Tag, Ban, Thread, Banner, Attachment, \
7 from boards.models import Post, Tag, Ban, Thread, Banner, Attachment, \
8 KeyPair, GlobalId, TagAlias
8 KeyPair, GlobalId, TagAlias
9 from boards.models.source import ThreadSource
9
10
10
11
11 @admin.register(Post)
12 @admin.register(Post)
@@ -179,3 +180,9 b' class GlobalIdAdmin(admin.ModelAdmin):'
179
180
180 list_display = ('__str__', 'is_linked',)
181 list_display = ('__str__', 'is_linked',)
181 readonly_fields = ('content',)
182 readonly_fields = ('content',)
183
184
185 @admin.register(ThreadSource)
186 class ThreadSourceAdmin(admin.ModelAdmin):
187 search_fields = ('name', 'source')
188
@@ -47,3 +47,4 b' MaxItems = 20'
47
47
48 [External]
48 [External]
49 ImageSearchHost=
49 ImageSearchHost=
50 SourceFetcherTripcode=
@@ -233,7 +233,7 b' class Thread(models.Model):'
233 return self.get_opening_post().pub_time
233 return self.get_opening_post().pub_time
234
234
235 def __str__(self):
235 def __str__(self):
236 return 'T#{}'.format(self.id)
236 return 'T#{}/{}'.format(self.id, self.get_opening_post())
237
237
238 def get_tag_url_list(self) -> list:
238 def get_tag_url_list(self) -> list:
239 return boards.models.Tag.objects.get_tag_url_list(self.get_tags().all())
239 return boards.models.Tag.objects.get_tag_url_list(self.get_tags().all())
@@ -262,6 +262,9 b' class Thread(models.Model):'
262 def is_archived(self):
262 def is_archived(self):
263 return self.get_status() == STATUS_ARCHIVE
263 return self.get_status() == STATUS_ARCHIVE
264
264
265 def is_bumplimit(self):
266 return self.get_status() == STATUS_BUMPLIMIT
267
265 def get_status(self):
268 def get_status(self):
266 return self.status
269 return self.status
267
270
@@ -1,9 +1,13 b''
1 import configparser
1 import configparser
2
2
3
3
4 CONFIG_DEFAULT_SETTINGS = 'boards/config/default_settings.ini'
5 CONFIG_SETTINGS = 'boards/config/settings.ini'
6
7
4 config = configparser.ConfigParser()
8 config = configparser.ConfigParser()
5 config.read('boards/config/default_settings.ini')
9 config.read(CONFIG_DEFAULT_SETTINGS)
6 config.read('boards/config/settings.ini')
10 config.read(CONFIG_SETTINGS)
7
11
8
12
9 def get(section, name):
13 def get(section, name):
@@ -22,3 +26,4 b' def get_list_dict(section, name):'
22 str_dict = get(section, name)
26 str_dict = get(section, name)
23 return [item.split(':') for item in str_dict.split(',')]
27 return [item.split(':') for item in str_dict.split(',')]
24
28
29
@@ -9,3 +9,4 b' bbcode'
9 django-debug-toolbar
9 django-debug-toolbar
10 pytz
10 pytz
11 ecdsa
11 ecdsa
12 feedparser
General Comments 0
You need to be logged in to leave comments. Login now