text: import os
  import re
  from flask import Flask, jsonify, render_template, request, url_for
  from flask_jsglue import JSGlue
  
  from cs50 import SQL
  from helpers import lookup
  
  # configure application
  app = Flask(__name__)
  JSGlue(app)
  
  # ensure responses aren't cached
  if app.config["DEBUG"]:
      @app.after_request
      def after_request(response):
          response.headers["Cache-Control"] = "no-cache, no-store, must-revalidate"
          response.headers["Expires"] = 0
          response.headers["Pragma"] = "no-cache"
          return response
  
  # configure CS50 Library to use SQLite database
  db = SQL("sqlite:///mashup.db")
  
  @app.route("/")
  def index():
      """Render map."""
      if not os.environ.get("API_KEY"):
          raise RuntimeError("API_KEY not set")
      return render_template("index.html", key=os.environ.get("API_KEY"))
  
  @app.route("/articles")
  def articles():
      """Look up articles for geo."""
  
      # retrieve the geographic location argument (the part in the URL after the question mark)
      geo = request.args.get("geo")
      
      # if the argument is missing raise a RuntimeError
      if not geo:
          raise RuntimeError("Geo is not set.")
          
      # search for articles in that geographic location
      articles = lookup(geo)
      
      # return up to five articles as a JSON object
      if len(articles) > 5:
          # return the first five articles (if there are more than five)
          return jsonify([articles[0], articles[1], articles[2], articles[3], articles[4]])
      else:
          # return all articles (if there are five or less)
          return jsonify(articles)
  
  @app.route("/search")
  def search():
      """Search for places that match query."""
  
      # retrieve q (could be a city, state, or postal code)
      # we add "%" to the end, SQL’s "wildcard" character that means "match any number of characters"
      # this will allow us to return rows matching whatever the user typed followed by any number of other characters
      # that way the user does not need to type the exact city, state, or postal code
      q = request.args.get("q") + "%"
      
      # query the database to find any city, state, or postal code that starts with q
      place = db.execute("SELECT * FROM places WHERE \
                          place_name LIKE :q OR \
                          admin_name1 LIKE :q OR \
                          postal_code LIKE :q", q=q)
                          
      # return up to ten places as a JSON object
      if len(place) > 10:
          # return the first ten places (if there are more than ten)
          return jsonify([place[0], place[1], place[2], place[3], place[4],
                          place[5], place[6], place[7], place[8], place[9]])
      else:
          # return all places (if there are ten or less)
          return jsonify(place)
  
  @app.route("/update")
  def update():
      """Find up to 10 places within view."""
  
      # ensure parameters are present
      if not request.args.get("sw"):
          raise RuntimeError("missing sw")
      if not request.args.get("ne"):
          raise RuntimeError("missing ne")
  
      # ensure parameters are in lat,lng format
      if not re.search("^-?\d+(?:\.\d+)?,-?\d+(?:\.\d+)?$", request.args.get("sw")):
          raise RuntimeError("invalid sw")
      if not re.search("^-?\d+(?:\.\d+)?,-?\d+(?:\.\d+)?$", request.args.get("ne")):
          raise RuntimeError("invalid ne")
  
      # explode southwest corner into two variables
      (sw_lat, sw_lng) = [float(s) for s in request.args.get("sw").split(",")]
  
      # explode northeast corner into two variables
      (ne_lat, ne_lng) = [float(s) for s in request.args.get("ne").split(",")]
  
      # find 10 cities within view, pseudorandomly chosen if more within view
      if (sw_lng <= ne_lng):
  
          # doesn't cross the antimeridian
          rows = db.execute("""SELECT * FROM places
              WHERE :sw_lat <= latitude AND latitude <= :ne_lat AND (:sw_lng <= longitude AND longitude <= :ne_lng)
              GROUP BY country_code, place_name, admin_code1
              ORDER BY RANDOM()
              LIMIT 10""",
              sw_lat=sw_lat, ne_lat=ne_lat, sw_lng=sw_lng, ne_lng=ne_lng)
  
      else:
  
          # crosses the antimeridian
          rows = db.execute("""SELECT * FROM places
              WHERE :sw_lat <= latitude AND latitude <= :ne_lat AND (:sw_lng <= longitude OR longitude <= :ne_lng)
              GROUP BY country_code, place_name, admin_code1
              ORDER BY RANDOM()
              LIMIT 10""",
              sw_lat=sw_lat, ne_lat=ne_lat, sw_lng=sw_lng, ne_lng=ne_lng)
  
      # output places as JSON
      return jsonify(rows)
  