<commit_before>__author__ = 'richard'
import sys, pygame
from pygame.locals import *
import time
import subprocess
import os
from pprint import pprint
from collections import defaultdict
from color import *
from button import Button
from label import Label
from CanvasRunner import CanvasRunner
from home import Home

program = CanvasRunner()

home = Home()
program.add_canvas('homescreen',home)
program.main()<commit_msg>fixed missing import<commit_after>__author__ = 'richard'
import sys, pygame
from pygame.locals import *
import time
import subprocess
import os
from pprint import pprint
from collections import defaultdict
from color import *
from button import Button
from label import Label
from CanvasRunner import CanvasRunner
from home import Home

program = CanvasRunner()

home = Home('homescreen')
program.add_canvas('homescreen',home)
program.main()<|endoftext|>


<commit_before>#!/usr/bin/env python
"""Script to run UNH-RVAT ALM case."""

import argparse
import os
import subprocess
from subprocess import call, check_output
import numpy as np
import pandas as pd
import glob
import foampy
from foampy.dictionaries import replace_value
import shutil
from pyurtf import processing as pr

def get_mesh_dims():
    """Get mesh dimensions by grepping `blockMeshDict`."""
    raw = check_output("grep blocks system/blockMeshDict -A3",
                       shell=True).decode().split("\n")[3]
    raw = raw.replace("(", "").replace(")", "").split()
    return {"nx": int(raw[0]), "ny": int(raw[1]), "nz": int(raw[2])}

def get_dt():
    """Read ``deltaT`` from ``controlDict``."""
    return foampy.dictionaries.read_single_line_value("controlDict",
                                                      keyword="deltaT")

def log_perf(param="tsr", append=True):
    """Log performance to file."""
    if not os.path.isdir("processed"):
        os.mkdir("processed")
    fpath = "processed/{}_sweep.csv".format(param)
    if append and os.path.isfile(fpath):
        df = pd.read_csv(fpath)
    else:
        df = pd.DataFrame(columns=["nx", "ny", "nz", "dt", "tsr", "cp", "cd"])
    d = pr.calc_perf(t1=3.0)
    d.update(get_mesh_dims())
    d["dt"] = get_dt()
    df = df.append(d, ignore_index=True)
    df.to_csv(fpath, index=False)

def set_blockmesh_resolution(nx=48, ny=None, nz=None):
    """Set mesh resolution in ``blockMeshDict``.

    If only ``nx`` is provided, the default resolutions for other dimensions are
    scaled proportionally.
    """
    defaults = {"nx": 48, "ny": 48, "nz": 32}
    if ny is None:
        ny = nx
    if nz is None:
        nz = int(nx*defaults["nz"]/defaults["nx"])
    print("Setting blockMesh resolution to ({} {} {})".format(nx, ny, nz))
    foampy.fill_template("system/blockMeshDict.template", nx=nx, ny=ny, nz=nz)

def set_dt(dt=0.005, tsr=None, tsr_0=1.9):
    """Set ``deltaT`` in ``controlDict``. Will scale proportionally if ``tsr``
    and ``tsr_0`` are supplied, such that steps-per-rev is consistent with
    ``tsr_0``.
    """
    if tsr is not None:
        dt = dt*tsr_0/tsr
        print("Setting deltaT = dt*tsr_0/tsr = {:.3f}".format(dt))
    dt = str(dt)
    foampy.fill_template("system/controlDict.template", dt=dt)

def set_talpha(val=6.25):
    """Set `TAlpha` value for the Leishman--Beddoes SGC dynamic stall model."""
    foampy.dictionaries.replace_value("system/fvOptions", "TAlpha", str(val))

def gen_sets_file():
    """Generate ``sets`` file for post-processing."""
    # Input parameters
    setformat = "raw"
    interpscheme = "cellPoint"
    fields = ["UMean", "UPrime2Mean", "kMean"]
    x = 1.0
    ymax = 1.5
    ymin = -1.5
    ny = 51
    zmax = 1.125
    zmin = -1.125
    nz = 19
    z_array = np.linspace(zmin, zmax, nz)
    txt = "\ntype sets;\n"
    txt +='libs ("libsampling.so");\n'
    txt += "setFormat " + setformat + ";\n"
    txt += "interpolationScheme " + interpscheme + ";\n\n"
    txt += "sets \n ( \n"
    for z in z_array:
        # Fix interpolation issues if directly on a face
        if z == 0.0:
            z += 1e-5
        txt += "    " + "profile_" + str(z) + "\n"
        txt += "    { \n"
        txt += "        type        uniform; \n"
        txt += "        axis        y; \n"
        txt += "        start       (" + str(x) + " " + str(ymin) + " " \
            + str(z) + ");\n"
        txt += "        end         (" + str(x) + " " + str(ymax) + " " \
            + str(z) + ");\n"
        txt += "        nPoints     " + str(ny) + ";\n    }\n\n"
    txt += ");\n\n"
    txt += "fields \n(\n"
    for field in fields:
        txt += "    " + field + "\n"
    txt += "); \n\n"
    txt += "//\
     *********************************************************************** //\
     \n"
    with open("system/sets", "w") as f:
        f.write(txt)

def post_process(parallel=False, tee=False, overwrite=True):
    """Execute all post-processing."""
    gen_sets_file()
    foampy.run("postProcess", args="-func -vorticity", parallel=parallel,
               logname="log.vorticity", tee=tee, overwrite=overwrite)
    foampy.run("postProcess", args="-dict system/controlDict.recovery "
               " -latestTime", parallel=parallel, logname="log.recovery",
               tee=tee, overwrite=overwrite)
    foampy.run("funkyDoCalc", args="system/funkyDoCalcDict -latestTime",
               parallel=parallel, tee=tee, overwrite=overwrite)

def param_sweep(param="tsr", start=None, stop=None, step=None, dtype=float,
                append=False, parallel=True, tee=False, **kwargs):
    """Run multiple simulations, varying ``quantity``.

    ``step`` is not included.
    """
    print("Running {} sweep".format(param))
    fpath = "processed/{}_sweep.csv".format(param)
    if not append and os.path.isfile(fpath):
        os.remove(fpath)
    if param == "nx":
        dtype = int
    param_list = np.arange(start, stop, step, dtype=dtype)
    for p in param_list:
        print("Running with {} = {}".format(param, p))
        if param == "talpha":
            set_talpha(p)
        if p == param_list[0] or param == "nx":
            foampy.clean(remove_zero=True)
            mesh = True
        else:
            mesh = False
        # Update kwargs for this value
        kwargs.update({param: p})
        run(parallel=parallel, tee=tee, mesh=mesh, reconstruct=False,
            post=False, **kwargs)
        os.rename("log.pimpleFoam", "log.pimpleFoam." + str(p))
        log_perf(param=param, append=True)
        foampy.clean(leave_mesh=True, remove_zero=True)
    # Set parameters back to defaults
    if param == "talpha":
        set_talpha()

def set_turbine_op_params(tsr=1.9, tsr_amp=0.0, tsr_phase=1.4):
    """Write file defining turbine operating parameters.

    ``tsr_phase`` is in radians.
    """
    txt="""
tipSpeedRatio   {tsr};
tsrAmplitude    {tsr_amp};
tsrPhase        {tsr_phase};
    """.format(tsr=tsr, tsr_amp=tsr_amp, tsr_phase=tsr_phase)
    with open("system/turbineOperatingParams", "w") as f:
        f.write(txt)

def run(tsr=1.9, tsr_amp=0.0, tsr_phase=1.4, nx=48, mesh=True, parallel=False,
        dt=0.005, tee=False, reconstruct=True, overwrite=False, post=True):
    """Run simulation once."""
    print("Setting TSR to", tsr)
    set_turbine_op_params(tsr=tsr, tsr_amp=tsr_amp, tsr_phase=tsr_phase)
    set_dt(dt=dt, tsr=tsr)
    if mesh:
        # Create blockMeshDict
        set_blockmesh_resolution(nx=nx)
        foampy.run("blockMesh", tee=tee)
    # Copy over initial conditions
    subprocess.call("cp -rf 0.orig 0 > /dev/null 2>&1", shell=True)
    if parallel and not glob.glob("processor*"):
        foampy.run("decomposePar", tee=tee)
        subprocess.call("for PROC in processor*; do cp -rf 0.orig/* $PROC/0; "
                        " done", shell=True)
    if mesh:
        foampy.run("snappyHexMesh", args="-overwrite", tee=tee,
                   parallel=parallel)
        foampy.run("topoSet", parallel=parallel, tee=tee)
        if parallel:
            foampy.run("reconstructParMesh", args="-constant -time 0", tee=tee)
    foampy.run("pimpleFoam", parallel=parallel, tee=tee, overwrite=overwrite)
    if parallel and reconstruct:
        foampy.run("reconstructPar", tee=tee, overwrite=overwrite)
    if post:
        post_process(overwrite=overwrite)

