Skip to content

Commit

Permalink
Use Flask-Sitemap to autogenerate an XML sitemap
Browse files Browse the repository at this point in the history
  • Loading branch information
dismantl committed Jul 29, 2020
1 parent ef0b9c2 commit 66e8885
Show file tree
Hide file tree
Showing 5 changed files with 59 additions and 1 deletion.
4 changes: 4 additions & 0 deletions OpenOversight/app/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from flask_login import LoginManager
from flask_mail import Mail
from flask_migrate import Migrate
from flask_sitemap import Sitemap

from .config import config

Expand All @@ -24,6 +25,8 @@
limiter = Limiter(key_func=get_remote_address,
default_limits=["100 per minute", "5 per second"])

sitemap = Sitemap()


def create_app(config_name='default'):
app = Flask(__name__)
Expand All @@ -36,6 +39,7 @@ def create_app(config_name='default'):
db.init_app(app)
login_manager.init_app(app)
limiter.init_app(app)
sitemap.init_app(app)

from .main import main as main_blueprint # noqa
app.register_blueprint(main_blueprint)
Expand Down
16 changes: 16 additions & 0 deletions OpenOversight/app/auth/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from flask_login import login_user, logout_user, login_required, \
current_user
from . import auth
from .. import sitemap
from ..models import User, db
from ..email import send_email
from .forms import LoginForm, RegistrationForm, ChangePasswordForm,\
Expand All @@ -13,6 +14,19 @@
from .utils import admin_required
from ..utils import set_dynamic_default

sitemap_endpoints = []


def sitemap_include(view):
sitemap_endpoints.append(view.__name__)
return view


@sitemap.register_generator
def static_routes():
for endpoint in sitemap_endpoints:
yield 'auth.' + endpoint, {}


@auth.before_app_request
def before_request():
Expand All @@ -34,6 +48,7 @@ def unconfirmed():
return render_template('auth/unconfirmed.html')


@sitemap_include
@auth.route('/login', methods=['GET', 'POST'])
def login():
form = LoginForm()
Expand All @@ -54,6 +69,7 @@ def logout():
return redirect(url_for('main.index'))


@sitemap_include
@auth.route('/register', methods=['GET', 'POST'])
def register():
jsloads = ['js/zxcvbn.js', 'js/password.js']
Expand Down
3 changes: 3 additions & 0 deletions OpenOversight/app/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ class DevelopmentConfig(BaseConfig):
DEBUG = True
SQLALCHEMY_DATABASE_URI = os.environ.get('SQLALCHEMY_DATABASE_URI')
NUM_OFFICERS = 15000
SITEMAP_URL_SCHEME = 'http'


class TestingConfig(BaseConfig):
Expand All @@ -60,10 +61,12 @@ class TestingConfig(BaseConfig):
WTF_CSRF_ENABLED = False
NUM_OFFICERS = 120
APPROVE_REGISTRATIONS = False
SITEMAP_URL_SCHEME = 'http'


class ProductionConfig(BaseConfig):
SQLALCHEMY_DATABASE_URI = os.environ.get('SQLALCHEMY_DATABASE_URI')
SITEMAP_URL_SCHEME = 'https'

@classmethod
def init_app(cls, app): # pragma: no cover
Expand Down
36 changes: 35 additions & 1 deletion OpenOversight/app/main/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
from flask_login import current_user, login_required, login_user

from . import main
from .. import limiter
from .. import limiter, sitemap
from ..utils import (serve_image, compute_leaderboard_stats, get_random_image,
allowed_file, add_new_assignment, edit_existing_assignment,
add_officer_profile, edit_officer_profile,
Expand Down Expand Up @@ -41,23 +41,39 @@
# Ensure the file is read/write by the creator only
SAVED_UMASK = os.umask(0o077)

sitemap_endpoints = []


def sitemap_include(view):
sitemap_endpoints.append(view.__name__)
return view


@sitemap.register_generator
def static_routes():
for endpoint in sitemap_endpoints:
yield 'main.' + endpoint, {}


def redirect_url(default='index'):
return request.args.get('next') or request.referrer or url_for(default)


@sitemap_include
@main.route('/')
@main.route('/index')
def index():
return render_template('index.html')


@sitemap_include
@main.route('/browse', methods=['GET'])
def browse():
departments = Department.query.filter(Department.officers.any())
return render_template('browse.html', departments=departments)


@sitemap_include
@main.route('/find', methods=['GET', 'POST'])
def get_officer():
jsloads = ['js/find_officer.js']
Expand Down Expand Up @@ -92,6 +108,7 @@ def get_ooid():
return render_template('input_find_ooid.html', form=form)


@sitemap_include
@main.route('/label', methods=['GET', 'POST'])
def get_started_labeling():
form = LoginForm()
Expand Down Expand Up @@ -121,6 +138,7 @@ def sort_images(department_id):
department_id=department_id)


@sitemap_include
@main.route('/tutorial')
def get_tutorial():
return render_template('tutorial.html')
Expand Down Expand Up @@ -175,6 +193,12 @@ def officer_profile(officer_id):
faces=faces, assignments=assignments, form=form)


@sitemap.register_generator
def sitemap_officers():
for officer in Officer.query.all():
yield 'main.officer_profile', {'officer_id': officer.id}


@main.route('/officer/<int:officer_id>/assignment/new', methods=['POST'])
@ac_or_admin_required
def add_assignment(officer_id):
Expand Down Expand Up @@ -755,6 +779,7 @@ def submit_complaint():
officer_image=request.args.get('officer_image'))


@sitemap_include
@main.route('/submit', methods=['GET', 'POST'])
@limiter.limit('5/minute')
def submit_data():
Expand Down Expand Up @@ -950,6 +975,7 @@ def download_incidents_csv(department_id):
return Response(csv, mimetype="text/csv", headers=csv_headers)


@sitemap_include
@main.route('/download/all', methods=['GET'])
def all_data():
departments = Department.query.all()
Expand Down Expand Up @@ -999,11 +1025,13 @@ def upload(department_id, officer_id=None):
return jsonify(error="Server error encountered. Try again later."), 500


@sitemap_include
@main.route('/about')
def about_oo():
return render_template('about.html')


@sitemap_include
@main.route('/privacy')
def privacy_oo():
return render_template('privacy.html')
Expand Down Expand Up @@ -1143,6 +1171,12 @@ def populate_obj(self, form, obj):
methods=['GET', 'POST'])


@sitemap.register_generator
def sitemap_incidents():
for incident in Incident.query.all():
yield 'main.incident_api', {'obj_id': incident.id}


class TextApi(ModelView):
order_by = 'date_created'
descending = True
Expand Down
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ Flask-Mail==0.9.1
Flask-Migrate==2.1.1
Flask-SQLAlchemy==2.4.4
Flask-WTF==0.14.3
Flask-Sitemap==0.3.0
Pillow==5.2.0
psycopg2==2.8.5 --no-binary psycopg2
SQLAlchemy==1.3.0
Expand Down

0 comments on commit 66e8885

Please sign in to comment.