Changeset 103
- Timestamp:
- 06/12/05 17:39:59 (4 years ago)
- Files:
-
- bbox/bbox/__init__.py (modified) (1 diff)
- bbox/bbox/config.py (modified) (1 diff)
- bbox/bbox/spatialStore.py (modified) (1 diff)
- bbox/bbox/ui/__init__.py (modified) (1 diff)
- bbox/bbox/ui/getitems.py (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
bbox/bbox/__init__.py
Revision 52 Revision 103 1 # bbox - an RSS / RDF aggregator 1 # bbox - an RSS / RDF aggregator 2 # Jo Walsh - Dec 2004 - Mar 2005 2 # Jo Walsh - Dec 2004 - Mar 2005 3 3 4 # This code owes heavily to the approach and source in Edd Dumbill's 4 # This code owes heavily to the approach and source in Edd Dumbill's 5 # IBM Developerworks article on aggregating RSS with contexts: 5 # IBM Developerworks article on aggregating RSS with contexts: 6 # http://www-106.ibm.com/developerworks/xml/library/x-rdfprov.html 6 # http://www-106.ibm.com/developerworks/xml/library/x-rdfprov.html 7 7 8 # It uses Mark Pilgrim's feedparser, at http://feedparser.org/ 8 # It uses Mark Pilgrim's feedparser, at http://feedparser.org/ 9 # This software has 2000 tests. The code is included in this package. 9 # This software has 2000 tests. The code is included in this package. 10 10 11 # It uses the 'rdfobj', an object interface to the python interface 11 # It uses the 'rdfobj', an object interface to the python interface 12 # to the redland rdf toolkit. This is also included. 12 # to the redland rdf toolkit. This is also included. 13 # redland is at http://www.redland.opensource.ac.uk/ 13 # redland is at http://www.redland.opensource.ac.uk/ 14 14 15 import feedparser 15 import feedparser 16 import time, datetime 16 import time, datetime 17 import rdfobj 17 import rdfobj 18 import RDF 18 import RDF 19 from politehttp import polite_request 19 from politehttp import polite_request 20 import bbox.config 20 import bbox.config 21 import os 21 import os 22 from warnings import warn 22 os.chdir(bbox.config.store) 23 os.chdir(bbox.config.store) 23 24 24 class BBox: 25 class BBox: 25 26 26 def __init__(self,spatialStore=None,verbose=None,always_visit=None): 27 def __init__(self,spatialStore=None,verbose=None,always_visit=None): 27 """We initialise a bbox by passing a database to it. If wishing to use an optional spatial index, a spatialStore object must be supplied. setting verbose to a true value turns on BBox's stream of consciousness.""" 28 """We initialise a bbox by passing a database to it. If wishing to use an optional spatial index, a spatialStore object must be supplied. setting verbose to a true value turns on BBox's stream of consciousness.""" 28 self.spatialStore = spatialStore 29 self.spatialStore = spatialStore 29 self._verbose = verbose 30 self._verbose = verbose 30 self._visit_true = always_visit 31 self._visit_true = always_visit 31 self.model = rdfobj.Model(bbox.config.db,db='hash') 32 self.model = rdfobj.Model(bbox.config.db,db='hash') 32 self.model.load(bbox.config.boot) 33 self.model.load(bbox.config.boot) 33 from rdfobj import fbox 34 from rdfobj import fbox 34 fbox = rdfobj.fbox 35 fbox = rdfobj.fbox 35 counter = self.model.fetch(fbox.Visit_Count) 36 counter = self.model.fetch(fbox.Visit_Count) 36 c = counter[fbox.count] 37 c = counter[fbox.count] 37 if c is None: c = 0 38 if c is None: c = 0 38 c = int(str(c))+1 39 c = int(str(c))+1 39 counter[fbox.count] = str(c) 40 counter[fbox.count] = str(c) 40 41 41 if counter is None: 42 if counter is None: 42 v = self.model.create(fbox.Visit_Count, uri=fbox.Visit_Count) 43 v = self.model.create(fbox.Visit_Count, uri=fbox.Visit_Count) 43 v[fbox.count] = 0 44 v[fbox.count] = 0 44 45 45 46 46 def mention(self,thought): 47 def mention(self,thought): 47 """If BBox is constructed with verbose=1, prints to STDOUT (currently) a record of what it's up to.""" 48 """If BBox is constructed with verbose=1, prints to STDOUT (currently) a record of what it's up to.""" 48 if self._verbose: 49 if self._verbose: 49 print(thought) 50 print(thought) 50 51 51 def read_subscriptions(self): 52 def read_subscriptions(self): 52 """read_subscriptions() picks up the latest RSS feed updates. """ 53 """read_subscriptions() picks up the latest RSS feed updates. """ 53 self.mention("checking subscriptions.") 54 self.mention("checking subscriptions.") 54 subs = self.subscriptions() 55 subs = self.subscriptions() 55 from rdfobj import fbox 56 from rdfobj import fbox 56 fbox = rdfobj.fbox 57 fbox = rdfobj.fbox 57 for s in subs: 58 for s in subs: 58 self.mention("reading "+str(s[fbox.channel])) 59 self.mention("reading "+str(s[fbox.channel])) 59 format = s[fbox.format].uri() 60 format = s[fbox.format].uri() 60 c = s[fbox.channel] 61 c = s[fbox.channel] 61 62 62 # see if we're actually due a visit 63 # see if we're actually due a visit 63 due = self.visit_scheduled(s) 64 due = self.visit_scheduled(s) 64 if due is None: 65 if due is None: 65 print "nothing due to look at!" 66 print "nothing due to look at!" 66 subs.next() 67 subs.next() 67 else: 68 else: 68 if format == fbox.rss: 69 if format == fbox.rss: 69 self.read_rss(c.uri(),subscription=s) 70 self.read_rss(c.uri(),subscription=s) 70 elif format == fbox.rdf: 71 elif format == fbox.rdf: 71 self.read_rdf(s[fbox.channel].uri(),subscription=s) 72 self.read_rdf(s[fbox.channel].uri(),subscription=s) 72 73 73 def read_rss(self,uri,context=None,subscription=None): 74 def read_rss(self,uri,context=None,subscription=None): 75 """Read updates from an RSS feed.""" 74 76 75 #if subscription is None: subscription = {} 77 #if subscription is None: subscription = {} 76 rss = rdfobj.rss 78 rss = rdfobj.rss 77 dc = rdfobj.dc 79 dc = rdfobj.dc 78 ical = rdfobj.ical 80 ical = rdfobj.ical 79 81 80 result = self.politely_get_uri(uri,subscription=subscription) 82 result = self.politely_get_uri(uri,subscription=subscription) 81 channel = self.model.fetch(uri) 83 channel = self.model.fetch(uri) 82 """If we got a feed object back from the request, then create a 84 """If we got a feed object back from the request, then create a 83 context for this visit to the feed, and store the entries that we 85 context for this visit to the feed, and store the entries that we 84 collected from it.""" 86 collected from it.""" 85 if result['status'] != 200: return 87 if result['status'] != 200: return 86 feed = feedparser.parse(result['data']) 88 feed = feedparser.parse(result['data']) 87 if feed.has_key('feed'): 89 if feed.has_key('feed'): 88 context = self.visit(uri) 90 context = self.visit(uri) 89 # existence of exact duplicates? 91 # existence of exact duplicates? 90 items = [] 92 items = [] 91 for e in feed.entries: 93 for e in feed.entries: 92 item = self.model.create( rss.item, uri=str(e.link), context = context ) 94 item = self.model.create( rss.item, uri=str(e.link), context = context ) 93 if e.has_key('summary'): item[rss.description] = str(e.summary) 95 if e.has_key('summary'): item[rss.description] = str(e.summary) 94 96 95 if e.has_key('content'): item[rss.description] = str(e.content.value) 97 if e.has_key('content'): item[rss.description] = str(e.content.value) 96 # d.entries[0].modified_parsed is common 98 # d.entries[0].modified_parsed is common 97 time_tuple = e.modified_parsed 99 time_tuple = e.modified_parsed 98 100 99 # future experiments with pdis and better ical handling 101 # future experiments with pdis and better ical handling 100 # item[ical.datetime] = some process with time_tuple and strftime 102 # item[ical.datetime] = some process with time_tuple and strftime 101 # d = datetime.datetime(time_tuple) 103 # d = datetime.datetime(time_tuple) 102 # ical_date = ical_datetime.datetime_to_string(d) 104 # ical_date = ical_datetime.datetime_to_string(d) 103 # print ical_date 105 # print ical_date 104 # item[ical.datetime] = ical_date 106 # item[ical.datetime] = ical_date 105 107 106 ical_enough = time.strftime("%Y%m%dT%H%M%SZ",time_tuple) 108 ical_enough = time.strftime("%Y%m%dT%H%M%SZ",time_tuple) 107 item[ical.datetime] = ical_enough 109 item[ical.datetime] = ical_enough 108 items.append(item) 110 items.append(item) 109 for i in channel[rss.items]: 111 its = channel[rss.items] 112 for i in its: 110 items.append(i) 113 items.append(i) 111 114 112 channel[rss.items] = items 115 channel[rss.items] = items 113 116 114 117 115 def read_rdf(self,uri,subscription=None): 118 def read_rdf(self,uri,subscription=None): 119 """Read updates from an RDF url.""" 116 result = self.politely_get_uri(uri,subscription=subscription) 120 result = self.politely_get_uri(uri,subscription=subscription) 117 if result['status'] == 200: 121 if result['status'] == 200: 118 self.model.load(uri) 122 self.model.load(uri) 119 123 120 def politely_get_uri(self,uri,subscription=None): 124 def politely_get_uri(self,uri,subscription=None): 121 125 """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.""" 122 """Parse RDF feeds directly into Redland""" 126 123 124 # we should deal with etag/last-mod politely here too @@TODO 127 # we should deal with etag/last-mod politely here too @@TODO 125 #visit = self.visit(uri) 128 #visit = self.visit(uri) 126 result = None 129 result = None 127 fbox = rdfobj.fbox 130 fbox = rdfobj.fbox 128 if subscription[fbox.last_etag] is not None: 131 if subscription[fbox.last_etag] is not None: 129 result = polite_request(str(uri),etag=str(subscription[fbox.last_etag])) 132 result = polite_request(str(uri),etag=str(subscription[fbox.last_etag])) 130 elif subscription[fbox.last_modified] is not None: 133 elif subscription[fbox.last_modified] is not None: 131 134 132 result = polite_request(str(uri),lastmodified=str(subscription[fbox.last_modified])) 135 result = polite_request(str(uri),lastmodified=str(subscription[fbox.last_modified])) 133 else: result = polite_request(str(uri)) 136 else: result = polite_request(str(uri)) 134 self.mention("received response: "+str(result['status'])) 137 self.mention("received response: "+str(result['status'])) 135 138 136 # look to the postgis index 139 # look to the postgis index 137 """Take actions about other kinds of HTTP statuses.(TODO)""" 140 """Take actions about other kinds of HTTP statuses.(TODO)""" 138 # handling different HTTP statuses. 141 # handling different HTTP statuses. 139 subscription[fbox.http_status] = str(result['status']) 142 subscription[fbox.http_status] = str(result['status']) 140 subscription[fbox.last_etag] = result['etag'] 143 subscription[fbox.last_etag] = result['etag'] 141 subscription[fbox.last_modified] = result['lastmodified'] 144 subscription[fbox.last_modified] = result['lastmodified'] 142 subscription[fbox.last_visited] = time.strftime("%Y%m%dT%H%M%SZ") 145 subscription[fbox.last_visited] = time.strftime("%Y%m%dT%H%M%SZ") 143 return result 146 return result 144 147 145 def subscriptions(self): 148 def subscriptions(self): 146 """ subscriptions() returns a list (Iterator type) of the URLs at which149 """Returns a list (Iterator type) of the URLs at which 147 there is a feed that we are subscribed to (fbox:Feed type)""" 150 there is a feed that we are subscribed to (fbox:Feed type)""" 148 from rdfobj import fbox, rdf 151 from rdfobj import fbox, rdf 149 fbox = rdfobj.fbox 152 fbox = rdfobj.fbox 150 rdf = rdfobj.rdf 153 rdf = rdfobj.rdf 151 subs = self.model.search(rdf.type,fbox.Feed) 154 subs = self.model.search(rdf.type,fbox.Feed) 152 return subs 155 return subs 153 156 154 def subscription(self,uri): 157 def subscription(self,uri): 158 """Given a uri, returns the rdfobj which is the subscription it represents.""" 155 obj = self.model.fetch(uri) 159 obj = self.model.fetch(uri) 156 return obj 160 return obj 157 161 158 def items(self,uri): 162 def items(self,uri,since=None,until=None): 159 from rdfobj import fbox 163 """Get items from a feed, optionally filtering by date. (not completely implemented)""" 164 from rdfobj import fbox, dc, rss 165 rss = fbox.rss 166 dc = fbox.dc 160 fbox = rdfobj.fbox 167 fbox = rdfobj.fbox 161 s = self.subscription(uri) 168 s = self.subscription(uri) 162 c = s[fbox.channel] 169 c = s[fbox.channel] 170 out = [] 171 if since is not None: 172 for i in c[rss.items]: 173 warn(i[dc.date]) 174 if i[dc.date] > since: 175 out.append(i) 176 163 return c[rss.items] 177 return c[rss.items] 164 178 165 def subscribe(self,feed=None,format=None,interval=None): 179 def subscribe(self,feed=None,format=None,interval=None): 166 """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.""" 180 """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.""" 167 from rdfobj import fbox 181 from rdfobj import fbox 168 fbox = rdfobj.fbox 182 fbox = rdfobj.fbox 169 if feed is None: return 183 if feed is None: return 170 184 171 f = self.model.search(fbox.channel,feed) 185 f = self.model.search(fbox.channel,feed) 172 found = None 186 found = None 173 for n in f: 187 for n in f: 174 found = 1 188 found = 1 175 if found is not None: 189 if found is not None: 176 return 190 return 177 191 178 self.mention("subscribing to "+str(feed)) 192 self.mention("subscribing to "+str(feed)) 179 193 180 if format is None: format = fbox.rdf 194 if format is None: format = fbox.rdf 181 195 182 if interval is None: interval = str(100) 196 if interval is None: interval = str(100) 183 197 184 ff = self.model.create( fbox.Feed, uri=None ) 198 ff = self.model.create( fbox.Feed, uri=None ) 185 ff[fbox.channel] = str(feed) 199 ff[fbox.channel] = str(feed) 186 ff[fbox.format] = str(format) 200 ff[fbox.format] = str(format) 187 ff[fbox.interval] = interval 201 ff[fbox.interval] = interval 188 202 189 return ff 203 return ff 190 204 191 def update(self): 205 def update(self): 192 """ update() causes all the subscribed URLs to be visited for updates."""206 """Causes all the subscribed URLs to be visited for updates.""" 193 subs = self.subscriptions() 207 subs = self.subscriptions() 194 from rdfobj import fbox 208 from rdfobj import fbox 195 fbox = rdfobj.fbox 209 fbox = rdfobj.fbox 196 while not subs.end(): 210 while not subs.end(): 197 s = subs.current() 211 s = subs.current() 198 self.visit(s[fbox.channel]) 212 self.visit(s[fbox.channel]) 199 subs.next() 213 subs.next() 200 214 201 def visit(self,uri=None): 215 def visit(self,uri=None): 202 """ visit() creates an anonymous object which records a visit that we216 """Creates an anonymous object which records a visit that we 203 paid to a feed, including a counter of times visited. This object is 217 paid to a feed, including a counter of times visited. This object is 204 used as a Redland context for all the information collected from a feed 218 used as a Redland context for all the information collected from a feed 205 during this visit.""" 219 during this visit.""" 206 # redland had problems serialising models with bnode context uris 220 # redland had problems serialising models with bnode context uris 207 count = self.counter() 221 count = self.counter() 208 from rdfobj import fbox 222 from rdfobj import fbox 209 fbox = rdfobj.fbox 223 fbox = rdfobj.fbox 210 visit_uri = str(fbox.visit)+'/'+str(count) 224 visit_uri = str(fbox.visit)+'/'+str(count) 211 visit = self.model.create( fbox.Visit , visit_uri) 225 visit = self.model.create( fbox.Visit , visit_uri) 212 226 213 visit[fbox.source] = uri 227 visit[fbox.source] = uri 214 t = time.strftime("%Y%m%dT%H%M%SZ") 228 t = time.strftime("%Y%m%dT%H%M%SZ") 215 visit[fbox.timestamp] = t 229 visit[fbox.timestamp] = t 216 return RDF.Node(RDF.Uri(str(visit.uri()))) 230 return RDF.Node(RDF.Uri(str(visit.uri()))) 217 231 218 def user(self,token=None,nick=None,mbox=None): 232 def user(self,token=None,nick=None,mbox=None): 219 """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!""" 233 """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!""" 220 from rdfobj import foaf 234 from rdfobj import foaf 221 foaf = rdfobj.foaf 235 foaf = rdfobj.foaf 222 if token is not None: 236 if token is not None: 223 users = self.model.search(foaf.auth_token,token) 237 users = self.model.search(foaf.auth_token,token) 224 for u in users: 238 for u in users: 225 return u[foaf.alias] 239 return u[foaf.alias] 226 if mbox is not None: 240 if mbox is not None: 227 users = self.model.search(foaf.mbox,mbox) 241 users = self.model.search(foaf.mbox,mbox) 228 for u in users: 242 for u in users: 229 return u 243 return u 230 if nick is not None: 244 if nick is not None: 231 o = [] 245 o = [] 232 users = self.model.search(foaf.name,nick) 246 users = self.model.search(foaf.name,nick) 233 for u in users: 247 for u in users: 234 o.append(u) 248 o.append(u) 235 users = self.model.search(foaf.givenName,nick) 249 users = self.model.search(foaf.givenName,nick) 236 for u in users: o.append(u) 250 for u in users: o.append(u) 237 return o 251 return o 238 252 239 def add_user(self,nick=None,mbox=None,password=None): 253 def add_user(self,nick=None,mbox=None,password=None): 240 """ Create a new user foaf:Person""" 254 """ Create a new user foaf:Person""" 241 store = self.store 255 store = self.store 242 from rdfobj import foaf, wlan 256 from rdfobj import foaf, wlan 243 foaf = rdfobj.foaf 257 foaf = rdfobj.foaf 244 wlan = rdfobj.wlan 258 wlan = rdfobj.wlan 245 259 246 obj = self.model.create(foaf.Person,uri=uri) 260 obj = self.model.create(foaf.Person,uri=uri) 247 obj[foaf.mbox] = mbox 261 obj[foaf.mbox] = mbox 248 obj[foaf.nick] = nick 262 obj[foaf.nick] = nick 249 if page is not None: obj[foaf.homepage] = page 263 if page is not None: obj[foaf.homepage] = page 250 self.obj = obj 264 self.obj = obj 251 265 252 """ 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. """ 266 """ 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. """ 253 267 254 auth = store.create(foaf.AuthedPerson) 268 auth = store.create(foaf.AuthedPerson) 255 auth[foaf.password] = password 269 auth[foaf.password] = password 256 auth[foaf.nick] = nick 270 auth[foaf.nick] = nick 257 auth[foaf.alias] = obj 271 auth[foaf.alias] = obj 258 272 259 token = self.auth_token() 273 token = self.auth_token() 260 auth[foaf.auth_token] = token 274 auth[foaf.auth_token] = token 261 self.model.sync() 275 self.model.sync() 262 return token 276 return token 263 277 264 def auth_token(self): 278 def auth_token(self): 279 """Generate a random auth token.""" 265 x = '' 280 x = '' 266 for n in range(0, 6): 281 for n in range(0, 6): 267 x = x + chr(65 + random.randint(0, 26)) 282 x = x + chr(65 + random.randint(0, 26)) 268 return x 283 return x 269 284 270 def visit_scheduled(self,sub): 285 def visit_scheduled(self,sub): 271 """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.""" 286 """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.""" 272 if self._visit_true is not None: 287 if self._visit_true is not None: 273 return 1 288 return 1 274 from rdfobj import fbox 289 from rdfobj import fbox 275 fbox = rdfobj.fbox 290 fbox = rdfobj.fbox 276 last = sub[fbox.last_visited] 291 last = sub[fbox.last_visited] 277 if last is None: 292 if last is None: 278 return 1 293 return 1 279 t = time.time() 294 t = time.time() 280 # convert last time simply from ical to epoch? 295 # convert last time simply from ical to epoch? 281 296 282 return 1 297 return 1 283 since = t - float(str(last)) 298 since = t - float(str(last)) 284 299 285 interval = sub[fbox.interval] 300 interval = sub[fbox.interval] 286 if interval is None: 301 if interval is None: 287 sub[fbox.interval] = str(100) 302 sub[fbox.interval] = str(100) 288 interval = sub[fbox.interval] 303 interval = sub[fbox.interval] 289 secs = int(str(interval))*60 304 secs = int(str(interval))*60 290 if since >= secs: 305 if since >= secs: 291 return 1 306 return 1 292 return None 307 return None 293 308 294 def counter(self): 309 def counter(self): 310 """Update the counter that's used to generate visit context URIs.""" 295 from rdfobj import fbox 311 from rdfobj import fbox 296 fbox = rdfobj.fbox 312 fbox = rdfobj.fbox 297 counter = self.model.fetch(fbox.Visit_Count) 313 counter = self.model.fetch(fbox.Visit_Count) 298 c = counter[fbox.count] 314 c = counter[fbox.count] 299 c = int(str(c))+1 315 c = int(str(c))+1 300 counter[fbox.count] = str(c) 316 counter[fbox.count] = str(c) 301 return c 317 return c 302 318 303 319 304 if __name__ == "__main__": 320 if __name__ == "__main__": 305 bbox = BBox(visit_true = 1) 321 bbox = BBox(visit_true = 1) 306 bbox.subscribe(feed='http://frot.org/wirelesslondon/bbox.rdf',format=fbox.rss) 322 bbox.subscribe(feed='http://frot.org/wirelesslondon/bbox.rdf',format=fbox.rss) 307 bbox.subscribe(feed='http://frot.org/devlog/index.rss',format=fbox.rss) 323 bbox.subscribe(feed='http://frot.org/devlog/index.rss',format=fbox.rss) 308 bbox.subscribe(feed='http://zooleika.org.uk/bio/foaf.rdf',format=fbox.rdf) 324 bbox.subscribe(feed='http://zooleika.org.uk/bio/foaf.rdf',format=fbox.rdf) 309 bbox.read_subscriptions() 325 bbox.read_subscriptions() bbox/bbox/config.py
Revision 47 Revision 103 1 boot='file:/home/ wirelesslondon/lib/consumotronic/bbox/boot.rdf'1 boot='file:/home/jo/consumotronic/bbox/boot.rdf' 2 store = '/home/ wirelesslondon/store/'2 store = '/home/jo/bbox/store/' 3 db = 'bbox' 3 db = 'bbox' 4 spatialdb = 'bbox' 5 utm_zone = '31' 6 srid = '32631' bbox/bbox/spatialStore.py
Revision 56 Revision 103 1 """provides the unified data store between redland RDF and postGISindex"""1 """provides the unified data store between redland RDF and spatial (for now PostGIS) index""" 2 import pgdb 2 import pgdb 3 import re 3 import re 4 import warnings 4 import warnings 5 import bbox.config 5 import bbox.config 6 6 7 _database = bbox.config._spatialdb 7 _database = bbox.config._spatialdb 8 _utm_zone = bbox.config._utm_zone 8 _utm_zone = bbox.config._utm_zone 9 _srid = bbox.config._srid 9 _srid = bbox.config._srid 10 10 11 class SpatialStore: 11 class SpatialStore: 12 12 13 def __init__(self,vars = None,database=None): 13 def __init__(self,vars = None,database=None): 14 """Create a new store interface to a certain database name, passed in here or defaulting to what's set in bbox.config._spatialdb""" 14 self.vars = vars 15 self.vars = vars 15 if database is None: 16 if database is None: 16 database = _database 17 database = _database 17 18 18 db = pgdb.connect(database=database) 19 db = pgdb.connect(database=database) 19 self.db = db 20 self.db = db 20 21 21 def geom(self,uri): 22 def geom(self,uri): 22 """ We are passed the uri of something in the DB"""23 """Passed the uri of something in the DB, returns geometry for it as a dict; if the geom is a point, {'x':x,'y':y}; if the geom is a line, x1,y1,x2,y2; polygon geom isn't really supported yet.""" 23 # again we have an issue of what kind of geom this is 24 # again we have an issue of what kind of geom this is 24 select = "select GeometryType(geom) from nodes where node='"+uri+"'" 25 select = "select GeometryType(geom) from nodes where node='"+uri+"'" 25 db = self.db.cursor() 26 db = self.db.cursor() 26 db.execute(select) 27 db.execute(select) 27 g = db.fetchone() 28 g = db.fetchone() 28 geom = {} 29 geom = {} 29 if g[0] == 'LINESTRING': 30 if g[0] == 'LINESTRING': 30 select = "SELECT name, type, X(StartPoint(geom)) as x, Y(StartPoint(geom)), GeometryFromText(geom) as y, X(EndPoint(geom)), Y(EndPoint(geom)) from nodes where node='"+uri+"'" 31 select = "SELECT name, type, X(StartPoint(geom)) as x, Y(StartPoint(geom)), GeometryFromText(geom) as y, X(EndPoint(geom)), Y(EndPoint(geom)) from nodes where node='"+uri+"'" 31 db.execute(select) 32 db.execute(select) 32 c = db.fetchone() 33 c = db.fetchone() 33 geom['x1'] = c[2] 34 geom['x1'] = c[2] 34 geom['x2'] = c[4] 35 geom['x2'] = c[4] 35 geom['y1'] = c[3] 36 geom['y1'] = c[3] 36 geom['y2'] = c[5] 37 geom['y2'] = c[5] 37 38 38 elif g[0] == 'POINT': 39 elif g[0] == 'POINT': 39 select = "SELECT name,type,X(geom),Y(geom) from nodes where node='"+uri+"'" 40 select = "SELECT name,type,X(geom),Y(geom) from nodes where node='"+uri+"'" 40 db.execute(select) 41 db.execute(select) 41 c = db.fetchone() 42 c = db.fetchone() 42 geom['x'] = c[2] 43 geom['x'] = c[2] 43 geom['y'] = c[3] 44 geom['y'] = c[3] 44 45 45 elif g[0] == 'POLYGON': 46 elif g[0] == 'POLYGON': 47 #@@TODO support this properly, as vectors, whatever 46 select = 'SELECT GeometryAsText(geom) where node = \''+uri+"'" 48 select = 'SELECT GeometryAsText(geom) where node = \''+uri+"'" 47 49 48 return geom 50 return geom 49 51 50 def add_geom(self,uri=None,name=None,type=None,rdf_type=None,status=None,owner=None,x=None,y=None,z=None,date=None): 52 def add_geom(self,uri=None,name=None,type=None,rdf_type=None,status=None,owner=None,x=None,y=None,z=None,date=None): 51 """ Add geometry for a Uri into a spatial index. needs uri and also name=[name] must be supplied.""" 53 """ Add geometry for a Uri into a spatial index. needs uri and also name=[name] must be supplied.""" 52 if uri is None: return 54 if uri is None: return 53 if type is None: type = '' 55 if type is None: type = '' 54 56 55 if rdf_type is None: 57 if rdf_type is None: 56 rdf_type = 'http://www.w3.org/2003/01/geo/wgs84_pos#SpatialThing' 58 rdf_type = 'http://www.w3.org/2003/01/geo/wgs84_pos#SpatialThing' 57 if status is None: status = '' 59 if status is None: status = '' 58 if owner is None: owner = 'wl@frot.org' 60 if owner is None: owner = 'wl@frot.org' 59 if date is None: 61 if date is None: 60 date = 'now()' 62 date = 'now()' 61 else: 63 else: 62 date = "'"+str(date)+"'" 64 date = "'"+str(date)+"'" 63 65 64 geom = "GeometryFromText('POINT("+str(x)+' '+str(y)+")',"+str(_srid)+")" 66 geom = "GeometryFromText('POINT("+str(x)+' '+str(y)+")',"+str(_srid)+")" 65 67 66 #insert = "INSERT INTO nodes (node,geom,name,status,rdf_type,type,created) values ('"+str(uri)+"',"+geom+",'"+str(name)+"','"+str(status)+"','"+str(rdf_type)+"','"+str(type)+"',"+date+')' 68 #insert = "INSERT INTO nodes (node,geom,name,status,rdf_type,type,created) values ('"+str(uri)+"',"+geom+",'"+str(name)+"','"+str(status)+"','"+str(rdf_type)+"','"+str(type)+"',"+date+')' 67 #print insert 69 #print insert 68 70 69 db = self.db 71 db = self.db 70 db.cursor().execute("INSERT INTO nodes (node,geom,name,status,rdf_type,type,created) values (%s,"+geom+",%s,%s,%s,%s,"+date+")", (str(uri),str(name),str(status),str(rdf_type),str(type))) 72 db.cursor().execute("INSERT INTO nodes (node,geom,name,status,rdf_type,type,created) values (%s,"+geom+",%s,%s,%s,%s,"+date+")", (str(uri),str(name),str(status),str(rdf_type),str(type))) 71 db.commit() 73 db.commit() 72 #print "new geometry for "+str(uri) 74 #print "new geometry for "+str(uri) 73 75 74 def find_near(self,node=None, lat=None,lon=None,x=None,y=None,r=None,type=None,terse=None): 76 def find_near(self,node=None, lat=None,lon=None,x=None,y=None,r=None,type=None,terse=None): 75 if lat is not None: 77 if lat is not None: 76 thing = wl.spatialThing.SpatialThing(spatialStore = self) 78 thing = wl.spatialThing.SpatialThing(spatialStore = self) 77 (x,y) = thing.latlon_to_utm(lat=lat,lon=lon) 79 (x,y) = thing.latlon_to_utm(lat=lat,lon=lon) 78 if r is None: 80 if r is None: 79 r = 1600 81 r = 1600 80 found = self.within_box(minx=x-r,miny=y-r,maxx=x+r,maxy=y+r,rdf_type=type,terse=terse) 82 found = self.within_box(minx=x-r,miny=y-r,maxx=x+r,maxy=y+r,rdf_type=type,terse=terse) 81 return found 83 return found 82 84 83 def within_box(self,minx=None,miny=None,maxx=None,maxy=None,type=None,rdf_type=None,terse=None): 85 def within_box(self,minx=None,miny=None,maxx=None,maxy=None,type=None,rdf_type=None,terse=None): 84 """Accepts a minx,miny,maxx,maxy bounding box; should return a list of spatialThings, unless you specify terse=1, in whih case you just get a array of dicts built from the postgis db contents.""" 86 """Accepts a minx,miny,maxx,maxy bounding box; should return a list of spatialThings, unless you specify terse=1, in whih case you just get a array of dicts built from the postgis db contents.""" 85 87 86 select = "SELECT node, name, rdf_type, X(geom) as x, Y(geom) as y FROM nodes where Within(geom,GeometryFromText('POLYGON(("+str(minx)+' '+str(miny)+','+str(minx)+' '+str(maxy)+','+str(maxx)+' '+str(maxy)+','+str(maxx)+' '+str(miny)+','+str(minx)+' '+str(miny)+"))',"+str(_srid)+"))" 88 select = "SELECT node, name, rdf_type, X(geom) as x, Y(geom) as y FROM nodes where Within(geom,GeometryFromText('POLYGON(("+str(minx)+' '+str(miny)+','+str(minx)+' '+str(maxy)+','+str(maxx)+' '+str(maxy)+','+str(maxx)+' '+str(miny)+','+str(minx)+' '+str(miny)+"))',"+str(_srid)+"))" 87 warnings.warn( select ) 89 warnings.warn( select ) 88 """Also optionally filter by type""" 90 """Also optionally filter by type""" 89 if type is not None: 91 if type is not None: 90 select = select+' AND type=\''+str(type)+'\'' 92 select = select+' AND type=\''+str(type)+'\'' 91 if rdf_type is not None: 93 if rdf_type is not None: 92 select = select+' AND rdf_type=\''+str(rdf_type)+'\'' 94 select = select+' AND rdf_type=\''+str(rdf_type)+'\'' 93 95 94 db = self.db 96 db = self.db 95 c = db.cursor() 97 c = db.cursor() 96 c.execute(select) 98 c.execute(select) 97 geoms = c.fetchall() 99 geoms = c.fetchall() 98 things = [] 100 things = [] 99 if terse is not None: 101 if terse is not None: 100 for g in geoms: 102 for g in geoms: 101 things.append({'uri':g[0],'name':g[1],'type':g[2],'x':g[3],'y':g[4]}) 103 things.append({'uri':g[0],'name':g[1],'type':g[2],'x':g[3],'y':g[4]}) 102 else: 104 else: 103 for g in geoms: 105 for g in geoms: 104 things.append(g[0]) 106 things.append(g[0]) 105 107 106 """Return a rdfobj node/object for each geometry.""" 108 """Return a rdfobj node/object for each geometry.""" 107 return things 109 return things 108 110 109 def recent(self,days=None,type=None): 111 def recent(self,days=None,type=None): 110 if days is None: days = 30 112 if days is None: days = 30 111 if type is None: type = 'http://xmlns.com/2003/wireless/Node' 113 if type is None: type = 'http://xmlns.com/2003/wireless/Node' 112 query = "SELECT node from nodes where (now() - interval '"+str(days)+" days') <= created and rdf_type = '"+type+"'" 114 query = "SELECT node from nodes where (now() - interval '"+str(days)+" days') <= created and rdf_type = '"+type+"'" 113 db = self.db 115 db = self.db 114 c = db.cursor() 116 c = db.cursor() 115 c.execute(query) 117 c.execute(query) 116 nodes = c.fetchall() 118 nodes = c.fetchall() 117 urls = [] 119 urls = [] 118 for n in nodes: 120 for n in nodes: 119 urls.append(n[0]) 121 urls.append(n[0]) 120 return urls 122 return urls 121 123 122 def update_geom(self,x=None,y=None,uri=None): 124 def update_geom(self,x=None,y=None,uri=None): 123 if x is None: return 125 if x is None: return 124 if uri is None: return 126 if uri is None: return 125 127 126 db = self.db 128 db = self.db 127 c = db.cursor() 129 c = db.cursor() 128 c.execute("UPDATE nodes SET geom = GeometryFromText('POINT("+str(x)+' '+str(y)+")',"+str(_srid)+") where node ='%s'",(str(uri))) 130 c.execute("UPDATE nodes SET geom = GeometryFromText('POINT("+str(x)+' '+str(y)+")',"+str(_srid)+") where node ='%s'",(str(uri))) 129 db.commit() 131 db.commit() 130 132 131 133 bbox/bbox/ui/__init__.py
Revision 48 Revision 103 1 from Cheetah.Template import Template 1 from Cheetah.Template import Template 2 from bbox import BBox 2 from bbox import BBox 3 #from spatialStore import Store 3 #from spatialStore import Store 4 import quixote 4 import quixote 5 import os 5 import os 6 import RDF 6 import RDF 7 import rdfobj 7 import rdfobj 8 8 9 _q_exports = ['_q_index','subs','_q_lookup','getitems' ]9 _q_exports = ['_q_index','subs','_q_lookup','getitems','items'] 10 10 11 def _q_index(r): 11 def _q_index(r): 12 return 'bbox' 12 return 'bbox' 13 13 14 def subs(r): 14 def subs(r): 15 """List current subscriptions.""" 15 """List current subscriptions.""" 16 bb = BBoxUI() 16 bb = BBoxUI() 17 return bb.subs(r) 17 return bb.subs(r) 18 18 19 def read_subs(r): 19 def read_subs(r): 20 """Trigger a read of updates to subscriptions, if found.""" 20 """Trigger a read of updates to subscriptions, if found.""" 21 bb = BBoxUI() 21 bb = BBoxUI() 22 return bb.read_subs(r) 22 return bb.read_subs(r) 23 23 24 def _q_lookup(r,n): 24 def _q_lookup(r,n): 25 pass 25 pass 26 26 27 #def _q_access(request): 27 #def _q_access(request): 28 # raise UnauthorizedError(realm='Foo Realm') 28 # raise UnauthorizedError(realm='Foo Realm') 29 29 30 class BBoxUI: 30 class BBoxUI: 31 def __init__(self): 31 def __init__(self): 32 self.bbox = BBox('bbox.db') 32 self.bbox = BBox('bbox.db') 33 33 34 def subscribe(self,r): 34 def subscribe(self,r,uri=None): 35 """Add a subscription to a uri, either as a query param or passed in as 'uri'.""" 35 uri = r.get_form_var('uri') 36 uri = r.get_form_var('uri') 36 self.bbox.subscribe(uri=uri) 37 self.bbox.subscribe(uri=uri) 37 context = self.bbox.visit(uri) 38 context = self.bbox.visit(uri) 38 self.bbox.read(uri,context=context) 39 self.bbox.read(uri,context=context) 39 40 40 def read_subs(self,r): 41 def read_subs(self,r): 41 self.bbox.read_subscriptions() 42 self.bbox.read_subscriptions() 42 43 43 def subs(self,r): 44 def subs(self,r): 44 subs = self.bbox.subscriptions() 45 subs = self.bbox.subscriptions() 45 xml = self.serialise(subs) 46 xml = self.serialise(subs) 46 return xml 47 return xml 47 48 48 def getitems(self,r): 49 def getitems(self,r): 49 """This is the bloglines-style interface""" 50 """This is the bloglines-style interface""" 50 """See http://www.bloglines.com/services/api/getitems """ 51 """See http://www.bloglines.com/services/api/getitems """ 51 52 52 read = r.get_form_var('n') 53 read = r.get_form_var('n') 53 """ 'n' parameter - 1 marks downloaded items as read, 0 does not.""" 54 """ 'n' parameter - 1 marks downloaded items as read, 0 does not.""" 54 55 55 date = r.get_form_var('d') 56 date = r.get_form_var('d') 56 """ 'd' parameter - date to get new entries since. """ 57 """ 'd' parameter - date to get new entries since. """ 57 58 58 s = r.get_form_var('s') 59 s = r.get_form_var('s') 59 60 60 """ 'subid'. we will accept an integer id which is a unique key in the node table, but this parameter is optional instead of compulsory; you can otherwise supply a URI-encoded url...""" 61 """ 'subid'. we will accept an integer id which is a unique key in the node table, but this parameter is optional instead of compulsory; you can otherwise supply a URI-encoded url...""" 61 #sub = bbox.subscription(s) 62 #sub = bbox.subscription(s) 62 items = bbox.items(s)63 items = self.bbox.items(s) 63 out = '' 64 out = '' 64 for i in items: 65 for i in items: 65 out = out + str(i) + "\n" 66 out = out + str(i) + "\n" 66 67 67 if d: 68 if d: 68 pass 69 pass 69 70 70 uri = r.get_form_var('uri') 71 uri = r.get_form_var('uri') 71 if uri is None: uri = r.get_form_var('u') 72 if uri is None: uri = r.get_form_var('u') 72 73 73 74 74 75 75 """HTTP Status Response 76 """HTTP Status Response 76 77 77 * 200 - normal 78 * 200 - normal 78 * 304 - the request produced no entries (either there were no unread entries, or no entries after the given date, depending on the parameters passed in) 79 * 304 - the request produced no entries (either there were no unread entries, or no entries after the given date, depending on the parameters passed in) 79 * 401 - incorrect email address or password 80 * 401 - incorrect email address or password 80 * 403 - invalid or missing BloglinesSubId 81 * 403 - invalid or missing BloglinesSubId 81 * 410 - subscription has been deleted 82 * 410 - subscription has been deleted 82 """ 83 """ 83 84 84 def serialise(self,objs): 85 def serialise(self,objs): 85 """Serialises a list of rdf objects, supplied, as RDF/XML.""" 86 """Serialises a list of rdf objects, supplied, as RDF/XML.""" 86 from rdfobj import dc 87 from rdfobj import dc 87 dc = rdfobj.dc 88 dc = rdfobj.dc 88 out = '' 89 out = '' 89 # proper serialisation 90 # proper serialisation 90 tmpstore = RDF.MemoryStorage() 91 tmpstore = RDF.MemoryStorage() 91 tmpmodel = RDF.Model(tmpstore) 92 tmpmodel = RDF.Model(tmpstore) 92 for o in objs:
