~ubuntu-branches/ubuntu/karmic/calibre/karmic

« back to all changes in this revision

Viewing changes to src/calibre/web/feeds/__init__.py

  • Committer: Bazaar Package Importer
  • Author(s): Martin Pitt
  • Date: 2009-07-30 12:49:41 UTC
  • mfrom: (1.3.2 upstream)
  • Revision ID: james.westby@ubuntu.com-20090730124941-qjdsmri25zt8zocn
Tags: 0.6.3+dfsg-0ubuntu1
* New upstream release. Please see http://calibre.kovidgoyal.net/new_in_6/
  for the list of new features and changes.
* remove_postinstall.patch: Update for new version.
* build_debug.patch: Does not apply any more, disable for now. Might not be
  necessary any more.
* debian/copyright: Fix reference to versionless GPL.
* debian/rules: Drop obsolete dh_desktop call.
* debian/rules: Add workaround for weird Python 2.6 setuptools behaviour of
  putting compiled .so files into src/calibre/plugins/calibre/plugins
  instead of src/calibre/plugins.
* debian/rules: Drop hal fdi moving, new upstream version does not use hal
  any more. Drop hal dependency, too.
* debian/rules: Install udev rules into /lib/udev/rules.d.
* Add debian/calibre.preinst: Remove unmodified
  /etc/udev/rules.d/95-calibre.rules on upgrade.
* debian/control: Bump Python dependencies to 2.6, since upstream needs
  it now.

Show diffs side-by-side

added added

removed removed

Lines of Context:
13
13
from lxml import html
14
14
 
15
15
class Article(object):
16
 
    
 
16
 
17
17
    time_offset = datetime.now() - datetime.utcnow()
18
18
 
19
 
    def __init__(self, id, title, url, summary, published, content):
 
19
    def __init__(self, id, title, url, author, summary, published, content):
20
20
        self.downloaded = False
21
21
        self.id = id
22
 
        self.title = title.strip() if title else title
 
22
        self._title = title.strip() if title else title
23
23
        try:
24
 
            self.title = re.sub(r'&(\S+);', 
25
 
                entity_to_unicode, self.title)
 
24
            self._title = re.sub(r'&(\S+);',
 
25
                entity_to_unicode, self._title)
26
26
        except:
27
27
            pass
 
28
        if not isinstance(self._title, unicode):
 
29
            self._title = self._title.decode('utf-8', 'replace')
28
30
        self.url = url
 
31
        self.author = author
 
32
        if author and not isinstance(author, unicode):
 
33
            author = author.decode('utf-8', 'replace')
29
34
        self.summary = summary
30
35
        if summary and not isinstance(summary, unicode):
31
36
            summary = summary.decode('utf-8', 'replace')
39
44
                traceback.print_exc()
40
45
                summary = u''
41
46
        self.text_summary = summary
 
47
        self.author = author
42
48
        self.content = content
43
49
        self.date = published
44
50
        self.utctime = datetime(*self.date[:6])
45
51
        self.localtime = self.utctime + self.time_offset
46
52
 
47
 
                
 
53
    @dynamic_property
 
54
    def title(self):
 
55
        def fget(self):
 
56
            t = self._title
 
57
            if not isinstance(t, unicode) and hasattr(t, 'decode'):
 
58
                t = t.decode('utf-8', 'replace')
 
59
            return t
 
60
        def fset(self, val):
 
61
            self._title = val
 
62
        return property(fget=fget, fset=fset)
 
63
 
 
64
 
48
65
    def __repr__(self):
49
66
        return \
