Bug fixes and backup functionality

This commit is contained in:
Ilya Zverev 2015-03-23 17:57:11 +03:00
parent 12861ced7f
commit 488260c733
5 changed files with 119 additions and 10 deletions

View file

@ -12,5 +12,16 @@ create table borders (
cmnt varchar(500)
);
create table borders_backup (
backup varchar(30) not null,
name varchar(200) not null,
geom geometry not null,
disabled boolean not null default FALSE,
count_k integer,
modified timestamp not null,
cmnt varchar(500),
primary key (backup, name)
);
create index border_idx on borders using gist (geom);
create index tiles_idx on tiles using gist (tile);

View file

@ -65,7 +65,7 @@ fi
# 3. Make osm_borders table
echo Creating osm_borders table
psql $DATABASE -c "drop table if exists osm_borders; create table osm_borders as select min(osm_id) as osm_id, ST_Transform(ST_Collect(way),4326) as way, admin_level::int as admin_level, coalesce(max(\"name:en\"), name) as name from planet_osm_polygon where boundary='administrative' and admin_level in ('2', '3', '4', '5', '6') group by name, admin_level;" || exit 3
psql $DATABASE -c "drop table if exists osm_borders; create table osm_borders as select min(osm_id) as osm_id, ST_Buffer(ST_Transform(ST_Collect(way),4326), 0) as way, admin_level::int as admin_level, coalesce(max(\"name:en\"), name) as name from planet_osm_polygon where boundary='administrative' and admin_level in ('2', '3', '4', '5', '6') group by name, admin_level;" || exit 3
# 4. Copy it to the borders database
echo Copying osm_borders table to the borders database

View file

@ -2,12 +2,13 @@
from flask import Flask, g, request, json, jsonify, abort, Response
from flask.ext.cors import CORS
from flask.ext.compress import Compress
import psycopg2
from lxml import etree
from xml.sax.saxutils import quoteattr
import psycopg2
TABLE = 'borders'
OSM_TABLE = 'osm_borders'
BACKUP = 'borders_backup'
READONLY = False
app = Flask(__name__)
@ -230,7 +231,13 @@ def divide():
if prefix != '':
prefix = '{}_'.format(prefix);
cur = g.conn.cursor()
cur.execute('insert into {table} (geom, name, modified, count_k) select o.way as way, %s || name, now(), -1 from {osm}, (select way from {osm} where name like %s) r where ST_Contains(r.way, o.way) and {query};'.format(table=TABLE, osm=OSM_TABLE, query=query), (prefix, like,))
cur.execute('''insert into {table} (geom, name, modified, count_k)
select o.way as way, %s || name, now(), -1
from {osm} o, (
select way from {osm} where name like %s
) r
where ST_Contains(r.way, o.way) and {query};
'''.format(table=TABLE, osm=OSM_TABLE, query=query), (prefix, like,))
cur.execute('delete from {} where name = %s;'.format(TABLE), (name,))
g.conn.commit()
return jsonify(status='ok')
@ -273,6 +280,39 @@ def draw_hull():
g.conn.commit()
return jsonify(status='ok')
@app.route('/backup')
def backup_do():
cur = g.conn.cursor()
cur.execute("SELECT to_char(now(), 'IYYY-MM-DD HH24:MI'), max(backup) from {};".format(BACKUP))
(timestamp, tsmax) = cur.fetchone()
if timestamp == tsmax:
return jsonify(status='please try again later')
cur.execute('INSERT INTO {backup} (backup, name, geom, disabled, count_k, modified, cmnt) SELECT %s, name, geom, disabled, count_k, modified, cmnt from {table};'.format(backup=BACKUP, table=TABLE), (timestamp,))
g.conn.commit()
return jsonify(status='ok')
@app.route('/restore')
def backup_restore():
ts = request.args.get('timestamp')
cur = g.conn.cursor()
cur.execute('SELECT count(1) from {} where backup = %s;'.format(BACKUP), (ts,))
(count,) = cur.fetchone()
if count <= 0:
return jsonify(status='no such timestamp')
cur.execute('DELETE FROM {};'.format(TABLE))
cur.execute('INSERT INTO {table} (name, geom, disabled, count_k, modified, cmnt) SELECT name, geom, disabled, count_k, modified, cmnt from {backup} where backup = %s;'.format(backup=BACKUP, table=TABLE), (ts,))
g.conn.commit()
return jsonify(status='ok')
@app.route('/backlist')
def backup_list():
cur = g.conn.cursor()
cur.execute("SELECT backup, count(1) from {} group by backup order by backup desc;".format(BACKUP))
result = []
for res in cur:
result.append({ 'timestamp': res[0], 'text': res[0], 'count': res[1] })
return jsonify(backups=result)
@app.route('/josm')
def make_osm():
xmin = request.args.get('xmin')
@ -496,7 +536,6 @@ def import_osm():
j = i + 1
while way[0] != way[-1] and j < len(multi):
print 'maybe way with start={}, end={}?'.format(multi[j]['nodes'][0], multi[j]['nodes'][-1])
# todo: do not modify source way!!!
new_way = append_way(way, multi[j]['nodes'])
if new_way:
multi[i] = dict(multi[i])
@ -553,7 +592,7 @@ def import_osm():
updated = updated + 1
else:
# create
cur.execute('insert into {table} (name, disabled, geom, modified, count_k) values (%s, %s, %s, now(), -1);'.format(table=TABLE), (name, region['disabled'], region['wkt']))
cur.execute('insert into {table} (name, disabled, geom, modified, count_k) values (%s, %s, ST_GeomFromText(%s, 4326), now(), -1);'.format(table=TABLE), (name, region['disabled'], region['wkt']))
added = added + 1
g.conn.commit()
return jsonify(regions=len(regions), added=added, updated=updated)

