<commit_before>#!/usr/bin/env python

from __future__ import division
import os
import sys
import time
import random
import json

import functools

from flask import Flask
from flask import render_template
from flask import send_file
from flask import jsonify
from flask import request
from flask import Response
from flask import abort
from flask import redirect

from utils import *
import backend
import config


#================================================================================
# HELPERS

def check_auth(username, password):
    """This function is called to check if a username /
    password combination is valid.
    """
    return username == 'uclabs' and password == '<PASSWORD>!!!!!'

def authenticate():
    """Sends a 401 response that enables basic auth"""
    return Response(
        'Could not verify your access level for that URL.\n'
        'You have to login with proper credentials', 401,
        {'WWW-Authenticate': 'Basic realm="Login Required"'}
    )

def use_basic_auth(f):
    @functools.wraps(f)
    def decorated(*args, **kwargs):
        auth = request.authorization
        if not auth or not check_auth(auth.username, auth.password):
            return authenticate()
        return f(*args, **kwargs)
    return decorated


#================================================================================
# FLASK

app = Flask(__name__)
app.config['SECRET_KEY'] = 'fpqf94y1och48OUGWO38yfoo8yroihf28y982heD'


#================================================================================
# ROUTING

def simulateSlow():
    debugDetail('...')
    if config.CROWD_FAKE_SLOW_DELAY > 0:
        time.sleep(config.CROWD_FAKE_SLOW_DELAY)
    debugDetail('   ...')

#===========================================
# INDEX

@app.route('/')
@use_basic_auth
def index():
    simulateSlow()
    return render_template('crowd_index.html')

#===========================================
# MISC API

@app.route('/stats')
@use_basic_auth
def stats():
    """
    {
        words_raw: 29,
        words_approved: 29,
        words_sliced: 29,
        words_total: 104,
    }
    """
    simulateSlow()
    return jsonify(backend.getCrowdStats(config.CROWD_DB))

#===========================================
# IMAGES

@app.route('/images')
@use_basic_auth
def images():
    simulateSlow()
    debugDetail('HTTP: /images')
    return render_template('crowd_images.html')

@app.route('/image/next')
@use_basic_auth
def redirectToNextImage():
    """Redirect to the API call for an available image to approve.
    """
    simulateSlow()
    debugDetail('HTTP: /images/next')
    image_id = backend.getNextCrowdImage(config.CROWD_DB)
    if image_id is None:
        abort(404)
    else:
        return redirect('/image/%s'%image_id)

@app.route('/image/<image_id>')
@use_basic_auth
def getImage(image_id):
    """
    Return details about a particular image.
    Includes a list of word annotations.
    Look in crowd_app.py for a list of the json properties returned here.
    """
    simulateSlow()
    debugDetail('HTTP: /images/%s'%image_id)
    image = backend.getImage(config.CROWD_DB, id=image_id)
    # rename image url to avoid collision
    image['url'] = image['url'].replace('/image/','/img/')
    # rename 'id' to 'image_id'
    image['image_id'] = image['id']
    del image['id']

    words = backend.getImageAnnotations(config.CROWD_DB, id=image_id)
    words = [word for word in words if word['domain'] == 'text:word']
    # rename 'id' to 'annotation_id'
    for word in words:
        word['annotation_id'] = word['id']
        del word['id']

    image['words'] = words

    return jsonify(image)

