| 43 | from rdfobj import fbox | 47 | from rdfobj import fbox |
|---|
| 44 | fbox = rdfobj.fbox | 48 | fbox = rdfobj.fbox |
|---|
| 45 | counter = self.model.fetch(fbox.Visit_Count) | 49 | counter = self.model.fetch(fbox.Visit_Count) |
|---|
| 46 | c = counter[fbox.count] | 50 | c = counter[fbox.count] |
|---|
| 47 | if c is None: c = 0 | 51 | if c is None: c = 0 |
|---|
| 48 | c = int(str(c))+1 | 52 | c = int(str(c))+1 |
|---|
| 49 | counter[fbox.count] = str(c) | 53 | counter[fbox.count] = str(c) |
|---|
| 50 | | 54 | |
|---|
| 51 | if counter is None: | 55 | if counter is None: |
|---|
| 52 | v = self.model.create(fbox.Visit_Count, uri=fbox.Visit_Count) | 56 | v = self.model.create(fbox.Visit_Count, uri=fbox.Visit_Count) |
|---|
| 53 | v[fbox.count] = 0 | 57 | v[fbox.count] = 0 |
|---|
| 54 | | 58 | |
|---|
| 55 | | 59 | |
|---|
| 56 | def mention(self,thought): | 60 | def mention(self,thought): |
|---|
| 57 | """If BBox is constructed with verbose=1, prints to STDOUT (currently) a record of what it's up to.""" | 61 | """If BBox is constructed with verbose=1, prints to STDOUT (currently) a record of what it's up to.""" |
|---|
| 58 | if self._verbose: | 62 | if self._verbose: |
|---|
| 59 | print(thought) | 63 | print(thought) |
|---|
| 60 | | 64 | |
|---|
| 61 | def read_subscriptions(self): | 65 | def read_subscriptions(self): |
|---|
| 62 | """read_subscriptions() picks up the latest RSS feed updates. """ | 66 | """read_subscriptions() picks up the latest RSS feed updates. """ |
|---|
| 63 | self.mention("checking subscriptions.") | 67 | self.mention("checking subscriptions.") |
|---|
| 64 | subs = self.subscriptions() | 68 | subs = self.subscriptions() |
|---|
| 65 | from rdfobj import fbox | 69 | from rdfobj import fbox |
|---|
| 66 | fbox = rdfobj.fbox | 70 | fbox = rdfobj.fbox |
|---|
| 67 | for s in subs: | 71 | for s in subs: |
|---|
| 68 | self.mention("reading "+str(s[fbox.channel])) | 72 | self.mention("reading "+str(s[fbox.channel])) |
|---|
| 69 | format = s[fbox.format].uri() | 73 | format = s[fbox.format].uri() |
|---|
| 70 | c = s[fbox.channel] | 74 | c = s[fbox.channel] |
|---|
| 71 | | 75 | |
|---|
| 72 | # see if we're actually due a visit | 76 | # see if we're actually due a visit |
|---|
| 73 | due = self.visit_scheduled(s) | 77 | due = self.visit_scheduled(s) |
|---|
| 74 | if due is None: | 78 | if due is None: |
|---|
| 75 | print "nothing due to look at!" | 79 | print "nothing due to look at!" |
|---|
| 76 | subs.next() | 80 | subs.next() |
|---|
| 77 | else: | 81 | else: |
|---|
| 78 | if format == fbox.rss: | 82 | if format == fbox.rss: |
|---|
| 79 | self.read_rss(c.uri(),subscription=s) | 83 | self.read_rss(c.uri(),subscription=s) |
|---|
| 80 | elif format == fbox.rdf: | 84 | elif format == fbox.rdf: |
|---|
| 81 | self.read_rdf(s[fbox.channel].uri(),subscription=s) | 85 | self.read_rdf(s[fbox.channel].uri(),subscription=s) |
|---|
| 82 | | 86 | |
|---|
| 83 | def read_rss(self,uri,context=None,subscription=None,xml=None): | 87 | def read_rss(self,uri,context=None,subscription=None,xml=None): |
|---|
| 84 | """Read updates from an RSS feed.""" | 88 | """Read updates from an RSS feed.""" |
|---|
| 85 | | 89 | |
|---|
| 86 | #if subscription is None: subscription = {} | 90 | #if subscription is None: subscription = {} |
|---|
| 87 | rss = rdfobj.rss | 91 | rss = rdfobj.rss |
|---|
| 88 | dc = rdfobj.dc | 92 | dc = rdfobj.dc |
|---|
| 89 | ical = rdfobj.ical | 93 | ical = rdfobj.ical |
|---|
| 90 | fbox = rdfobj.fbox | 94 | fbox = rdfobj.fbox |
|---|
| 91 | geo = rdfobj.geo | 95 | geo = rdfobj.geo |
|---|
| 92 | | 96 | |
|---|
| 93 | result = self.politely_get_uri(uri,subscription=subscription) | 97 | result = self.politely_get_uri(uri,subscription=subscription) |
|---|
| 94 | | 98 | |
|---|
| 95 | channel = self.model.fetch(uri) | 99 | channel = self.model.fetch(uri) |
|---|
| 96 | """If we got a feed object back from the request, then create a | 100 | """If we got a feed object back from the request, then create a |
|---|
| 97 | context for this visit to the feed, and store the entries that we | 101 | context for this visit to the feed, and store the entries that we |
|---|
| 98 | collected from it.""" | 102 | collected from it.""" |
|---|
| 99 | if self._visit_true: | 103 | if self._visit_true: |
|---|
| 100 | pass | 104 | pass |
|---|
| 101 | elif result['status'] != 200: | 105 | elif result['status'] != 200: |
|---|
| 102 | return | 106 | return |
|---|
| 103 | items = [] | 107 | items = [] |
|---|
| 104 | feed = feedparser.parse(result['data']) | 108 | feed = feedparser.parse(result['data']) |
|---|
| 105 | if feed.has_key('feed'): | 109 | if feed.has_key('feed'): |
|---|
| 106 | context = self.visit(uri) | 110 | context = self.visit(uri) |
|---|
| 107 | # existence of exact duplicates? | 111 | # existence of exact duplicates? |
|---|
| 108 | | 112 | |
|---|
| 109 | for e in feed.entries: | 113 | for e in feed.entries: |
|---|
| 110 | link = str(e.link) | 114 | link = str(e.link) |
|---|
| 111 | title = None | 115 | title = None |
|---|
| 112 | item = self.model.create( rss.item, uri=link, context = context ) | 116 | item = self.model.create( rss.item, uri=link, context = context ) |
|---|
| 113 | | 117 | |
|---|
| 114 | if e.has_key('summary'): item[rss.description] = str(e.summary) | 118 | if e.has_key('summary'): item[rss.description] = str(e.summary) |
|---|
| 115 | | 119 | |
|---|
| 116 | if e.has_key('content'): item[rss.description] = str(e.content) | 120 | if e.has_key('content'): item[rss.description] = str(e.content) |
|---|
| 117 | | 121 | |
|---|
| 118 | if e.has_key('title'): | 122 | if e.has_key('title'): |
|---|
| 119 | item[rss.title] = str(e.title) | 123 | item[rss.title] = str(e.title) |
|---|
| 120 | title = str(e.title) | 124 | title = str(e.title) |
|---|
| 121 | | 125 | |
|---|
| 122 | item[fbox.channel] = channel | 126 | item[fbox.channel] = channel |
|---|
| 123 | | 127 | |
|---|
| 124 | # d.entries[0].modified_parsed is common | 128 | # d.entries[0].modified_parsed is common |
|---|
| 125 | | 129 | |
|---|
| 126 | time_tuple = None | 130 | time_tuple = None |
|---|
| 127 | if e.has_key('modified_parsed'): | 131 | if e.has_key('modified_parsed'): |
|---|
| 128 | time_tuple = e.modified_parsed | 132 | time_tuple = e.modified_parsed |
|---|
| 129 | elif e.has_key('created_parsed'): | 133 | elif e.has_key('created_parsed'): |
|---|
| 130 | time_tuple = e.created_parsed | 134 | time_tuple = e.created_parsed |
|---|
| 131 | | 135 | |
|---|
| 132 | # item[ical.datetime] = some process with time_tuple and strftime | 136 | # item[ical.datetime] = some process with time_tuple and strftime |
|---|
| 133 | # d = datetime.datetime(time_tuple) | 137 | # d = datetime.datetime(time_tuple) |
|---|
| 134 | # ical_date = ical_datetime.datetime_to_string(d) | 138 | # ical_date = ical_datetime.datetime_to_string(d) |
|---|
| 135 | # print ical_date | 139 | # print ical_date |
|---|
| 136 | # item[ical.datetime] = ical_date | 140 | # item[ical.datetime] = ical_date |
|---|
| 137 | # not much use without a timestamp | 141 | # not much use without a timestamp |
|---|
| 138 | | 142 | |
|---|
| 139 | if time_tuple is None: | 143 | if time_tuple is None: |
|---|
| 140 | continue | 144 | continue |
|---|
| 141 | ical_enough = time.strftime("%Y%m%dT%H%M%SZ",time_tuple) | 145 | ical_enough = time.strftime("%Y%m%dT%H%M%SZ",time_tuple) |
|---|
| 142 | item[ical.datetime] = ical_enough | 146 | item[ical.datetime] = ical_enough |
|---|
| 143 | | 147 | |
|---|
| 144 | rdf_type = None | 148 | rdf_type = None |
|---|
| 145 | if e.has_key('rdf_type'): | 149 | if e.has_key('rdf_type'): |
|---|
| 146 | rdf_type = str(e['rdf_type']) | 150 | rdf_type = str(e['rdf_type']) |
|---|
| 147 | item[rdf.type] = rdf_type | 151 | item[rdf.type] = rdf_type |
|---|
| 148 | | 152 | |
|---|
| 149 | if e.has_key('geo_lat'): print "LATT!!! "+str(e['geo_lat']) | 153 | if e.has_key('geo_lat'): print "LATT!!! "+str(e['geo_lat']) |
|---|
| 150 | if e.has_key('geo_lat') and e.has_key('geo_long'): | 154 | if e.has_key('geo_lat') and e.has_key('geo_long'): |
|---|
| 151 | lat = str(e['geo_lat']) | 155 | lat = str(e['geo_lat']) |
|---|
| 152 | long = str(e['geo_long']) | 156 | long = str(e['geo_long']) |
|---|
| 153 | item[geo.lat] = lat | 157 | item[geo.lat] = lat |
|---|
| 154 | item[geo.long] = long | 158 | item[geo.long] = long |
|---|
| 155 | | 159 | |
|---|
| 156 | """Update the spatial index, if we have one.""" | 160 | """Update the spatial index, if we have one.""" |
|---|
| 157 | if self.spatialStore is not None: | 161 | if self.spatialStore is not None: |
|---|
| 158 | self.spatialStore.add_or_update_geom(rdf_type=rdf_type,name=title,x=long,y=lat,uri=link) | 162 | self.spatialStore.add_or_update_geom(rdf_type=rdf_type,name=title,x=long,y=lat,uri=link) |
|---|
| 159 | items.append(item) | 163 | items.append(item) |
|---|
| 160 | return items | 164 | return items |
|---|
| 161 | | 165 | |
|---|
| 162 | def read_rdf(self,uri,subscription=None,xml=None): | 166 | def read_rdf(self,uri,subscription=None,xml=None): |
|---|
| 163 | """Read updates from an RDF url.""" | 167 | """Read updates from an RDF url.""" |
|---|
| 164 | from rdfobj import geo, dc, rdf | 168 | from rdfobj import geo, dc, rdf |
|---|
| 165 | geo = rdfobj.geo | 169 | geo = rdfobj.geo |
|---|
| 166 | dc = rdfobj.dc | 170 | dc = rdfobj.dc |
|---|
| 167 | rdf = rdfobj.rdf | 171 | rdf = rdfobj.rdf |
|---|
| 168 | | 172 | |
|---|
| 169 | result = self.politely_get_uri(uri,subscription=subscription) | 173 | result = self.politely_get_uri(uri,subscription=subscription) |
|---|
| 170 | if self._visit_true: | 174 | if self._visit_true: |
|---|
| 171 | pass | 175 | pass |
|---|
| 172 | elif result['status'] != 200: | 176 | elif result['status'] != 200: |
|---|
| 173 | return | 177 | return |
|---|
| 174 | | 178 | |
|---|
| 175 | context = self.visit(uri) | 179 | context = self.visit(uri) |
|---|
| 176 | | 180 | |
|---|
| 177 | # we can't just use load() because we want the visit context, and to search for spatial things and index them while we're parsing... | 181 | # we can't just use load() because we want the visit context, and to search for spatial things and index them while we're parsing... |
|---|
| 178 | lats = {} | 182 | lats = {} |
|---|
| 179 | longs = {} | 183 | longs = {} |
|---|
| 180 | titles = {} | 184 | titles = {} |
|---|
| 181 | types = {} | 185 | types = {} |
|---|
| 182 | parser = RDF.Parser('raptor') | 186 | parser = RDF.Parser('raptor') |
|---|
| 183 | stream = parser.parse_as_stream(RDF.Uri(uri)) | 187 | stream = parser.parse_as_stream(RDF.Uri(uri)) |
|---|
| 184 | subjects = {} | 188 | subjects = {} |
|---|
| 185 | if stream: | 189 | if stream: |
|---|
| 186 | while not stream.end(): | 190 | while not stream.end(): |
|---|
| 187 | statement = stream.current() | 191 | statement = stream.current() |
|---|
| 188 | subjects[statement.subject] = 1 | 192 | subjects[statement.subject] = 1 |
|---|
| 189 | self.model.model.add_statement(statement,context) | 193 | self.model.model.add_statement(statement,context) |
|---|
| 190 | # pls don't blame me, i just want to get something working fast | 194 | # pls don't blame me, i just want to get something working fast |
|---|
| 191 | if self.spatialStore is not None: | 195 | if self.spatialStore is not None: |
|---|
| 192 | if statement.predicate == RDF.Node(uri_string=str(geo.lat)): | 196 | if statement.predicate == RDF.Node(uri_string=str(geo.lat)): |
|---|
| 193 | lats[str(statement.subject)] = str(statement.object) | 197 | lats[str(statement.subject)] = str(statement.object) |
|---|
| 194 | | 198 | |
|---|
| 195 | elif statement.predicate == RDF.Node(uri_string=str(geo.long)): | 199 | elif statement.predicate == RDF.Node(uri_string=str(geo.long)): |
|---|
| 196 | longs[str(statement.subject)] = str(statement.object) | 200 | longs[str(statement.subject)] = str(statement.object) |
|---|
| 197 | | 201 | |
|---|
| 198 | elif statement.predicate == RDF.Node(uri_string=str(dc.title)): | 202 | elif statement.predicate == RDF.Node(uri_string=str(dc.title)): |
|---|
| 199 | titles[str(statement.subject)] = str(statement.object) | 203 | titles[str(statement.subject)] = str(statement.object) |
|---|
| 200 | | 204 | |
|---|
| 201 | elif statement.predicate == RDF.Node(uri_string=str(rdf.type)): | 205 | elif statement.predicate == RDF.Node(uri_string=str(rdf.type)): |
|---|
| 202 | types[str(statement.subject)] = str(statement.object) | 206 | types[str(statement.subject)] = str(statement.object) |
|---|
| 203 | | 207 | |
|---|
| 204 | stream.next() | 208 | stream.next() |
|---|
| 205 | objects = [] | 209 | objects = [] |
|---|
| 206 | for s in subjects.keys(): | 210 | for s in subjects.keys(): |
|---|
| 207 | objects.append(self.model.fetch(s)) | 211 | objects.append(self.model.fetch(s)) |
|---|
| 208 | | 212 | |
|---|
| 209 | if self.spatialStore is not None: | 213 | if self.spatialStore is not None: |
|---|
| 210 | for k in lats.keys(): | 214 | for k in lats.keys(): |
|---|
| 211 | lat = lats[k] | 215 | lat = lats[k] |
|---|
| 212 | long = longs[k] | 216 | long = longs[k] |
|---|
| 213 | title = None | 217 | title = None |
|---|
| 214 | type = None | 218 | type = None |
|---|
| 215 | if titles.has_key(k): | 219 | if titles.has_key(k): |
|---|
| 216 | title = titles[k] | 220 | title = titles[k] |
|---|
| 217 | if types.has_key(k): | 221 | if types.has_key(k): |
|---|
| 218 | type = types[k] | 222 | type = types[k] |
|---|
| 219 | warn("updating "+k) | 223 | warn("updating "+k) |
|---|
| 220 | self.spatialStore.add_or_update_geom(uri=k,rdf_type=type,name=title,x=long,y=lat) | 224 | self.spatialStore.add_or_update_geom(uri=k,rdf_type=type,name=title,x=long,y=lat) |
|---|
| 221 | return objects | 225 | return objects |
|---|
| 222 | | 226 | |
|---|
| 223 | def politely_get_uri(self,uri,subscription=None): | 227 | def politely_get_uri(self,uri,subscription=None): |
|---|
| 224 | """Request a copy of the document at a url, first checking that it has changed since what we record as last-modified and the last etag that we have for it.""" | 228 | """Request a copy of the document at a url, first checking that it has changed since what we record as last-modified and the last etag that we have for it.""" |
|---|
| 225 | | 229 | |
|---|
| 226 | # we should deal with etag/last-mod politely here too @@TODO | 230 | # we should deal with etag/last-mod politely here too @@TODO |
|---|
| 227 | #visit = self.visit(uri) | 231 | #visit = self.visit(uri) |
|---|
| 228 | result = None | 232 | result = None |
|---|
| 229 | fbox = rdfobj.fbox | 233 | fbox = rdfobj.fbox |
|---|
| 230 | | 234 | |
|---|
| 231 | if subscription is None: | 235 | if subscription is None: |
|---|
| 232 | # we might just be using the parser without the context management | 236 | # we might just be using the parser without the context management |
|---|
| 233 | result = polite_request(str(uri)) | 237 | result = polite_request(str(uri)) |
|---|
| 234 | subscription = {'fake':1} | 238 | subscription = {'fake':1} |
|---|
| 235 | | 239 | |
|---|
| 236 | elif self._visit_true is not None: | 240 | elif self._visit_true is not None: |
|---|
| 237 | # we might always want to read the feed content (for debugging reasons) | 241 | # we might always want to read the feed content (for debugging reasons) |
|---|
| 238 | result = polite_request(str(uri)) | 242 | result = polite_request(str(uri)) |
|---|
| 239 | | 243 | |
|---|
| 240 | elif subscription[fbox.last_etag] is not None: | 244 | elif subscription[fbox.last_etag] is not None: |
|---|
| 241 | result = polite_request(str(uri),etag=str(subscription[fbox.last_etag])) | 245 | result = polite_request(str(uri),etag=str(subscription[fbox.last_etag])) |
|---|
| 242 | elif subscription[fbox.last_modified] is not None: | 246 | elif subscription[fbox.last_modified] is not None: |
|---|
| 243 | result = polite_request(str(uri),last_modified=str(subscription[fbox.last_modified])) | 247 | result = polite_request(str(uri),last_modified=str(subscription[fbox.last_modified])) |
|---|
| 244 | else: result = polite_request(str(uri)) | 248 | else: result = polite_request(str(uri)) |
|---|
| 245 | if result is None: | 249 | if result is None: |
|---|
| 246 | result = {'status':404} | 250 | result = {'status':404} |
|---|
| 247 | return result | 251 | return result |
|---|
| 248 | | 252 | |
|---|
| 249 | if result.has_key('status'): | 253 | if result.has_key('status'): |
|---|
| 250 | # this was a HTTP request | 254 | # this was a HTTP request |
|---|
| 251 | self.mention("received response: "+str(result['status'])) | 255 | self.mention("received response: "+str(result['status'])) |
|---|
| 252 | | 256 | |
|---|
| 253 | """Take actions about other kinds of HTTP statuses.(TODO)""" | 257 | """Take actions about other kinds of HTTP statuses.(TODO)""" |
|---|
| 254 | # handling different HTTP statuses. | 258 | # handling different HTTP statuses. |
|---|
| 255 | if subscription.has_key('fake') is None: | 259 | if subscription.has_key('fake') is None: |
|---|
| 256 | warn(type(subscription)) | 260 | warn(type(subscription)) |
|---|
| 257 | subscription[fbox.http_status] = str(result['status']) | 261 | subscription[fbox.http_status] = str(result['status']) |
|---|
| 258 | subscription[fbox.last_etag] = result['etag'] | 262 | subscription[fbox.last_etag] = result['etag'] |
|---|
| 259 | subscription[fbox.last_modified] = result['lastmodified'] | 263 | subscription[fbox.last_modified] = result['lastmodified'] |
|---|
| 260 | subscription[fbox.last_visited] = time.strftime("%Y%m%dT%H%M%SZ") | 264 | subscription[fbox.last_visited] = time.strftime("%Y%m%dT%H%M%SZ") |
|---|
| 261 | | 265 | |
|---|
| 262 | # a 'file:/' uri will only have result['data'] | 266 | # a 'file:/' uri will only have result['data'] |
|---|
| 263 | elif result['data'] is not None: | 267 | elif result['data'] is not None: |
|---|
| 264 | # pretend we have a positive HTTP status | 268 | # pretend we have a positive HTTP status |
|---|
| 265 | result['status'] = 200 | 269 | result['status'] = 200 |
|---|
| 266 | | 270 | |
|---|
| 267 | return result | 271 | return result |
|---|
| 268 | | 272 | |
|---|
| 269 | def subscriptions(self): | 273 | def subscriptions(self): |
|---|
| 270 | """Returns a list (Iterator type) of the URLs at which | 274 | """Returns a list (Iterator type) of the URLs at which |
|---|
| 271 | there is a feed that we are subscribed to (fbox:Feed type)""" | 275 | there is a feed that we are subscribed to (fbox:Feed type)""" |
|---|
| 272 | from rdfobj import fbox, rdf | 276 | from rdfobj import fbox, rdf |
|---|
| 273 | fbox = rdfobj.fbox | 277 | fbox = rdfobj.fbox |
|---|
| 274 | rdf = rdfobj.rdf | 278 | rdf = rdfobj.rdf |
|---|
| 275 | subs = self.model.search(rdf.type,fbox.Feed) | 279 | subs = self.model.search(rdf.type,fbox.Feed) |
|---|
| 276 | return subs | 280 | return subs |
|---|
| 277 | | 281 | |
|---|
| 278 | def subscription(self,uri): | 282 | def subscription(self,uri): |
|---|
| 279 | """Given a uri, returns the rdfobj which is the subscription it represents.""" | 283 | """Given a uri, returns the rdfobj which is the subscription it represents.""" |
|---|
| 280 | obj = self.model.fetch(uri) | 284 | obj = self.model.fetch(uri) |
|---|
| 281 | return obj | 285 | return obj |
|---|
| 282 | | 286 | |
|---|
| 283 | def items(self,uri,since=None,until=None): | 287 | def items(self,uri,since=None,until=None): |
|---|
| 284 | """Get items from a feed, optionally filtering by date. (not completely implemented)""" | 288 | """Get items from a feed, optionally filtering by date. (not completely implemented)""" |
|---|
| 285 | from rdfobj import fbox, dc, rss | 289 | from rdfobj import fbox, dc, rss |
|---|
| 286 | rss = fbox.rss | 290 | rss = fbox.rss |
|---|
| 287 | dc = fbox.dc | 291 | dc = fbox.dc |
|---|
| 288 | fbox = rdfobj.fbox | 292 | fbox = rdfobj.fbox |
|---|
| 289 | s = self.subscription(uri) | 293 | s = self.subscription(uri) |
|---|
| 290 | c = s[fbox.channel] | 294 | c = s[fbox.channel] |
|---|
| 291 | out = [] | 295 | out = [] |
|---|
| 292 | if since is not None: | 296 | if since is not None: |
|---|
| 293 | for i in c[rss.items]: | 297 | for i in c[rss.items]: |
|---|
| 294 | warn(i[dc.date]) | 298 | warn(i[dc.date]) |
|---|
| 295 | if i[dc.date] > since: | 299 | if i[dc.date] > since: |
|---|
| 296 | out.append(i) | 300 | out.append(i) |
|---|
| 297 | | 301 | |
|---|
| 298 | return c.rss_items | 302 | return c.rss_items |
|---|
| 299 | | 303 | |
|---|
| 300 | def subscribe(self,feed=None,format=None,interval=None): | 304 | def subscribe(self,feed=None,format=None,interval=None): |
|---|
| 301 | """subscribe() creates a subscription to a uri. format is either 'rss' or 'rdf'. RDF is assumed if none is specified. Interval is the maximum interval in minutes that a feed should be checked at. It sends polite HTTP requests so don't worry about setting it to a bit more often than you might need. A value in minutes - defaults to 100 minutes.""" | 305 | """subscribe() creates a subscription to a uri. format is either 'rss' or 'rdf'. RDF is assumed if none is specified. Interval is the maximum interval in minutes that a feed should be checked at. It sends polite HTTP requests so don't worry about setting it to a bit more often than you might need. A value in minutes - defaults to 100 minutes.""" |
|---|
| 302 | from rdfobj import fbox | 306 | from rdfobj import fbox |
|---|
| 303 | fbox = rdfobj.fbox | 307 | fbox = rdfobj.fbox |
|---|
| 304 | if feed is None: return | 308 | if feed is None: return |
|---|
| 305 | | 309 | |
|---|
| 306 | f = self.model.search(fbox.channel,feed) | 310 | f = self.model.search(fbox.channel,feed) |
|---|
| 307 | found = None | 311 | found = None |
|---|
| 308 | for n in f: | 312 | for n in f: |
|---|
| 309 | found = 1 | 313 | found = 1 |
|---|
| 310 | if found is not None: | 314 | if found is not None: |
|---|
| 311 | return | 315 | return |
|---|
| 312 | | 316 | |
|---|
| 313 | self.mention("subscribing to "+str(feed)) | 317 | self.mention("subscribing to "+str(feed)) |
|---|
| 314 | | 318 | |
|---|
| 315 | if format is None: | 319 | if format is None: |
|---|
| 316 | format = fbox.rdf | 320 | format = fbox.rdf |
|---|
| 317 | elif format == 'rss': | 321 | elif format == 'rss': |
|---|
| 318 | format = fbox.rss | 322 | format = fbox.rss |
|---|
| 319 | elif format == 'rdf': | 323 | elif format == 'rdf': |
|---|
| 320 | format = fbox.rdf | 324 | format = fbox.rdf |
|---|
| 321 | | 325 | |
|---|
| 322 | if interval is None: interval = str(100) | 326 | if interval is None: interval = str(100) |
|---|
| 323 | | 327 | |
|---|
| 324 | ff = self.model.create( fbox.Feed, uri=None ) | 328 | ff = self.model.create( fbox.Feed, uri=None ) |
|---|
| 325 | ff[fbox.channel] = str(feed) | 329 | ff[fbox.channel] = str(feed) |
|---|
| 326 | ff[fbox.format] = str(format) | 330 | ff[fbox.format] = str(format) |
|---|
| 327 | ff[fbox.interval] = interval | 331 | ff[fbox.interval] = interval |
|---|
| 328 | | 332 | |
|---|
| 329 | return ff | 333 | return ff |
|---|
| 330 | | 334 | |
|---|
| 331 | def update(self): | 335 | def update(self): |
|---|
| 332 | """Causes all the subscribed URLs to be visited for updates.""" | 336 | """Causes all the subscribed URLs to be visited for updates.""" |
|---|
| 333 | subs = self.subscriptions() | 337 | subs = self.subscriptions() |
|---|
| 334 | from rdfobj import fbox | 338 | from rdfobj import fbox |
|---|
| 335 | fbox = rdfobj.fbox | 339 | fbox = rdfobj.fbox |
|---|
| 336 | while not subs.end(): | 340 | while not subs.end(): |
|---|
| 337 | s = subs.current() | 341 | s = subs.current() |
|---|
| 338 | self.visit(s[fbox.channel]) | 342 | self.visit(s[fbox.channel]) |
|---|
| 339 | subs.next() | 343 | subs.next() |
|---|
| 340 | | 344 | |
|---|
| 341 | def visit(self,uri=None): | 345 | def visit(self,uri=None): |
|---|
| 342 | """Creates an anonymous object which records a visit that we | 346 | """Creates an anonymous object which records a visit that we |
|---|
| 343 | paid to a feed, including a counter of times visited. This object is | 347 | paid to a feed, including a counter of times visited. This object is |
|---|
| 344 | used as a Redland context for all the information collected from a feed | 348 | used as a Redland context for all the information collected from a feed |
|---|
| 345 | during this visit.""" | 349 | during this visit.""" |
|---|
| 346 | # redland had problems serialising models with bnode context uris | 350 | # redland had problems serialising models with bnode context uris |
|---|
| 347 | count = self.counter() | 351 | count = self.counter() |
|---|
| 348 | from rdfobj import fbox | 352 | from rdfobj import fbox |
|---|
| 349 | fbox = rdfobj.fbox | 353 | fbox = rdfobj.fbox |
|---|
| 350 | visit_uri = str(fbox.visit)+'/'+str(count) | 354 | visit_uri = str(fbox.visit)+'/'+str(count) |
|---|
| 351 | visit = self.model.create( fbox.Visit , visit_uri) | 355 | visit = self.model.create( fbox.Visit , visit_uri) |
|---|
| 352 | | 356 | |
|---|
| 353 | visit[fbox.source] = uri | 357 | visit[fbox.source] = uri |
|---|
| 354 | t = time.strftime("%Y%m%dT%H%M%SZ") | 358 | t = time.strftime("%Y%m%dT%H%M%SZ") |
|---|
| 355 | visit[fbox.timestamp] = t | 359 | visit[fbox.timestamp] = t |
|---|
| 356 | return RDF.Node(RDF.Uri(str(visit.uri()))) | 360 | return RDF.Node(RDF.Uri(str(visit.uri()))) |
|---|
| 357 | | 361 | |
|---|
| 358 | def user(self,token=None,nick=None,mbox=None): | 362 | def user(self,token=None,nick=None,mbox=None): |
|---|
| 359 | """Passed either a user's login token, mbox and name, resolved to mutual exclusion in that order, and returns any corresponding user / foaf:Person object. No security - handle this yourself elsewhere!""" | 363 | """Passed either a user's login token, mbox and name, resolved to mutual exclusion in that order, and returns any corresponding user / foaf:Person object. No security - handle this yourself elsewhere!""" |
|---|
| 360 | from rdfobj import foaf | 364 | from rdfobj import foaf |
|---|
| 361 | foaf = rdfobj.foaf | 365 | foaf = rdfobj.foaf |
|---|
| 362 | if token is not None: | 366 | if token is not None: |
|---|
| 363 | users = self.model.search(foaf.auth_token,token) | 367 | users = self.model.search(foaf.auth_token,token) |
|---|
| 364 | for u in users: | 368 | for u in users: |
|---|
| 365 | return u[foaf.alias] | 369 | return u[foaf.alias] |
|---|
| 366 | if mbox is not None: | 370 | if mbox is not None: |
|---|
| 367 | users = self.model.search(foaf.mbox,mbox) | 371 | users = self.model.search(foaf.mbox,mbox) |
|---|
| 368 | for u in users: | 372 | for u in users: |
|---|
| 369 | return u | 373 | return u |
|---|
| 370 | if nick is not None: | 374 | if nick is not None: |
|---|
| 371 | o = [] | 375 | o = [] |
|---|
| 372 | users = self.model.search(foaf.name,nick) | 376 | users = self.model.search(foaf.name,nick) |
|---|
| 373 | for u in users: | 377 | for u in users: |
|---|
| 374 | o.append(u) | 378 | o.append(u) |
|---|
| 375 | users = self.model.search(foaf.givenName,nick) | 379 | users = self.model.search(foaf.givenName,nick) |
|---|
| 376 | for u in users: o.append(u) | 380 | for u in users: o.append(u) |
|---|
| 377 | return o | 381 | return o |
|---|
| 378 | | 382 | |
|---|
| 379 | def add_user(self,nick=None,mbox=None,password=None): | 383 | def add_user(self,nick=None,mbox=None,password=None): |
|---|
| 380 | """ Create a new user foaf:Person""" | 384 | """ Create a new user foaf:Person""" |
|---|
| 381 | store = self.store | 385 | store = self.store |
|---|
| 382 | from rdfobj import foaf, wlan | 386 | from rdfobj import foaf, wlan |
|---|
| 383 | foaf = rdfobj.foaf | 387 | foaf = rdfobj.foaf |
|---|
| 384 | wlan = rdfobj.wlan | 388 | wlan = rdfobj.wlan |
|---|
| 385 | | 389 | |
|---|
| 386 | obj = self.model.create(foaf.Person,uri=uri) | 390 | obj = self.model.create(foaf.Person,uri=uri) |
|---|
| 387 | obj[foaf.mbox] = mbox | 391 | obj[foaf.mbox] = mbox |
|---|
| 388 | obj[foaf.nick] = nick | 392 | obj[foaf.nick] = nick |
|---|
| 389 | if page is not None: obj[foaf.homepage] = page | 393 | if page is not None: obj[foaf.homepage] = page |
|---|
| 390 | self.obj = obj | 394 | self.obj = obj |
|---|
| 391 | | 395 | |
|---|
| 392 | """ Create a kind of shadow user where we store the password and the logged-in token, so they won't get serialised accidentally along with the user. """ | 396 | """ Create a kind of shadow user where we store the password and the logged-in token, so they won't get serialised accidentally along with the user. """ |
|---|
| 393 | | 397 | |
|---|
| 394 | auth = store.create(foaf.AuthedPerson) | 398 | auth = store.create(foaf.AuthedPerson) |
|---|
| 395 | auth[foaf.password] = password | 399 | auth[foaf.password] = password |
|---|
| 396 | auth[foaf.nick] = nick | 400 | auth[foaf.nick] = nick |
|---|
| 397 | auth[foaf.alias] = obj | 401 | auth[foaf.alias] = obj |
|---|
| 398 | | 402 | |
|---|
| 399 | token = self.auth_token() | 403 | token = self.auth_token() |
|---|
| 400 | auth[foaf.auth_token] = token | 404 | auth[foaf.auth_token] = token |
|---|
| 401 | self.model.sync() | 405 | self.model.sync() |
|---|
| 402 | return token | 406 | return token |
|---|
| 403 | | 407 | |
|---|
| 404 | def auth_token(self): | 408 | def auth_token(self): |
|---|
| 405 | """Generate a random auth token.""" | 409 | """Generate a random auth token.""" |
|---|
| 406 | x = '' | 410 | x = '' |
|---|
| 407 | for n in range(0, 6): | 411 | for n in range(0, 6): |
|---|
| 408 | x = x + chr(65 + random.randint(0, 26)) | 412 | x = x + chr(65 + random.randint(0, 26)) |
|---|
| 409 | return x | 413 | return x |
|---|
| 410 | | 414 | |
|---|
| 411 | def visit_scheduled(self,sub): | 415 | def visit_scheduled(self,sub): |
|---|
| 412 | """Compare the last visited time, if that's applicable, to the interval between events (rather than a schedule? perhaps we'll have to re-think this later.""" | 416 | """Compare the last visited time, if that's applicable, to the interval between events (rather than a schedule? perhaps we'll have to re-think this later.""" |
|---|
| 413 | if self._visit_true is not None: | 417 | if self._visit_true is not None: |
|---|
| 414 | return 1 | 418 | return 1 |
|---|
| 415 | from rdfobj import fbox | 419 | from rdfobj import fbox |
|---|
| 416 | fbox = rdfobj.fbox | 420 | fbox = rdfobj.fbox |
|---|
| 417 | last = sub[fbox.last_visited] | 421 | last = sub[fbox.last_visited] |
|---|
| 418 | if last is None: | 422 | if last is None: |
|---|
| 419 | return 1 | 423 | return 1 |
|---|
| 420 | t = time.time() | 424 | t = time.time() |
|---|
| 421 | # convert last time simply from ical to epoch? | 425 | # convert last time simply from ical to epoch? |
|---|
| 422 | | 426 | |
|---|
| 423 | return 1 | 427 | return 1 |
|---|
| 424 | since = t - float(str(last)) | 428 | since = t - float(str(last)) |
|---|
| 425 | | 429 | |
|---|
| 426 | interval = sub[fbox.interval] | 430 | interval = sub[fbox.interval] |
|---|
| 427 | if interval is None: | 431 | if interval is None: |
|---|
| 428 | sub[fbox.interval] = str(100) | 432 | sub[fbox.interval] = str(100) |
|---|
| 429 | interval = sub[fbox.interval] | 433 | interval = sub[fbox.interval] |
|---|
| 430 | secs = int(str(interval))*60 | 434 | secs = int(str(interval))*60 |
|---|
| 431 | if since >= secs: | 435 | if since >= secs: |
|---|
| 432 | return 1 | 436 | return 1 |
|---|
| 433 | return None | 437 | return None |
|---|
| 434 | | 438 | |
|---|
| 435 | def counter(self): | 439 | def counter(self): |
|---|
| 436 | """Update the counter that's used to generate visit context URIs.""" | 440 | """Update the counter that's used to generate visit context URIs.""" |
|---|
| 437 | from rdfobj import fbox | 441 | from rdfobj import fbox |
|---|
| 438 | fbox = rdfobj.fbox | 442 | fbox = rdfobj.fbox |
|---|
| 439 | counter = self.model.fetch(fbox.Visit_Count) | 443 | counter = self.model.fetch(fbox.Visit_Count) |
|---|
| 440 | c = counter[fbox.count] | 444 | c = counter[fbox.count] |
|---|
| 441 | c = int(str(c))+1 | 445 | c = int(str(c))+1 |
|---|
| 442 | counter[fbox.count] = str(c) | 446 | counter[fbox.count] = str(c) |
|---|
| 443 | return c | 447 | return c |
|---|
| 444 | | 448 | |
|---|
| 445 | | 449 | |
|---|
| 446 | if __name__ == "__main__": | 450 | if __name__ == "__main__": |
|---|
| 447 | bbox = BBox(visit_true = 1) | 451 | bbox = BBox(visit_true = 1) |
|---|
| 448 | bbox.subscribe(feed='http://frot.org/wirelesslondon/bbox.rdf',format=fbox.rss) | 452 | bbox.subscribe(feed='http://frot.org/wirelesslondon/bbox.rdf',format=fbox.rss) |
|---|
| 449 | bbox.subscribe(feed='http://frot.org/devlog/index.rss',format=fbox.rss) | 453 | bbox.subscribe(feed='http://frot.org/devlog/index.rss',format=fbox.rss) |
|---|
| 450 | bbox.subscribe(feed='http://zooleika.org.uk/bio/foaf.rdf',format=fbox.rdf) | 454 | bbox.subscribe(feed='http://zooleika.org.uk/bio/foaf.rdf',format=fbox.rdf) |
|---|
| 451 | bbox.read_subscriptions() | 455 | bbox.read_subscriptions() |
|---|