<commit_before>import bleach
from datetime import datetime, timedelta
from markdown import markdown
from difflib import SequenceMatcher

from flask import (
    abort,
    flash,
    g,
    redirect,
    render_template,
    request,
    url_for,
    session,
    )
from flask.ext.mail import Message
from hasjob import app, forms, mail
from hasjob.models import (
    agelimit,
    db,
    JobCategory,
    JobType,
    JobPost,
    JobPostReport,
    POSTSTATUS,
    ReportCode,
    unique_hash,
    )
from hasjob.twitter import tweet
from hasjob.uploads import uploaded_logos
from hasjob.utils import get_email_domain, get_word_bag, md5sum
from hasjob.views import ALLOWED_TAGS

@app.route('/view/<hashid>', methods=('GET', 'POST'))
def jobdetail(hashid):
    post = JobPost.query.filter_by(hashid=hashid).first()
    if post is None:
        abort(404)
    if post.status in [POSTSTATUS.DRAFT, POSTSTATUS.PENDING]:
        if post.edit_key not in session.get('userkeys', []):
            abort(403)
    if post.status in [POSTSTATUS.REJECTED, POSTSTATUS.WITHDRAWN]:
        abort(410)
    reportform = forms.ReportForm()
    reportform.report_code.choices = [(ob.id, ob.title) for ob in ReportCode.query.filter_by(public=True).order_by('seq')]
    if reportform.validate_on_submit():
        report = JobPostReport(post=post, reportcode_id = reportform.report_code.data)
        report.ipaddr = request.environ['REMOTE_ADDR']
        report.useragent = request.user_agent.string
        db.session.add(report)
        db.session.commit()
        if request.is_xhr:
            return "<p>Thanks! This job listing has been flagged for review.</p>" #FIXME: Ugh!
        else:
            flash("Thanks! This job listing has been flagged for review.", "interactive")
    elif request.method == 'POST' and request.is_xhr:
        return render_template('inc/reportform.html', reportform=reportform, ajaxreg=True)
    return render_template('detail.html', post=post, reportform=reportform)


@app.route('/confirm/<hashid>', methods=('GET', 'POST'))
def confirm(hashid):
    post = JobPost.query.filter_by(hashid=hashid).first()
    form = forms.ConfirmForm()
    if post is None:
        abort(404)
    elif post.status == POSTSTATUS.REJECTED:
        abort(410)
    elif post.status == POSTSTATUS.DRAFT:
        if post.edit_key not in session.get('userkeys', []):
            abort(403)
    else:
        # Any other status: no confirmation required (via this handler)
        return redirect(url_for('jobdetail', hashid=post.hashid), code=302)
    if 'form.id' in request.form and form.validate_on_submit():
        # User has accepted terms of service. Now send email and/or wait for payment
        if not post.email_sent:
            msg = Message(subject="Confirmation of your job listing at the HasGeek Job Board",
                recipients=[post.email])
            msg.body = render_template("confirm_email.md", post=post)
            msg.html = markdown(msg.body)
            mail.send(msg)
            post.email_sent = True
            post.status = POSTSTATUS.PENDING
            db.session.commit()
        session.get('userkeys', []).remove(post.edit_key)
        session.modified = True # Since it won't detect changes to lists
        session.permanent = True
        return render_template('mailsent.html', post=post)
    return render_template('confirm.html', post=post, form=form)