if __name__ == "__main__":
    parser = argparse.ArgumentParser(description="Run UNH-RVAT ALM case")
    parser.add_argument("--tsr", "-t", default=1.9, type=float, help="TSR")
    parser.add_argument("--nx", "-x", default=48, type=int, help="Number of "
                        "cells in the x-direction for the base mesh")
    parser.add_argument("--dt", default=0.005, type=float, help="Time step")
    parser.add_argument("--leave-mesh", "-l", default=False,
                        action="store_true", help="Leave existing mesh")
    parser.add_argument("--post", "-P", default=False, action="store_true",
                        help="Run post-processing (done by default at end of "
                             " run)")
    parser.add_argument("--param-sweep", "-p",
                        help="Run multiple simulations varying a parameter",
                        choices=["tsr", "nx", "dt", "talpha"])
    parser.add_argument("--start", default=0.4, type=float)
    parser.add_argument("--stop", default=3.5, type=float)
    parser.add_argument("--step", default=0.5, type=float)
    parser.add_argument("--serial", "-S", default=False, action="store_true")
    parser.add_argument("--append", "-a", default=False, action="store_true")
    parser.add_argument("--tee", "-T", default=False, action="store_true",
                        help="Print log files to terminal while running")
    args = parser.parse_args()

    if args.param_sweep:
        param_sweep(args.param_sweep, args.start, args.stop, args.step,
                    append=args.append, parallel=not args.serial,
                    tee=args.tee, nx=args.nx, dt=args.dt, tsr=args.tsr)
    elif not args.post:
        run(tsr=args.tsr, nx=args.nx, dt=args.dt, parallel=not args.serial,
            tee=args.tee, mesh=not args.leave_mesh, overwrite=args.leave_mesh)
    if args.post:
        post_process()
<commit_msg>Do not run tsr sweep past 3.1 since Re is too high<commit_after>#!/usr/bin/env python
"""Script to run UNH-RVAT ALM case."""

import argparse
import os
import subprocess
from subprocess import call, check_output
import numpy as np
import pandas as pd
import glob
import foampy
from foampy.dictionaries import replace_value
import shutil
from pyurtf import processing as pr

def get_mesh_dims():
    """Get mesh dimensions by grepping `blockMeshDict`."""
    raw = check_output("grep blocks system/blockMeshDict -A3",
                       shell=True).decode().split("\n")[3]
    raw = raw.replace("(", "").replace(")", "").split()
    return {"nx": int(raw[0]), "ny": int(raw[1]), "nz": int(raw[2])}

def get_dt():
    """Read ``deltaT`` from ``controlDict``."""
    return foampy.dictionaries.read_single_line_value("controlDict",
                                                      keyword="deltaT")

def log_perf(param="tsr", append=True):
    """Log performance to file."""
    if not os.path.isdir("processed"):
        os.mkdir("processed")
    fpath = "processed/{}_sweep.csv".format(param)
    if append and os.path.isfile(fpath):
        df = pd.read_csv(fpath)
    else:
        df = pd.DataFrame(columns=["nx", "ny", "nz", "dt", "tsr", "cp", "cd"])
    d = pr.calc_perf(t1=3.0)
    d.update(get_mesh_dims())
    d["dt"] = get_dt()
    df = df.append(d, ignore_index=True)
    df.to_csv(fpath, index=False)

def set_blockmesh_resolution(nx=48, ny=None, nz=None):
    """Set mesh resolution in ``blockMeshDict``.

    If only ``nx`` is provided, the default resolutions for other dimensions are
    scaled proportionally.
    """
    defaults = {"nx": 48, "ny": 48, "nz": 32}
    if ny is None:
        ny = nx
    if nz is None:
        nz = int(nx*defaults["nz"]/defaults["nx"])
    print("Setting blockMesh resolution to ({} {} {})".format(nx, ny, nz))
    foampy.fill_template("system/blockMeshDict.template", nx=nx, ny=ny, nz=nz)

def set_dt(dt=0.005, tsr=None, tsr_0=1.9):
    """Set ``deltaT`` in ``controlDict``. Will scale proportionally if ``tsr``
    and ``tsr_0`` are supplied, such that steps-per-rev is consistent with
    ``tsr_0``.
    """
    if tsr is not None:
        dt = dt*tsr_0/tsr
        print("Setting deltaT = dt*tsr_0/tsr = {:.3f}".format(dt))
    dt = str(dt)
    foampy.fill_template("system/controlDict.template", dt=dt)

def set_talpha(val=6.25):
    """Set `TAlpha` value for the Leishman--Beddoes SGC dynamic stall model."""
    foampy.dictionaries.replace_value("system/fvOptions", "TAlpha", str(val))

def gen_sets_file():
    """Generate ``sets`` file for post-processing."""
    # Input parameters
    setformat = "raw"
    interpscheme = "cellPoint"
    fields = ["UMean", "UPrime2Mean", "kMean"]
    x = 1.0
    ymax = 1.5
    ymin = -1.5
    ny = 51
    zmax = 1.125
    zmin = -1.125
    nz = 19
    z_array = np.linspace(zmin, zmax, nz)
    txt = "\ntype sets;\n"
    txt +='libs ("libsampling.so");\n'
    txt += "setFormat " + setformat + ";\n"
    txt += "interpolationScheme " + interpscheme + ";\n\n"
    txt += "sets \n ( \n"
    for z in z_array:
        # Fix interpolation issues if directly on a face
        if z == 0.0:
            z += 1e-5
        txt += "    " + "profile_" + str(z) + "\n"
        txt += "    { \n"
        txt += "        type        uniform; \n"
        txt += "        axis        y; \n"
        txt += "        start       (" + str(x) + " " + str(ymin) + " " \
            + str(z) + ");\n"
        txt += "        end         (" + str(x) + " " + str(ymax) + " " \
            + str(z) + ");\n"
        txt += "        nPoints     " + str(ny) + ";\n    }\n\n"
    txt += ");\n\n"
    txt += "fields \n(\n"
    for field in fields:
        txt += "    " + field + "\n"
    txt += "); \n\n"
    txt += "//\
     *********************************************************************** //\
     \n"
    with open("system/sets", "w") as f:
        f.write(txt)

def post_process(parallel=False, tee=False, overwrite=True):
    """Execute all post-processing."""
    gen_sets_file()
    foampy.run("postProcess", args="-func -vorticity", parallel=parallel,
               logname="log.vorticity", tee=tee, overwrite=overwrite)
    foampy.run("postProcess", args="-dict system/controlDict.recovery "
               " -latestTime", parallel=parallel, logname="log.recovery",
               tee=tee, overwrite=overwrite)
    foampy.run("funkyDoCalc", args="system/funkyDoCalcDict -latestTime",
               parallel=parallel, tee=tee, overwrite=overwrite)

def param_sweep(param="tsr", start=None, stop=None, step=None, dtype=float,
                append=False, parallel=True, tee=False, **kwargs):
    """Run multiple simulations, varying ``quantity``.

    ``step`` is not included.
    """
    print("Running {} sweep".format(param))
    fpath = "processed/{}_sweep.csv".format(param)
    if not append and os.path.isfile(fpath):
        os.remove(fpath)
    if param == "nx":
        dtype = int
    param_list = np.arange(start, stop, step, dtype=dtype)
    for p in param_list:
        print("Running with {} = {}".format(param, p))
        if param == "talpha":
            set_talpha(p)
        if p == param_list[0] or param == "nx":
            foampy.clean(remove_zero=True)
            mesh = True
        else:
            mesh = False
        # Update kwargs for this value
        kwargs.update({param: p})
        run(parallel=parallel, tee=tee, mesh=mesh, reconstruct=False,
            post=False, **kwargs)
        os.rename("log.pimpleFoam", "log.pimpleFoam." + str(p))
        log_perf(param=param, append=True)
        foampy.clean(leave_mesh=True, remove_zero=True)
    # Set parameters back to defaults
    if param == "talpha":
        set_talpha()

def set_turbine_op_params(tsr=1.9, tsr_amp=0.0, tsr_phase=1.4):
    """Write file defining turbine operating parameters.

    ``tsr_phase`` is in radians.
    """
    txt="""
tipSpeedRatio   {tsr};
tsrAmplitude    {tsr_amp};
tsrPhase        {tsr_phase};
    """.format(tsr=tsr, tsr_amp=tsr_amp, tsr_phase=tsr_phase)
    with open("system/turbineOperatingParams", "w") as f:
        f.write(txt)

def run(tsr=1.9, tsr_amp=0.0, tsr_phase=1.4, nx=48, mesh=True, parallel=False,
        dt=0.005, tee=False, reconstruct=True, overwrite=False, post=True):
    """Run simulation once."""
    print("Setting TSR to", tsr)
    set_turbine_op_params(tsr=tsr, tsr_amp=tsr_amp, tsr_phase=tsr_phase)
    set_dt(dt=dt, tsr=tsr)
    if mesh:
        # Create blockMeshDict
        set_blockmesh_resolution(nx=nx)
        foampy.run("blockMesh", tee=tee)
    # Copy over initial conditions
    subprocess.call("cp -rf 0.orig 0 > /dev/null 2>&1", shell=True)
    if parallel and not glob.glob("processor*"):
        foampy.run("decomposePar", tee=tee)
        subprocess.call("for PROC in processor*; do cp -rf 0.orig/* $PROC/0; "
                        " done", shell=True)
    if mesh:
        foampy.run("snappyHexMesh", args="-overwrite", tee=tee,
                   parallel=parallel)
        foampy.run("topoSet", parallel=parallel, tee=tee)
        if parallel:
            foampy.run("reconstructParMesh", args="-constant -time 0", tee=tee)
    foampy.run("pimpleFoam", parallel=parallel, tee=tee, overwrite=overwrite)
    if parallel and reconstruct:
        foampy.run("reconstructPar", tee=tee, overwrite=overwrite)
    if post:
        post_process(overwrite=overwrite)

