Add exclusions feature: map popup Exclude button, exclusions page, server API
This commit is contained in:
+77
-1
@@ -426,7 +426,7 @@ def serve_data(filename):
|
||||
|
||||
# ── Config file download / upload ──────────────────────────────────────────────
|
||||
|
||||
_ALLOWED_CONFIGS = {'counties.yml', 'exceptions.yml'}
|
||||
_ALLOWED_CONFIGS = {'counties.yml', 'exceptions.yml', 'exclusions.yml'}
|
||||
|
||||
@app.route('/api/config/<filename>', methods=['GET'])
|
||||
@require_auth
|
||||
@@ -450,6 +450,8 @@ def upload_config(filename):
|
||||
return jsonify({'error': 'Invalid counties.yml: missing counties list'}), 400
|
||||
if filename == 'exceptions.yml' and not isinstance((data or {}).get('corrections'), list):
|
||||
return jsonify({'error': 'Invalid exceptions.yml: missing corrections list'}), 400
|
||||
if filename == 'exclusions.yml' and not isinstance((data or {}).get('exclusions'), list):
|
||||
return jsonify({'error': 'Invalid exclusions.yml: missing exclusions list'}), 400
|
||||
except Exception as e:
|
||||
return jsonify({'error': f'Invalid YAML: {e}'}), 400
|
||||
dest = f'/data/{filename}'
|
||||
@@ -459,6 +461,80 @@ def upload_config(filename):
|
||||
os.replace(tmp, dest)
|
||||
return jsonify({'success': True})
|
||||
|
||||
# ── Exclusions config ─────────────────────────────────────────────────────────
|
||||
|
||||
EXCLUSIONS_FILE = '/data/exclusions.yml'
|
||||
|
||||
|
||||
def _ensure_exclusions_file():
|
||||
if not os.path.exists(EXCLUSIONS_FILE):
|
||||
os.makedirs(os.path.dirname(EXCLUSIONS_FILE), exist_ok=True)
|
||||
with open(EXCLUSIONS_FILE, 'w') as f:
|
||||
yaml.dump({'exclusions': []}, f, default_flow_style=False, allow_unicode=True)
|
||||
|
||||
_ensure_exclusions_file()
|
||||
|
||||
|
||||
def get_exclusions():
|
||||
try:
|
||||
with open(EXCLUSIONS_FILE) as f:
|
||||
data = yaml.safe_load(f) or {}
|
||||
return data.get('exclusions', [])
|
||||
except Exception:
|
||||
return []
|
||||
|
||||
|
||||
@app.route('/exclusions')
|
||||
def exclusions_page():
|
||||
return render_template('exclusions.html', current_user=session.get('username'))
|
||||
|
||||
|
||||
@app.route('/api/exclusions', methods=['GET'])
|
||||
def get_exclusions_api():
|
||||
return jsonify({'exclusions': get_exclusions()})
|
||||
|
||||
|
||||
@app.route('/api/exclusions', methods=['POST'])
|
||||
@require_auth
|
||||
def save_exclusions():
|
||||
data = request.json
|
||||
items = data.get('exclusions', [])
|
||||
for item in items:
|
||||
if not isinstance(item.get('field'), str) or not item['field'].strip():
|
||||
return jsonify({'error': 'Each exclusion must have a non-empty "field"'}), 400
|
||||
if not isinstance(item.get('value'), str) or not item['value'].strip():
|
||||
return jsonify({'error': 'Each exclusion must have a non-empty "value"'}), 400
|
||||
tmp = EXCLUSIONS_FILE + '.tmp'
|
||||
with open(tmp, 'w') as f:
|
||||
yaml.dump({'exclusions': items}, f, default_flow_style=False, allow_unicode=True)
|
||||
os.replace(tmp, EXCLUSIONS_FILE)
|
||||
return jsonify({'success': True})
|
||||
|
||||
|
||||
@app.route('/api/exclusions/add', methods=['POST'])
|
||||
@require_auth
|
||||
def add_exclusions():
|
||||
"""Append new exclusion entries, skipping duplicates."""
|
||||
data = request.json
|
||||
new_items = data.get('exclusions', [])
|
||||
existing = get_exclusions()
|
||||
existing_keys = {(e['field'], e['value']) for e in existing}
|
||||
added = 0
|
||||
for item in new_items:
|
||||
field = (item.get('field') or '').strip()
|
||||
value = (item.get('value') or '').strip()
|
||||
if not field or not value:
|
||||
continue
|
||||
if (field, value) not in existing_keys:
|
||||
existing.append({'field': field, 'value': value})
|
||||
existing_keys.add((field, value))
|
||||
added += 1
|
||||
tmp = EXCLUSIONS_FILE + '.tmp'
|
||||
with open(tmp, 'w') as f:
|
||||
yaml.dump({'exclusions': existing}, f, default_flow_style=False, allow_unicode=True)
|
||||
os.replace(tmp, EXCLUSIONS_FILE)
|
||||
return jsonify({'success': True, 'added': added})
|
||||
|
||||
# ── Counties config ────────────────────────────────────────────────────────────
|
||||
|
||||
COUNTIES_FILE = '/data/counties.yml'
|
||||
|
||||
Reference in New Issue
Block a user