@app.route('/confirm/<hashid>/<key>')
def confirm_email(hashid, key):
    # If post is in pending state and email key is correct, convert to published
    # and update post.datetime to utcnow() so it'll show on top of the stack
    # This function expects key to be email_verify_key, not edit_key like the others
    post = JobPost.query.filter_by(hashid=hashid).first()
    if post is None:
        abort(404)
    elif post.status == POSTSTATUS.REJECTED:
        abort(410)
    elif post.status in [POSTSTATUS.CONFIRMED, POSTSTATUS.REVIEWED]:
        flash("This job listing has already been confirmed and published", "interactive")
        return redirect(url_for('jobdetail', hashid=post.hashid), code=302)
    elif post.status == POSTSTATUS.DRAFT:
        # This should not happen. The user doesn't have this URL until they
        # pass the confirm form
        return redirect(url_for('confirm', hashid=post.hashid), code=302)
    elif post.status == POSTSTATUS.PENDING:
        if key != post.email_verify_key:
            abort(403)
        else:
            if app.config.get('THROTTLE_LIMIT', 0) > 0:
                post_count = JobPost.query.filter(JobPost.email_domain == post.email_domain).filter(
                                              JobPost.status > POSTSTATUS.PENDING).filter(
                                              JobPost.datetime > datetime.utcnow() - timedelta(days=1)).count()
                if post_count > app.config['THROTTLE_LIMIT']:
                    flash(u"We've received too many listings from %s in the last 24 hours. Please try again in a few hours. "
                        "If you believe this to be an error, please email us at %s." % (post.email_domain,
                        app.config['SUPPORT_EMAIL']), category='info')
                    return redirect(url_for('index'))
            post.email_verified = True
            post.status = POSTSTATUS.CONFIRMED
            post.datetime = datetime.utcnow()
            db.session.commit()
            if app.config['TWITTER_ENABLED']:
                try:
                    tweet(post.headline, url_for('jobdetail', hashid=post.hashid,
                        _external=True), post.location)
                    flash("Congratulations! Your job listing has been published and tweeted",
                          "interactive")
                except:  # FIXME: Catch-all
                    flash("Congratulations! Your job listing has been published "
                          "(Twitter was not reachable for tweeting)", "interactive")
            else:
                flash("Congratulations! Your job listing has been published", "interactive")
    return redirect(url_for('jobdetail', hashid=post.hashid), code=302)


@app.route('/withdraw/<hashid>/<key>', methods=('GET', 'POST'))
def withdraw(hashid, key):
    # TODO: Support for withdrawing job posts
    post = JobPost.query.filter_by(hashid=hashid).first()
    form = forms.WithdrawForm()
    if post is None:
        abort(404)
    if key != post.edit_key:
        abort(403)
    if post.status == POSTSTATUS.WITHDRAWN:
        flash("Your job listing has already been withdrawn", "info")
        return redirect(url_for('index'), code=303)
    if post.status not in [POSTSTATUS.CONFIRMED, POSTSTATUS.REVIEWED]:
        flash("Your post cannot be withdrawn because it is not public", "info")
        return redirect(url_for('index'), code=303)
    if form.validate_on_submit():
        post.status = POSTSTATUS.WITHDRAWN
        post.closed_datetime = datetime.utcnow()
        db.session.commit()
        flash("Your job listing has been withdrawn and is no longer available", "info")
        return redirect(url_for('index'), code=303)
    return render_template("withdraw.html", post=post, form=form)