50
67
(u'''\
51
68
Title       : %s
52
69
URL         : %s
 
70
Author      : %s
53
71
Summary     : %s
54
72
Date        : %s
55
73
Has content : %s
56
 
'''%(self.title, self.url, self.summary[:20]+'...', self.localtime.strftime('%a, %d %b, %Y %H:%M'),
 
74
'''%(self.title, self.url, self.author, self.summary[:20]+'...', self.localtime.strftime('%a, %d %b, %Y %H:%M'),
57
75
     bool(self.content))).encode('utf-8')
58
76
 
59
77
    def __str__(self):
60
78
        return repr(self)
61
 
    
 
79
 
62
80
    def is_same_as(self, other_article):
63
81
        #if self.title != getattr(other_article, 'title', False):
64
82
        #    return False
65
83
        if self.url:
66
84
            return self.url == getattr(other_article, 'url', False)
67
85
        return self.content == getattr(other_article, 'content', False)
68
 
    
 
86
 
69
87
 
70
88
class Feed(object):
71
89
 
75
93
        '''
76
94
        self.logger = logging.getLogger('feeds2disk')
77
95
        self.get_article_url = get_article_url
78
 
        
79
 
    def populate_from_feed(self, feed, title=None, oldest_article=7, 
 
96
 
 
97
    def populate_from_feed(self, feed, title=None, oldest_article=7,
80
98
                           max_articles_per_feed=100):
81
99
        entries = feed.entries
82
100
        feed = feed.feed
87
105
        self.image_width  = image.get('width', 88)
88
106
        self.image_height = image.get('height', 31)
89
107
        self.image_alt    = image.get('title', '')
90
 
        
 
108
 
91
109
        self.articles = []
92
110
        self.id_counter = 0
93
111
        self.added_articles = []
94
 
        
 
112
 
95
113
        self.oldest_article = oldest_article
96
 
        
 
114
 
97
115
        for item in entries:
98
116
            if len(self.articles) >= max_articles_per_feed:
99
117
                break
100
118
            self.parse_article(item)
101
 
        
102
 
        
103
 
    def populate_from_preparsed_feed(self, title, articles, oldest_article=7, 
 
119
 
 
120
 
 
121
    def populate_from_preparsed_feed(self, title, articles, oldest_article=7,
104
122
                           max_articles_per_feed=100):
105
123
        self.title      = title if title else _('Unknown feed')
106
124
        self.descrition = ''
107
125
        self.image_url  = None
108
126
        self.articles   = []
109
127
        self.added_articles = []
110
 
         
 
128
 
111
129
        self.oldest_article = oldest_article
112
130
        self.id_counter = 0
113
 
        
 
131
 
114
132
        for item in articles:
115
133
            if len(self.articles) >= max_articles_per_feed:
116
134
                break
124
142
            link        = item.get('url', None)
125
143
            description = item.get('description', '')
126
144
            content     = item.get('content', '')
127
 
            article = Article(id, title, link, description, published, content)
 
145
            author      = item.get('author', '')
 
146
            article = Article(id, title, link, author, description, published, content)
128
147
            delta = datetime.utcnow() - article.utctime
129
148
            if delta.days*24*3600 + delta.seconds <= 24*3600*self.oldest_article:
130
149
                self.articles.append(article)
131
150
            else:
132
151
                self.logger.debug('Skipping article %s (%s) from feed %s as it is too old.'%(title, article.localtime.strftime('%a, %d %b, %Y %H:%M'), self.title))
133
 
         
134
 
    
 
152
 
 
153
 
135
154
    def parse_article(self, item):
136
155
        id = item.get('id', 'internal id#'+str(self.id_counter))
137
156
        if id in self.added_articles:
141
160
            published = time.gmtime()
142
161
        self.id_counter += 1
143
162
        self.added_articles.append(id)
144
 
        
 
163
 
145
164
        title = item.get('title', _('Untitled article'))
146
165
        try:
147
166
            link  = self.get_article_url(item)
149
168
            self.logger.warning('Failed to get link for %s'%title)
150
169
            self.logger.debug(traceback.format_exc())
151
170
            link = None
 
171
 
152
172
        description = item.get('summary', None)
153
 
        
154
 
        content = '\n'.join(i.value for i in item.get('content', []))
 
173
        author = item.get('author', None)
 
174
 
 
175
        content = [i.value for i in item.get('content', []) if i.value]
 
176
        content = [i if isinstance(i, unicode) else i.decode('utf-8', 'replace')
 
177
                for i in content]
 
178
        content = u'\n'.join(content)
155
179
        if not content.strip():
156
180
            content = None
157
181
        if not link and not content:
158
182
            return
159
 
        article = Article(id, title, link, description, published, content)
 
183
        article = Article(id, title, link, author, description, published, content)
160
184
        delta = datetime.utcnow() - article.utctime
161
185
        if delta.days*24*3600 + delta.seconds <= 24*3600*self.oldest_article:
162
186
            self.articles.append(article)
167
191
                if not isinstance(title, unicode):
168
192
                    title = title.decode('utf-8', 'replace')
169
193
                self.logger.debug('Skipping article %s as it is too old'%title)
170
 
        
 
194
 
171
195
    def __iter__(self):
172
196
        return iter(self.articles)
173
 
    
 
197
 
174
198
    def __len__(self):
175
199
        return len(self.articles)
176
 
    
 
200
 
177
201
    def __repr__(self):
178
202
        res = [('%20s\n'%'').replace(' ', '_')+repr(art) for art in self]
179
 
        
 
203
 
180
204
        return '\n'+'\n'.join(res)+'\n'
181
 
    
 
205
 
182
206
    def __str__(self):
183
207
        return repr(self)
184
 
    
 
208
 
185
209
    def __bool__(self):
186
210
        for article in self:
187
211
            if getattr(article, 'downloaded', False):
188
212
                return True
189
213
        return False
190
 
    
 
214
 
191
215
    def has_embedded_content(self):
192
216
        length = 0
193
217
        for a in self:
194
218
            if a.content or a.summary:
195
 
                length += max(len(a.content if a.content else ''), 
 
219
                length += max(len(a.content if a.content else ''),
196
220
                              len(a.summary if a.summary else ''))
197
 
                
 
221
 
198
222
        return length > 2000 * len(self)
199
 
    
 
223
 
200
224
    def has_article(self, article):
201
225
        for a in self:
202
226
            if a.is_same_as(article):
203
227
                return True
204
228
        return False
205
 
    
 
229
 
206
230
    def find(self, article):
207
231
        for i, a in enumerate(self):
208
232
            if a.is_same_as(article):
209
233
                return i
210
234
        return -1
211
 
    
 
235
 
212
236
    def remove(self, article):
213
237
        i = self.index(article)
214
238
        if i > -1:
215
239
            self.articles[i:i+1] = []
216
240
 
217
241
class FeedCollection(list):
218
 
    
 
242
 
219
243
    def __init__(self, feeds):
220
244
        list.__init__(self, [f for f in feeds if len(f.articles) > 0])
221
245
        found_articles = set([])
222
246
        duplicates = set([])
223
 
        
 
247
 
224
248
        def in_set(s, a):
225
249
            for x in s:
226
250
                if a.is_same_as(x):
227
251
                    return x
228
252
            return None
229
 
        
 
253
 
230
254
        print '#feeds', len(self)
231
255
        print map(len, self)
232
256
        for f in self:
240
264
                    found_articles.add(a)
241
265
            for x in dups:
242
266
                f.articles.remove(x)
243
 
                
 
267
 
244
268
        self.duplicates = duplicates
245
269
        print len(duplicates)
246
270
        print map(len, self)
247
271
        #raise
248
 
                
 
272
 
249
273
    def find_article(self, article):
250
274
        for j, f in enumerate(self):
251
275
            for i, a in enumerate(f):
252
276
                if a is article:
253
277
                    return (j, i)
254
 
    
 
278
 
255
279
    def restore_duplicates(self):
256
280
        temp = []
257
281
        for article, feed in self.duplicates:
261
285
            temp.append((feed, art))
262
286
        for feed, art in temp:
263
287
            feed.articles.append(art)
264
 
        
265
 
 
266
 
def feed_from_xml(raw_xml, title=None, oldest_article=7, 
 
288
 
 
289
 
 
290
def feed_from_xml(raw_xml, title=None, oldest_article=7,
267
291
                  max_articles_per_feed=100, get_article_url=lambda item: item.get('link', None)):
268
292
    feed = parse(raw_xml)
269
293
    pfeed = Feed(get_article_url=get_article_url)
270
 
    pfeed.populate_from_feed(feed, title=title, 
 
294
    pfeed.populate_from_feed(feed, title=title,
271
295
                            oldest_article=oldest_article,
272
296
                            max_articles_per_feed=max_articles_per_feed)
273
297
    return pfeed
281
305
    feeds = []
282
306
    for title, articles in index:
283
307
        pfeed = Feed()
284
 
        pfeed.populate_from_preparsed_feed(title, articles, oldest_article=oldest_article, 
 
308
        pfeed.populate_from_preparsed_feed(title, articles, oldest_article=oldest_article,
285
309
                                       max_articles_per_feed=max_articles_per_feed)
286
310
        feeds.append(pfeed)
287
 
    return feeds
 
 
b'\\ No newline at end of file'
 
311
    return feeds