if __name__ == "__main__":
    parser = argparse.ArgumentParser(description="Run UNH-RVAT ALM case")
    parser.add_argument("--tsr", "-t", default=1.9, type=float, help="TSR")
    parser.add_argument("--nx", "-x", default=48, type=int, help="Number of "
                        "cells in the x-direction for the base mesh")
    parser.add_argument("--dt", default=0.005, type=float, help="Time step")
    parser.add_argument("--leave-mesh", "-l", default=False,
                        action="store_true", help="Leave existing mesh")
    parser.add_argument("--post", "-P", default=False, action="store_true",
                        help="Run post-processing (done by default at end of "
                             " run)")
    parser.add_argument("--param-sweep", "-p",
                        help="Run multiple simulations varying a parameter",
                        choices=["tsr", "nx", "dt", "talpha"])
    parser.add_argument("--start", default=0.4, type=float)
    parser.add_argument("--stop", default=3.1, type=float)
    parser.add_argument("--step", default=0.5, type=float)
    parser.add_argument("--serial", "-S", default=False, action="store_true")
    parser.add_argument("--append", "-a", default=False, action="store_true")
    parser.add_argument("--tee", "-T", default=False, action="store_true",
                        help="Print log files to terminal while running")
    args = parser.parse_args()

    if args.param_sweep:
        param_sweep(args.param_sweep, args.start, args.stop, args.step,
                    append=args.append, parallel=not args.serial,
                    tee=args.tee, nx=args.nx, dt=args.dt, tsr=args.tsr)
    elif not args.post:
        run(tsr=args.tsr, nx=args.nx, dt=args.dt, parallel=not args.serial,
            tee=args.tee, mesh=not args.leave_mesh, overwrite=args.leave_mesh)
    if args.post:
        post_process()
<|endoftext|>


<commit_before>#!/usr/bin/env python3
"""Wrapper calling stagpy module entry point"""

from stagpy.stagpy import main

if __name__ == '__main__':
    main()
<commit_msg>Remove run.py<commit_after><|endoftext|>


<commit_before>import curses
import argparse
from generator import MineGenerator
from utils import show_colors

def main(screen, args):
    curses.start_color()
    curses.use_default_colors()
    curses.curs_set(0)

    screen.nodelay(1)

    height, width = screen.getmaxyx()
    width = args.width or width * 3
    height = args.depth or height * 2

    for i in range(0, curses.COLORS):
        curses.init_pair(i + 1, i, 16)
    pad = curses.newpad(height,width)
    g = MineGenerator(width - 1, height - 1, args)
    m = g.build_mine(screen, pad)

    if args.lights:
        m.dark = False

    while True:
        m.action()

if __name__ == '__main__':
    parser = argparse.ArgumentParser()
    parser.add_argument("-c", "--show-colors", help="shows a grid of available colors", action="store_true")
    parser.add_argument("-l", "--lights", help="shows all grid cells, not just explored ones", action="store_true")
    parser.add_argument("--miners", help="how many miners to add", type=int)
    parser.add_argument("--treasures", help="how many treasures to add", type=int)
    parser.add_argument("--kings", help="how many dwarf kings to add", type=int, default=1)
    parser.add_argument("--wizards", help="how many wizards to add", type=int)
    parser.add_argument("--snakes", help="how many snakes to add", type=int)
    parser.add_argument("--tribes", help="how many opposing dwarf tribes", type=int, default=2)
    parser.add_argument("--saboteurs", help="how many saboteurs to add", type=int)
    parser.add_argument("--width", help="mine width", type=int)
    parser.add_argument("--depth", help="mine depth", type=int)
    args = parser.parse_args()
    if args.show_colors:
        curses.wrapper(show_colors)
    else:
        curses.wrapper(main, args)
<commit_msg>Default to visible screen size only<commit_after>import curses
import argparse
from generator import MineGenerator
from utils import show_colors

def main(screen, args):
    curses.start_color()
    curses.use_default_colors()
    curses.curs_set(0)

    screen.nodelay(1)

    height, width = screen.getmaxyx()
    width = args.width or width
    height = args.depth or height

    for i in range(0, curses.COLORS):
        curses.init_pair(i + 1, i, 16)
    pad = curses.newpad(height,width)
    g = MineGenerator(width - 1, height - 1, args)
    m = g.build_mine(screen, pad)

    if args.lights:
        m.dark = False

    while True:
        m.action()

if __name__ == '__main__':
    parser = argparse.ArgumentParser()
    parser.add_argument("-c", "--show-colors", help="shows a grid of available colors", action="store_true")
    parser.add_argument("-l", "--lights", help="shows all grid cells, not just explored ones", action="store_true")
    parser.add_argument("--miners", help="how many miners to add", type=int)
    parser.add_argument("--treasures", help="how many treasures to add", type=int)
    parser.add_argument("--kings", help="how many dwarf kings to add", type=int, default=1)
    parser.add_argument("--wizards", help="how many wizards to add", type=int)
    parser.add_argument("--snakes", help="how many snakes to add", type=int)
    parser.add_argument("--tribes", help="how many opposing dwarf tribes", type=int, default=2)
    parser.add_argument("--saboteurs", help="how many saboteurs to add", type=int)
    parser.add_argument("--width", help="mine width", type=int)
    parser.add_argument("--depth", help="mine depth", type=int)
    args = parser.parse_args()
    if args.show_colors:
        curses.wrapper(show_colors)
    else:
        curses.wrapper(main, args)
<|endoftext|>


<commit_before>#!/usr/bin/env python
# -*- encoding: utf-8 -*-
# vim: set et sw=4 ts=4 sts=4 ff=unix fenc=utf8:
# Author: Binux<i@binux.me>
#         http://binux.me
# Created on 2014-03-05 00:11:49

import os
import sys
import time
import logging
import logging.config

import click
from pyspider.database import connect_database
from pyspider.libs.utils import run_in_thread, run_in_subprocess, Get, ObjectDict

def connect_db(ctx, param, value):
    if value is None:
        return
    return Get(lambda : connect_database(value))

def connect_rpc(ctx, param, value):
    if value is None:
        return
    import xmlrpclib
    return xmlrpclib.ServerProxy(value)

@click.group(invoke_without_command=True)
@click.option('--debug', envvar='DEBUG', is_flag=True, help='debug mode')
@click.option('--queue-maxsize', envvar='QUEUE_MAXSIZE', default=100,
        help='maxsize of queue')
@click.option('--taskdb', envvar='TASKDB', callback=connect_db,
        help='database url for taskdb, default: sqlite')
@click.option('--projectdb', envvar='PROJECTDB', callback=connect_db,
        help='database url for projectdb, default: sqlite')
@click.option('--resultdb', envvar='RESULTDB', callback=connect_db,
        help='database url for resultdb, default: sqlite')
@click.option('--amqp-url', help='amqp url for rabbitmq, default: built-in Queue')
@click.option('--phantomjs-proxy', help="phantomjs proxy ip:port")
@click.option('-c', '--config', type=click.File('r'),
        help='a json file with default values for subcommands. {"webui": {"port":5001}}')
@click.pass_context
def cli(ctx, **kwargs):
    """
    A powerful spider system in python.
    """
    logging.config.fileConfig("logging.conf")

    # get db from env
    for db in ('taskdb', 'projectdb', 'resultdb'):
        if kwargs[db] is not None:
            continue
        if os.environ.get('MYSQL_NAME'):
            kwargs[db] = Get(lambda db=db: connect_database('mysql+%s://%s:%s/%s' % (
                db, os.environ['MYSQL_PORT_3306_TCP_ADDR'],
                os.environ['MYSQL_PORT_3306_TCP_PORT'], db)))
        elif os.environ.get('MONGODB_NAME'):
            kwargs[db] = Get(lambda db=db: connect_database('mongodb+%s://%s:%s/%s' % (
                db, os.environ['MONGODB_PORT_27017_TCP_ADDR'],
                os.environ['MYSQL_PORT_3306_TCP_PORT'], db)))
        else:
            kwargs[db] = Get(lambda db=db: connect_database('sqlite+%s:///data/%s.db' % (
                db, db[:-2])))

    # queue
    if kwargs.get('amqp_url'):
        from pyspider.libs.rabbitmq import Queue
        for name in ('newtask_queue', 'status_queue', 'scheduler2fetcher',
                'fetcher2processor', 'processor2result'):
            kwargs[name] = Get(lambda name=name: Queue(name, amqp_url=kwargs['amqp_url'],
                maxsize=kwargs['queue_maxsize']))
    elif os.environ.get('RABBITMQ_NAME'):
        from pyspider.libs.rabbitmq import Queue
        amqp_url = ("amqp://guest:guest@%(RABBITMQ_PORT_5672_TCP_ADDR)s"
                    ":%(RABBITMQ_PORT_5672_TCP_PORT)s/%%2F" % os.environ)
        for name in ('newtask_queue', 'status_queue', 'scheduler2fetcher',
                'fetcher2processor', 'processor2result'):
            kwargs[name] = Get(lambda name=name: Queue(name, amqp_url=amqp_url,
                maxsize=kwargs['queue_maxsize']))
    else:
        from multiprocessing import Queue
        for name in ('newtask_queue', 'status_queue', 'scheduler2fetcher',
                'fetcher2processor', 'processor2result'):
            kwargs[name] = Queue(kwargs['queue_maxsize'])

    # phantomjs-proxy
    if kwargs.get('phantomjs_proxy'):
        pass
    elif os.environ.get('PHANTOMJS_NAME'):
        kwargs['phantomjs_proxy'] = os.environ['PHANTOMJS_PORT'][len('tcp://'):]

    # config
    if kwargs['config']:
        import json
        kwargs['config'] = json.load(kwargs['config'])
        ctx.default_map = kwargs['config']
    else:
        kwargs['config'] = {}

    ctx.obj['instances'] = []
    ctx.obj.update(kwargs)

    if ctx.invoked_subcommand is None:
        ctx.invoke(all)

