summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile2
-rwxr-xr-xavon.py154
-rw-r--r--requirements.txt27
-rw-r--r--templates/index.html76
4 files changed, 211 insertions, 48 deletions
diff --git a/Makefile b/Makefile
index 484791c..6713e46 100644
--- a/Makefile
+++ b/Makefile
@@ -1,7 +1,7 @@
1all: install 1all: install
2 2
3run: 3run:
4 venv/bin/python avon.py -P 8080 & 4 PYTHONIOENCODING=UTF-8 venv/bin/python avon.py -H 127.0.1.24 -P 8080
5 5
6venv: 6venv:
7 python3 -m venv ./venv 7 python3 -m venv ./venv
diff --git a/avon.py b/avon.py
index d47382d..79b5a1b 100755
--- a/avon.py
+++ b/avon.py
@@ -4,19 +4,18 @@ import os
4import sys 4import sys
5import pprint 5import pprint
6import json 6import json
7from flask import Flask,render_template,request 7from flask import Flask,render_template,request,url_for
8from argparse import ArgumentParser 8from argparse import ArgumentParser
9import psycopg2 9import psycopg
10 10
11app = Flask(__name__) 11app = Flask(__name__)
12 12
13class Convert_jinja_object: 13class Convert_jinja_object:
14 14
15 def __init__(self): 15 def __init__(self):
16 self.myvar = 'sample_var' 16 self.year_map = [ "92Q2", "95Q0", "96Q0", "96Q1", "97Q1", "97Q3", "98Q1", "98Q3", "99Q1", "99Q3", "00Q1", "00Q3", "01Q1", "01Q2", "01Q3", "01Q4", "02Q1", "02Q3", "03Q1", "03Q3", "04Q1", "04Q3", "05Q1", "05Q3", "06Q1", "06Q3", "07Q1", "07Q3", "08Q1", "08Q3", "09Q1", "09Q3", "10Q1", "10Q3", "11Q1", "11Q3", "12Q1", "12Q3", "13Q1", "13Q3", "14Q1", "14Q3", "15Q1", "15Q3", "16Q1", "16Q3", "17Q1", "17Q3", "18Q1", "18Q3", "19Q1", "19Q3", "20Q1", "20Q3", "21Q1", "21Q3", "22Q1", "22Q3", "23Q1", "23Q3", "24Q1", "24Q3", "25Q1", "25Q3" ]
17 17
18 def bits_to_years(self, x): 18 def bits_to_years(self, x):
19 year_map = [ "92Q2", "95Q0", "96Q0", "96Q1", "97Q1", "97Q3", "98Q1", "98Q3", "99Q1", "99Q3", "00Q1", "00Q3", "01Q1", "01Q2", "01Q3", "01Q4", "02Q1", "02Q3", "03Q1", "03Q3", "04Q1", "04Q3", "05Q1", "05Q3", "06Q1", "06Q3", "07Q1", "07Q3", "08Q1", "08Q3", "09Q1", "09Q3", "10Q1", "10Q3", "11Q1", "11Q3", "12Q1", "12Q3", "13Q1", "13Q3", "14Q1", "14Q3", "15Q1", "15Q3", "16Q1", "16Q3", "17Q1", "17Q3", "18Q1", "18Q3", "19Q1", "19Q3", "20Q1", "20Q3" ]
20 if x == 0: 19 if x == 0:
21 return '/' 20 return '/'
22 years = '' 21 years = ''
@@ -28,59 +27,170 @@ class Convert_jinja_object:
28 x = ~(x >> start_off) 27 x = ~(x >> start_off)
29 end_off = (x&-x).bit_length()-1 28 end_off = (x&-x).bit_length()-1
30 x = ~(x >> (end_off + 1)) 29 x = ~(x >> (end_off + 1))
31 years = years + (year_map[index + start_off]) 30 years = years + (self.year_map[index + start_off])
32 if end_off > 1: 31 if end_off > 1:
33 years = years + '-' + year_map[index + start_off + end_off - 1] 32 years = years + '-' + self.year_map[index + start_off + end_off - 1]
34 index = index + start_off + end_off + 1 33 index = index + start_off + end_off + 1
35 return years 34 return years
36 35
36 def multirange_to_years(self, row):
37 # Format of multi-ranges is {[low,high),[low,high],(low,high),(low,high)]}
38 # Although Telefonbuch only ever inserts the [low,high) range types, so for now let's keep the parser simple
39 years = []
40 for range_item in row:
41 low = range_item.lower
42 high = range_item.upper - 1
43 year = self.year_map[low]
44 if (low != high):
45 year += '-' + self.year_map[high]
46 years.append(year)
47 return ', '.join(years)
48
37@app.route("/", methods=['GET', 'POST']) 49@app.route("/", methods=['GET', 'POST'])
38def root(): 50def root():
39 content = request.json 51 convert = Convert_jinja_object()
40 52
41 query = "SELECT DISTINCT ON (id) presence_flag, reverse_flag, biz_flag, zip, nachname, vorname, zusaetze, strasse, hausnummer, verweise, ort, vorwahl, rufnummer, web, email, coords FROM Telefonbuch " 53 query = "SELECT DISTINCT ON (id) presence_flag, reverse_flag, biz_flag, zip, nachname, vorname, zusaetze, strasse, hausnummer, verweise, ort, vorwahl, rufnummer, web, email, coords FROM Telefonbuch "
42 where = "" 54 where = ""
43 params = [] 55 params = []
44 anyjoin = "" 56 anyjoin = ""
57 pastjoin = []
58 rv = request.values.to_dict(flat=True)
59
60 # pprint.pprint(request.values)
45 61
46 for i in range(1,5): 62 for i in range(1,5):
47 search = request.form.get("search_{0}_string".format(i)) 63 search = rv.get("s{0}_val".format(i))
48 if not search: continue 64 if not search:
49 column = request.form.get("search_{0}_column".format(i)) 65 rv.pop("s{0}_val".format(i), '')
50 operator = request.form.get("search_{0}_operator".format(i)) 66 rv.pop("s{0}_col".format(i), '')
51 isany = request.form.get("search_{0}_any".format(i)) 67 rv.pop("s{0}_op".format(i), '')
68 rv.pop("s{0}_any".format(i), '')
69 continue
70 column = rv.get("s{0}_col".format(i))
71 operator = rv.get("s{0}_op".format(i))
72 isany = rv.get("s{0}_any".format(i))
73
74 if not column in [ 'zip', 'nachname', 'vorname', 'zusaetze', 'strasse', 'hausnummer', 'verweise', 'ort', 'vorwahl', 'rufnummer', 'web', 'email', 'coords', 'id' ]: continue
52 75
53 if not column in [ 'zip', 'nachname', 'vorname', 'zusaetze', 'strasse', 'hausnummer', 'verweise', 'ort', 'vorwahl', 'rufnummer', 'web', 'email', 'coords' ]: continue 76 if not column in pastjoin:
77 query = query + "INNER JOIN table_{0} ON Telefonbuch.id = table_{0}.telefonbuch_id ".format(column)
78 pastjoin.append(column)
54 79
55 query = query + "INNER JOIN table_{0} ON Telefonbuch.id = table_{0}.telefonbuch_id ".format(column)
56 if len(where): 80 if len(where):
57 where = where + "AND " 81 where = where + "AND "
58 else: 82 else:
59 where = "WHERE " 83 where = "WHERE "
60 where = where + "table_{0}.value {1} %s ".format(column, {'equals': '=', 'equalsnot': '<>', 'contains': 'ILIKE', 'containsnot': 'NOT ILIKE', 'beginswith': 'ILIKE'}[operator]) 84 where = where + "table_{0}.value {1} %s ".format(column, {'equals': '=', 'equalsnot': '<>', 'contains': 'ILIKE', 'containsnot': 'NOT ILIKE', 'beginswith': 'LIKE', 'beginslike': 'ILIKE'}[operator])
61 if not isany: 85 if not isany:
62 if len(anyjoin) == 0: 86 if len(anyjoin) == 0 or "table_" + column == anyjoin:
63 anyjoin = "table_" + column 87 anyjoin = "table_" + column
64 else: 88 else:
65 where = where + "AND " + anyjoin + ".offs = table_" + column + ".offs " 89 where = where + "AND " + anyjoin + ".offs = table_" + column + ".offs "
66 if operator in ['contains','containsnot']: 90 if operator in ['contains','containsnot']:
67 search = "%" + search + "%" 91 search = "%" + search + "%"
68 if operator in ['beginswith']: 92 if operator in ['beginswith', 'beginslike']:
93 search = search + "%"
94 params.append(search)
95
96 range_min = int(rv.get("year_range_min", '0'))
97 range_max = int(rv.get("year_range_max", len(convert.year_map) - 1))
98 if range_min > range_max:
99 range_max, range_min = range_min, range_max
100 rv["year_range_min"] = range_min
101 rv["year_range_max"] = range_max
102
103 if range_min > 0 or range_max < len(convert.year_map) - 1:
104 if len(where) > 0:
105 where += "AND presence_flag && int4range({},{})" .format(range_min, range_max)
106 else:
107 rv.pop("year_range_min", 0)
108 rv.pop("year_range_max", 0)
109
110 limit = "10000"
111 if rv.get("huge"):
112 limit = "100000"
113
114 if len(where) > 0:
115 conn = psycopg.connect(dbname="telefonbuch", user="postgres", password="", host="127.0.0.1")
116 cur = conn.cursor()
117 print (query + where + ' LIMIT ' + limit)
118 pprint.pprint( params )
119
120 cur.execute(query + where + ' LIMIT ' + limit, params)
121 rows = cur.fetchall()
122 else:
123 rows = []
124
125 return render_template('index.html', rows=rows, request=request, rv=rv, convert=convert)
126
127@app.route("/facebook", methods=['GET', 'POST'])
128def facebook():
129
130 query = "SELECT phone, id, first_name, last_name, gender, location, originally_from, relationship_state, ocupation, email, birthday FROM facebook "
131 where = ""
132 params = []
133 anyjoin = ""
134 pastjoin = []
135 rv = request.values.to_dict(flat=True)
136
137 pprint.pprint(request.values)
138
139 for i in range(1,5):
140 search = rv.get("s{0}_val".format(i))
141 if not search:
142 rv.pop("s{0}_val".format(i), '')
143 rv.pop("s{0}_col".format(i), '')
144 rv.pop("s{0}_op".format(i), '')
145 rv.pop("s{0}_any".format(i), '')
146 continue
147 column = rv.get("s{0}_col".format(i))
148 operator = rv.get("s{0}_op".format(i))
149 isany = rv.get("s{0}_any".format(i))
150
151 if not column in [ 'phone', 'first_name', 'last_name', 'gender', 'location', 'originally_from', 'relationship_state', 'ocupation', 'email', 'id' ]: continue
152
153 if column == 'phone':
154 search = search.lstrip('+')
155
156 if len(where):
157 where = where + "AND "
158 else:
159 where = "WHERE "
160 where = where + "{0} {1} %s ".format(column, {'equals': '=', 'equalsnot': '<>', 'contains': 'ILIKE', 'containsnot': 'NOT ILIKE', 'beginswith': 'LIKE', 'beginslike': 'ILIKE'}[operator])
161 if operator in ['contains','containsnot']:
162 search = "%" + search + "%"
163 if operator in ['beginswith', 'beginslike']:
69 search = search + "%" 164 search = search + "%"
70 params.append(search) 165 params.append(search)
71 166
167 limit = "10000"
168 if rv.get("huge"):
169 limit = "100000"
170
72 if len(where) > 0: 171 if len(where) > 0:
73 conn = psycopg2.connect(database="erdgeist", user="postgres", password="", host="127.0.0.1") 172 conn = psycopg.connect(dbname="tbuch", user="postgres", password="", host="127.0.0.1")
74 cur = conn.cursor() 173 cur = conn.cursor()
75 print (query + where + ' LIMIT 10000') 174 print (query + where + ' LIMIT ' + limit)
76 pprint.pprint( params ) 175 pprint.pprint( params )
77 176
78 cur.execute(query + where + ' LIMIT 10000', params) 177 cur.execute(query + where + ' LIMIT ' + limit, params)
79 rows = cur.fetchall() 178 rows = cur.fetchall()
80 else: 179 else:
81 rows = [[0,0,0,{},{},{},{},{},{},{},{},{},{}]] 180 rows = []
181
182 return render_template('facebook.html', rows=rows, request=request, rv=rv)
183
184def column_and_op_to_where(column, operator):
185 return "{0} {1} %s ".format(column, {'equals': '=', 'equalsnot': '<>', 'contains': 'ILIKE', 'containsnot': 'NOT ILIKE', 'beginswith': 'LIKE', 'beginslike': 'ILIKE'}[operator])
186
187def search_and_operator_to_search(search, operator):
188 if operator in ['contains','containsnot']:
189 search = "%" + search + "%"
190 if operator in ['beginswith', 'beginslike']:
191 search = search + "%"
192 return search
82 193
83 return render_template('index.html', rows=rows, request=request, convert=Convert_jinja_object())
84 194
85if __name__ == "__main__": 195if __name__ == "__main__":
86 parser = ArgumentParser(description="AVON") 196 parser = ArgumentParser(description="AVON")
diff --git a/requirements.txt b/requirements.txt
index 7ed34e8..40d4446 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,12 +1,15 @@
1certifi==2018.11.29 1certifi
2chardet==3.0.4 2chardet
3Click==7.0 3Click
4Flask==1.0.2 4Flask
5idna==2.8 5idna
6itsdangerous==1.1.0 6itsdangerous
7Jinja2==2.10 7Jinja2
8MarkupSafe==1.1.1 8MarkupSafe
9psycopg2==2.7.7 9psycopg
10requests==2.21.0 10psycopg-c
11urllib3==1.24.1 11requests
12Werkzeug==0.14.1 12typing_extensions
13urllib3
14Werkzeug
15
diff --git a/templates/index.html b/templates/index.html
index cf4e616..955d813 100644
--- a/templates/index.html
+++ b/templates/index.html
@@ -5,39 +5,89 @@
5 td { vertical-align: top; } 5 td { vertical-align: top; }
6 tr.color_1 { background: #ccc; } 6 tr.color_1 { background: #ccc; }
7 table { border: none; } 7 table { border: none; }
8 #year-slider { margin: 0.3em; width: 50vw; }
9 #slider-container { display: inline; padding: 0.3em; }
8</style> 10</style>
11<link rel="stylesheet" type="text/css" href="static/nouislider.min.css">
12<script src="static/nouislider.min.js"></script>
9</head> 13</head>
10<body> 14<body>
11<form action="/" METHOD="POST"> 15<div>Jahre: <div id="slider-container"><div id="year-slider" class="search_entry"></div></div>
16<div id="main-form"><form action="/" METHOD="POST">
17 {% set yrmin = rv.get('year_range_min', 0) | int %}
18 {% set yrmax = rv.get('year_range_max', convert.year_map | length - 1 ) | int %}
19 <label for="year-range-value-min">Ab Jahr</label>
20 <select id="year-range-value-min" name="year_range_min">
21 {%- for year in convert.year_map -%}
22 <option value="{{loop.index - 1}}" {% if loop.index - 1 == yrmin %} selected="selected"{% endif %}>{{year}}</option>
23 {%- endfor -%}
24 </select>
25 <label for="year-range-value-max">Bis Jahr</label>
26 <select id="year-range-value-max" name="year_range_max">
27 {%- for year in convert.year_map -%}
28 <option value="{{loop.index - 1}}" {% if loop.index - 1 == yrmax %} selected="selected"{% endif %}>{{year}}</option>
29 {%- endfor -%}
30 </select>
31
32 <!--p><b>Wir importieren momentan das neue Telefonbuch Herbst 2025. Wahrscheinlich wird die Suche solange nicht korrekt funktionieren.</b></p-->
33
12 {%- for field in range(1,5) -%} 34 {%- for field in range(1,5) -%}
13 <div class="search_entry"> 35 <div class="search_entry">
14 <select name="search_{{ field }}_column"> 36 <select name="s{{ field }}_col">
15 {% set column_search = request.form.get('search_'+(field|string)+'_string', '') %} 37 {% set column_search = rv.get('s'+(field|string)+'_val', '') %}
16 {% if column_search | length %} 38 {% if column_search | length %}
17 {% set column_val = request.form.get('search_'+(field|string)+'_column') %} 39 {% set column_val = rv.get('s'+(field|string)+'_col') %}
18 {% set operator_val = request.form.get('search_'+(field|string)+'_operator') %} 40 {% set operator_val = rv.get('s'+(field|string)+'_op') %}
19 {% set any_val = request.form.get('search_'+(field|string)+'_any') %} 41 {% set any_val = rv.get('s'+(field|string)+'_any') %}
20 {% else %} 42 {% else %}
21 {% set column_val = ['nachname', 'vorname', 'ort', 'rufnummer'][field-1] %} 43 {% set column_val = ['nachname', 'vorname', 'ort', 'rufnummer'][field-1] %}
22 {% set operator_val = ['equals', 'contains', 'equals', 'contains'][field-1] %} 44 {% set operator_val = ['equals', 'equals', 'equals', 'beginswith'][field-1] %}
23 {% set any_val = ['', '', 'on', ''][field-1] %} 45 {% set any_val = ['', '', 'on', ''][field-1] %}
24 {% endif %} 46 {% endif %}
25 {%- for column in [ ['vorname', 'Vorname'], ['nachname', 'Nachname'], ['zip', 'PLZ'], ['ort', 'Ort'], ['vorwahl', 'Vorwahl'], ['rufnummer', 'Rufnummer'], ['strasse', 'Straße'], ['hausnummer', 'Hausnummer'], ['zusaetze', 'Zusatz'], ['verweise', 'Verweise / Branche'], ['email', 'E-Mail'], ['web', 'Webseite'] ] -%} 47 {%- for column in [ ['vorname', 'Vorname'], ['nachname', 'Nachname'], ['zip', 'PLZ'], ['ort', 'Ort'], ['vorwahl', 'Vorwahl'], ['rufnummer', 'Rufnummer'], ['strasse', 'Straße'], ['hausnummer', 'Hausnummer'], ['zusaetze', 'Zusatz'], ['verweise', 'Verweise / Branche'], ['email', 'E-Mail'], ['web', 'Webseite'] ] -%}
26 <option value="{{ column[0] }}" {%- if column_val== column[0] -%}selected="selected"{%- endif -%}>{{ column[1] }}</option> 48 <option value="{{ column[0] }}" {%- if column_val== column[0] -%}selected="selected"{%- endif -%}>{{ column[1] }}</option>
27 {%- endfor -%} 49 {%- endfor -%}
28 </select> 50 </select>
29 <select name="search_{{ field }}_operator"> 51 <select name="s{{ field }}_op">
30 {%- for operator in [ [ 'equals', 'equals'], ['beginswith', 'begins with'], ['contains', 'contains' ], ['equalsnot', 'equals not'], ['containsnot', 'contains not'] ] -%} 52 {%- for operator in [ [ 'equals', 'equals'], ['beginswith', 'begins with'], ['beginslike', 'begins like'], ['contains', 'contains' ], ['equalsnot', 'equals not'], ['containsnot', 'contains not'] ] -%}
31 <option value="{{ operator[0] }}" {%- if operator_val == operator[0] -%}selected="selected"{%- endif -%}>{{ operator[1] }}</option> 53 <option value="{{ operator[0] }}" {%- if operator_val == operator[0] -%}selected="selected"{%- endif -%}>{{ operator[1] }}</option>
32 {%- endfor -%} 54 {%- endfor -%}
33 </select> 55 </select>
34 <input type="checkbox" name="search_{{ field }}_any" {%- if any_val == 'on' -%}checked="{%- endif -%}">any</input> 56 <input type="checkbox" name="s{{ field }}_any" {%- if any_val == 'on' -%}checked="{%- endif -%}">any</input>
35 <input type="text" name="search_{{ field }}_string" value="{{ request.form.get('search_'+(field|string)+'_string', '') }}"> 57 <input type="text" name="s{{ field }}_val" value="{{ rv.get('s'+(field|string)+'_val', '') }}">
36 </div> 58 </div>
37 {%- endfor -%} 59 {%- endfor -%}
60 <input type="checkbox" value="on" name="huge" id="huge" {%- if rv.get('huge', '') == "on" -%}checked{%- endif -%}>
61 <label for="huge">unlimited</label>
38 <input type="submit" value="Search"> 62 <input type="submit" value="Search">
39</form> 63</form>
40{% if rows | length > 0 -%}<h3>Found {{ rows | length }} matches.</h3>{% endif %} 64<script>
65 var slider = document.getElementById('year-slider');
66 var dropdownmin = document.getElementById('year-range-value-min');
67 var dropdownmax = document.getElementById('year-range-value-max');
68 noUiSlider.create(slider, {
69 start: [ {{ yrmin }}, {{ yrmax }} ],
70 connect: true,
71 step: 1,
72 range: { 'min': 0, 'max': {{ convert.year_map | length - 1}} },
73 });
74 var year_map = [
75 {%- for year in convert.year_map -%}
76 '{{ year }}',
77 {%- endfor -%}
78 ];
79 var year_range_display = document.getElementById('year-range-display');
80 var year_range_value_min = document.getElementById('year-range-value-min');
81 var year_range_value_max = document.getElementById('year-range-value-max');
82 slider.noUiSlider.on('update', function (values, handle) {
83 year_range_value_min.value = Math.floor(values[0]);
84 year_range_value_max.value = Math.floor(values[1]);
85 });
86 dropdownmin.onchange = function() { slider.noUiSlider.set( [this.value, null]); }
87 dropdownmax.onchange = function() { slider.noUiSlider.set( [null, this.value]); }
88</script>
89</div>
90<h3>Found {{ rows | length }} matches for your <a href="{{ url_for(request.endpoint,**rv) }}">query</a>. <small>You may also try <a href="/facebook">the facebook search</a></small>.</h3> <!-- *dict(request.values)) }}">query</a>.</h3-->
41{% set color = { 'value': 0 } %} 91{% set color = { 'value': 0 } %}
42<table> 92<table>
43 <thead><td>flags</td><td>zip</td></thead> 93 <thead><td>flags</td><td>zip</td></thead>
@@ -47,7 +97,7 @@
47 {% if ( r == 0 ) and ( color.update({ 'value': 1 - color.value }) ) %} {% endif %} 97 {% if ( r == 0 ) and ( color.update({ 'value': 1 - color.value }) ) %} {% endif %}
48 <tr class="color_{{ color.value }}"> 98 <tr class="color_{{ color.value }}">
49 {%- if r == 0 -%} 99 {%- if r == 0 -%}
50 <td rowspan="{{span}}">{{ convert.bits_to_years(row[0]|int) }} : {{ convert.bits_to_years(row[1]|int) }} : {{ convert.bits_to_years(row[2]|int) }}</td> 100 <td rowspan="{{span}}">{{ convert.multirange_to_years(row[0]) }} : {{ convert.multirange_to_years(row[1]) }} : {{ convert.multirange_to_years(row[2]) }}</td>
51 {%- endif -%} 101 {%- endif -%}
52 {%- for c in range(3, 16) -%} 102 {%- for c in range(3, 16) -%}
53 <td>{{ row[c][r] or '' }}</td> 103 <td>{{ row[c][r] or '' }}</td>