@app.route('/edit/<hashid>/<key>', methods=('GET', 'POST'))
def editjob(hashid, key, form=None, post=None, validated=False):
    if form is None:
        form = forms.ListingForm(request.form)
        form.job_type.choices = [(ob.id, ob.title) for ob in JobType.query.filter_by(public=True).order_by('seq')]
        form.job_category.choices = [(ob.id, ob.title) for ob in JobCategory.query.filter_by(public=True).order_by('seq')]
    if post is None:
        post = JobPost.query.filter_by(hashid=hashid).first_or_404()
    if key != post.edit_key:
        abort(403)
    # Don't allow email address to be changed once its confirmed
    if request.method == 'POST' and post.status >= POSTSTATUS.PENDING:
        form.poster_email.data = post.email
    if request.method == 'POST' and (validated or form.validate()):
        form_description = bleach.clean(form.job_description.data, tags=ALLOWED_TAGS)
        form_description = bleach.linkify(form_description)
        form_perks = bleach.linkify(bleach.clean(form.job_perks_description.data, tags=ALLOWED_TAGS)) if form.job_perks.data else ''
        form_how_to_apply = form.job_how_to_apply.data
        form_email_domain = get_email_domain(form.poster_email.data)
        form_words = get_word_bag(u' '.join((form_description, form_perks, form_how_to_apply)))

        similar = False
        for oldpost in JobPost.query.filter(JobPost.email_domain == form_email_domain).filter(<commit_msg>More linkification<commit_after>import bleach
from datetime import datetime, timedelta
from markdown import markdown
from difflib import SequenceMatcher

from flask import (
    abort,
    flash,
    g,
    redirect,
    render_template,
    request,
    url_for,
    session,
    )
from flask.ext.mail import Message
from hasjob import app, forms, mail
from hasjob.models import (
    agelimit,
    db,
    JobCategory,
    JobType,
    JobPost,
    JobPostReport,
    POSTSTATUS,
    ReportCode,
    unique_hash,
    )
from hasjob.twitter import tweet
from hasjob.uploads import uploaded_logos
from hasjob.utils import get_email_domain, get_word_bag, md5sum
from hasjob.views import ALLOWED_TAGS

@app.route('/view/<hashid>', methods=('GET', 'POST'))
def jobdetail(hashid):
    post = JobPost.query.filter_by(hashid=hashid).first()
    if post is None:
        abort(404)
    if post.status in [POSTSTATUS.DRAFT, POSTSTATUS.PENDING]:
        if post.edit_key not in session.get('userkeys', []):
            abort(403)
    if post.status in [POSTSTATUS.REJECTED, POSTSTATUS.WITHDRAWN]:
        abort(410)
    reportform = forms.ReportForm()
    reportform.report_code.choices = [(ob.id, ob.title) for ob in ReportCode.query.filter_by(public=True).order_by('seq')]
    if reportform.validate_on_submit():
        report = JobPostReport(post=post, reportcode_id = reportform.report_code.data)
        report.ipaddr = request.environ['REMOTE_ADDR']
        report.useragent = request.user_agent.string
        db.session.add(report)
        db.session.commit()
        if request.is_xhr:
            return "<p>Thanks! This job listing has been flagged for review.</p>" #FIXME: Ugh!
        else:
            flash("Thanks! This job listing has been flagged for review.", "interactive")
    elif request.method == 'POST' and request.is_xhr:
        return render_template('inc/reportform.html', reportform=reportform, ajaxreg=True)
    return render_template('detail.html', post=post, reportform=reportform)


@app.route('/confirm/<hashid>', methods=('GET', 'POST'))
def confirm(hashid):
    post = JobPost.query.filter_by(hashid=hashid).first()
    form = forms.ConfirmForm()
    if post is None:
        abort(404)
    elif post.status == POSTSTATUS.REJECTED:
        abort(410)
    elif post.status == POSTSTATUS.DRAFT:
        if post.edit_key not in session.get('userkeys', []):
            abort(403)
    else:
        # Any other status: no confirmation required (via this handler)
        return redirect(url_for('jobdetail', hashid=post.hashid), code=302)
    if 'form.id' in request.form and form.validate_on_submit():
        # User has accepted terms of service. Now send email and/or wait for payment
        if not post.email_sent:
            msg = Message(subject="Confirmation of your job listing at the HasGeek Job Board",
                recipients=[post.email])
            msg.body = render_template("confirm_email.md", post=post)
            msg.html = markdown(msg.body)
            mail.send(msg)
            post.email_sent = True
            post.status = POSTSTATUS.PENDING
            db.session.commit()
        session.get('userkeys', []).remove(post.edit_key)
        session.modified = True # Since it won't detect changes to lists
        session.permanent = True
        return render_template('mailsent.html', post=post)
    return render_template('confirm.html', post=post, form=form)


@app.route('/confirm/<hashid>/<key>')
def confirm_email(hashid, key):
    # If post is in pending state and email key is correct, convert to published
    # and update post.datetime to utcnow() so it'll show on top of the stack
    # This function expects key to be email_verify_key, not edit_key like the others
    post = JobPost.query.filter_by(hashid=hashid).first()
    if post is None:
        abort(404)
    elif post.status == POSTSTATUS.REJECTED:
        abort(410)
    elif post.status in [POSTSTATUS.CONFIRMED, POSTSTATUS.REVIEWED]:
        flash("This job listing has already been confirmed and published", "interactive")
        return redirect(url_for('jobdetail', hashid=post.hashid), code=302)
    elif post.status == POSTSTATUS.DRAFT:
        # This should not happen. The user doesn't have this URL until they
        # pass the confirm form
        return redirect(url_for('confirm', hashid=post.hashid), code=302)
    elif post.status == POSTSTATUS.PENDING:
        if key != post.email_verify_key:
            abort(403)
        else:
            if app.config.get('THROTTLE_LIMIT', 0) > 0:
                post_count = JobPost.query.filter(JobPost.email_domain == post.email_domain).filter(
                                              JobPost.status > POSTSTATUS.PENDING).filter(
                                              JobPost.datetime > datetime.utcnow() - timedelta(days=1)).count()
                if post_count > app.config['THROTTLE_LIMIT']:
                    flash(u"We've received too many listings from %s in the last 24 hours. Please try again in a few hours. "
                        "If you believe this to be an error, please email us at %s." % (post.email_domain,
                        app.config['SUPPORT_EMAIL']), category='info')
                    return redirect(url_for('index'))
            post.email_verified = True
            post.status = POSTSTATUS.CONFIRMED
            post.datetime = datetime.utcnow()
            db.session.commit()
            if app.config['TWITTER_ENABLED']:
                try:
                    tweet(post.headline, url_for('jobdetail', hashid=post.hashid,
                        _external=True), post.location)
                    flash("Congratulations! Your job listing has been published and tweeted",
                          "interactive")
                except:  # FIXME: Catch-all
                    flash("Congratulations! Your job listing has been published "
                          "(Twitter was not reachable for tweeting)", "interactive")
            else:
                flash("Congratulations! Your job listing has been published", "interactive")
    return redirect(url_for('jobdetail', hashid=post.hashid), code=302)


@app.route('/withdraw/<hashid>/<key>', methods=('GET', 'POST'))
def withdraw(hashid, key):
    # TODO: Support for withdrawing job posts
    post = JobPost.query.filter_by(hashid=hashid).first()
    form = forms.WithdrawForm()
    if post is None:
        abort(404)
    if key != post.edit_key:
        abort(403)
    if post.status == POSTSTATUS.WITHDRAWN:
        flash("Your job listing has already been withdrawn", "info")
        return redirect(url_for('index'), code=303)
    if post.status not in [POSTSTATUS.CONFIRMED, POSTSTATUS.REVIEWED]:
        flash("Your post cannot be withdrawn because it is not public", "info")
        return redirect(url_for('index'), code=303)
    if form.validate_on_submit():
        post.status = POSTSTATUS.WITHDRAWN
        post.closed_datetime = datetime.utcnow()
        db.session.commit()
        flash("Your job listing has been withdrawn and is no longer available", "info")
        return redirect(url_for('index'), code=303)
    return render_template("withdraw.html", post=post, form=form)


@app.route('/edit/<hashid>/<key>', methods=('GET', 'POST'))
def editjob(hashid, key, form=None, post=None, validated=False):
    if form is None:
        form = forms.ListingForm(request.form)
        form.job_type.choices = [(ob.id, ob.title) for ob in JobType.query.filter_by(public=True).order_by('seq')]
        form.job_category.choices = [(ob.id, ob.title) for ob in JobCategory.query.filter_by(public=True).order_by('seq')]
    if post is None:
        post = JobPost.query.filter_by(hashid=hashid).first_or_404()
    if key != post.edit_key:
        abort(403)
    # Don't allow email address to be changed once its confirmed
    if request.method == 'POST' and post.status >= POSTSTATUS.PENDING:
        form.poster_email.data = post.email
    if request.method == 'POST' and (validated or form.validate()):
        form_description = bleach.linkify(bleach.clean(form.job_description.data, tags=ALLOWED_TAGS))
        form_perks = bleach.linkify(bleach.clean(form.job_perks_description.data, tags=ALLOWED_TAGS)) if form.job_perks.data else ''
        form_how_to_apply = bleach.linkify(form.job_how_to_apply.data)
        form_email_domain = get_email_domain(form.poster_email.data)
        form_words = get_word_bag(u' '.join((form_description, form_perks, form_how_to_apply)))

        similar = False
        for oldpost in JobPost.query.filter(JobPost.email_domain == form_email_domain).filter(
{'fraction_non_alphanumeric': 0.10869439127660808, 'fraction_numerical': 0.007656168435705586, 'mean_word_length': 2.782799473453269, 'pattern_counts': {'":': 0, '<': 23, '<?xml version=': 0, '>': 33, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 8, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5358240', 'n_tokens_mistral': 5228, 'n_tokens_neox': 5048, 'n_words': 1294}