@cli.command()
@click.option('--xmlrpc/--no-xmlrpc', default=True)
@click.option('--xmlrpc-host', default='0.0.0.0')
@click.option('--xmlrpc-port', envvar='SCHEDULER_XMLRPC_PORT', default=23333)
@click.option('--inqueue-limit', default=0,
        help='size limit of task queue for each project, '
        'tasks will been ignored when overflow')
@click.option('--delete-time', default=24*60*60,
        help='delete time before marked as delete')
@click.option('--active-tasks', default=100, help='active log size')
@click.pass_context
def scheduler(ctx, xmlrpc, xmlrpc_host, xmlrpc_port,
        inqueue_limit, delete_time, active_tasks):
    g = ctx.obj
    from pyspider.scheduler import Scheduler
    scheduler = Scheduler(taskdb=g.taskdb, projectdb=g.projectdb, resultdb=g.resultdb,
            newtask_queue=g.newtask_queue, status_queue=g.status_queue,
            out_queue=g.scheduler2fetcher)
    scheduler.INQUEUE_LIMIT = inqueue_limit
    scheduler.DELETE_TIME = delete_time
    scheduler.ACTIVE_TASKS = active_tasks
    g.instances.append(scheduler)

    if xmlrpc:
        run_in_thread(scheduler.xmlrpc_run, port=xmlrpc_port, bind=xmlrpc_host)
    scheduler.run()

@cli.command()
@click.option('--xmlrpc/--no-xmlrpc', default=False)
@click.option('--xmlrpc-host', default='0.0.0.0')
@click.option('--xmlrpc-port', envvar='FETCHER_XMLRPC_PORT', default=24444)
@click.pass_context
def fetcher(ctx, xmlrpc, xmlrpc_host, xmlrpc_port):
    g = ctx.obj
    from pyspider.fetcher.tornado_fetcher import Fetcher
    fetcher = Fetcher(inqueue=g.scheduler2fetcher, outqueue=g.fetcher2processor)
    fetcher.phantomjs_proxy = g.phantomjs_proxy
    g.instances.append(fetcher)

    if xmlrpc:
        run_in_thread(fetcher.xmlrpc_run, port=xmlrpc_port, bind=xmlrpc_host)
    fetcher.run()

@cli.command()
@click.pass_context
def processor(ctx):
    g = ctx.obj
    from pyspider.processor import Processor
    processor = Processor(projectdb=g.projectdb,
            inqueue=g.fetcher2processor, status_queue=g.status_queue,
            newtask_queue=g.newtask_queue, result_queue=g.processor2result)
    g.instances.append(processor)

    processor.run()

@cli.command()
@click.pass_context
def result_worker(ctx):
    g = ctx.obj
    from pyspider.result import ResultWorker
    result_worker = ResultWorker(resultdb=g.resultdb, inqueue=g.processor2result)
    g.instances.append(result_worker)

    result_worker.run()

@cli.command()
@click.option('--host', default='0.0.0.0', envvar='WEBUI_HOST',
        help='webui bind to host')
@click.option('--port', default=5000, envvar='WEBUI_PORT',
        help='webui bind to host')
@click.option('--cdn', default='//cdnjscn.b0.upaiyun.com/libs/',
        help='js/css cdn server')
@click.option('--scheduler-rpc', callback=connect_rpc, help='xmlrpc path of scheduler')
@click.option('--fetcher-rpc', callback=connect_rpc, help='xmlrpc path of fetcher')
@click.option('--max-rate', type=float, help='max rate for each project')
@click.option('--max-burst', type=float, help='max burst for each project')
@click.option('--username', envvar='WEBUI_USERNAME',
        help='username of lock -ed projects')
@click.option('--password', envvar='WEBUI_PASSWORD',
        help='password of lock -ed projects')
@click.pass_context
def webui(ctx, host, port, cdn, scheduler_rpc, fetcher_rpc,
        max_rate, max_burst, username, password):
    g = ctx.obj
    from pyspider.webui.app import app
    app.config['taskdb'] = g.taskdb
    app.config['projectdb'] = g.projectdb
    app.config['resultdb'] = g.resultdb
    app.config['cdn'] = cdn

    if max_rate:
        app.config['max_rate'] = max_rate
    if max_burst:
        app.config['max_burst'] = max_burst
    if username:
        app.config['webui_username'] = username
    if password:
        app.config['webui_password'] = password

    # fetcher rpc
    if isinstance(fetcher_rpc, basestring):
        fetcher_rpc = connect_rpc(ctx, None, fetcher_rpc)
    if fetcher_rpc is None:
        from pyspider.fetcher.tornado_fetcher import Fetcher
        fetcher = Fetcher(inqueue=None, outqueue=None, async=False)
        fetcher.phantomjs_proxy = g.phantomjs_proxy
        app.config['fetch'] = lambda x: fetcher.fetch(x)[1]
    else:
        import umsgpack
        app.config['fetch'] = lambda x: umsgpack.unpackb(fetcher_rpc.fetch(x).data)

    if isinstance(scheduler_rpc, basestring):
        scheduler_rpc = connect_rpc(ctx, None, scheduler_rpc)
    if scheduler_rpc is None and os.environ.get('SCHEDULER_NAME'):
        app.config['scheduler_rpc'] = connect_rpc(ctx, None, 'http://%s/' % (
            os.environ['SCHEDULER_PORT_23333_TCP'][len('tcp://'):]))
    else:
        app.config['scheduler_rpc'] = scheduler_rpc

    app.debug = g.debug
    app.run(host=host, port=port)

@cli.command()
@click.option('--fetcher-num', default=1, help='instance num of fetcher')
@click.option('--processor-num', default=1, help='instance num of processor')
@click.option('--result-worker-num', default=1,
        help='instance num of result worker')
@click.option('--run-in', default='subprocess', type=click.Choice(['subprocess', 'thread']),
        help='run each components in thread or subprocess. '
        'always using thread for windows.')
@click.pass_context
def all(ctx, fetcher_num, processor_num, result_worker_num, run_in):
    ctx.obj['debug'] = False
    g = ctx.obj

    if run_in == 'subprocess' and os.name != 'nt':
        run_in = run_in_subprocess
    else:
        run_in = run_in_thread

    threads = []

    # result worker
    result_worker_config = g.config.get('result_worker', {})
    for i in range(result_worker_num):
        threads.append(run_in(ctx.invoke, result_worker, **result_worker_config))

    # processor
    processor_config = g.config.get('processor', {})
    for i in range(processor_num):
        threads.append(run_in(ctx.invoke, processor, **processor_config))

    # fetcher
    fetcher_config = g.config.get('fetcher', {})
    fetcher_config.setdefault('xmlrpc_host', '127.0.0.1')
    for i in range(fetcher_num):
        threads.append(run_in(ctx.invoke, fetcher, **fetcher_config))

    # scheduler
    scheduler_config = g.config.get('scheduler', {})
    scheduler_config.setdefault('xmlrpc_host', '127.0.0.1')
    threads.append(run_in(ctx.invoke, scheduler, **scheduler_config))

    # running webui in main thread to make it exitable
    webui_config = g.config.get('webui', {})
    webui_config.setdefault('scheduler_rpc', 'http://localhost:%s/'\
            % g.config.get('scheduler', {}).get('xmlrpc_port', 23333))
    ctx.invoke(webui, **webui_config)

    for each in g.instances:
        each.quit()

    for each in threads:
        each.join()

if __name__ == '__main__':
    cli(obj=ObjectDict(), default_map={})
<commit_msg>fix run.py mongodb port is using mysql's bug, fix #38<commit_after>#!/usr/bin/env python
# -*- encoding: utf-8 -*-
# vim: set et sw=4 ts=4 sts=4 ff=unix fenc=utf8:
# Author: Binux<i@binux.me>
#         http://binux.me
# Created on 2014-03-05 00:11:49

import os
import sys
import time
import logging
import logging.config

import click
from pyspider.database import connect_database
from pyspider.libs.utils import run_in_thread, run_in_subprocess, Get, ObjectDict

def connect_db(ctx, param, value):
    if value is None:
        return
    return Get(lambda : connect_database(value))

def connect_rpc(ctx, param, value):
    if value is None:
        return
    import xmlrpclib
    return xmlrpclib.ServerProxy(value)

@click.group(invoke_without_command=True)
@click.option('--debug', envvar='DEBUG', is_flag=True, help='debug mode')
@click.option('--queue-maxsize', envvar='QUEUE_MAXSIZE', default=100,
        help='maxsize of queue')
@click.option('--taskdb', envvar='TASKDB', callback=connect_db,
        help='database url for taskdb, default: sqlite')
@click.option('--projectdb', envvar='PROJECTDB', callback=connect_db,
        help='database url for projectdb, default: sqlite')
@click.option('--resultdb', envvar='RESULTDB', callback=connect_db,
        help='database url for resultdb, default: sqlite')
@click.option('--amqp-url', help='amqp url for rabbitmq, default: built-in Queue')
@click.option('--phantomjs-proxy', help="phantomjs proxy ip:port")
@click.option('-c', '--config', type=click.File('r'),
        help='a json file with default values for subcommands. {"webui": {"port":5001}}')