@app.route('/img/<locator>.<ext>',methods=['GET'])
@use_basic_auth
def getImageFile(locator, ext):
    simulateSlow()
    locator = locator.replace('-','').replace('/','').replace('..','')
    ext = ext.replace('/','').replace('..','')
    path = '/data/rigor/images/%s/%s/%s.%s' % (
                locator[:2],
                locator[2:4],
                locator.replace('-',''),
                ext<commit_msg>Sort words so smaller ones are clickable in front of bigger ones<commit_after>#!/usr/bin/env python

from __future__ import division
import os
import sys
import time
import random
import json

import functools

from flask import Flask
from flask import render_template
from flask import send_file
from flask import jsonify
from flask import request
from flask import Response
from flask import abort
from flask import redirect

from utils import *
import backend
import config


#================================================================================
# HELPERS

def check_auth(username, password):
    """This function is called to check if a username /
    password combination is valid.
    """
    return username == 'uclabs' and password == '<PASSWORD>!!!!!'

def authenticate():
    """Sends a 401 response that enables basic auth"""
    return Response(
        'Could not verify your access level for that URL.\n'
        'You have to login with proper credentials', 401,
        {'WWW-Authenticate': 'Basic realm="Login Required"'}
    )

def use_basic_auth(f):
    @functools.wraps(f)
    def decorated(*args, **kwargs):
        auth = request.authorization
        if not auth or not check_auth(auth.username, auth.password):
            return authenticate()
        return f(*args, **kwargs)
    return decorated


#================================================================================
# FLASK

app = Flask(__name__)
app.config['SECRET_KEY'] = 'fpqf94y1och48OUGWO38yfoo8yroihf28y982heD'


#================================================================================
# ROUTING

def simulateSlow():
    debugDetail('...')
    if config.CROWD_FAKE_SLOW_DELAY > 0:
        time.sleep(config.CROWD_FAKE_SLOW_DELAY)
    debugDetail('   ...')

#===========================================
# INDEX

@app.route('/')
@use_basic_auth
def index():
    simulateSlow()
    return render_template('crowd_index.html')

#===========================================
# MISC API

@app.route('/stats')
@use_basic_auth
def stats():
    """
    {
        words_raw: 29,
        words_approved: 29,
        words_sliced: 29,
        words_total: 104,
    }
    """
    simulateSlow()
    return jsonify(backend.getCrowdStats(config.CROWD_DB))

#===========================================
# IMAGES

@app.route('/images')
@use_basic_auth
def images():
    simulateSlow()
    debugDetail('HTTP: /images')
    return render_template('crowd_images.html')

@app.route('/image/next')
@use_basic_auth
def redirectToNextImage():
    """Redirect to the API call for an available image to approve.
    """
    simulateSlow()
    debugDetail('HTTP: /images/next')
    image_id = backend.getNextCrowdImage(config.CROWD_DB)
    if image_id is None:
        abort(404)
    else:
        return redirect('/image/%s'%image_id)

@app.route('/image/<image_id>')
@use_basic_auth
def getImage(image_id):
    """
    Return details about a particular image.
    Includes a list of word annotations.
    Look in crowd_app.py for a list of the json properties returned here.
    """
    simulateSlow()
    debugDetail('HTTP: /images/%s'%image_id)
    image = backend.getImage(config.CROWD_DB, id=image_id)
    # rename image url to avoid collision
    image['url'] = image['url'].replace('/image/','/img/')
    # rename 'id' to 'image_id'
    image['image_id'] = image['id']
    del image['id']

    words = backend.getImageAnnotations(config.CROWD_DB, id=image_id)
    words = [word for word in words if word['domain'] == 'text:word']

    # sort words largest first for optimal clickability
    def wordSize(word):
        maxEdge = max(abs(word["boundary"][0][0] - word["boundary"][1][0]),
                      abs(word["boundary"][0][1] - word["boundary"][1][1]),
                      abs(word["boundary"][1][0] - word["boundary"][2][0]),
                      abs(word["boundary"][1][1] - word["boundary"][2][1]))
        return maxEdge
    words.sort(key = wordSize) # smallest first
    words.reverse()

    # rename 'id' to 'annotation_id'
    for word in words:
        word['annotation_id'] = word['id']
        del word['id']

    image['words'] = words

    return jsonify(image)

@app.route('/img/<locator>.<ext>',methods=['GET'])
@use_basic_auth
def getImageFile(locator, ext):
    simulateSlow()
    locator = locator.replace('-','').replace('/','').replace('..','')
    ext = ext.replace('/','').replace('..','')
    path = '/data/rigor/images/%s/%s/%s.%s' % (
                locator[:2],
                locator[2:4],
                locator.replace('-',''),
                ext
{'fraction_non_alphanumeric': 0.21202312138728324, 'fraction_numerical': 0.009942196531791908, 'mean_word_length': 3.531691985332635, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 13, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4348273', 'n_tokens_mistral': 2711, 'n_tokens_neox': 2557, 'n_words': 647}