-
Notifications
You must be signed in to change notification settings - Fork 7
/
story.py
159 lines (128 loc) · 4.77 KB
/
story.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
# -*- coding: utf-8 -*-
#Canto - ncurses RSS reader
# Copyright (C) 2008 Jack Miller <jack@codezen.org>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 as
# published by the Free Software Foundation.
# Story is a slick little class. It's main purpose is to hold all of the data
# for each story, both content and state. To save memory, 0.7.0 will attempt to
# fetch data from disk if it's not held by default. A good example is
# description information which can take up kilobytes or megabytes of memory but
# are rarely used.
# Story doesn't care which feed or tag it's associated with. If you really want
# to get the feed, story["feed"] contains the unique URL, but you'd have to use
# the config to get the Feed() object. The only thing that the Story() gets from
# the feed is the get_ufp function that gets the feedparser dict from disk.
from const import STORY_SAVED, STORY_UPDATED
import cPickle
import fcntl
class Story():
def __init__(self, d, ufp_path, updated):
self.updated = updated
self.ufp_path = ufp_path
self.ondisk = None
self.d = d
self.sel = 0
self.in_reader = 0
def __eq__(self, other):
if self["id"] != other["id"]:
return 0
# The reason that we have to check for membership is
# that sometimes (like when writing to disk) it's convenient
# to compare against a dict in the feedparser block than
# another Story object. In all of these cases, the feeds
# are guaranteed to be the same anyway.
if "feed" in other and (self["feed"] != other["feed"]):
return 0
return 1
def __str__(self):
return self.d["title"] + " " + str(id(self))
# Where get_ufp reads the ufp from disk, this narrows that down to a
# particular feed entry.
def get_ufp_entry(self):
if not self.ufp_path:
return {}
try:
f = open(self.ufp_path, "r")
try:
fcntl.flock(f.fileno(), fcntl.LOCK_SH)
ufp = cPickle.load(f)
except:
return {}
finally:
fcntl.flock(f.fileno(), fcntl.LOCK_UN)
f.close()
except:
return {}
for ondisk in ufp["entries"]:
if ondisk["id"] == self["id"]:
break
else:
self.ondisk = None
self.ondisk = ondisk
def __getitem__(self, key):
if key in self.d:
return self.d[key]
# If the key isn't stored by default, get it from disk.
else:
if not self.ondisk:
self.get_ufp_entry()
if not self.ondisk:
return ""
if key in self.ondisk:
return self.ondisk[key]
return ""
def __setitem__(self, key, item):
self.d[key] = item
def __contains__(self, key):
if key in self.d:
return True
else:
if not self.ondisk:
self.get_ufp_entry()
if not self.ondisk:
return False
return key in self.ondisk
def was(self, tag):
return tag in self.d["canto_state"]
def set(self, tag):
if not tag in self.d["canto_state"]:
self.d["canto_state"].append(tag)
self.updated = STORY_UPDATED
def unset(self,tag):
if tag in self.d["canto_state"]:
self.d["canto_state"].remove(tag)
self.updated = STORY_UPDATED
# These are separate from the was/set/unset since the selected status isn't
# stored in the ufp.
def selected(self):
return self.sel
def select(self):
self.sel = 1
def unselect(self):
self.sel = 0
def get_text(self):
if "content" in self:
for c in self["content"]:
if "type" in c and "text" in c["type"]:
return c["value"]
return self["description"]
def get_type(self):
if "content" in self:
for c in self["content"]:
if "type" in c and "text" in c["type"]:
return c["type"]
if "summary_detail" in self and "type" in self["summary_detail"]:
return self["summary_detail"]["type"]
return "text/html"
def get_title_type(self):
if "title_detail" in self and "type" in self["title_detail"]:
return self["title_detail"]["type"]
return "text/html"
# Free makes the Story() forget all of the uncommon items. Should be called
# after anything that could cause Story() to fetch items (ie. in the
# Renderer hooks).
def free(self):
if self.ondisk:
self.ondisk = None