@click.pass_context
def cli(ctx, **kwargs):
    """
    A powerful spider system in python.
    """
    logging.config.fileConfig("logging.conf")

    # get db from env
    for db in ('taskdb', 'projectdb', 'resultdb'):
        if kwargs[db] is not None:
            continue
        if os.environ.get('MYSQL_NAME'):
            kwargs[db] = Get(lambda db=db: connect_database('mysql+%s://%s:%s/%s' % (
                db, os.environ['MYSQL_PORT_3306_TCP_ADDR'],
                os.environ['MYSQL_PORT_3306_TCP_PORT'], db)))
        elif os.environ.get('MONGODB_NAME'):
            kwargs[db] = Get(lambda db=db: connect_database('mongodb+%s://%s:%s/%s' % (
                db, os.environ['MONGODB_PORT_27017_TCP_ADDR'],
                os.environ['MONGODB_PORT_27017_TCP_PORT'], db)))
        else:
            kwargs[db] = Get(lambda db=db: connect_database('sqlite+%s:///data/%s.db' % (
                db, db[:-2])))

    # queue
    if kwargs.get('amqp_url'):
        from pyspider.libs.rabbitmq import Queue
        for name in ('newtask_queue', 'status_queue', 'scheduler2fetcher',
                'fetcher2processor', 'processor2result'):
            kwargs[name] = Get(lambda name=name: Queue(name, amqp_url=kwargs['amqp_url'],
                maxsize=kwargs['queue_maxsize']))
    elif os.environ.get('RABBITMQ_NAME'):
        from pyspider.libs.rabbitmq import Queue
        amqp_url = ("amqp://guest:guest@%(RABBITMQ_PORT_5672_TCP_ADDR)s"
                    ":%(RABBITMQ_PORT_5672_TCP_PORT)s/%%2F" % os.environ)
        for name in ('newtask_queue', 'status_queue', 'scheduler2fetcher',
                'fetcher2processor', 'processor2result'):
            kwargs[name] = Get(lambda name=name: Queue(name, amqp_url=amqp_url,
                maxsize=kwargs['queue_maxsize']))
    else:
        from multiprocessing import Queue
        for name in ('newtask_queue', 'status_queue', 'scheduler2fetcher',
                'fetcher2processor', 'processor2result'):
            kwargs[name] = Queue(kwargs['queue_maxsize'])

    # phantomjs-proxy
    if kwargs.get('phantomjs_proxy'):
        pass
    elif os.environ.get('PHANTOMJS_NAME'):
        kwargs['phantomjs_proxy'] = os.environ['PHANTOMJS_PORT'][len('tcp://'):]

    # config
    if kwargs['config']:
        import json
        kwargs['config'] = json.load(kwargs['config'])
        ctx.default_map = kwargs['config']
    else:
        kwargs['config'] = {}

    ctx.obj['instances'] = []
    ctx.obj.update(kwargs)

    if ctx.invoked_subcommand is None:
        ctx.invoke(all)

@cli.command()
@click.option('--xmlrpc/--no-xmlrpc', default=True)
@click.option('--xmlrpc-host', default='0.0.0.0')
@click.option('--xmlrpc-port', envvar='SCHEDULER_XMLRPC_PORT', default=23333)
@click.option('--inqueue-limit', default=0,
        help='size limit of task queue for each project, '
        'tasks will been ignored when overflow')
@click.option('--delete-time', default=24*60*60,
        help='delete time before marked as delete')
@click.option('--active-tasks', default=100, help='active log size')
@click.pass_context
def scheduler(ctx, xmlrpc, xmlrpc_host, xmlrpc_port,
        inqueue_limit, delete_time, active_tasks):
    g = ctx.obj
    from pyspider.scheduler import Scheduler
    scheduler = Scheduler(taskdb=g.taskdb, projectdb=g.projectdb, resultdb=g.resultdb,
            newtask_queue=g.newtask_queue, status_queue=g.status_queue,
            out_queue=g.scheduler2fetcher)
    scheduler.INQUEUE_LIMIT = inqueue_limit
    scheduler.DELETE_TIME = delete_time
    scheduler.ACTIVE_TASKS = active_tasks
    g.instances.append(scheduler)

    if xmlrpc:
        run_in_thread(scheduler.xmlrpc_run, port=xmlrpc_port, bind=xmlrpc_host)
    scheduler.run()

@cli.command()
@click.option('--xmlrpc/--no-xmlrpc', default=False)
@click.option('--xmlrpc-host', default='0.0.0.0')
@click.option('--xmlrpc-port', envvar='FETCHER_XMLRPC_PORT', default=24444)
@click.pass_context
def fetcher(ctx, xmlrpc, xmlrpc_host, xmlrpc_port):
    g = ctx.obj
    from pyspider.fetcher.tornado_fetcher import Fetcher
    fetcher = Fetcher(inqueue=g.scheduler2fetcher, outqueue=g.fetcher2processor)
    fetcher.phantomjs_proxy = g.phantomjs_proxy
    g.instances.append(fetcher)

    if xmlrpc:
        run_in_thread(fetcher.xmlrpc_run, port=xmlrpc_port, bind=xmlrpc_host)
    fetcher.run()

@cli.command()
@click.pass_context
def processor(ctx):
    g = ctx.obj
    from pyspider.processor import Processor
    processor = Processor(projectdb=g.projectdb,
            inqueue=g.fetcher2processor, status_queue=g.status_queue,
            newtask_queue=g.newtask_queue, result_queue=g.processor2result)
    g.instances.append(processor)

    processor.run()

@cli.command()
@click.pass_context
def result_worker(ctx):
    g = ctx.obj
    from pyspider.result import ResultWorker
    result_worker = ResultWorker(resultdb=g.resultdb, inqueue=g.processor2result)
    g.instances.append(result_worker)

    result_worker.run()

@cli.command()
@click.option('--host', default='0.0.0.0', envvar='WEBUI_HOST',
        help='webui bind to host')
@click.option('--port', default=5000, envvar='WEBUI_PORT',
        help='webui bind to host')
@click.option('--cdn', default='//cdnjscn.b0.upaiyun.com/libs/',
        help='js/css cdn server')
@click.option('--scheduler-rpc', callback=connect_rpc, help='xmlrpc path of scheduler')
@click.option('--fetcher-rpc', callback=connect_rpc, help='xmlrpc path of fetcher')
@click.option('--max-rate', type=float, help='max rate for each project')
@click.option('--max-burst', type=float, help='max burst for each project')
@click.option('--username', envvar='WEBUI_USERNAME',
        help='username of lock -ed projects')
@click.option('--password', envvar='WEBUI_PASSWORD',
        help='password of lock -ed projects')
@click.pass_context
def webui(ctx, host, port, cdn, scheduler_rpc, fetcher_rpc,
        max_rate, max_burst, username, password):
    g = ctx.obj
    from pyspider.webui.app import app
    app.config['taskdb'] = g.taskdb
    app.config['projectdb'] = g.projectdb
    app.config['resultdb'] = g.resultdb
    app.config['cdn'] = cdn

    if max_rate:
        app.config['max_rate'] = max_rate
    if max_burst:
        app.config['max_burst'] = max_burst
    if username:
        app.config['webui_username'] = username
    if password:
        app.config['webui_password'] = password

    # fetcher rpc
    if isinstance(fetcher_rpc, basestring):
        fetcher_rpc = connect_rpc(ctx, None, fetcher_rpc)
    if fetcher_rpc is None:
        from pyspider.fetcher.tornado_fetcher import Fetcher
        fetcher = Fetcher(inqueue=None, outqueue=None, async=False)
        fetcher.phantomjs_proxy = g.phantomjs_proxy
        app.config['fetch'] = lambda x: fetcher.fetch(x)[1]
    else:
        import umsgpack
        app.config['fetch'] = lambda x: umsgpack.unpackb(fetcher_rpc.fetch(x).data)

    if isinstance(scheduler_rpc, basestring):
        scheduler_rpc = connect_rpc(ctx, None, scheduler_rpc)
    if scheduler_rpc is None and os.environ.get('SCHEDULER_NAME'):
        app.config['scheduler_rpc'] = connect_rpc(ctx, None, 'http://%s/' % (
            os.environ['SCHEDULER_PORT_23333_TCP'][len('tcp://'):]))
    elif scheduler_rpc is None:
        app.config['scheduler_rpc'] = connect_rpc(ctx, None, 'http://localhost:23333/')
    else:
        app.config['scheduler_rpc'] = scheduler_rpc

    app.debug = g.debug
    app.run(host=host, port=port)

@cli.command()
@click.option('--fetcher-num', default=1, help='instance num of fetcher')
@click.option('--processor-num', default=1, help='instance num of processor')
@click.option('--result-worker-num', default=1,
        help='instance num of result worker')
@click.option('--run-in', default='subprocess', type=click.Choice(['subprocess', 'thread']),
        help='run each components in thread or subprocess. '
        'always using thread for windows.')
@click.pass_context
def all(ctx, fetcher_num, processor_num, result_worker_num, run_in):
    ctx.obj['debug'] = False
    g = ctx.obj

    if run_in == 'subprocess' and os.name != 'nt':
        run_in = run_in_subprocess
    else:
        run_in = run_in_thread

    threads = []

    # result worker
    result_worker_config = g.config.get('result_worker', {})
    for i in range(result_worker_num):
        threads.append(run_in(ctx.invoke, result_worker, **result_worker_config))

    # processor
    processor_config = g.config.get('processor', {})
    for i in range(processor_num):
        threads.append(run_in(ctx.invoke, processor, **processor_config))

    # fetcher
    fetcher_config = g.config.get('fetcher', {})
    fetcher_config.setdefault('xmlrpc_host', '127.0.0.1')
    for i in range(fetcher_num):
        threads.append(run_in(ctx.invoke, fetcher, **fetcher_config))

    # scheduler
    scheduler_config = g.config.get('scheduler', {})
    scheduler_config.setdefault('xmlrpc_host', '127.0.0.1')
    threads.append(run_in(ctx.invoke, scheduler, **scheduler_config))

    # running webui in main thread to make it exitable
    webui_config = g.config.get('webui', {})
    webui_config.setdefault('scheduler_rpc', 'http://localhost:%s/'\
            % g.config.get('scheduler', {}).get('xmlrpc_port', 23333))
    ctx.invoke(webui, **webui_config)

    for each in g.instances:
        each.quit()

    for each in threads:
        each.join()

