raw
logotron_genesis.kv     1 #!/usr/bin/python
logotron_genesis.kv 2
logotron_genesis.kv 3 ##############################################################################
logotron_genesis.kv 4 import ConfigParser, sys
logotron_genesis.kv 5 import psycopg2, psycopg2.extras
logotron_genesis.kv 6 import psycopg2.extensions
logotron_genesis.kv 7 psycopg2.extensions.register_type(psycopg2.extensions.UNICODE)
logotron_genesis.kv 8 psycopg2.extensions.register_type(psycopg2.extensions.UNICODEARRAY)
logotron_genesis.kv 9 import time
frontend_updates.kv 10 from datetime import timedelta, datetime
logotron_genesis.kv 11 import sys
logotron_genesis.kv 12 reload(sys)
logotron_genesis.kv 13 sys.setdefaultencoding('utf8')
logotron_genesis.kv 14 import os
logotron_genesis.kv 15 import threading
logotron_genesis.kv 16 import re
logotron_genesis.kv 17 from urlparse import urljoin
raw_line_export.kv 18 from flask import Flask, request, session, url_for, redirect, Response, \
logotron_genesis.kv 19 render_template, abort, g, flash, _app_ctx_stack, make_response, \
logotron_genesis.kv 20 jsonify
logotron_genesis.kv 21 ##############################################################################
logotron_genesis.kv 22
logotron_genesis.kv 23 ##############################################################################
logotron_genesis.kv 24 # Single mandatory arg: config file path
hide_inactive.kv 25
logotron_genesis.kv 26 if len(sys.argv[1:]) != 1:
hide_inactive.kv 27 # Default path for WSGI use (change to yours) :
hide_inactive.kv 28 config_path = "/home/nsabot/logger/nsabot.conf"
hide_inactive.kv 29 else:
hide_inactive.kv 30 # Read Config from given conf file
hide_inactive.kv 31 config_path = sys.argv[1]
logotron_genesis.kv 32
hide_inactive.kv 33 #config_path = os.path.abspath(config_path)
logotron_genesis.kv 34 cfg = ConfigParser.ConfigParser()
logotron_genesis.kv 35 cfg.readfp(open(config_path))
logotron_genesis.kv 36
logotron_genesis.kv 37 try:
logotron_genesis.kv 38 # IRCism:
logotron_genesis.kv 39 Nick = cfg.get("irc", "nick")
logotron_genesis.kv 40 Channels = [x.strip() for x in cfg.get("irc", "chans").split(',')]
logotron_genesis.kv 41 Bots = [x.strip() for x in cfg.get("logotron", "bots").split(',')]
logotron_genesis.kv 42 Bots.append(Nick) # Add our own bot to the bot list
logotron_genesis.kv 43 # DBism:
logotron_genesis.kv 44 DB_Name = cfg.get("db", "db_name")
logotron_genesis.kv 45 DB_User = cfg.get("db", "db_user")
raw_line_export.kv 46 DB_DEBUG = int(cfg.get("db", "db_debug"))
logotron_genesis.kv 47 # Logism:
logotron_genesis.kv 48 Base_URL = cfg.get("logotron", "base_url")
frontend_updates.kv 49 App_Root = cfg.get("logotron", "app_root")
frontend_updates.kv 50 CSS_File = cfg.get("logotron", "css_file")
logotron_genesis.kv 51 Era = int(cfg.get("logotron", "era"))
raw_line_export.kv 52 DEBUG = int(cfg.get("logotron", "www_dbg"))
raw_line_export.kv 53 Max_Raw_Ln = int(cfg.get("logotron", "max_raw"))
hide_inactive.kv 54 Days_Hide = int(cfg.get("logotron", "days_hide"))
logotron_genesis.kv 55 # WWW:
logotron_genesis.kv 56 WWW_Port = int(cfg.get("logotron", "www_port"))
searchpg_oldlinks.kv 57 Max_Search_Results = int(cfg.get("logotron", "max_search"))
logotron_genesis.kv 58
logotron_genesis.kv 59 except Exception as e:
logotron_genesis.kv 60 print "Invalid config: ", e
logotron_genesis.kv 61 exit(1)
logotron_genesis.kv 62
logotron_genesis.kv 63 ##############################################################################
logotron_genesis.kv 64
logotron_genesis.kv 65 ##############################################################################
logotron_genesis.kv 66 ### Knobs not made into config yet ###
logotron_genesis.kv 67 Default_Chan = Channels[0]
logotron_genesis.kv 68 Min_Query_Length = 3
logotron_genesis.kv 69
logotron_genesis.kv 70 ## Format for Date in Log Lines
logotron_genesis.kv 71 Date_Short_Format = "%Y-%m-%d"
logotron_genesis.kv 72 ##############################################################################
logotron_genesis.kv 73
logotron_genesis.kv 74 app = Flask(__name__)
logotron_genesis.kv 75 app.config.from_object(__name__)
logotron_genesis.kv 76
logotron_genesis.kv 77 def get_db():
logotron_genesis.kv 78 db = getattr(g, 'db', None)
logotron_genesis.kv 79 if db is None:
logotron_genesis.kv 80 db = g.db = psycopg2.connect("dbname=%s user=%s" % (DB_Name, DB_User))
logotron_genesis.kv 81 return db
logotron_genesis.kv 82
logotron_genesis.kv 83 def close_db():
logotron_genesis.kv 84 if hasattr(g, 'db'):
logotron_genesis.kv 85 g.db.close()
logotron_genesis.kv 86
logotron_genesis.kv 87 @app.before_request
logotron_genesis.kv 88 def before_request():
logotron_genesis.kv 89 g.db = get_db()
logotron_genesis.kv 90
logotron_genesis.kv 91 @app.teardown_request
logotron_genesis.kv 92 def teardown_request(exception):
logotron_genesis.kv 93 close_db()
logotron_genesis.kv 94
logotron_genesis.kv 95 def query_db(query, args=(), one=False):
logotron_genesis.kv 96 cur = get_db().cursor(cursor_factory=psycopg2.extras.RealDictCursor)
logotron_genesis.kv 97 if (DB_DEBUG): print "query: '{0}'".format(query)
logotron_genesis.kv 98 cur.execute(query, args)
logotron_genesis.kv 99 rv = cur.fetchone() if one else cur.fetchall()
logotron_genesis.kv 100 if (DB_DEBUG): print "query res: '{0}'".format(rv)
logotron_genesis.kv 101 return rv
logotron_genesis.kv 102
logotron_genesis.kv 103 ##############################################################################
logotron_genesis.kv 104
logotron_genesis.kv 105 ## All eggogs redirect to main page
logotron_genesis.kv 106 @app.errorhandler(404)
logotron_genesis.kv 107 def page_not_found(error):
logotron_genesis.kv 108 return redirect(url_for('log'))
logotron_genesis.kv 109
logotron_genesis.kv 110 ##############################################################################
logotron_genesis.kv 111
logotron_genesis.kv 112 html_escape_table = {
logotron_genesis.kv 113 "&": "&",
logotron_genesis.kv 114 '"': """,
logotron_genesis.kv 115 "'": "'",
logotron_genesis.kv 116 ">": ">",
logotron_genesis.kv 117 "<": "&lt;",
logotron_genesis.kv 118 }
logotron_genesis.kv 119
logotron_genesis.kv 120 def html_escape(text):
logotron_genesis.kv 121 return "".join(html_escape_table.get(c,c) for c in text)
logotron_genesis.kv 122
logotron_genesis.kv 123 ##############################################################################
logotron_genesis.kv 124
logotron_genesis.kv 125 ## Get base URL
logotron_genesis.kv 126 def get_base():
logotron_genesis.kv 127 if DEBUG:
frontend_updates.kv 128 return request.host_url.rstrip('/')
frontend_updates.kv 129 return Base_URL.rstrip('/')
logotron_genesis.kv 130
logotron_genesis.kv 131 # Get perma-URL corresponding to given log line
logotron_genesis.kv 132 def line_url(l):
frontend_updates.kv 133 return "{0}{1}{2}/{3}#{4}".format(get_base(),
frontend_updates.kv 134 App_Root,
logotron_genesis.kv 135 l['chan'],
logotron_genesis.kv 136 l['t'].strftime(Date_Short_Format),
logotron_genesis.kv 137 l['idx'])
logotron_genesis.kv 138
hide_inactive.kv 139 def gen_chanlist(selected_chan, show_all_chans=False):
logotron_genesis.kv 140 # Get current time
logotron_genesis.kv 141 now = datetime.now()
hide_inactive.kv 142 # Data for channel display :
frontend_updates.kv 143 chan_list = []
frontend_updates.kv 144 chan_idx = 0
logotron_genesis.kv 145 for chan in Channels:
logotron_genesis.kv 146 last_time = query_db(
logotron_genesis.kv 147 '''select t, idx from loglines where chan=%s
logotron_genesis.kv 148 and idx = (select max(idx) from loglines where chan=%s) ;''',
logotron_genesis.kv 149 [chan, chan], one=True)
logotron_genesis.kv 150
logotron_genesis.kv 151 last_time_txt = ""
frontend_updates.kv 152 last_time_url = ""
logotron_genesis.kv 153 if last_time != None:
logotron_genesis.kv 154 span = (now - last_time['t'])
logotron_genesis.kv 155 days = span.days
frontend_updates.kv 156
frontend_updates.kv 157 # Only add to the list if it should be visible, otherwise continue
frontend_updates.kv 158 if days > Days_Hide and chan != selected_chan and not show_all_chans:
frontend_updates.kv 159 continue
frontend_updates.kv 160
logotron_genesis.kv 161 hours = span.seconds/3600
logotron_genesis.kv 162 minutes = (span.seconds%3600)/60
logotron_genesis.kv 163
logotron_genesis.kv 164 if days != 0:
logotron_genesis.kv 165 last_time_txt += '%dd ' % days
logotron_genesis.kv 166 if hours != 0:
logotron_genesis.kv 167 last_time_txt += '%dh ' % hours
logotron_genesis.kv 168 if minutes != 0:
logotron_genesis.kv 169 last_time_txt += '%dm' % minutes
frontend_updates.kv 170
frontend_updates.kv 171 last_time_url = "{0}{1}{2}/{3}#{4}".format(
logotron_genesis.kv 172 get_base(),
frontend_updates.kv 173 App_Root,
logotron_genesis.kv 174 chan,
logotron_genesis.kv 175 last_time['t'].strftime(Date_Short_Format),
frontend_updates.kv 176 last_time['idx'])
logotron_genesis.kv 177
frontend_updates.kv 178 chan_list.append({ 'name': chan })
logotron_genesis.kv 179
frontend_updates.kv 180 chan_list[chan_idx]['last_time_url'] = last_time_url
frontend_updates.kv 181 chan_list[chan_idx]['last_time_txt'] = last_time_txt
frontend_updates.kv 182 chan_list[chan_idx]['chan_url'] = "{0}{1}{2}{3}".format(
frontend_updates.kv 183 get_base(), App_Root, chan, '/' if chan == Default_Chan else '')
logotron_genesis.kv 184
frontend_updates.kv 185 chan_idx += 1
logotron_genesis.kv 186
frontend_updates.kv 187 return chan_list
logotron_genesis.kv 188
logotron_genesis.kv 189
logotron_genesis.kv 190 # HTML Tag Regex
logotron_genesis.kv 191 tag_regex = re.compile("(<[^>]+>)")
logotron_genesis.kv 192
logotron_genesis.kv 193
logotron_genesis.kv 194 # Find the segments of a block of text which constitute HTML tags
logotron_genesis.kv 195 def get_link_intervals(str):
logotron_genesis.kv 196 links = []
logotron_genesis.kv 197 span = []
logotron_genesis.kv 198 for match in tag_regex.finditer(str):
logotron_genesis.kv 199 span = match.span()
logotron_genesis.kv 200 links += [span]
logotron_genesis.kv 201 return links
logotron_genesis.kv 202
logotron_genesis.kv 203
logotron_genesis.kv 204 # Highlight all matched tokens in given text
logotron_genesis.kv 205 def highlight_matches(strings, text):
logotron_genesis.kv 206 e = '(' + ('|'.join(strings)) + ')'
logotron_genesis.kv 207 return re.sub(e,
logotron_genesis.kv 208 r"""<span class='highlight'>\1</span>""",
logotron_genesis.kv 209 text,
logotron_genesis.kv 210 flags=re.I)
logotron_genesis.kv 211
logotron_genesis.kv 212
logotron_genesis.kv 213 # Highlight matched tokens in the display of a search result logline,
logotron_genesis.kv 214 # but leave HTML tags alone
logotron_genesis.kv 215 def highlight_text(strings, text):
logotron_genesis.kv 216 result = ""
logotron_genesis.kv 217 last = 0
logotron_genesis.kv 218 for i in get_link_intervals(text):
logotron_genesis.kv 219 i_start, i_end = i
logotron_genesis.kv 220 result += highlight_matches(strings, text[last:i_start])
logotron_genesis.kv 221 result += text[i_start:i_end] # the HTML tag, leave it alone
logotron_genesis.kv 222 last = i_end
logotron_genesis.kv 223 result += highlight_matches(strings, text[last:]) # last block
logotron_genesis.kv 224 return result
logotron_genesis.kv 225
logotron_genesis.kv 226
logotron_genesis.kv 227 # Regexps used in format_logline:
uniturds_etc.kv 228 boxlinks_re = re.compile(
uniturds_etc.kv 229 '\[\s*<a href="(http[^ \[\]]+)"[^>]*>[^ <]+</a>\s*\]\[([^\[\]]+)\]')
uniturds_etc.kv 230
logotron_genesis.kv 231 stdlinks_re = re.compile('(http[^ \[\]]+)')
logotron_genesis.kv 232
searchpg_oldlinks.kv 233 # For era 1 ('bitcoin-assets') links :
aug2020_errata.kv 234 era1_re = re.compile('(<a href="http[^ \[\]]\/\/log\d*\.bitcoin-assets\.com\/+\?date=\d+-\d+-\d+#)(\d+)')
searchpg_oldlinks.kv 235
searchpg_oldlinks.kv 236 # For era 2 ('btcbase') links :
searchpg_oldlinks.kv 237 era2_re = re.compile('(<a href="http[^ \[\]]\/\/btcbase\.org\/log\/\d+-\d+-\d+#)(\d+)')
searchpg_oldlinks.kv 238
logotron_genesis.kv 239
logotron_genesis.kv 240 ## Format given log line for display
search_all_chans.kv 241 def format_logline(l, highlights = [], select=[], showchan=False):
logotron_genesis.kv 242 payload = html_escape(l['payload'])
frontend_updates.kv 243
logotron_genesis.kv 244 # Format ordinary links:
uniturds_etc.kv 245 payload = re.sub(stdlinks_re,
uniturds_etc.kv 246 r'<a href="\1" target=\'_blank\'>\1</a>', payload)
logotron_genesis.kv 247
logotron_genesis.kv 248 # Now also format [link][text] links :
uniturds_etc.kv 249 payload = re.sub(boxlinks_re,
uniturds_etc.kv 250 r'<a href="\1" target=\'_blank\'>\2</a>', payload)
frontend_updates.kv 251
searchpg_oldlinks.kv 252 # For ancient logs strictly: substitute orig. link with our logger :
searchpg_oldlinks.kv 253 if l['era'] < 3:
searchpg_oldlinks.kv 254 payload = re.sub(era1_re,
aug2020_errata.kv 255 r'<a href="/ilog/trilema/\2', payload)
frontend_updates.kv 256
searchpg_oldlinks.kv 257 # Adjust era 2 links in all cases:
searchpg_oldlinks.kv 258 payload = re.sub(era2_re,
aug2020_errata.kv 259 r'<a href="/ilog/trilema/\2', payload)
frontend_updates.kv 260
logotron_genesis.kv 261 # If this is a search result, illuminate the matched strings:
logotron_genesis.kv 262 if highlights != []:
logotron_genesis.kv 263 payload = highlight_text(highlights, payload)
frontend_updates.kv 264
logotron_genesis.kv 265 bot = ""
logotron_genesis.kv 266 if l['speaker'] in Bots:
logotron_genesis.kv 267 bot = " bot"
logotron_genesis.kv 268
multsel_and_datef... 269 # default -- no selection
multsel_and_datef... 270 dclass = l['speaker']
multsel_and_datef... 271
multsel_and_datef... 272 # If selection is given:
multsel_and_datef... 273 if select != []:
multsel_and_datef... 274 ss, se = select
multsel_and_datef... 275 if ss <= l['idx'] <= se:
multsel_and_datef... 276 dclass = "highlight"
multsel_and_datef... 277
sept_fixes.kv 278 speaker = l['speaker']
sept_fixes.kv 279 separator = ":"
sept_fixes.kv 280
search_all_chans.kv 281 if showchan:
search_all_chans.kv 282 speaker = '<small>(' + l['chan'] + ')</small> ' + speaker
frontend_updates.kv 283
sept_fixes.kv 284 # If 'action', annotate:
sept_fixes.kv 285 if l['self']:
sept_fixes.kv 286 separator = ""
sept_fixes.kv 287 payload = "<i>" + payload + "</i>"
sept_fixes.kv 288 speaker = "<i>" + speaker + "</i>"
frontend_updates.kv 289
logotron_genesis.kv 290 # HTMLize the given line :
frontend_updates.kv 291 s = ("<div id='{0}' class='logline {6}{5}'>"
logotron_genesis.kv 292 "<a class='nick' title='{2}'"
sept_errata.kv 293 " href=\"{3}\">{1}</a>{7} {4}</div>").format(l['idx'],
sept_fixes.kv 294 speaker,
sept_fixes.kv 295 l['t'],
sept_fixes.kv 296 line_url(l),
sept_fixes.kv 297 payload,
sept_fixes.kv 298 bot,
sept_fixes.kv 299 dclass,
sept_fixes.kv 300 separator)
logotron_genesis.kv 301 return s
logotron_genesis.kv 302
logotron_genesis.kv 303 # Make above callable from inside htm templater:
frontend_updates.kv 304 app.jinja_env.globals.update(format_logline=format_logline)
logotron_genesis.kv 305
logotron_genesis.kv 306
uniturds_etc.kv 307 @app.route('/rnd/<chan>')
uniturds_etc.kv 308 def rnd(chan):
uniturds_etc.kv 309 # Handle rubbish chan:
uniturds_etc.kv 310 if chan not in Channels:
uniturds_etc.kv 311 return redirect(url_for('log'))
uniturds_etc.kv 312
uniturds_etc.kv 313 rnd_line = query_db(
uniturds_etc.kv 314 '''select * from loglines where chan=%s
uniturds_etc.kv 315 order by random() limit 1 ;''',
uniturds_etc.kv 316 [chan], one=True)
uniturds_etc.kv 317
uniturds_etc.kv 318 return redirect(line_url(rnd_line))
uniturds_etc.kv 319
uniturds_etc.kv 320
frontend_updates.kv 321 @app.route('%s<chan>/<date>' % App_Root)
frontend_updates.kv 322 @app.route('%s<chan>' % App_Root, defaults={'date': None})
frontend_updates.kv 323 @app.route('%s' % App_Root, defaults={'chan': Default_Chan, 'date': None})
logotron_genesis.kv 324 def log(chan, date):
logotron_genesis.kv 325 # Handle rubbish chan:
logotron_genesis.kv 326 if chan not in Channels:
logotron_genesis.kv 327 return redirect(url_for('log'))
logotron_genesis.kv 328
multsel_and_datef... 329 # Get possible selection start and end
multsel_and_datef... 330 sel_start = request.args.get('ss', default = 0, type = int)
multsel_and_datef... 331 sel_end = request.args.get('se', default = 0, type = int)
uniturds_etc.kv 332
uniturds_etc.kv 333 # Get possible 'reverse gear'
uniturds_etc.kv 334 rev = request.args.get('rev', default = 0, type = int)
hide_inactive.kv 335
hide_inactive.kv 336 # Get possible 'show all'
hide_inactive.kv 337 show_all = request.args.get('all', default = 0, type = int)
frontend_updates.kv 338
logotron_genesis.kv 339 # Get current time
logotron_genesis.kv 340 now = datetime.now()
logotron_genesis.kv 341
logotron_genesis.kv 342 # Whether we are viewing 'current' tail
logotron_genesis.kv 343 tail = False
frontend_updates.kv 344
logotron_genesis.kv 345 # If viewing 'current' log:
logotron_genesis.kv 346 if date == None:
logotron_genesis.kv 347 date = now.strftime(Date_Short_Format)
logotron_genesis.kv 348 tail = True
logotron_genesis.kv 349
logotron_genesis.kv 350 # Parse given date, and redirect to default log if rubbish:
logotron_genesis.kv 351 try:
logotron_genesis.kv 352 day_start = datetime.strptime(date, Date_Short_Format)
logotron_genesis.kv 353 except Exception, e:
logotron_genesis.kv 354 return redirect(url_for('log'))
frontend_updates.kv 355
logotron_genesis.kv 356 # Determine the end of the interval being shown
logotron_genesis.kv 357 day_end = day_start + timedelta(days=1)
logotron_genesis.kv 358
multsel_and_datef... 359 # Enable 'tail' is day_end is after end of current day
rle_errata.kv 360 if day_end > now:
multsel_and_datef... 361 tail = True
navbar_date_auto.kv 362
logotron_genesis.kv 363 # Get the loglines from DB
logotron_genesis.kv 364 lines = query_db(
logotron_genesis.kv 365 '''select * from loglines where chan=%s
logotron_genesis.kv 366 and t between %s and %s order by idx asc;''',
logotron_genesis.kv 367 [chan, day_start, day_end], one=False)
frontend_updates.kv 368
uniturds_etc.kv 369 # Optional 'reverse gear' knob:
uniturds_etc.kv 370 if rev == 1:
uniturds_etc.kv 371 lines.reverse()
frontend_updates.kv 372
navbar_date_auto.kv 373 # Generate navbar for the given date:
navbar_date_auto.kv 374 prev_day = ""
navbar_date_auto.kv 375 next_day = ""
frontend_updates.kv 376
navbar_date_auto.kv 377 prev_t = query_db(
navbar_date_auto.kv 378 '''select t from loglines where chan=%s
navbar_date_auto.kv 379 and t < %s order by idx desc limit 1;''',
navbar_date_auto.kv 380 [chan, day_start], one=True)
frontend_updates.kv 381
navbar_date_auto.kv 382 if prev_t != None:
navbar_date_auto.kv 383 prev_day = prev_t['t'].strftime(Date_Short_Format)
frontend_updates.kv 384
navbar_date_auto.kv 385 if not tail:
navbar_date_auto.kv 386 next_t = query_db(
navbar_date_auto.kv 387 '''select t from loglines where chan=%s
navbar_date_auto.kv 388 and t > %s order by idx asc limit 1;''',
navbar_date_auto.kv 389 [chan, day_end], one=True)
frontend_updates.kv 390
navbar_date_auto.kv 391 if next_t != None:
navbar_date_auto.kv 392 next_day = next_t['t'].strftime(Date_Short_Format)
frontend_updates.kv 393
frontend_updates.kv 394 # Generate url for css file based on config value
frontend_updates.kv 395 css_url = url_for('static', filename=CSS_File)
frontend_updates.kv 396
frontend_updates.kv 397 chan_list = gen_chanlist(chan, show_all)
frontend_updates.kv 398
logotron_genesis.kv 399 # Return the HTMLized text
logotron_genesis.kv 400 return render_template('log.html',
frontend_updates.kv 401 css_url = css_url,
frontend_updates.kv 402 app_root = App_Root,
frontend_updates.kv 403 chan = chan,
frontend_updates.kv 404 chan_list = chan_list,
frontend_updates.kv 405 loglines = lines,
frontend_updates.kv 406 sel = (sel_start, sel_end),
frontend_updates.kv 407 date = date,
frontend_updates.kv 408 prev_day = prev_day,
frontend_updates.kv 409 next_day = next_day,
frontend_updates.kv 410 rev = not rev,
frontend_updates.kv 411 idle_day = Days_Hide,
frontend_updates.kv 412 show_all = show_all)
logotron_genesis.kv 413
logotron_genesis.kv 414
searchpg_oldlinks.kv 415 # Primarily for use with 'era 1' and 'era 2' :
searchpg_oldlinks.kv 416 # Get arbitrary log item by chan and raw line index
searchpg_oldlinks.kv 417 @app.route('/ilog/<chan>/<idx>')
searchpg_oldlinks.kv 418 def ilog(chan, idx):
searchpg_oldlinks.kv 419 # Handle rubbish chan:
searchpg_oldlinks.kv 420 if chan not in Channels:
searchpg_oldlinks.kv 421 return redirect(url_for('log'))
searchpg_oldlinks.kv 422
searchpg_oldlinks.kv 423 # Attempt to locate given chan/idx:
searchpg_oldlinks.kv 424 item = query_db(
searchpg_oldlinks.kv 425 '''select * from loglines where chan=%s and idx = %s ;''',
searchpg_oldlinks.kv 426 [chan, idx], one=True)
searchpg_oldlinks.kv 427
searchpg_oldlinks.kv 428 # If given chan/idx not found:
searchpg_oldlinks.kv 429 if item == None:
searchpg_oldlinks.kv 430 return redirect(url_for('log'))
frontend_updates.kv 431
searchpg_oldlinks.kv 432 # Determine date where item appears in log :
searchpg_oldlinks.kv 433 item_date = item['t'].strftime(Date_Short_Format)
frontend_updates.kv 434
searchpg_oldlinks.kv 435 # Go there:
frontend_updates.kv 436 return redirect(App_Root + chan + "/" + item_date + "#" + idx)
frontend_updates.kv 437
searchpg_oldlinks.kv 438
raw_line_export.kv 439 @app.route('/log-raw/<chan>')
raw_line_export.kv 440 def rawlog(chan):
raw_line_export.kv 441 res = ""
frontend_updates.kv 442
raw_line_export.kv 443 # Handle rubbish chan:
raw_line_export.kv 444 if chan not in Channels:
raw_line_export.kv 445 return Response("EGGOG: No such Channel!", mimetype='text/plain')
frontend_updates.kv 446
raw_line_export.kv 447 # Get start and end indices:
raw_line_export.kv 448 idx_start = request.args.get('istart', default = 0, type = int)
raw_line_export.kv 449 idx_end = request.args.get('iend', default = 0, type = int)
frontend_updates.kv 450
raw_line_export.kv 451 # Malformed bounds?
raw_line_export.kv 452 if idx_start > idx_end:
raw_line_export.kv 453 return Response("EGGOG: Start must precede End!",
raw_line_export.kv 454 mimetype='text/plain')
raw_line_export.kv 455
raw_line_export.kv 456 # Demanded too many in one burst ?
raw_line_export.kv 457 if (idx_end - idx_start) > Max_Raw_Ln :
raw_line_export.kv 458 return Response("EGGOG: May request Max. of %s Lines !" % Max_Raw_Ln,
raw_line_export.kv 459 mimetype='text/plain')
raw_line_export.kv 460
raw_line_export.kv 461 # Get the loglines from DB
raw_line_export.kv 462 lines = query_db(
raw_line_export.kv 463 '''select * from loglines where chan=%s
raw_line_export.kv 464 and idx between %s and %s order by idx asc;''',
raw_line_export.kv 465 [chan, idx_start, idx_end], one=False)
frontend_updates.kv 466
raw_line_export.kv 467 # Retrieve raw lines in classical Phf format:
raw_line_export.kv 468 for l in lines:
raw_line_export.kv 469 action = ""
raw_line_fix.kv 470 speaker = "%s;" % l['speaker']
raw_line_export.kv 471 if l['self']:
raw_line_export.kv 472 action = "*;"
raw_line_fix.kv 473 speaker = "%s " % l['speaker']
raw_line_fix.kv 474 res += "%s;%s;%s%s%s\n" % (l['idx'],
raw_line_export.kv 475 l['t'].strftime('%s'),
raw_line_export.kv 476 action,
raw_line_fix.kv 477 speaker,
raw_line_export.kv 478 l['payload'])
frontend_updates.kv 479
raw_line_export.kv 480 # Return plain text:
raw_line_export.kv 481 return Response(res, mimetype='text/plain')
raw_line_export.kv 482
logotron_genesis.kv 483
logotron_genesis.kv 484 Name_Chars = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_-"
logotron_genesis.kv 485
logotron_genesis.kv 486 def sanitize_speaker(s):
logotron_genesis.kv 487 return "".join([ch for ch in s if ch in Name_Chars])
logotron_genesis.kv 488
logotron_genesis.kv 489
logotron_genesis.kv 490 def re_escape(s):
logotron_genesis.kv 491 return re.sub(r"[(){}\[\].*?|^$\\+-]", r"\\\g<0>", s)
logotron_genesis.kv 492
logotron_genesis.kv 493 # Search knob. Supports 'chan' parameter.
logotron_genesis.kv 494 @app.route('/log-search')
logotron_genesis.kv 495 def logsearch():
logotron_genesis.kv 496 # The query params:
logotron_genesis.kv 497 chan = request.args.get('chan', default = Default_Chan, type = str)
logotron_genesis.kv 498 query = request.args.get('q', default = '', type = str)
searchpg_oldlinks.kv 499 offset = request.args.get('after', default = 0, type = int)
frontend_updates.kv 500 show_all = 0
frontend_updates.kv 501
frontend_updates.kv 502 if len(query) < Min_Query_Length:
frontend_updates.kv 503 return redirect(url_for('log'))
frontend_updates.kv 504
search_all_chans.kv 505 # channels to search in
search_all_chans.kv 506 chans = []
logotron_genesis.kv 507
search_all_chans.kv 508 # whether to indicate chan per log line
search_all_chans.kv 509 showchan = False
search_all_chans.kv 510
search_all_chans.kv 511 if chan == 'all':
search_all_chans.kv 512 # search in all logged channels
search_all_chans.kv 513 chans = Channels
search_all_chans.kv 514 legend = "<i>all logged channels</i>"
search_all_chans.kv 515 showchan = True
frontend_updates.kv 516 show_all = 1
search_all_chans.kv 517 else:
search_all_chans.kv 518 # Handle possible rubbish chan:
search_all_chans.kv 519 if chan not in Channels:
search_all_chans.kv 520 return redirect(url_for('log'))
search_all_chans.kv 521 else:
search_all_chans.kv 522 # search in selected channel only
search_all_chans.kv 523 chans = [chan]
search_all_chans.kv 524 legend = chan
frontend_updates.kv 525
logotron_genesis.kv 526 nres = 0
logotron_genesis.kv 527 searchres = []
logotron_genesis.kv 528 tokens_orig = []
logotron_genesis.kv 529 search_head = "Query is too short!"
logotron_genesis.kv 530 # Forbid query that is too short:
logotron_genesis.kv 531 if len(query) >= Min_Query_Length:
logotron_genesis.kv 532 # Get the search tokens to use:
shlex_removal.kv 533 tokens = query.split()
logotron_genesis.kv 534 tokens_standard = []
logotron_genesis.kv 535 from_users = []
logotron_genesis.kv 536
logotron_genesis.kv 537 # separate out "from:foo" tokens and ordinary:
logotron_genesis.kv 538 for t in tokens:
logotron_genesis.kv 539 if t.startswith("from:") or t.startswith("f:"):
logotron_genesis.kv 540 from_users.append(t.split(':')[1]) # Record user for 'from' query
logotron_genesis.kv 541 else:
logotron_genesis.kv 542 tokens_standard.append(t)
frontend_updates.kv 543
logotron_genesis.kv 544 from_users = ['%' + sanitize_speaker(t) + '%' for t in from_users]
logotron_genesis.kv 545 tokens_orig = [re_escape(t) for t in tokens_standard]
logotron_genesis.kv 546 tokens_formed = ['%' + t + '%' for t in tokens_orig]
frontend_updates.kv 547
logotron_genesis.kv 548 # Query is usable; perform the search on DB and get the finds
logotron_genesis.kv 549 if from_users == []:
logotron_genesis.kv 550 searchres = query_db(
search_all_chans.kv 551 '''select * from loglines where chan = any(%s)
searchpg_oldlinks.kv 552 and payload ilike all(%s) order by t desc
searchpg_oldlinks.kv 553 limit %s offset %s;''',
search_all_chans.kv 554 [(chans,),
logotron_genesis.kv 555 tokens_formed,
searchpg_oldlinks.kv 556 Max_Search_Results,
searchpg_oldlinks.kv 557 offset], one=False)
logotron_genesis.kv 558 else:
logotron_genesis.kv 559 searchres = query_db(
search_all_chans.kv 560 '''select * from loglines where chan = any(%s)
logotron_genesis.kv 561 and speaker ilike any(%s)
searchpg_oldlinks.kv 562 and payload ilike all(%s) order by t desc
searchpg_oldlinks.kv 563 limit %s offset %s;''',
search_all_chans.kv 564 [(chans,),
logotron_genesis.kv 565 from_users,
logotron_genesis.kv 566 tokens_formed,
searchpg_oldlinks.kv 567 Max_Search_Results,
searchpg_oldlinks.kv 568 offset], one=False)
frontend_updates.kv 569
searchpg_oldlinks.kv 570 # Number of search results returned in this query
logotron_genesis.kv 571 nres = len(searchres)
searchpg_oldlinks.kv 572
searchpg_oldlinks.kv 573 # Whether to display 'back' button :
searchpg_oldlinks.kv 574 back = (offset != 0)
frontend_updates.kv 575
searchpg_oldlinks.kv 576 # Whether to display 'forward' button :
searchpg_oldlinks.kv 577 forw = (nres == Max_Search_Results)
frontend_updates.kv 578
searchpg_oldlinks.kv 579 # Starting index of search results
searchpg_oldlinks.kv 580 sres = offset
frontend_updates.kv 581
searchpg_oldlinks.kv 582 # Ending index of search results
searchpg_oldlinks.kv 583 eres = offset + min(nres, Max_Search_Results)
frontend_updates.kv 584
frontend_updates.kv 585 # Generate url for css file based on config value
frontend_updates.kv 586 css_url = url_for('static', filename=CSS_File)
frontend_updates.kv 587
frontend_updates.kv 588 chan_list = gen_chanlist(chan, show_all)
frontend_updates.kv 589
logotron_genesis.kv 590 return render_template('searchres.html',
frontend_updates.kv 591 css_url = css_url,
logotron_genesis.kv 592 query = query,
searchpg_oldlinks.kv 593 hquery = html_escape(query),
searchpg_oldlinks.kv 594 legend = legend,
searchpg_oldlinks.kv 595 sres = sres,
searchpg_oldlinks.kv 596 eres = eres,
searchpg_oldlinks.kv 597 back = back,
searchpg_oldlinks.kv 598 forw = forw,
searchpg_oldlinks.kv 599 psize = Max_Search_Results,
logotron_genesis.kv 600 chan = chan,
frontend_updates.kv 601 chan_list = chan_list,
logotron_genesis.kv 602 tokens = tokens_orig,
search_all_chans.kv 603 loglines = searchres,
frontend_updates.kv 604 showchan = showchan,
frontend_updates.kv 605 show_all = show_all)
logotron_genesis.kv 606
logotron_genesis.kv 607 # Comment this out if you don't have one
logotron_genesis.kv 608 @app.route('/favicon.ico')
logotron_genesis.kv 609 def favicon():
logotron_genesis.kv 610 return redirect(url_for('static', filename='favicon.ico'))
logotron_genesis.kv 611
logotron_genesis.kv 612
logotron_genesis.kv 613 ## App Mode
logotron_genesis.kv 614 if __name__ == '__main__':
logotron_genesis.kv 615 app.run(threaded=True, port=WWW_Port)