#!/usr/bin/env flask # -*- encoding: utf-8 -*- from flask import Flask, Response, request, render_template, redirect, send_from_directory, jsonify from flask_sqlalchemy import SQLAlchemy from flask_cors import CORS from flask_wtf import FlaskForm from pathlib import Path import datetime import sqlite3 import wtforms import json COOKIE_BASE_DOMAIN = '.furmeet.app' SITE_NAME = 'FurmeetApp' sensible_info = Path('creds.txt').read_text().splitlines() app = Flask(__name__) cors = CORS(app, origins='*') app.secret_key = sensible_info[0] app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False app.config['SQLALCHEMY_DATABASE_URI'] = sensible_info[1] db = SQLAlchemy(app) class Country(db.Model): __tablename__ = 'countries' id = db.Column('rowid', db.Integer, primary_key=True) name = db.Column(db.String()) iso2 = db.Column(db.String()) iso3 = db.Column(db.String()) capital = db.Column(db.String()) capital_id = db.Column(db.Integer()) lat = db.Column(db.Float()) lng = db.Column(db.Float()) @property def serialize(self): return { 'id': self.id, 'name': self.name, 'iso2': self.iso2, 'iso3': self.iso3, 'lat': self.lat, 'lng': self.lng, 'capital': self.capital, 'capital_id': self.capital_id, } class State(db.Model): __tablename__ = 'states' id = db.Column('rowid', db.Integer, primary_key=True) parent_id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String()) capital = db.Column(db.String()) capital_id = db.Column(db.Integer()) lat = db.Column(db.Float()) lng = db.Column(db.Float()) @property def serialize(self): return { 'id': self.id, 'name': self.name, 'lat': self.lat, 'lng': self.lng, 'capital': self.capital, 'capital_id': self.capital_id, 'parent_id': self.parent_id, 'parent': Country.query.filter_by(id=self.parent_id).first().serialize, } class City(db.Model): __tablename__ = 'cities' id = db.Column('rowid', db.Integer, primary_key=True) parent_id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String()) name_ascii = db.Column(db.String()) capital_of = db.Column(db.String()) capital_of_no = db.Column(db.Integer()) lat = db.Column(db.Float()) lng = db.Column(db.Float()) @property def serialize(self): return { 'id': self.id, 'name': self.name, 'name_ascii': self.name_ascii, 'lat': self.lat, 'lng': self.lng, 'capital_of': self.capital_of, 'capital_of_no': self.capital_of_no, 'parent_id': self.parent_id, 'parent': State.query.filter_by(id=self.parent_id).first().serialize, } HIDDEN_ROUTES = [ 'static', 'search', 'index', ] class UnsafeFlaskForm(FlaskForm): def __init__(self, *args, **kwargs): super().__init__(*args, **{'csrf_enabled': False}, **kwargs) def JsonResponse(obj, kind='any[]', human_readable=False, http_code=200): return Response(json.dumps( {'kind': kind, 'data': obj}, indent=(4 if human_readable else None) ), mimetype='application/json', status=http_code) @app.route("/") def index(): return redirect("/search.html") @app.route("/search.html") def search(): return send_from_directory("cached", "search.html") @app.route("/routes") def routes(): entries = [{ 'name': rule.endpoint, 'route': rule.rule, 'methods': sorted(rule.methods), } for rule in app.url_map.iter_rules() if rule.endpoint not in HIDDEN_ROUTES] return JsonResponse(entries, 'List[Route]', True) def score_country(country, matching, score=0): if country['name'] in matching: score += 1 if country['iso2'] in matching: score += 1 if country['iso3'] in matching: score += 1 return score def score_state(state, matching, score=0): if state['name'] in matching: score += 1 return score_country(state['parent'], matching, score) def score_city(city, matching, score=0): if city['name'] in matching: score += 1 if city['name_ascii'] in matching: score += 1 return score_state(city['parent'], matching, score) @app.route("/query", methods=['POST']) def query(): place = request.form.get('place', '') human_readable = request.form.get('human_readable', '') if len(place) <= 0: return JsonResponse(list(), 'List[Geolocated]') GEONAMES = json.loads( Path('simplemaps_worldcities_basic_names.json').read_text()) place_lwr = place.lower() place_parts = [ GEONAMES[i] for i, nm in enumerate(GEONAMES) if nm.lower() in place_lwr and nm != "" ] locations = dict() locations['country'] = Country.query.filter( Country.name.in_(place_parts) | Country.iso2.in_(place_parts) | Country.iso3.in_(place_parts) ).all() locations['state'] = State.query.filter( State.name.in_(place_parts) ).all() locations['city'] = City.query.filter( City.name.in_(place_parts) | City.name_ascii.in_(place_parts) ).all() for i in ['country', 'state', 'city']: locations[i] = list(map( lambda a: a.serialize, locations[i] )) final = [ { 'kind': x[5], 'score': -x[0], 'data': x[4], } for x in sorted( [ (-score_city(city, place_parts), -len(city['name']), city['name'], city['id'], city, 'City') for city in locations['city'] ] + [ (-score_state(state, place_parts), -len(state['name']), state['name'], state['id'], state, 'State') for state in locations['state'] ] + [ (-score_country(country, place_parts), -len(country['name']), country['name'], country['id'], country, 'Country') for country in locations['country'] ] ) ] return JsonResponse(final, 'List[Geolocated]', len(human_readable))