if __name__ == '__main__':
    cli(obj=ObjectDict(), default_map={})
<|endoftext|>


<commit_before>from flask import Flask, request, jsonify
from postmail import MailSender

app = Flask(__name__)
mail_sender = MailSender()

@app.route('/')
def homepage():
    return "MailSender is online!"

@app.route('/mail', methods=['POST'])
def mail():
    mail_sender.send(**request.form.to_dict())
    try:
        return jsonify({'status': 'succeed', 'msg': ""})
    except Exception as ex:
        return jsonify({'status': 'failed', 'msg': str(ex)})

if __name__ == '__main__':
    app.run(host="0.0.0.0")
<commit_msg>Fix: put the sending code back into try block<commit_after>from flask import Flask, request, jsonify
from postmail import MailSender

app = Flask(__name__)
mail_sender = MailSender()

@app.route('/')
def homepage():
    return "MailSender is online!"

@app.route('/mail', methods=['POST'])
def mail():
    try:
        mail_sender.send(**request.form.to_dict())
        return jsonify({'status': 'succeed', 'msg': ""})
    except Exception as ex:
        return jsonify({'status': 'failed', 'msg': str(ex)})

if __name__ == '__main__':
    app.run(host="0.0.0.0")
<|endoftext|>


<commit_before>"""
    atlas.run.py
    ~~~~~~~~~~~
    The API launch script.
"""
import os
import sys
import json
import logging
from logging.handlers import WatchedFileHandler
import ssl

from collections import Counter
from datetime import datetime
from eve import Eve
from eve.auth import requires_auth
from flask import jsonify, make_response, abort, request

from atlas import callbacks
from atlas import commands
from atlas import tasks
from atlas import utilities
from atlas.config import (ATLAS_LOCATION, VERSION_NUMBER, SSL_KEY_FILE, SSL_CRT_FILE, LOG_LOCATION,
                          ENVIRONMENT, API_URLS)

if ATLAS_LOCATION not in sys.path:
    sys.path.append(ATLAS_LOCATION)

# Load the settings file using a robust path so it works when
# the script is imported from the test suite.
THIS_DIRECTORY = os.path.dirname(os.path.realpath(__file__))
SETTINGS_FILE = os.path.join(THIS_DIRECTORY, 'atlas/data_structure.py')

# Name our app (using 'import_name') so that we can easily create sub loggers.
# Use our HTTP Basic Auth class which checks against LDAP.
# Import the data structures and Eve settings.
app = Eve(import_name='atlas', auth=utilities.AtlasBasicAuth, settings=SETTINGS_FILE)
# TODO: Remove debug mode.
app.debug = True

# Enable logging to 'atlas.log' file.
LOG_HANDLER = WatchedFileHandler(LOG_LOCATION)
LOG_HANDLER.setFormatter(logging.Formatter('%(asctime)s | %(name)s | %(levelname)s | %(message)s'))

# The default log level is set to WARNING, so we have to explicitly set the logging level to Info.
app.logger.setLevel(logging.INFO)
if ENVIRONMENT == 'local':
    app.logger.setLevel(logging.DEBUG)
# Append the handler to the default application logger
app.logger.addHandler(LOG_HANDLER)

# We will use Flask to serve the Commands endpoint.
@app.route('/commands', methods=['GET'])
def get_commands():
    """Get a list of available commands."""
    return jsonify({'commands': commands.COMMANDS})

@app.route('/commands/<string:machine_name>', methods=['GET', 'POST'])
def get_command(machine_name):
    """
    Get a single command.
    :param machine_name: command to return a definition for.
    """
    command = [command for command in commands.COMMANDS if command['machine_name'] == machine_name]
    if not command:
        abort(404)
    else:
        command = command[0]['machine_name']
    if request.method == 'GET':
        return jsonify({'command': command})
    elif request.method == 'POST':
        # Loop through the commands list and grab the one we want
        app.logger.debug('Command | Execute | %s', command)
        if command == 'clear_php_cache':
            tasks.clear_php_cache.delay()
        elif command == 'import_code':
            # Grab payload
            payload = request.data
            if not payload.get('env'):
                abort(409, 'This command requires a payload containing a target `env`.')
            tasks.import_code.delay(payload['env'])
        elif command == 'rebalance_update_groups':
            tasks.rebalance_update_groups.delay()
        elif command == 'update_homepage_files':
            tasks.update_homepage_files.delay()
        elif command == 'update_settings_files':
            query = 'where={"type":"express"}&max_results=2000'
            sites = utilities.get_eve('sites', query)
            timestamp = datetime.now()
            count = 0
            total = sites['_meta']['total']
            for instance in sites['_items']:
                count += 1
                tasks.update_settings_file.delay(instance, timestamp, count, total)
                continue
            tasks.clear_php_cache.delay()
        elif command == 'heal_code':
            code_items = utilities.get_eve('code')
            for code in code_items['_items']:
                tasks.heal_code.delay(code)
                continue
        elif command == 'heal_instances':
            instance_query = 'where={"type":"express","f5only":false}&max_results=2000'
            instances = utilities.get_eve('sites', instance_query)
            for instance in instances['_items']:
                tasks.heal_instance.delay(instance)
                continue
        elif command == 'heal_instances_no_db':
            instance_query = 'where={"type":"express","f5only":false}&max_results=2000'
            instances = utilities.get_eve('sites', instance_query)
            for instance in instances['_items']:
                tasks.heal_instance.delay(instance, db=False)
                continue
        elif command == 'correct_nfs_file_permissions':
            instance_query = 'where={"type":"express","f5only":false}&max_results=2000'
            instances = utilities.get_eve('sites', instance_query)
            for instance in instances['_items']:
                tasks.correct_nfs_file_permissions.delay(instance)
                continue
        elif command == 'backup_all_instances':
            tasks.backup_instances_all.delay(backup_type='on_demand')
        return make_response('Command "{0}" has been initiated.'.format(command))

@app.route('/backup/import', methods=['POST'])
@requires_auth('backup')
def import_backup():
    """
    Import a backup to a new instance.
    """
    backup_request = request.get_json()
    app.logger.debug('Backup | Import | %s', backup_request)
    # Get the backup and then the site records.
    if not (backup_request.get('env') and backup_request.get('id')):
        abort(409, 'Error: Missing env (local, dev, test, prod, o-dev, o-test, o-prod) and id.')
    elif not backup_request.get('env'):
        abort(409, 'Error: Missing env (local, dev, test, prod, o-dev, o-test, o-prod).')
    elif not backup_request.get('id'):
        abort(409, 'Error: Missing id.')
    elif backup_request['env'] not in ['local', 'dev', 'test', 'prod', 'o-dev', 'o-test', 'o-prod']:
        abort(
            409, 'Error: Invalid env choose from [local, dev, test, prod, o-dev, o-test, o-prod].')
    elif not backup_request.get('target_id'):
        abort(409, 'Error: Missing target_id.')
    """
    Taking this part out for now. This is required for cloning between env, not for the migration.
    """
    # backup_record = utilities.get_single_eve(
    #     'backup', backup_request['id'], env=backup_request['env'])
    # app.logger.debug('Backup | Import | Backup record - %s', backup_record)
    # site_record = utilities.get_single_eve(
    #     'sites', backup_record['site'], backup_record['site_version'], env=backup_request['env'])
    # app.logger.debug('Backup | Import | Site record - %s', site_record)

    # try:
    #     package_list = utilities.package_import(site_record, env=backup_request['env'])
    # except Exception as error:
    #     abort(500, error)

    tasks.import_backup.delay(
        env=backup_request['env'],
        backup_id=backup_request['id'],
        target_instance=backup_request['target_id'])

    return make_response('Attempting to import backup')

@app.route('/backup/<string:backup_id>/restore', methods=['POST'])
# TODO: Test what happens with 404 for backup_id
@requires_auth('backup')
def restore_backup(backup_id):
    """
    Restore a backup to a new instance.
    :param machine_name: id of backup to restore
    """
    app.logger.debug('Backup | Restore | %s', backup_id)
    backup_record = utilities.get_single_eve('backup', backup_id)
    original_instance = utilities.get_single_eve(
        'sites', backup_record['site'], backup_record['site_version'])
    # If packages are still active, add them; if not, find a current version
    # and add it; if none, error
    try:
        package_list = utilities.package_import(original_instance)
    except Exception as error:
        abort(409, error)

    tasks.backup_restore.delay(backup_record, original_instance, package_list)
    response = make_response('Restore started')
    return response

@app.route('/sites/<string:site_id>/backup', methods=['POST'])
# TODO: Test what happens with 404 for site_id
@requires_auth('backup')
def create_backup(site_id):
    """
    Create a backup of an instance.
    :param machine_name: id of instance to restore
    """
    app.logger.debug('Backup | Create | Site ID - %s', site_id)
    site = utilities.get_single_eve('sites', site_id)
    app.logger.debug('Backup | Create | Site Response - %s', site)
    tasks.backup_create.delay(site=site, backup_type='on_demand')
    response = make_response('Backup started')
    return response

