comparison lookup.py @ 30:e86642cf7393

style and requirements.txt cleanup
author drewp@bigasterisk.com
date Sun, 12 Jul 2020 13:33:54 -0700
parents 7c82ffbca5d0
children c23acc88324b
comparison
equal deleted inserted replaced
29:890584020372 30:e86642cf7393
1 #!bin/python
2 """ 1 """
3 serve some queries over bookmarks: 2 serve some queries over bookmarks:
4 3
5 /user 4 /user
6 /user/tag+tag+tag 5 /user/tag+tag+tag
7 6
8 and the add-bookmark stuff 7 and the add-bookmark stuff
9
10 """ 8 """
11 import pymongo, bottle, time, urllib.request, urllib.parse, urllib.error, datetime, json, logging 9 from collections import defaultdict
10 import datetime
11 import json
12 import logging
13 import time
14 import urllib.error
15 import urllib.parse
16 import urllib.request
17
18 from bottle import static_file
19 from dateutil.tz import tzlocal
20 import bottle
21 import pymongo
12 import requests 22 import requests
13 from collections import defaultdict 23
14 from urllib.parse import urlparse
15 from dateutil.tz import tzlocal
16 from bottle import static_file
17 from jadestache import Renderer 24 from jadestache import Renderer
25 from link import Links, NotFound
18 from pagetitle import PageTitle 26 from pagetitle import PageTitle
19 from link import Links, NotFound 27
20 db = pymongo.Connection('mongodb.default.svc.cluster.local', tz_aware=True)['href'] 28 db = pymongo.Connection('mongodb.default.svc.cluster.local',
29 tz_aware=True)['href']
21 pageTitle = PageTitle(db) 30 pageTitle = PageTitle(db)
22 links = Links(db) 31 links = Links(db)
23 renderer = Renderer(search_dirs=['template'], debug=bottle.DEBUG) 32 renderer = Renderer(search_dirs=['template'], debug=bottle.DEBUG)
24 log = logging.getLogger() 33 log = logging.getLogger()
25 34
35
26 def getLoginBar(): 36 def getLoginBar():
27 return requests.get("http://openid-proxy.default.svc.cluster.local:9023/_loginBar", 37 return requests.get(
28 headers={ 38 "http://openid-proxy.default.svc.cluster.local:9023/_loginBar",
29 "Cookie" : bottle.request.headers.get('cookie'), 39 headers={
30 'x-site': 'http://bigasterisk.com/openidProxySite/href', 40 "Cookie": bottle.request.headers.get('cookie'),
31 }).text 41 'x-site': 'http://bigasterisk.com/openidProxySite/href',
42 }).text
43
32 44
33 def getUser(): 45 def getUser():
34 agent = bottle.request.headers.get('x-foaf-agent', None) 46 agent = bottle.request.headers.get('x-foaf-agent', None)
35 username = db['user'].find_one({'_id':agent})['username'] if agent else None 47 username = db['user'].find_one({'_id': agent
48 })['username'] if agent else None
36 return username, agent 49 return username, agent
50
37 51
38 def siteRoot(): 52 def siteRoot():
39 try: 53 try:
40 return bottle.request.headers['x-site-root'].rstrip('/') 54 return bottle.request.headers['x-site-root'].rstrip('/')
41 except KeyError: 55 except KeyError:
42 log.warn(repr(bottle.request.__dict__)) 56 log.warn(repr(bottle.request.__dict__))
43 raise 57 raise
44 58
59
45 @bottle.route('/static/<path:path>') 60 @bottle.route('/static/<path:path>')
46 def server_static(path): 61 def server_static(path):
47 return static_file(path, root='static') 62 return static_file(path, root='static')
48 63
64
49 def recentLinks(user, tags, allowEdit): 65 def recentLinks(user, tags, allowEdit):
50 out = {'links':[]} 66 out = {'links': []}
51 t1 = time.time() 67 t1 = time.time()
52 spec = {'user':user} 68 spec = {'user': user}
53 if tags: 69 if tags:
54 spec['extracted.tags'] = {'$all' : tags} 70 spec['extracted.tags'] = {'$all': tags}
55 for doc in db['links'].find(spec, sort=[('t', -1)], limit=50): 71 for doc in db['links'].find(spec, sort=[('t', -1)], limit=50):
56 link = links.forDisplay(doc) 72 link = links.forDisplay(doc)
57 link['allowEdit'] = allowEdit 73 link['allowEdit'] = allowEdit
58 out['links'].append(link) 74 out['links'].append(link)
59 out['stats'] = {'queryTimeMs' : round((time.time() - t1) * 1000, 2)} 75 out['stats'] = {'queryTimeMs': round((time.time() - t1) * 1000, 2)}
60 return out 76 return out
77
61 78
62 def allTags(user, withTags=[]): 79 def allTags(user, withTags=[]):
63 """withTags limits results to other tags that have been used with 80 """withTags limits results to other tags that have been used with
64 those tags""" 81 those tags"""
65 withTags = set(withTags) 82 withTags = set(withTags)
66 count = defaultdict(lambda: 0) # tag : count 83 count = defaultdict(lambda: 0) # tag : count
67 for doc in db['links'].find({'user':user}, fields=['extracted.tags']): 84 for doc in db['links'].find({'user': user}, fields=['extracted.tags']):
68 docTags = set(doc.get('extracted', {}).get('tags', [])) 85 docTags = set(doc.get('extracted', {}).get('tags', []))
69 if withTags and not withTags.issubset(docTags): 86 if withTags and not withTags.issubset(docTags):
70 continue 87 continue
71 for t in docTags.difference(withTags): 88 for t in docTags.difference(withTags):
72 count[t] = count[t] + 1 89 count[t] = count[t] + 1
73 byFreq = [(n, t) for t,n in count.items()] 90 byFreq = [(n, t) for t, n in count.items()]
74 byFreq.sort(key=lambda n_t: (-n_t[0], n_t[1])) 91 byFreq.sort(key=lambda n_t: (-n_t[0], n_t[1]))
75 return [{'label': t, 'count': n} for n, t in byFreq] 92 return [{'label': t, 'count': n} for n, t in byFreq]
76 93
94
77 def renderWithTime(name, data): 95 def renderWithTime(name, data):
78 t1 = time.time() 96 t1 = time.time()
79 rendered = renderer.render_name(name, data) 97 rendered = renderer.render_name(name, data)
80 dt = (time.time() - t1) * 1000 98 dt = (time.time() - t1) * 1000
81 rendered = rendered.replace('TEMPLATETIME', "%.02f ms" % dt) 99 rendered = rendered.replace('TEMPLATETIME', "%.02f ms" % dt)
82 return rendered 100 return rendered
83 101
102
84 @bottle.route('/addLink') 103 @bottle.route('/addLink')
85 def addLink(): 104 def addLink():
86 out = { 105 out = {
87 'toRoot': siteRoot(), 106 'toRoot': siteRoot(),
88 'absRoot': siteRoot(), 107 'absRoot': siteRoot(),
89 'user': getUser()[0], 108 'user': getUser()[0],
90 'withKnockout': True, 109 'withKnockout': True,
91 'fillHrefJson': json.dumps(bottle.request.params.get('url', '')), 110 'fillHrefJson': json.dumps(bottle.request.params.get('url', '')),
92 'loginBar': getLoginBar(), 111 'loginBar': getLoginBar(),
93 } 112 }
94 return renderWithTime('add.jade', out) 113 return renderWithTime('add.jade', out)
114
95 115
96 @bottle.route('/addOverlay') 116 @bottle.route('/addOverlay')
97 def addOverlay(): 117 def addOverlay():
98 p = bottle.request.params 118 p = bottle.request.params
99 119
100 return "" 120 return ""
101 121
102 122
103 @bottle.route('/addLink/proposedUri') 123 @bottle.route('/addLink/proposedUri')
104 def proposedUri(): 124 def proposedUri():
105 uri = bottle.request.params.uri 125 uri = bottle.request.params.uri
106 user, _ = getUser() 126 user, _ = getUser()
107 127
108 try: 128 try:
109 prevDoc = links.find(uri, user) 129 prevDoc = links.find(uri, user)
110 except NotFound: 130 except NotFound:
111 prevDoc = None 131 prevDoc = None
112 132
113 return { 133 return {
114 'description': prevDoc['description'] if prevDoc else pageTitle.pageTitle(uri), 134 'description':
115 'tag' : prevDoc['tag'] if prevDoc else '', 135 prevDoc['description'] if prevDoc else pageTitle.pageTitle(uri),
116 'extended' : prevDoc['extended'] if prevDoc else '', 136 'tag':
117 'shareWith' : prevDoc.get('shareWith', []) if prevDoc else [], 137 prevDoc['tag'] if prevDoc else '',
138 'extended':
139 prevDoc['extended'] if prevDoc else '',
140 'shareWith':
141 prevDoc.get('shareWith', []) if prevDoc else [],
118 'suggestedTags': ['tag1', 'tag2'], 142 'suggestedTags': ['tag1', 'tag2'],
119 'existed': prevDoc is not None, 143 'existed':
120 } 144 prevDoc is not None,
121 145 }
122 if 0: 146
123 pass#proposal check existing links, get page title (stuff that in db), get tags from us and other serviecs. maybe the deferred ones ater 147
148 # proposal check existing links, get page title (stuff that in db), get tags from us and other serviecs. maybe the deferred ones ater
124 149
125 150
126 @bottle.route('/tags') 151 @bottle.route('/tags')
127 def tagFilterComplete(): 152 def tagFilterComplete():
128 params = bottle.request.params 153 params = bottle.request.params
134 159
135 out = [] 160 out = []
136 for t in allTags(params.user, withTags=haveTags): 161 for t in allTags(params.user, withTags=haveTags):
137 if partialTerm and partialTerm not in t['label']: 162 if partialTerm and partialTerm not in t['label']:
138 continue 163 continue
139 out.append({'id': t['label'], 164 out.append({
140 'text': "%s (%s%s)" % (t['label'], 165 'id':
141 t['count'], 166 t['label'],
142 " left" if haveTags else "")}) 167 'text':
143 168 "%s (%s%s)" % (t['label'], t['count'], " left" if haveTags else "")
144 return {'tags' : out} 169 })
145 170
171 return {'tags': out}
172
173
146 @bottle.route('/<user>/') 174 @bottle.route('/<user>/')
147 def userSlash(user): 175 def userSlash(user):
148 bottle.redirect(siteRoot() + "/%s" % urllib.parse.quote(user)) 176 bottle.redirect(siteRoot() + "/%s" % urllib.parse.quote(user))
177
149 178
150 @bottle.route('/<user>.json', method='GET') 179 @bottle.route('/<user>.json', method='GET')
151 def userAllJson(user): 180 def userAllJson(user):
152 data = recentLinks(user, [], allowEdit=getUser()[0] == user) 181 data = recentLinks(user, [], allowEdit=getUser()[0] == user)
153 data['toRoot'] = siteRoot() 182 data['toRoot'] = siteRoot()
154 return json.dumps(data) 183 return json.dumps(data)
155 184
185
156 @bottle.route('/<user>', method='GET') 186 @bottle.route('/<user>', method='GET')
157 def userAll(user): 187 def userAll(user):
158 return userLinks(user, "") 188 return userLinks(user, "")
159 189
160 190
161 @bottle.route('/<user>', method='POST') 191 @bottle.route('/<user>', method='POST')
162 def userAddLink(user): 192 def userAddLink(user):
163 if getUser()[0] != user: 193 if getUser()[0] != user:
164 raise ValueError("not logged in as %s" % user) 194 raise ValueError("not logged in as %s" % user)
165 print(repr(bottle.request.params.__dict__)) 195 print(repr(bottle.request.params.__dict__))
166 doc = links.fromPostdata(bottle.request.params, 196 doc = links.fromPostdata(bottle.request.params, user,
167 user,
168 datetime.datetime.now(tzlocal())) 197 datetime.datetime.now(tzlocal()))
169 links.insertOrUpdate(doc) 198 links.insertOrUpdate(doc)
170 199
171 print("notify about sharing to", repr(doc['shareWith'])) 200 print("notify about sharing to", repr(doc['shareWith']))
172 201
173 bottle.redirect(siteRoot() + '/' + user) 202 bottle.redirect(siteRoot() + '/' + user)
203
174 204
175 def parseTags(tagComponent): 205 def parseTags(tagComponent):
176 # the %20 is coming from davis.js, not me :( 206 # the %20 is coming from davis.js, not me :(
177 return [_f for _f in tagComponent.replace("%20", "+").split('+') if _f] 207 return [_f for _f in tagComponent.replace("%20", "+").split('+') if _f]
178 208
209
179 @bottle.route('/<user>/<tags:re:.*>.json') 210 @bottle.route('/<user>/<tags:re:.*>.json')
180 def userLinksJson(user, tags): 211 def userLinksJson(user, tags):
181 tags = parseTags(tags) 212 tags = parseTags(tags)
182 data = recentLinks(user, tags, allowEdit=getUser()[0] == user) 213 data = recentLinks(user, tags, allowEdit=getUser()[0] == user)
183 data['toRoot'] = siteRoot() 214 data['toRoot'] = siteRoot()
184 return json.dumps(data) 215 return json.dumps(data)
185 216
186 217
187 @bottle.route('/<user>/<tags>') 218 @bottle.route('/<user>/<tags>')
188 def userLinks(user, tags): 219 def userLinks(user, tags):
189 tags = parseTags(tags) 220 tags = parseTags(tags)
190 log.info('userLinks user=%r tags=%r', user, tags) 221 log.info('userLinks user=%r tags=%r', user, tags)
191 data = recentLinks(user, tags, allowEdit=getUser()[0] == user) 222 data = recentLinks(user, tags, allowEdit=getUser()[0] == user)
192 data['loginBar'] = getLoginBar() 223 data['loginBar'] = getLoginBar()
193 data['desc'] = ("%s's recent links" % user) + (" tagged %s" % (tags,) if tags else "") 224 data['desc'] = ("%s's recent links" % user) + (" tagged %s" %
225 (tags, ) if tags else "")
194 data['toRoot'] = siteRoot() 226 data['toRoot'] = siteRoot()
195 data['allTags'] = allTags(user) 227 data['allTags'] = allTags(user)
196 data['user'] = user 228 data['user'] = user
197 data['showPrivateData'] = (user == getUser()[0]) 229 data['showPrivateData'] = (user == getUser()[0])
198 230
199 data['pageTags'] = [{"word":t} for t in tags] 231 data['pageTags'] = [{"word": t} for t in tags]
200 data['stats']['template'] = 'TEMPLATETIME' 232 data['stats']['template'] = 'TEMPLATETIME'
201 return renderWithTime('links.jade', data) 233 return renderWithTime('links.jade', data)
234
202 235
203 @bottle.route('/templates') 236 @bottle.route('/templates')
204 def templates(): 237 def templates():
205 return json.dumps({'linklist': renderer.load_template("linklist.jade")}) 238 return json.dumps({'linklist': renderer.load_template("linklist.jade")})
206 239
240
207 @bottle.route('/') 241 @bottle.route('/')
208 def root(): 242 def root():
209 data = { 243 data = {
210 'loginBar': getLoginBar(), 244 'loginBar': getLoginBar(),
211 'toRoot': siteRoot(), 245 'toRoot': siteRoot(),
212 'stats': {'template': 'TEMPLATETIME'}, 246 'stats': {
213 'users': [{'user':doc['username']} for doc in db['user'].find()], 247 'template': 'TEMPLATETIME'
214 } 248 },
249 'users': [{
250 'user': doc['username']
251 } for doc in db['user'].find()],
252 }
215 return renderWithTime('index.jade', data) 253 return renderWithTime('index.jade', data)
216 254
255
217 if __name__ == '__main__': 256 if __name__ == '__main__':
218 logging.basicConfig(level=logging.INFO) 257 logging.basicConfig(level=logging.INFO)
219 bottle.run(server='gunicorn', host='0.0.0.0', port=10002, workers=4) 258 bottle.run(server='gunicorn', host='0.0.0.0', port=10002, workers=4)