View file

@ -18,9 +18,9 @@ function init() {
map.addLayer(bordersLayer);
map.on('moveend', function() {
if( map.getZoom() >= 4 )
if( map.getZoom() >= 5 )
updateBorders();
$('#b_josm').css('visibility', map.getZoom() >= 8 ? 'visible' : 'hidden');
$('#b_josm').css('visibility', map.getZoom() >= 7 ? 'visible' : 'hidden');
});
document.getElementById('filefm').action = server + '/import';
@ -147,7 +147,7 @@ function selectLayer(e) {
if( props['disabled'] )
e.target.setStyle({ fillOpacity: 0.01 });
$('#b_name').text(props['name']);
$('#b_size').text(Math.round(props['count_k'] * 8 / 1000000) + ' MB');
$('#b_size').text(Math.round(props['count_k'] * 8 / 1024 / 1024) + ' MB');
//$('#b_nodes').text(borders[selectedId].layer.getLatLngs()[0].length);
$('#b_nodes').text(props['nodes']);
$('#b_date').text(props['modified']);
@ -503,7 +503,7 @@ function bDivideDrawPreview(geojson) {
return;
divPreview = L.geoJson(geojson, {
style: function(f) {
return { color: 'blue', weight: 1 };
return { color: 'blue', weight: 1, fill: false };
}
});
map.addLayer(divPreview);
@ -551,3 +551,52 @@ function bHull() {
success: updateBorders
});
}
function bBackup() {
$('#actions').css('display', 'none');
$('#backup_saving').css('display', 'none');
$('#backup_restoring').css('display', 'none');
$('#backup_save').attr('disabled', false);
$('#backup_list').text('');
$('#backup').css('display', 'block');
$.ajax(server + '/backlist', {
success: updateBackupList
});
}
function bBackupCancel() {
$('#actions').css('display', 'block');
$('#backup').css('display', 'none');
}
function updateBackupList(data) {
var list = $('#backup_list');
list.text('');
if( !data || !('backups' in data) )
return;
for( var i = 0; i < data.backups.length; i++ ) {
var b = data.backups[i];
var a = document.createElement('a');
a.href = '#';
a.onclick = (function(id, name) { return function() { bBackupRestore(id); return false } })(b['timestamp']);
list.append(a, $('<br>'));
$(a).text(b['text'] + ' (' + b['count'] + ')');
}
}
function bBackupSave() {
$.ajax(server + '/backup', {
success: bBackupCancel
});
$('#backup_save').attr('disabled', true);
$('#backup_saving').css('display', 'block');
}
function bBackupRestore(timestamp) {
$.ajax(server + '/restore', {
data: { 'timestamp': timestamp },
success: function() { bBackupCancel(); updateBorders(); }
});
$('#backup_list').text('');
$('#backup_restoring').css('display', 'block');
}

View file

@ -19,10 +19,11 @@
#osm_actions { display: none; margin-top: 1em; }
#info { margin-top: 2em; }
#b_delete, #b_clear { font-size: 8pt; }
#rename, #split, #join, #point, #divide { display: none; }
#rename, #split, #join, #point, #divide, #backup { display: none; }
.actions input[type='text'] { width: 150px; }
#header { border-bottom: 1px solid gray; margin-bottom: 1em; padding-bottom: 1em; }
#f_topo, #f_chars, #f_comments { font-size: 10pt; }
#backup_saving, #backup_restoring { margin-bottom: 1em; }
</style>
</head>
<body onload="init();">
@ -58,6 +59,7 @@
Импорт <input type="file" accept=".osm,.xml" name="file" id="b_import" onchange="bImport();" style="max-width: 100px;">
</form>
<iframe name="import_frame" style="display: none;" width="230" height="80" src="about:blank"></iframe>
<button onclick="bBackup()">Архив границ</button>
</div>
<div id="actions" class="actions">
<button onclick="bDisable()" id="b_disable">Убрать</button>
@ -131,6 +133,14 @@
<br>
<button onclick="bDivideCancel()">Вернуться</button>
</div>
<div id="backup" class="actions">
<button onclick="bBackupSave()" id="backup_save">Сохранить границы</button>
<div id="backup_saving">Копирую границы...</div>
<div id="backup_restoring">Восстанавливаю границы...</div>
<div>Или выберите набор границ для восстановления:</div>
<div id="backup_list"></div>
<button onclick="bBackupCancel()">Вернуться</button>
</div>
</div>
<div id="map"></div>
</body>