@app.route('/sites/aggregation', methods=['GET'])
@app.route('/sites/agg', methods=['GET'])
@requires_auth('sites')
def sites_statistics():
    """
    Give some basic aggregations about site objects
    """
    app.logger.debug('Sites | Aggregations')
    express_result = utilities.get_eve('sites','where={"type":"express","f5only":false}&max_results=2000')
    legacy_result = utilities.get_eve('sites','where={"type":"legacy","f5only":false}&max_results=2000')
    app.logger.debug('Sites | Aggregations | Express Result - %s', express_result)
    app.logger.debug('Sites | Aggregations | Legacy Result - %s', legacy_result)
    # Express sites
    express_sites = express_result['_items']
    agg = {}
    count = Counter()
    group = Counter()
    ## Total by state
    for site in express_sites:
        count[site['status']] += 1
        group[site['update_group']] += 1
    agg['express'] = {
        'status': dict(count),
        'update_group': dict(group)
    }
    # Total
    agg['express']['status']['total'] = express_result['_meta']['total']
    # Legacy
    ## Total routes
    agg['legacy'] = {'total': legacy_result['_meta']['total']}

    response = make_response(jsonify(agg))
    return response

@app.route('/sites/<string:site_id>/heal', methods=['POST'])
# TODO: Test what happens with 404 for site_id
@requires_auth('sites')
def heal_instance(site_id):
    """
    Create a backup of an instance.
    :param machine_name: id of instance to restore
    """
    app.logger.debug('Site | Heal | Site ID - %s', site_id)
    instance = utilities.get_single_eve('sites', site_id)
    tasks.heal_instance.delay(instance)
    return make_response('Instance heal has been initiated.')

@app.route('/sites/<string:site_id>/file_permissions', methods=['POST'])
# TODO: Test what happens with 404 for site_id
@requires_auth('sites')
def correct_nfs_file_permissions(site_id):
    """
    Correct file permissions for an instance's NFS files.
    :param machine_name: id of instance to fix
    """
    app.logger.debug('Site | Correct file permissions | Site ID - %s', site_id)
    instance = utilities.get_single_eve('sites', site_id)
    tasks.correct_nfs_file_permissions.delay(instance)
    return make_response('Fixing permissions for NFS mounted files.')

@app.route('/drush/<string:drush_id>/execute', methods=['POST'])
# TODO: Test what happens with 404 for drush_id
@requires_auth('drush')
def execute_drush(drush_id):
    """
    Execute a drush command.
    :param machine_name: id of instance to restore
    """
    tasks.drush_prepare.delay(drush_id)
    response = make_response('Drush command started, check the logs for outcomes.')
    return response

@app.route('/f5')
@requires_auth('sites')
def f5():
    """
    Generate output for f5 config.
    """
    app.logger.debug('f5 data requested')
    query = 'where={"type":"legacy"}&max_results=2000'
    legacy_sites = utilities.get_eve('sites', query)
    app.logger.debug('f5 | Site Response - %s', legacy_sites)
    f5_list = []
    for site in legacy_sites['_items']:
        if 'path' in site:
            # In case a path was saved with a leading slash
            path = site["path"] if site["path"][0] == '/' else '/' + site["path"]
            f5_list.append('"{0}" := "legacy",'.format(path))
    response = make_response('\n'.join(f5_list))
    return response

# Specific callbacks.
# Use pre event hooks if there is a chance you want to abort.
# Use DB hooks if you want to modify data on the way in.

# Request event hooks.
app.on_pre_POST += callbacks.pre_post
app.on_pre_POST_sites += callbacks.pre_post_sites
app.on_pre_PATCH_sites += callbacks.pre_patch_sites
app.on_pre_PUT_sites += callbacks.pre_patch_sites
app.on_pre_DELETE_code += callbacks.pre_delete_code
app.on_pre_DELETE_sites += callbacks.pre_delete_sites
# Database event hooks.
app.on_insert_code += callbacks.on_insert_code
app.on_insert_sites += callbacks.on_insert_sites
app.on_inserted_sites += callbacks.on_inserted_sites
app.on_update_code += callbacks.on_update_code
app.on_update_sites += callbacks.on_update_sites
app.on_updated_code += callbacks.on_updated_code
app.on_delete_item_code += callbacks.on_delete_item_code
app.on_insert += callbacks.pre_insert
app.on_update += callbacks.pre_update
app.on_replace += callbacks.pre_replace
app.on_delete_item += callbacks.on_delete_item
app.on_deleted_sites += callbacks.on_deleted_item_sites

@app.errorhandler(409)
def custom409(error):
    response = jsonify({'message': error.description})
    response.status_code = 409
    return response

@app.route('/version')
def version():
    response = make_response(VERSION_NUMBER)
    return response

@app.route('/saml/create', methods=['GET', 'POST'])
@requires_auth('sites')
def saml_create():
    if request.method == 'POST':
        response = make_response("Started SAML database creation")
        tasks.saml_create.delay()
    else:
        response = make_response("Did you mean to POST?")
    return response

@app.route('/saml/delete', methods=['GET', 'POST'])
@requires_auth('sites')
def saml_delete():
    if request.method == 'POST':
        response = make_response("Started SAML database delete")
        tasks.saml_delete.delay()
    else:
        response = make_response("Did you mean to POST?")
    return response

# This config is only used when running via python, rather than mod_wsgi
if __name__ == '__main__':
    ctx = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
    ctx.load_cert_chain(SSL_CRT_FILE, SSL_KEY_FILE)

    # This goes last.
    app.run(host='0.0.0.0', ssl_context=ctx)
<commit_msg>Remove field that is no longer in use.<commit_after>"""
    atlas.run.py
    ~~~~~~~~~~~
    The API launch script.
"""
import os
import sys
import json
import logging
from logging.handlers import WatchedFileHandler
import ssl

from collections import Counter
from datetime import datetime
from eve import Eve
from eve.auth import requires_auth
from flask import jsonify, make_response, abort, request

from atlas import callbacks
from atlas import commands
from atlas import tasks
from atlas import utilities
from atlas.config import (ATLAS_LOCATION, VERSION_NUMBER, SSL_KEY_FILE, SSL_CRT_FILE, LOG_LOCATION,
                          ENVIRONMENT, API_URLS)

if ATLAS_LOCATION not in sys.path:
    sys.path.append(ATLAS_LOCATION)

# Load the settings file using a robust path so it works when
# the script is imported from the test suite.
THIS_DIRECTORY = os.path.dirname(os.path.realpath(__file__))
SETTINGS_FILE = os.path.join(THIS_DIRECTORY, 'atlas/data_structure.py')

# Name our app (using 'import_name') so that we can easily create sub loggers.
# Use our HTTP Basic Auth class which checks against LDAP.
# Import the data structures and Eve settings.
app = Eve(import_name='atlas', auth=utilities.AtlasBasicAuth, settings=SETTINGS_FILE)
# TODO: Remove debug mode.
app.debug = True

# Enable logging to 'atlas.log' file.
LOG_HANDLER = WatchedFileHandler(LOG_LOCATION)
LOG_HANDLER.setFormatter(logging.Formatter('%(asctime)s | %(name)s | %(levelname)s | %(message)s'))

# The default log level is set to WARNING, so we have to explicitly set the logging level to Info.
app.logger.setLevel(logging.INFO)
if ENVIRONMENT == 'local':
    app.logger.setLevel(logging.DEBUG)
# Append the handler to the default application logger
app.logger.addHandler(LOG_HANDLER)

# We will use Flask to serve the Commands endpoint.
@app.route('/commands', methods=['GET'])
def get_commands():
    """Get a list of available commands."""
    return jsonify({'commands': commands.COMMANDS})

@app.route('/commands/<string:machine_name>', methods=['GET', 'POST'])
def get_command(machine_name):
    """
    Get a single command.
    :param machine_name: command to return a definition for.
    """
    command = [command for command in commands.COMMANDS if command['machine_name'] == machine_name]
    if not command:
        abort(404)
    else:
        command = command[0]['machine_name']
    if request.method == 'GET':
        return jsonify({'command': command})
    elif request.method == 'POST':
        # Loop through the commands list and grab the one we want
        app.logger.debug('Command | Execute | %s', command)
        if command == 'clear_php_cache':
            tasks.clear_php_cache.delay()
        elif command == 'import_code':
            # Grab payload
            payload = request.data
            if not payload.get('env'):
                abort(409, 'This command requires a payload containing a target `env`.')
            tasks.import_code.delay(payload['env'])
        elif command == 'rebalance_update_groups':
            tasks.rebalance_update_groups.delay()
        elif command == 'update_homepage_files':
            tasks.update_homepage_files.delay()
        elif command == 'update_settings_files':
            query = 'where={"type":"express"}&max_results=2000'
            sites = utilities.get_eve('sites', query)
            timestamp = datetime.now()
            count = 0
            total = sites['_meta']['total']
            for instance in sites['_items']:
                count += 1
                tasks.update_settings_file.delay(instance, timestamp, count, total)
                continue
            tasks.clear_php_cache.delay()
        elif command == 'heal_code':
            code_items = utilities.get_eve('code')
            for code in code_items['_items']:
                tasks.heal_code.delay(code)
                continue
        elif command == 'heal_instances':
            instance_query = 'where={"type":"express"}&max_results=2000'
            instances = utilities.get_eve('sites', instance_query)
            for instance in instances['_items']:
                tasks.heal_instance.delay(instance)
                continue
        elif command == 'heal_instances_no_db':
            instance_query = 'where={"type":"express"}&max_results=2000'
            instances = utilities.get_eve('sites', instance_query)
            for instance in instances['_items']:
                tasks.heal_instance.delay(instance, db=False)
                continue
        elif command == 'correct_nfs_file_permissions':
            instance_query = 'where={"type":"express"}&max_results=2000'
            instances = utilities.get_eve('sites', instance_query)
            for instance in instances['_items']:
                tasks.correct_nfs_file_permissions.delay(instance)
                continue
        elif command == 'backup_all_instances':
            tasks.backup_instances_all.delay(backup_type='on_demand')
        return make_response('Command "{0}" has been initiated.'.format(command))

@app.route('/backup/import', methods=['POST'])
@requires_auth('backup')
def import_backup():
    """
    Import a backup to a new instance.
    """
    backup_request = request.get_json()
    app.logger.debug('Backup | Import | %s', backup_request)
    # Get the backup and then the site records.
    if not (backup_request.get('env') and backup_request.get('id')):
        abort(409, 'Error: Missing env (local, dev, test, prod, o-dev, o-test, o-prod) and id.')
    elif not backup_request.get('env'):
        abort(409, 'Error: Missing env (local, dev, test, prod, o-dev, o-test, o-prod).')
    elif not backup_request.get('id'):
        abort(409, 'Error: Missing id.')
    elif backup_request['env'] not in ['local', 'dev', 'test', 'prod', 'o-dev', 'o-test', 'o-prod']:
        abort(
            409, 'Error: Invalid env choose from [local, dev, test, prod, o-dev, o-test, o-prod].')
    elif not backup_request.get('target_id'):
        abort(409, 'Error: Missing target_id.')
    """
    Taking this part out for now. This is required for cloning between env, not for the migration.
    """
    # backup_record = utilities.get_single_eve(
    #     'backup', backup_request['id'], env=backup_request['env'])
    # app.logger.debug('Backup | Import | Backup record - %s', backup_record)
    # site_record = utilities.get_single_eve(
    #     'sites', backup_record['site'], backup_record['site_version'], env=backup_request['env'])
    # app.logger.debug('Backup | Import | Site record - %s', site_record)

    # try:
    #     package_list = utilities.package_import(site_record, env=backup_request['env'])
    # except Exception as error:
    #     abort(500, error)

    tasks.import_backup.delay(
        env=backup_request['env'],
        backup_id=backup_request['id'],
        target_instance=backup_request['target_id'])

    return make_response('Attempting to import backup')

@app.route('/backup/<string:backup_id>/restore', methods=['POST'])
# TODO: Test what happens with 404 for backup_id
@requires_auth('backup')
def restore_backup(backup_id):
    """
    Restore a backup to a new instance.
    :param machine_name: id of backup to restore
    """
    app.logger.debug('Backup | Restore | %s', backup_id)
    backup_record = utilities.get_single_eve('backup', backup_id)
    original_instance = utilities.get_single_eve(
        'sites', backup_record['site'], backup_record['site_version'])
    # If packages are still active, add them; if not, find a current version
    # and add it; if none, error
    try:
        package_list = utilities.package_import(original_instance)
    except Exception as error:
        abort(409, error)

    tasks.backup_restore.delay(backup_record, original_instance, package_list)
    response = make_response('Restore started')
    return response

@app.route('/sites/<string:site_id>/backup', methods=['POST'])
# TODO: Test what happens with 404 for site_id
@requires_auth('backup')
def create_backup(site_id):
    """
    Create a backup of an instance.
    :param machine_name: id of instance to restore
    """
    app.logger.debug('Backup | Create | Site ID - %s', site_id)
    site = utilities.get_single_eve('sites', site_id)
    app.logger.debug('Backup | Create | Site Response - %s', site)
    tasks.backup_create.delay(site=site, backup_type='on_demand')
    response = make_response('Backup started')
    return response

@app.route('/sites/aggregation', methods=['GET'])
@app.route('/sites/agg', methods=['GET'])
@requires_auth('sites')
def sites_statistics():
    """
    Give some basic aggregations about site objects
    """
    app.logger.debug('Sites | Aggregations')
    express_result = utilities.get_eve('sites','where={"type":"express","f5only":false}&max_results=2000')
    legacy_result = utilities.get_eve('sites','where={"type":"legacy","f5only":false}&max_results=2000')
    app.logger.debug('Sites | Aggregations | Express Result - %s', express_result)
    app.logger.debug('Sites | Aggregations | Legacy Result - %s', legacy_result)
    # Express sites
    express_sites = express_result['_items']
    agg = {}
    count = Counter()
    group = Counter()
    ## Total by state
    for site in express_sites:
        count[site['status']] += 1
        group[site['update_group']] += 1
    agg['express'] = {
        'status': dict(count),
        'update_group': dict(group)
    }
    # Total
    agg['express']['status']['total'] = express_result['_meta']['total']
    # Legacy
    ## Total routes
    agg['legacy'] = {'total': legacy_result['_meta']['total']}

    response = make_response(jsonify(agg))
    return response

@app.route('/sites/<string:site_id>/heal', methods=['POST'])
# TODO: Test what happens with 404 for site_id
@requires_auth('sites')
def heal_instance(site_id):
    """
    Create a backup of an instance.
    :param machine_name: id of instance to restore
    """
    app.logger.debug('Site | Heal | Site ID - %s', site_id)
    instance = utilities.get_single_eve('sites', site_id)
    tasks.heal_instance.delay(instance)
    return make_response('Instance heal has been initiated.')

@app.route('/sites/<string:site_id>/file_permissions', methods=['POST'])
# TODO: Test what happens with 404 for site_id
@requires_auth('sites')
def correct_nfs_file_permissions(site_id):
    """
    Correct file permissions for an instance's NFS files.
    :param machine_name: id of instance to fix
    """
    app.logger.debug('Site | Correct file permissions | Site ID - %s', site_id)
    instance = utilities.get_single_eve('sites', site_id)
    tasks.correct_nfs_file_permissions.delay(instance)
    return make_response('Fixing permissions for NFS mounted files.')

@app.route('/drush/<string:drush_id>/execute', methods=['POST'])
# TODO: Test what happens with 404 for drush_id
@requires_auth('drush')
def execute_drush(drush_id):
    """
    Execute a drush command.
    :param machine_name: id of instance to restore
    """
    tasks.drush_prepare.delay(drush_id)
    response = make_response('Drush command started, check the logs for outcomes.')
    return response

@app.route('/f5')
@requires_auth('sites')
def f5():
    """
    Generate output for f5 config.
    """
    app.logger.debug('f5 data requested')
    query = 'where={"type":"legacy"}&max_results=2000'
    legacy_sites = utilities.get_eve('sites', query)
    app.logger.debug('f5 | Site Response - %s', legacy_sites)
    f5_list = []
    for site in legacy_sites['_items']:
        if 'path' in site:
            # In case a path was saved with a leading slash
            path = site["path"] if site["path"][0] == '/' else '/' + site["path"]
            f5_list.append('"{0}" := "legacy",'.format(path))
    response = make_response('\n'.join(f5_list))
    return response

# Specific callbacks.
# Use pre event hooks if there is a chance you want to abort.
# Use DB hooks if you want to modify data on the way in.

# Request event hooks.
app.on_pre_POST += callbacks.pre_post
app.on_pre_POST_sites += callbacks.pre_post_sites
app.on_pre_PATCH_sites += callbacks.pre_patch_sites
app.on_pre_PUT_sites += callbacks.pre_patch_sites
app.on_pre_DELETE_code += callbacks.pre_delete_code
app.on_pre_DELETE_sites += callbacks.pre_delete_sites
# Database event hooks.
app.on_insert_code += callbacks.on_insert_code
app.on_insert_sites += callbacks.on_insert_sites
app.on_inserted_sites += callbacks.on_inserted_sites
app.on_update_code += callbacks.on_update_code
app.on_update_sites += callbacks.on_update_sites
app.on_updated_code += callbacks.on_updated_code
app.on_delete_item_code += callbacks.on_delete_item_code
app.on_insert += callbacks.pre_insert
app.on_update += callbacks.pre_update
app.on_replace += callbacks.pre_replace
app.on_delete_item += callbacks.on_delete_item
app.on_deleted_sites += callbacks.on_deleted_item_sites

@app.errorhandler(409)
def custom409(error):
    response = jsonify({'message': error.description})
    response.status_code = 409
    return response

@app.route('/version')
def version():
    response = make_response(VERSION_NUMBER)
    return response

@app.route('/saml/create', methods=['GET', 'POST'])
@requires_auth('sites')
def saml_create():
    if request.method == 'POST':
        response = make_response("Started SAML database creation")
        tasks.saml_create.delay()
    else:
        response = make_response("Did you mean to POST?")
    return response

@app.route('/saml/delete', methods=['GET', 'POST'])
@requires_auth('sites')
def saml_delete():
    if request.method == 'POST':
        response = make_response("Started SAML database delete")
        tasks.saml_delete.delay()
    else:
        response = make_response("Did you mean to POST?")
    return response

# This config is only used when running via python, rather than mod_wsgi
if __name__ == '__main__':
    ctx = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
    ctx.load_cert_chain(SSL_CRT_FILE, SSL_KEY_FILE)

    # This goes last.
    app.run(host='0.0.0.0', ssl_context=ctx)
<|endoftext|>