<commit_before>import os
import redis
import logging
import datetime
import json

from flask import Flask , send_from_directory, render_template, url_for, request, jsonify

#Redis instance from heroku
r = redis.from_url(os.environ.get("REDIS_URL"), db =None, decode_responses = True)

app = Flask(__name__)

FAIL_MESSAGE = 'la pulenta oe'

data = None
with open('classrooms.json') as json_data:
    data = json.dumps(json.load(json_data))

#"Landing page"
@app.route('/')
def webprint():
    global data
    return render_template('index.html')

#Returning JSON
@app.route('/salones')
def salones():
    return jsonify(data)

#Saves the string <strng> in the DB
@app.route('/db/<strng>')
def saveInDB(strng):
    r.lpush('db', '%s' % strng)
    return 'OK'

#Recalls the stored info in the DB
@app.route('/db/recall/test', methods = ['GET'])
def recallDB():
    try:
        return str(r.lrange('db', 0, -1))
    except Exception as error:
        return formatError(error)

#DevMode Handling---------------------------------------------

#Returns devMode.html [GET] #Checks login credentials [POST]
@app.route('/devMode', methods = ['POST', 'GET'])
def devMode():
    if request.method == 'GET':
        r.incr('devModeGet')
        return render_template('devMode.html')
    elif request.method == 'POST':
        pss = request.get_json() or request.form #Matching Axios request type
        if checkPassword(pss['password'], pss['info']):
            return os.environ.get('PASSWORD_MESSAGE')
        else:
            return FAIL_MESSAGE
    else: #Message for other methods (DELETE, PUT, CONNECT, ...)
        return '¿?'

#Recalls the failed log attemps of devMode
@app.route('/devMode/recall/logAttempts', methods = ['POST'])
def recallLogAttempts():
    pss = request.get_json() or request.form
    if checkPassword(pss['password'], pss['info']):
        try:
            return str(r.lrange('devModeGetList', 0, -1))[1:-2]
        except Exception as error:
            return formatError(error)
    else:
        return FAIL_MESSAGE

#Recalls the times devMode's URL was accessed
@app.route('/devMode/recall/getTimes', methods = ['POST'])
def recallGetTimes():
    pss = request.get_json() or request.form
    if checkPassword(pss['password'], pss['info']):
        try:
            return str(r.get('devModeGet'))
        except Exception as error:
            return formatError(error)
    else:
        return FAIL_MESSAGE

#Posts classroom information
@app.route('/devMode/post/classroomInfo', methods = ['POST'])
def postClassroomInfo():
    pss = request.get_json() or request.form
    if checkPassword(pss['password'], pss['info']):
        r.append("classrooms", "<<" + str(pss))
        return "yay!"
    else:
        return "Wrong password"

@app.route('/devMode/get/classroomInfo', methods = ['POST'])
def getClassroomInfo():
    pss = request.get_json() or request.form
    if checkPassword(pss['password'], pss['info']):
        return r.get("classrooms")
    else:
        return "Wrong password"

#Non-server functions-----------------

#Checks for password and logs failed attempts
def checkPassword(password, info):
    date = str(datetime.datetime.utcnow())
    attempt = password == os.environ.get('REDIS_URL')
    if not attempt:
        r.lpush('devModeGetList', "Info: " + info + " --- " + "Timestamp: " + date + " // Attempt: " + password + "\n")
    return attempt

#Format function to redis errors
def formatError(error):
    msg = 'Type: ' + type(error) + '\n'
    msg += 'Exception: ' + error
    return msg<commit_msg>JSON GET improved<commit_after>import os
import redis
import logging
import datetime
import json

from flask import Flask , send_from_directory, render_template, url_for, request, jsonify

#Redis instance from heroku
r = redis.from_url(os.environ.get("REDIS_URL"), db =None, decode_responses = True)

app = Flask(__name__)

FAIL_MESSAGE = 'la pulenta oe'

data = None
with open('classrooms.json') as json_data:
    data = json.dumps(json.load(json_data))

#"Landing page"
@app.route('/')
def webprint():
    global data
    return render_template('index.html')

#Returning JSON
@app.route('/salones')
def salones():
    global data
    return jsonify(data)

#Saves the string <strng> in the DB
@app.route('/db/<strng>')
def saveInDB(strng):
    r.lpush('db', '%s' % strng)
    return 'OK'

#Recalls the stored info in the DB
@app.route('/db/recall/test', methods = ['GET'])
def recallDB():
    try:
        return str(r.lrange('db', 0, -1))
    except Exception as error:
        return formatError(error)

#DevMode Handling---------------------------------------------

#Returns devMode.html [GET] #Checks login credentials [POST]
@app.route('/devMode', methods = ['POST', 'GET'])
def devMode():
    if request.method == 'GET':
        r.incr('devModeGet')
        return render_template('devMode.html')
    elif request.method == 'POST':
        pss = request.get_json() or request.form #Matching Axios request type
        if checkPassword(pss['password'], pss['info']):
            return os.environ.get('PASSWORD_MESSAGE')
        else:
            return FAIL_MESSAGE
    else: #Message for other methods (DELETE, PUT, CONNECT, ...)
        return '¿?'

#Recalls the failed log attemps of devMode
@app.route('/devMode/recall/logAttempts', methods = ['POST'])
def recallLogAttempts():
    pss = request.get_json() or request.form
    if checkPassword(pss['password'], pss['info']):
        try:
            return str(r.lrange('devModeGetList', 0, -1))[1:-2]
        except Exception as error:
            return formatError(error)
    else:
        return FAIL_MESSAGE

#Recalls the times devMode's URL was accessed
@app.route('/devMode/recall/getTimes', methods = ['POST'])
def recallGetTimes():
    pss = request.get_json() or request.form
    if checkPassword(pss['password'], pss['info']):
        try:
            return str(r.get('devModeGet'))
        except Exception as error:
            return formatError(error)
    else:
        return FAIL_MESSAGE

#Posts classroom information
@app.route('/devMode/post/classroomInfo', methods = ['POST'])
def postClassroomInfo():
    pss = request.get_json() or request.form
    if checkPassword(pss['password'], pss['info']):
        r.append("classrooms", "<<" + str(pss))
        return "yay!"
    else:
        return "Wrong password"

@app.route('/devMode/get/classroomInfo', methods = ['POST'])
def getClassroomInfo():
    pss = request.get_json() or request.form
    if checkPassword(pss['password'], pss['info']):
        return r.get("classrooms")
    else:
        return "Wrong password"

#Non-server functions-----------------

#Checks for password and logs failed attempts
def checkPassword(password, info):
    date = str(datetime.datetime.utcnow())
    attempt = password == os.environ.get('REDIS_URL')
    if not attempt:
        r.lpush('devModeGetList', "Info: " + info + " --- " + "Timestamp: " + date + " // Attempt: " + password + "\n")
    return attempt

#Format function to redis errors
def formatError(error):
    msg = 'Type: ' + type(error) + '\n'
    msg += 'Exception: ' + error
    return msg<|endoftext|>


<commit_before>import os

from flask import Flask, render_template
from flask.ext.cache import Cache
from thumbnails import engines, get_thumbnail
from helpers import thumbnails_path

app = Flask(__name__)
cache = Cache(app, config={'CACHE_TYPE': 'simple'})
app.debug = True
save = engines.get_current_engine().engine_save_image

URL = 'https://unsplash.imgix.net/photo-1422405153578-4bd676b19036' \
      '?q=75&fm=jpg&s=5ecc4c704ea97d85ea550f84a1499228'

os.makedirs(thumbnails_path(None), exist_ok=True)

for size in ['200x200', '200', 'x200']:
    save(
        get_thumbnail(URL, size).image,
        thumbnails_path('no-crop-{}.jpg'.format(size))
    )
for crop in ['top', 'right', 'bottom', 'left', 'center']:
    save(
        get_thumbnail(URL, '200x200', crop).image,
        thumbnails_path('crop-{}-200x200.jpg'.format(crop))
    )

@app.route("/")
@cache.cached(timeout=50)
def index():
    images = []
    files = os.listdir(thumbnails_path(None))
    files.sort()
    for f in files:
        images.append({
            'url': '/thumbnails/{}'.format(f),
            'title': f.replace('.jpg', '')
        })

    return render_template('index.html', thumbnails=images)
<commit_msg>Update server to latest version of python-thumbnails<commit_after>import os

from flask import Flask, render_template
from flask.ext.cache import Cache

import thumbnails
import thumbnails.backends

from helpers import thumbnails_path

app = Flask(__name__)
cache = Cache(app, config={'CACHE_TYPE': 'simple'})
app.debug = True
save = thumbnails.backends.get_engine().engine_save_image

URL = 'https://unsplash.imgix.net/photo-1422405153578-4bd676b19036' \
      '?q=75&fm=jpg&s=5ecc4c704ea97d85ea550f84a1499228'

os.makedirs(thumbnails_path(None), exist_ok=True)

for size in ['200x200', '200', 'x200']:
    save(
        thumbnails.get_thumbnail(URL, size).image,
        thumbnails_path('no-crop-{}.jpg'.format(size))
    )
for crop in ['top', 'right', 'bottom', 'left', 'center']:
    save(
        thumbnails.get_thumbnail(URL, '200x200', crop).image,
        thumbnails_path('crop-{}-200x200.jpg'.format(crop))
    )

@app.route("/")
@cache.cached(timeout=50)
def index():
    images = []
    files = os.listdir(thumbnails_path(None))
    files.sort()
    for f in files:
        images.append({
            'url': '/thumbnails/{}'.format(f),
            'title': f.replace('.jpg', '')
        })

    return render_template('index.html', thumbnails=images)
<|endoftext|>


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

"""
my sms proxy server
"""

import select
import socket
import sys
import json

host = ''
port = 6666
backlog = 5
size = 1024
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server.bind((host, port))
server.listen(backlog)
# inputs = [server, sys.stdin]
inputs = [server,]

socket_map = {}
queue = []
worker_socket = None
def identify(s, data):
    global socket_map, worker_socket
    identity = data.strip()
    if identity == "WORKER":
        worker_socket = s
        return
    elif identity == "REQUESTER":
        socket_map[s] = ["REQUESTER", ]
        return
def command(s, data):
    global socket_map, queue
    identity = socket_map[s][0]
    cmd_str = data.strip()
    if identity == "REQUESTER":
        cmd = json.loads(cmd_str)
        cmd_type = cmd['type']
        number = cmd['number']
        msg = cmd['message']
        queue.append(cmd_str)
        worker_online = worker_socket is not None
        worker_status = "ONLINE" if worker_online else "OFFLINE"
        s.send('SERVER_GOT_COMMAND: %s, STATUS: %s\n' %(cmd, worker_status))
        if worker_online:
            for item in queue:
                worker_socket.send(cmd_str+'\n')
            queue = []
        else:
            print "worker_socket is not present! saved into queue, result: %s" %queue
def worker_response(data):
    pass
def disconnect(s):
    global socket_map, worker_socket
    if worker_socket == s:
        worker_socket = None
        print "worker_socket removed: %s" %s
    elif socket_map.has_key(s):
        item = socket_map[s]
        del socket_map[s]
        print "socket removed from map: %s, %s" %(s, item)
# main
while True:
    inputready, outputready, exceptready = select.select(inputs, [], [])
    # print "len(inputready): %s" %len(inputready)
    for s in inputready:
        if s == server:
            # handle the server socket
            client, address = server.accept()
            # client.settimeout(3)
            print "peer connected %s:%s" %address
            inputs.append(client)
        elif s == sys.stdin:
            # handle standard input
            # junk = sys.stdin.readline()
            # running = 0
            pass
        else:
            # handle all other sockets
            # print dir(s)
            f = s.makefile()
            data = f.readline()
            # data = s.recv(size)
            if data:
                print "received: %s" %repr(data)
                if s == worker_socket:
                    worker_response(s, data)
                if socket_map.has_key(s):
                    command(s, data)
                else:
                    identify(s, data)
            else:
                # EOF: remote connection closed
                s.close()
                inputs.remove(s)
                disconnect(s)
                print "remote socket closed: " + str(s)
server.close()

<commit_msg>sms<commit_after>#!/usr/bin/env python

"""
my sms proxy server
"""

import select
import socket
import sys
import json

host = ''
port = 6666
backlog = 5
size = 1024
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server.bind((host, port))
server.listen(backlog)
# inputs = [server, sys.stdin]
inputs = [server,]

socket_map = {}
queue = []
worker_socket = None
def identify(s, data):
    global socket_map, worker_socket
    identity = data.strip()
    if identity == "WORKER":
        worker_socket = s
        # try send message
        try_send_one_message(s)
        return
    elif identity == "REQUESTER":
        socket_map[s] = ["REQUESTER", ]
        return
def try_send_one_message(s):
    global queue
    if len(queue) == 0:
        print "queue empty, waiting for request."
        return
    else:
        item = queue.pop(0)
        s.send(item+'\n')
def command(s, data):
    global socket_map, queue
    identity = socket_map[s][0]
    cmd_str = data.strip()
    if identity == "REQUESTER":
        cmd = json.loads(cmd_str)
        cmd_type = cmd['type']
        number = cmd['number']
        msg = cmd['message']
        queue.append(cmd_str)
        worker_online = worker_socket is not None
        worker_status = "ONLINE" if worker_online else "OFFLINE"
        s.send('SERVER_GOT_COMMAND: %s, STATUS: %s\n' %(cmd, worker_status))
        if worker_online:
            try_send_one_message(worker_socket)
        else:
            print "worker_socket is not present! saved into queue, result: %s" %queue
def worker_response(data):
    pass
def disconnect(s):
    global socket_map, worker_socket
    if worker_socket == s:
        worker_socket = None
        print "worker_socket removed: %s" %s
    elif socket_map.has_key(s):
        item = socket_map[s]
        del socket_map[s]
        print "socket removed from map: %s, %s" %(s, item)
# main
while True:
    inputready, outputready, exceptready = select.select(inputs, [], [])
    # print "len(inputready): %s" %len(inputready)
    for s in inputready:
        if s == server:
            # handle the server socket
            client, address = server.accept()
            # client.settimeout(3)
	    print "peer connected %s:%s,"%address, "socket: %s"%client
            inputs.append(client)
        elif s == sys.stdin:
            # handle standard input
            # junk = sys.stdin.readline()
            # running = 0
            pass
        else:
            # handle all other sockets
            # print dir(s)
            f = s.makefile()
            data = f.readline()
            # data = s.recv(size)
            if data:
                print "received: %s" %repr(data)
                if s == worker_socket:
                    worker_response(s, data)
                if socket_map.has_key(s):
                    command(s, data)
                else:
                    identify(s, data)
            else:
                # EOF: remote connection closed
                s.close()
                inputs.remove(s)
                disconnect(s)
                print "remote socket closed: " + str(s)
server.close()

<|endoftext|>


<commit_before>import csv
import json
from StringIO import StringIO

from vsm.corpus import Corpus
from vsm.model.beaglecomposite import BeagleComposite
from vsm.viewer.beagleviewer import BeagleViewer
from vsm.model.ldacgsmulti import LdaCgsMulti as LCM
from vsm.viewer.ldagibbsviewer import LDAGibbsViewer as LDAViewer

from bottle import request, response, route, run, static_file

path = '/var/inphosemantics/data/20140801/sep/vsm-data/'

lda_c = Corpus.load(path + 'sep-nltk-freq1.npz')
lda_m = None
lda_v = None
def load_model(k):
    global lda_m, lda_v
    lda_m = LCM.load(path + 'sep-nltk-freq1-article-LDA-K%s.npz' % k)
    lda_v = LDAViewer(lda_c, lda_m)

@route('/doc_topics/<sep_dir>')
def doc_topic_csv(sep_dir):
    sep_dir = sep_dir.lower()

    response.content_type = 'text/csv; charset=UTF8'

    doc_id = sep_dir + '.txt'
    data = lda_v.doc_topics(doc_id)

    output=StringIO()
    writer = csv.writer(output)
    writer.writerow(['topic','prob'])
    writer.writerows([(t, "%6f" % p) for t,p in data])

    return output.getvalue()

@route('/docs/<sep_dir>')
def doc_csv(sep_dir, threshold=0.2):
    sep_dir = sep_dir.lower()

    response.content_type = 'text/csv; charset=UTF8'

    doc_id = sep_dir + '.txt'
    data = lda_v.sim_doc_doc(doc_id)

    output=StringIO()
    writer = csv.writer(output)
    writer.writerow(['doc','prob'])
    writer.writerows([(d[:-4], "%6f" % p) for d,p in data if p > threshold and d != 'sample.txt'])

    return output.getvalue()

@route('/topics/<topic_no>.json')
def topic_csv(topic_no, N=40):
    response.content_type = 'application/json; charset=UTF8'
    try:
        N = int(request.query.n)
    except:
        pass

    if N > 0:
        data = lda_v.sim_top_doc([int(topic_no)])[:N]
    else:
        data = lda_v.sim_top_doc([int(topic_no)])[N:]
        data = reversed(data)

    js = []
    for doc, prob in data:
        if doc != 'sample.txt':
            js.append({'doc' : doc[:-4], 'prob' : 1-prob,
                'topics' : dict([(str(t), p) for t,p in lda_v.doc_topics(doc)])})

    return json.dumps(js)

@route('/docs_topics/<sep_dir>.json')
def doc_topics(sep_dir, N=40):
    sep_dir = sep_dir.lower()
    try:
        N = int(request.query.n)
    except:
        pass

    response.content_type = 'application/json; charset=UTF8'

    doc_id = sep_dir + '.txt'
    if N > 0:
        data = lda_v.sim_doc_doc(doc_id)[:N]
    else:
        data = lda_v.sim_doc_doc(doc_id)[N:]
        data = reversed(data)

    js = []
    for doc, prob in data:
        if doc != 'sample.txt':
            js.append({'doc' : doc[:-4], 'prob' : 1-prob,
                'topics' : dict([(str(t), p) for t,p in lda_v.doc_topics(doc)])})

    return json.dumps(js)

@route('/topics.json')
def topics():
    response.content_type = 'application/json; charset=UTF8'

    data = lda_v.topics()

    js ={}
    for i,topic in enumerate(data):
        for word, prob in topic[:10]:
            js.update({str(i) : dict([(w, p) for w,p in topic[:10]])})

    return json.dumps(js)

@route('/docs.json')
def docs():
    response.content_type = 'application/json; charset=UTF8'

    data = lda_v.topics()
    js = [label[:-4] for label in lda_c.view_metadata('article')['article_label'] if label != 'sample.txt']

    return json.dumps(js)

@route('/<filename:path>')
def send_static(filename):
    return static_file(filename, root='www/')

@route('/')
def index():
    return send_static('index.html')

if __name__ == '__main__':
    from argparse import ArgumentParser
    parser = ArgumentParser()
    parser.add_argument('k', type=int)
    parser.add_argument('-p', dest='port', type=int,
        help="Port Number", default=None)
    args = parser.parse_args()

    if args.port is None:
        port = '16%03d' % args.k
    else:
        port = args.port

    load_model(args.k)
    run(host='0.0.0.0', port=port)

<commit_msg>adding cache directives<commit_after>import csv
from datetime import datetime, timedelta
import json
from StringIO import StringIO

from vsm.corpus import Corpus
from vsm.model.beaglecomposite import BeagleComposite
from vsm.viewer.beagleviewer import BeagleViewer
from vsm.model.ldacgsmulti import LdaCgsMulti as LCM
from vsm.viewer.ldagibbsviewer import LDAGibbsViewer as LDAViewer

from bottle import request, response, route, run, static_file

path = '/var/inphosemantics/data/20140801/sep/vsm-data/'

lda_c = Corpus.load(path + 'sep-nltk-freq1.npz')
lda_m = None
lda_v = None
def load_model(k):
    global lda_m, lda_v
    lda_m = LCM.load(path + 'sep-nltk-freq1-article-LDA-K%s.npz' % k)
    lda_v = LDAViewer(lda_c, lda_m)

def _cache_date(days=1):
    time = datetime.now() + timedelta(days=days)
    return time.strftime("%a, %d %b %Y %I:%M:%S GMT")

@route('/doc_topics/<sep_dir>')
def doc_topic_csv(sep_dir):
    sep_dir = sep_dir.lower()

    response.content_type = 'text/csv; charset=UTF8'

    doc_id = sep_dir + '.txt'
    data = lda_v.doc_topics(doc_id)

    output=StringIO()
    writer = csv.writer(output)
    writer.writerow(['topic','prob'])
    writer.writerows([(t, "%6f" % p) for t,p in data])

    return output.getvalue()

@route('/docs/<sep_dir>')
def doc_csv(sep_dir, threshold=0.2):
    sep_dir = sep_dir.lower()

    response.content_type = 'text/csv; charset=UTF8'

    doc_id = sep_dir + '.txt'
    data = lda_v.sim_doc_doc(doc_id)

    output=StringIO()
    writer = csv.writer(output)
    writer.writerow(['doc','prob'])
    writer.writerows([(d[:-4], "%6f" % p) for d,p in data if p > threshold and d != 'sample.txt'])

    return output.getvalue()

@route('/topics/<topic_no>.json')
def topic_csv(topic_no, N=40):
    response.content_type = 'application/json; charset=UTF8'
    try:
        N = int(request.query.n)
    except:
        pass

    if N > 0:
        data = lda_v.sim_top_doc([int(topic_no)])[:N]
    else:
        data = lda_v.sim_top_doc([int(topic_no)])[N:]
        data = reversed(data)

    js = []
    for doc, prob in data:
        if doc != 'sample.txt':
            js.append({'doc' : doc[:-4], 'prob' : 1-prob,
                'topics' : dict([(str(t), p) for t,p in lda_v.doc_topics(doc)])})

    return json.dumps(js)

@route('/docs_topics/<sep_dir>.json')
def doc_topics(sep_dir, N=40):
    sep_dir = sep_dir.lower()
    try:
        N = int(request.query.n)
    except:
        pass

    response.content_type = 'application/json; charset=UTF8'

    doc_id = sep_dir + '.txt'
    if N > 0:
        data = lda_v.sim_doc_doc(doc_id)[:N]
    else:
        data = lda_v.sim_doc_doc(doc_id)[N:]
        data = reversed(data)

    js = []
    for doc, prob in data:
        if doc != 'sample.txt':
            js.append({'doc' : doc[:-4], 'prob' : 1-prob,
                'topics' : dict([(str(t), p) for t,p in lda_v.doc_topics(doc)])})

    return json.dumps(js)

@route('/topics.json')
def topics():
    response.content_type = 'application/json; charset=UTF8'
    response.set_header('Expires', _cache_date())

    data = lda_v.topics()

    js ={}
    for i,topic in enumerate(data):
        for word, prob in topic[:10]:
            js.update({str(i) : dict([(w, p) for w,p in topic[:10]])})

    return json.dumps(js)

@route('/docs.json')
def docs():
    response.content_type = 'application/json; charset=UTF8'
    response.set_header('Expires', _cache_date())

    data = lda_v.topics()
    js = [label[:-4] for label in lda_c.view_metadata('article')['article_label'] if label != 'sample.txt']

    return json.dumps(js)

@route('/<filename:path>')
def send_static(filename):
    return static_file(filename, root='www/')

@route('/')
def index():
    return send_static('index.html')

if __name__ == '__main__':
    from argparse import ArgumentParser
    parser = ArgumentParser()
    parser.add_argument('k', type=int)
    parser.add_argument('-p', dest='port', type=int,
        help="Port Number", default=None)
    args = parser.parse_args()

    if args.port is None:
        port = '16%03d' % args.k
    else:
        port = args.port

    load_model(args.k)
    run(host='0.0.0.0', port=port)

<|endoftext|>


<commit_before>from rootio.config import *
from rootio.content.models import ContentTrack
import json
from .utils.audio import PlayStatus

class NewsAction:

    def __init__(self, track_id, start_time, duration, program):
        self.__track_id = track_id
        self.__track = None
        self.__is_valid = True
        self.start_time = start_time
        self.duration = duration
        self.program = program
        self.__media_expected_to_stop = False
        self.__call_answer_info = None
        self.__call_handler = self.program.radio_station.call_handler
        self.program.log_program_activity("Done initialising news action for program {0}".format(self.program.name))

    def start(self):
        self.program.set_running_action(self)
        try:
            self.__load_track()
            if self.__track is not None and len(self.__track.files) > 0:
                call_result = self.__request_station_call()
                # CARLOS - is this supposed to be a no_media status?
                if not call_result[0]:  # !!
                    self.stop(PlayStatus.no_media)
            else:  # Track exists but contains no content
                self.stop(PlayStatus.no_media)
        except Exception as e:
            self.stop(PlayStatus.failed)
            self.program.radio_station.logger.error("error {err} in news_action.__start".format(err=e.message))

    def pause(self):
        self.__pause_media()

    def stop(self, status_type=PlayStatus.success, call_info=None):
        if call_info is not None:
            self.__stop_media(call_info)
        elif self.__call_answer_info is not None:
            self.__stop_media(self.__call_answer_info)
        self.__deregister_listeners()
        # Fix this - clash of names btn programs and scheduled instances
        self.program.notify_program_action_stopped(status_type, call_info)

    def notify_call_answered(self, answer_info):
        self.program.log_program_activity(
            "Received call answer notification for Media action of {0} program".format(self.program.name))
        self.__call_answer_info = answer_info
        self.__call_handler.register_for_call_hangup(self, answer_info['Caller-Destination-Number'][-11:])
        self.__play_media(self.__call_answer_info)

    def __load_track(self):  # load the media to be played
        self.__track = self.program.radio_station.db.query(ContentTrack).filter(
            ContentTrack.id == self.__track_id).first()
        self.__track.files.sort(key=lambda x: x.date_created, reverse=True)

    def __request_station_call(self):  # call the number specified thru plivo
        # Check if the call exists, start with the least likely number to be called
        if self.program.radio_station.station.secondary_transmitter_phone is not None and self.__call_handler.call_exists(
                self.program.radio_station.station.secondary_transmitter_phone.raw_number):
            result = self.__call_handler.call(self,
                                              self.program.radio_station.station.secondary_transmitter_phone.raw_number,
                                              self.program.name, False, self.duration)
            return result
        elif self.program.radio_station.station.primary_transmitter_phone is not None and self.__call_handler.call_exists(
                self.program.radio_station.station.primary_transmitter_phone.raw_number):
            result = self.__call_handler.call(self,
                                              self.program.radio_station.station.primary_transmitter_phone.raw_number,
                                              self.program.name, False, self.duration)
            return result
        elif self.program.radio_station.station.sip_username is not None and self.__call_handler.call_exists(
                self.program.radio_station.station.sip_username):
            result = self.__call_handler.call(self, self.program.radio_station.station.sip_username, self.program.name,
                                              True, self.duration)
            self.program.log_program_activity("result of station call via SIP is " + str(result))
            return result

        # At this point we are sure that no call to the station exists. We will try to initiate a new call
        if self.program.radio_station.station.is_high_bandwidth:
            result = self.__call_station_via_sip()
            if result is None or not result[0]:  # Now try calling the SIM (ideally do primary, then secondary)
                result = self.__call_station_via_goip()
        else:
            result = self.__call_station_via_goip()
        return result

    def __call_station_via_sip(self):
        # Try a high bandwidth call first
        if self.program.radio_station.station.sip_username is not None:
            result = self.__call_handler.call(self, self.program.radio_station.station.sip_username, self.program.name,
                                              True,
                                              self.duration)
            self.program.log_program_activity("result of station call via SIP is " + str(result))
            return result

    def __call_station_via_goip(self):
        result = None
        if self.program.radio_station.station.primary_transmitter_phone is not None:
            result = self.__call_handler.call(self,
                                              self.program.radio_station.station.primary_transmitter_phone.raw_number,
                                              self.program.name, False,
                                              self.duration)
            self.program.log_program_activity("result of station call (primary) via GoIP is " + str(result))
            if not result[
                0] and self.program.radio_station.station.secondary_transmitter_phone is not None:  # Go for the secondary line of the station, if duo SIM phone
                result = self.__call_handler.call(self,
                                                  self.program.radio_station.station.secondary_transmitter_phone.raw_number,
                                                  self.program.name, False,
                                                  self.duration)
                self.program.log_program_activity("result of station call (secondary) via GoIP is " + str(result))
        return result

    def __play_media(self, call_info):  # play the media in the array
        self.program.log_program_activity(
            "Playing media {0}".format(self.__track.files[0].name))
        self.__listen_for_media_play_stop()

        # Always play the last file for news
        result = self.__call_handler.play(call_info['Channel-Call-UUID'], os.path.join(DefaultConfig.CONTENT_DIR,
                                                                                self.__track.files[0].
                                                                                uri))
        self.program.log_program_activity('result of play is ' + result)
        if result.split(" ")[0] != "+OK":
            self.stop(PlayStatus.failed, call_info)

    def __pause_media(self):  # pause the media in the array
        pass

    def __stop_media(self, event_json):  # stop the media being played by the player
        try:
            self.program.log_program_activity(
                "Deregistered, all good, about to order hangup for {0}".format(self.program.name))
            result = self.__call_handler.stop_play(self.__call_answer_info['Channel-Call-UUID'],
                                                   self.__track.files[0].uri)
            self.program.log_program_activity('result of stop play is ' + result)
        except Exception as e:
            self.program.radio_station.logger.error("error {err} in news_action.__stop_media".format(err=e.message))
            return

    def notify_call_hangup(self, event_json):
        self.program.log_program_activity('Call hangup before end of program!')
        #self.stop(False, event_json)
        self.__request_station_call()

    def notify_media_play_stop(self, event_json):
        self.program.radio_station.logger.info(
            "Played all media, stopping media play in Media action for {0}".format(self.program.name))
        self.program.log_program_activity("Hangup on complete is true for {0}".format(self.program.name))
        if event_json["Media-Bug-Target"] == os.path.join(DefaultConfig.CONTENT_DIR, self.__track.files[0].uri):
            self.stop(PlayStatus.success, event_json)  # program.notify_program_action_stopped(self)
        self.__is_valid = False

    def __listen_for_media_play_stop(self):
        self.__call_handler.register_for_media_playback_stop(self, str(
            self.__call_answer_info['Caller-Destination-Number'][-11:]))

    def __deregister_listeners(self):
        if self.__call_answer_info is not None:
            self.__call_handler.deregister_for_media_playback_stop(
                self.__call_answer_info['Caller-Destination-Number'][-11:])
            self.__call_handler.deregister_for_call_hangup(self.__call_answer_info['Caller-Destination-Number'][-11:])<commit_msg>Fixed left over issue that hampered hangup for news type tracks<commit_after>from rootio.config import *
from rootio.content.models import ContentTrack
import json
from .utils.audio import PlayStatus

class NewsAction:

    def __init__(self, track_id, start_time, duration, program):
        self.__track_id = track_id
        self.__track = None
        self.__is_valid = True
        self.start_time = start_time
        self.duration = duration
        self.program = program
        self.__media_expected_to_stop = False
        self.__call_answer_info = None
        self.__call_handler = self.program.radio_station.call_handler
        self.program.log_program_activity("Done initialising news action for program {0}".format(self.program.name))

    def start(self):
        self.program.set_running_action(self)
        try:
            self.__load_track()
            if self.__track is not None and len(self.__track.files) > 0:
                call_result = self.__request_station_call()
                # CARLOS - is this supposed to be a no_media status?
                if not call_result[0]:  # !!
                    self.stop(PlayStatus.no_media)
            else:  # Track exists but contains no content
                self.stop(PlayStatus.no_media)
        except Exception as e:
            self.stop(PlayStatus.failed)
            self.program.radio_station.logger.error("error {err} in news_action.__start".format(err=e.message))

    def pause(self):
        self.__pause_media()

    def stop(self, status_type=PlayStatus.success, call_info=None):
        if call_info is not None:
            self.__stop_media(call_info)
        elif self.__call_answer_info is not None:
            self.__stop_media(self.__call_answer_info)
        self.__deregister_listeners()
        # Fix this - clash of names btn programs and scheduled instances
        self.program.notify_program_action_stopped(status_type, call_info)

    def notify_call_answered(self, answer_info):
        self.program.log_program_activity(
            "Received call answer notification for Media action of {0} program".format(self.program.name))
        self.__call_answer_info = answer_info
        self.__call_handler.register_for_call_hangup(self, answer_info['Caller-Destination-Number'][-11:])
        self.__play_media(self.__call_answer_info)

    def __load_track(self):  # load the media to be played
        self.__track = self.program.radio_station.db.query(ContentTrack).filter(
            ContentTrack.id == self.__track_id).first()
        self.__track.files.sort(key=lambda x: x.date_created, reverse=True)

    def __request_station_call(self):  # call the number specified thru plivo
        # Check if the call exists, start with the least likely number to be called
        if self.program.radio_station.station.secondary_transmitter_phone is not None and self.__call_handler.call_exists(
                self.program.radio_station.station.secondary_transmitter_phone.raw_number):
            result = self.__call_handler.call(self,
                                              self.program.radio_station.station.secondary_transmitter_phone.raw_number,
                                              self.program.name, False, self.duration)
            return result
        elif self.program.radio_station.station.primary_transmitter_phone is not None and self.__call_handler.call_exists(
                self.program.radio_station.station.primary_transmitter_phone.raw_number):
            result = self.__call_handler.call(self,
                                              self.program.radio_station.station.primary_transmitter_phone.raw_number,
                                              self.program.name, False, self.duration)
            return result
        elif self.program.radio_station.station.sip_username is not None and self.__call_handler.call_exists(
                self.program.radio_station.station.sip_username):
            result = self.__call_handler.call(self, self.program.radio_station.station.sip_username, self.program.name,
                                              True, self.duration)
            self.program.log_program_activity("result of station call via SIP is " + str(result))
            return result

        # At this point we are sure that no call to the station exists. We will try to initiate a new call
        if self.program.radio_station.station.is_high_bandwidth:
            result = self.__call_station_via_sip()
            if result is None or not result[0]:  # Now try calling the SIM (ideally do primary, then secondary)
                result = self.__call_station_via_goip()
        else:
            result = self.__call_station_via_goip()
        return result

    def __call_station_via_sip(self):
        # Try a high bandwidth call first
        if self.program.radio_station.station.sip_username is not None:
            result = self.__call_handler.call(self, self.program.radio_station.station.sip_username, self.program.name,
                                              True,
                                              self.duration)
            self.program.log_program_activity("result of station call via SIP is " + str(result))
            return result

    def __call_station_via_goip(self):
        result = None
        if self.program.radio_station.station.primary_transmitter_phone is not None:
            result = self.__call_handler.call(self,
                                              self.program.radio_station.station.primary_transmitter_phone.raw_number,
                                              self.program.name, False,
                                              self.duration)
            self.program.log_program_activity("result of station call (primary) via GoIP is " + str(result))
            if not result[
                0] and self.program.radio_station.station.secondary_transmitter_phone is not None:  # Go for the secondary line of the station, if duo SIM phone
                result = self.__call_handler.call(self,
                                                  self.program.radio_station.station.secondary_transmitter_phone.raw_number,
                                                  self.program.name, False,
                                                  self.duration)
                self.program.log_program_activity("result of station call (secondary) via GoIP is " + str(result))
        return result

    def __play_media(self, call_info):  # play the media in the array
        self.program.log_program_activity(
            "Playing media {0}".format(self.__track.files[0].name))
        self.__listen_for_media_play_stop()

        # Always play the last file for news
        result = self.__call_handler.play(call_info['Channel-Call-UUID'], os.path.join(DefaultConfig.CONTENT_DIR,
                                                                                self.__track.files[0].
                                                                                uri))
        self.program.log_program_activity('result of play is ' + result)
        if result.split(" ")[0] != "+OK":
            self.stop(PlayStatus.failed, call_info)

    def __pause_media(self):  # pause the media in the array
        pass

    def __stop_media(self, event_json):  # stop the media being played by the player
        try:
            self.program.log_program_activity(
                "Deregistered, all good, about to order hangup for {0}".format(self.program.name))
            result = self.__call_handler.stop_play(self.__call_answer_info['Channel-Call-UUID'],
                                                   self.__track.files[0].uri)
            self.program.log_program_activity('result of stop play is ' + result)
        except Exception as e:
            self.program.radio_station.logger.error("error {err} in news_action.__stop_media".format(err=e.message))
            return

    def notify_call_hangup(self, event_json):
        self.program.log_program_activity('Call hangup before end of program!')
        #self.stop(False, event_json)
        self.__request_station_call()

    def notify_media_play_stop(self, event_json):
        self.program.radio_station.logger.info(
            "Played all media, stopping media play in Media action for {0}".format(self.program.name))
        self.program.log_program_activity("Hangup on complete is true for {0}".format(self.program.name))
        #if event_json["Media-Bug-Target"] == os.path.join(DefaultConfig.CONTENT_DIR, self.__track.files[0].uri):
        self.stop(PlayStatus.success, event_json)  # program.notify_program_action_stopped(self)
        self.__is_valid = False

    def __listen_for_media_play_stop(self):
        self.__call_handler.register_for_media_playback_stop(self, str(
            self.__call_answer_info['Caller-Destination-Number'][-11:]))

    def __deregister_listeners(self):
        if self.__call_answer_info is not None:
            self.__call_handler.deregister_for_media_playback_stop(
                self.__call_answer_info['Caller-Destination-Number'][-11:])
            self.__call_handler.deregister_for_call_hangup(self.__call_answer_info['Caller-Destination-Number'][-11:])
<|endoftext|>


<commit_before>#! /usr/bin/env python2
from SimpleHTTPServer import SimpleHTTPRequestHandler
import BaseHTTPServer as bhs
import os
#print os.environ['trelloapikey']

class CORSRequestHandler (SimpleHTTPRequestHandler):
    def end_headers (self):
        self.send_header('Access-Control-Allow-Origin', '*')
        SimpleHTTPRequestHandler.end_headers(self)

if __name__ == '__main__':
    # Bind to PORT if defined, otherwise default to 5000.
    port = int(os.environ.get('PORT', 5000))
    bhs.HTTPServer(("", port), CORSRequestHandler).serve_forever()
    #BaseHTTPServer.test(CORSRequestHandler, BaseHTTPServer.HTTPServer)
<commit_msg>resatt til enkleste htprequesthandler<commit_after>#! /usr/bin/env python2
from SimpleHTTPServer import SimpleHTTPRequestHandler
import BaseHTTPServer

class CORSRequestHandler (SimpleHTTPRequestHandler):
    def end_headers (self):
        self.send_header('Access-Control-Allow-Origin', '*')
        SimpleHTTPRequestHandler.end_headers(self)

if __name__ == '__main__':
    BaseHTTPServer.test(CORSRequestHandler, BaseHTTPServer.HTTPServer)<|endoftext|>


<commit_before>from __future__ import unicode_literals

import regex

from django.db import models
from django.utils import timezone
from django.utils.translation import ugettext_lazy as _
from smartmin.models import SmartModel
from temba.channels.models import Channel, ChannelEvent
from temba.contacts.models import Contact, ContactGroup
from temba.flows.models import Flow, FlowRun
from temba.ivr.models import IVRCall
from temba.msgs.models import Msg
from temba.orgs.models import Org

class Trigger(SmartModel):
    """
    A Trigger is used to start a user in a flow based on an event. For example, triggers might fire
    for missed calls, inboud sms messages starting with a keyword, or on a repeating schedule.
    """
    TYPE_KEYWORD = 'K'
    TYPE_SCHEDULE = 'S'
    TYPE_MISSED_CALL = 'M'
    TYPE_INBOUND_CALL = 'V'
    TYPE_CATCH_ALL = 'C'
    TYPE_FOLLOW = 'F'
    TYPE_NEW_CONVERSATION = 'N'
    TYPE_USSD_PULL = 'U'

    TRIGGER_TYPES = ((TYPE_KEYWORD, _("Keyword Trigger")),
                     (TYPE_SCHEDULE, _("Schedule Trigger")),
                     (TYPE_INBOUND_CALL, _("Inbound Call Trigger")),
                     (TYPE_MISSED_CALL, _("Missed Call Trigger")),
                     (TYPE_CATCH_ALL, _("Catch All Trigger")),
                     (TYPE_FOLLOW, _("Follow Account Trigger")),
                     (TYPE_NEW_CONVERSATION, _("New Conversation Trigger")),
                     (TYPE_USSD_PULL, _("USSD Pull Session Trigger")))

    org = models.ForeignKey(Org, verbose_name=_("Org"), help_text=_("The organization this trigger belongs to"))

    keyword = models.CharField(verbose_name=_("Keyword"), max_length=16, null=True, blank=True,
                               help_text=_("The first word in the message text"))

    flow = models.ForeignKey(Flow, verbose_name=_("Flow"),
                             help_text=_("Which flow will be started"), related_name="triggers")

    last_triggered = models.DateTimeField(verbose_name=_("Last Triggered"), default=None, null=True,
                                          help_text=_("The last time this trigger was fired"))

    trigger_count = models.IntegerField(verbose_name=_("Trigger Count"), default=0,
                                        help_text=_("How many times this trigger has fired"))

    is_archived = models.BooleanField(verbose_name=_("Is Archived"), default=False,
                                      help_text=_("Whether this trigger is archived"))

    groups = models.ManyToManyField(ContactGroup, verbose_name=_("Groups"),
                                    help_text=_("The groups to broadcast the flow to"))

    contacts = models.ManyToManyField(Contact, verbose_name=_("Contacts"),
                                      help_text=_("Individual contacts to broadcast the flow to"))

    schedule = models.OneToOneField('schedules.Schedule', verbose_name=_("Schedule"),
                                    null=True, blank=True, related_name='trigger',
                                    help_text=_('Our recurring schedule'))

    trigger_type = models.CharField(max_length=1, choices=TRIGGER_TYPES, default=TYPE_KEYWORD,
                                    verbose_name=_("Trigger Type"), help_text=_('The type of this trigger'))

    channel = models.ForeignKey(Channel, verbose_name=_("Channel"), null=True, help_text=_("The associated channel"))

    class Meta:
        unique_together = ('keyword', 'channel',)

    def __unicode__(self):
        if self.trigger_type == Trigger.TYPE_KEYWORD:
            return self.keyword
        return self.get_trigger_type_display()

    def name(self):
        return self.__unicode__()

    def as_json(self):
        """
        An exportable dict representing our trigger
        """
        return dict(trigger_type=self.trigger_type,
                    keyword=self.keyword,
                    flow=dict(uuid=self.flow.uuid, name=self.flow.name),
                    groups=[dict(uuid=group.uuid, name=group.name) for group in self.groups.all()],
                    channel=self.channel.uuid if self.channel else None)

    def trigger_scopes(self):
        """
        Returns keys that represents the scopes that this trigger can operate against (and might conflict with other triggers with)
        """
        groups = ['**'] if not self.groups else [str(g.id) for g in self.groups.all().order_by('id')]
        return ['%s_%s_%s_%s' % (self.trigger_type, str(self.channel_id), group, str(self.keyword)) for group in groups]

    def restore(self, user):
        self.modified_on = timezone.now()
        self.modified_by = user
        self.is_archived = False
        self.save()

        # archive any conflicts
        self.archive_conflicts(user)

        # if this is new conversation trigger, register for the FB callback
        if self.trigger_type == Trigger.TYPE_NEW_CONVERSATION:
            self.channel.set_fb_call_to_action_payload(Channel.GET_STARTED)

    def archive_conflicts(self, user):
        """
        Archives any triggers that conflict with this one
        """
        now = timezone.now()

        if not self.trigger_type == Trigger.TYPE_SCHEDULE:
            matches = Trigger.objects.filter(org=self.org, is_active=True, is_archived=False, trigger_type=self.trigger_type)

            # if this trigger has a keyword, only archive others with the same keyword
            if self.keyword:
                matches = matches.filter(keyword=self.keyword)

            # if this trigger has a group, only archive others with the same group
            if self.groups.all():
                matches = matches.filter(groups__in=self.groups.all())
            else:
                matches = matches.filter(groups=None)

            # if this trigger has a channel, only archive others with the same channel
            if self.channel:
                matches = matches.filter(channel=self.channel)

            # archive any conflicting triggers
            matches.exclude(id=self.id).update(is_archived=True, modified_on=now, modified_by=user)

    @classmethod
    def import_triggers(cls, exported_json, org, user, same_site=False):
        """
        Import triggers from our export file
        """
        from temba.orgs.models import EARLIEST_IMPORT_VERSION
        if exported_json.get('version', 0) < EARLIEST_IMPORT_VERSION:
            raise ValueError(_("Unknown version (%s)" % exported_json.get('version', 0)))

        # first things first, let's create our groups if necesary and map their ids accordingly
        if 'triggers' in exported_json:
            for trigger_spec in exported_json['triggers']:

                # resolve our groups
                groups = []
                for group_spec in trigger_spec['groups']:

                    group = None

                    if same_site:
                        group = ContactGroup.user_groups.filter(org=org, uuid=group_spec['uuid']).first()

                    if not group:
                        group = ContactGroup.get_user_group(org, group_spec['name'])

                    if not group:
                        group = ContactGroup.create_static(org, user, group_spec['name'])

                    if not group.is_active:
                        group.is_active = True
                        group.save()

                    groups.append(group)

                flow = Flow.objects.get(org=org, uuid=trigger_spec['flow']['uuid'], is_active=True)

                # see if that trigger already exists
                trigger = Trigger.objects.filter(org=org, trigger_type=trigger_spec['trigger_type'])

                if trigger_spec['keyword']:
                    trigger = trigger.filter(keyword__iexact=trigger_spec['keyword'])

                if groups:
                    trigger = trigger.filter(groups__in=groups)

                trigger = trigger.first()
                if trigger:
                    trigger.is_archived = False
                    trigger.flow = flow
                    trigger.save()
                else:

                    # if we have a channel resolve it
                    channel = trigger_spec.get('channel', None)  # older exports won't have a channel
                    if channel:
                        channel = Channel.objects.filter(uuid=channel, org=org).first()

                    trigger = Trigger.objects.create(org=org, trigger_type=trigger_spec['trigger_type'],
                                                     keyword=trigger_spec['keyword'], flow=flow,
                                                     created_by=user, modified_by=user,
                                                     channel=channel)

                    for group in groups:
                        trigger.groups.add(group)

    @classmethod
    def get_triggers_of_type(cls, org, trigger_type):
        return Trigger.objects.filter(org=org, trigger_type=trigger_type, is_active=True, is_archived=False)

    @classmethod
    def catch_triggers(cls, entity, trigger_type, channel):
        if isinstance(entity, Msg):
            contact = entity.contact
            start_msg = entity
        elif isinstance(entity, ChannelEvent) or isinstance(entity, IVRCall):
            contact = entity.contact
            start_msg = Msg(org=entity.org, contact=contact, channel=entity.channel, created_on=timezone.now(), id=0)
        elif isinstance(entity, Contact):
            contact = entity
            start_msg = Msg(org=entity.org, contact=contact, channel=channel, created_on=timezone.now(), id=0)
        else:
            raise ValueError("Entity must be of type msg, call or contact")

        triggers = Trigger.get_triggers_of_type(entity.org, trigger_type)

        if trigger_type in [Trigger.TYPE_FOLLOW, Trigger.TYPE_NEW_CONVERSATION]:
            triggers = triggers.filter(channel=channel)

        # is there a match for a group specific trigger?
        group_ids = contact.user_groups.values_list('pk', flat=True)
        group_triggers = triggers.filter(groups__in=group_ids).order_by('groups__name')

        # if we match with a group restriction, that takes precedence
        if group_triggers:
            triggers = group_triggers

        # otherwise, restrict to triggers that don't filter by group
        else:
            triggers = triggers.filter(groups=None)

        # only fire the first matching trigger
        if triggers:
            triggers[0].flow.start([], [contact], start_msg=start_msg, restart_participants=True)

        return bool(triggers)

    @classmethod
    def find_and_handle(cls, msg):
        from temba.msgs.models import INTERRUPTED, TRIGGERED

        if msg.status == INTERRUPTED:
            return False
        elif msg.status == TRIGGERED:
            matched_object = regex.match('^[\d\*\#]+$', msg.text.strip(), flags=regex.UNICODE)
            keyword = matched_object.string if matched_object else None
        else:
            # get the first word out of our message
            words = regex.split(r"[\W]+", msg.text.strip(), flags=regex.UNICODE | regex.V0)

            while words and not words[0]:
                words = words[1:]

            if not words:
                return False

            keyword = words[0].lower()

        if not keyword:
            return False

        active_run_qs = FlowRun.objects.filter(is_active=True, contact=msg.contact,
                                               flow__is_active=True, flow__is_archived=False)
        active_run = active_run_qs.prefetch_related('steps').order_by("-created_on", "-pk").first()

        if active_run and active_run.flow.ignore_triggers and not active_run.is_completed():
            return False

        groups_ids = msg.contact.user_groups.values_list('pk', flat=True)

        # Check first if we have a trigger for the contact groups
        matching = Trigger.objects.filter(is_archived=False, is_active=True, org=msg.org, keyword__iexact=keyword,
                                          flow__is_archived=False, flow__is_active=True, groups__in=groups_ids).order_by('groups__name').prefetch_related('groups', 'groups__contacts')

        # If no trigger for contact groups find there is a no group trigger
        if not matching:
            matching = Trigger.objects.filter(is_archived=False, is_active=True, org=msg.org, keyword__iexact=keyword,
                                              flow__is_archived=False, flow__is_active=True, groups=None).prefetch_related('groups', 'groups__contacts')

        if not matching:
            return False

        contact = msg.contact
        trigger = matching[0]

        if not contact.is_test:
            trigger.last_triggered = msg.created_on
            trigger.trigger_count += 1
            trigger.save()

        # There is no actual message, it was only used for triggering the flow
        if msg.status == TRIGGERED:
            msg = None

        # if we have an associated flow, start this contact in it
        trigger.flow.start([], [contact], start_msg=msg, restart_participants=True)

        return True

    @classmethod
    def find_flow_for_inbound_call(cls, contact):

        groups_ids = contact.user_groups.values_list('pk', flat=True)

        # Check first if we have a trigger for the contact groups
        matching = Trigger.objects.filter(is_archived=False, is_active=True, org=contact.org,
                                          trigger_type=Trigger.TYPE_INBOUND_CALL, flow__is_archived=False,
                                          flow__is_active=True, groups__in=groups_ids).order_by('groups__name')\
                                  .prefetch_related('groups', 'groups__contacts')

        # If no trigger for contact groups find there is a no group trigger
        if not matching:
            matching = Trigger.objects.filter(is_archived=False, is_active=True, org=contact.org,
                                              trigger_type=Trigger.TYPE_INBOUND_CALL, flow__is_archived=False,
                                              flow__is_active=True, groups=None)\
                                      .prefetch_related('groups', 'groups__contacts')

        if not matching:
            return None

        trigger = matching[0]
        trigger.last_triggered = timezone.now()
        trigger.trigger_count += 1
        trigger.save()

        return trigger.flow

    @classmethod
    def apply_action_archive(cls, user, triggers):
        triggers.update(is_archived=True)

        # for any new convo triggers, clear out the call to action payload
        for trigger in triggers.filter(trigger_type=Trigger.TYPE_NEW_CONVERSATION):
            trigger.channel.set_fb_call_to_action_payload(None)

        return [each_trigger.pk for each_trigger in triggers]

    @classmethod
    def apply_action_restore(cls, user, triggers):
        restore_priority = triggers.order_by('-last_triggered', '-modified_on')
        trigger_scopes = set()

        # work through all the restored triggers in order of most recent used
        for trigger in restore_priority:
            trigger_scope = set(trigger.trigger_scopes())

            # if we haven't already restored a trigger with this scope
            if not trigger_scopes.intersection(trigger_scope):
                trigger.restore(user)
                trigger_scopes = trigger_scopes | trigger_scope

        return [t.pk for t in triggers]

    def release(self):
        """
        Releases this Trigger, use this instead of delete
        """
        self.is_active = False
        self.save()

    def fire(self):
        if self.is_archived or not self.is_active:
            return None

        channels = self.flow.org.channels.all()
        if not channels:
            return None

        groups = list(self.groups.all())
        contacts = list(self.contacts.all())

        if groups or contacts:
            self.last_triggered = timezone.now()
            self.trigger_count += 1
            self.save()

            return self.flow.start(groups, contacts, restart_participants=True)

        return False
<commit_msg>revert back to 1to1 field for trigger.channel, it will be in a separate PR later<commit_after>from __future__ import unicode_literals

import regex

from django.db import models
from django.utils import timezone
from django.utils.translation import ugettext_lazy as _
from smartmin.models import SmartModel
from temba.channels.models import Channel, ChannelEvent
from temba.contacts.models import Contact, ContactGroup
from temba.flows.models import Flow, FlowRun
from temba.ivr.models import IVRCall
from temba.msgs.models import Msg
from temba.orgs.models import Org

class Trigger(SmartModel):
    """
    A Trigger is used to start a user in a flow based on an event. For example, triggers might fire
    for missed calls, inboud sms messages starting with a keyword, or on a repeating schedule.
    """
    TYPE_KEYWORD = 'K'
    TYPE_SCHEDULE = 'S'
    TYPE_MISSED_CALL = 'M'
    TYPE_INBOUND_CALL = 'V'
    TYPE_CATCH_ALL = 'C'
    TYPE_FOLLOW = 'F'
    TYPE_NEW_CONVERSATION = 'N'
    TYPE_USSD_PULL = 'U'

    TRIGGER_TYPES = ((TYPE_KEYWORD, _("Keyword Trigger")),
                     (TYPE_SCHEDULE, _("Schedule Trigger")),
                     (TYPE_INBOUND_CALL, _("Inbound Call Trigger")),
                     (TYPE_MISSED_CALL, _("Missed Call Trigger")),
                     (TYPE_CATCH_ALL, _("Catch All Trigger")),
                     (TYPE_FOLLOW, _("Follow Account Trigger")),
                     (TYPE_NEW_CONVERSATION, _("New Conversation Trigger")),
                     (TYPE_USSD_PULL, _("USSD Pull Session Trigger")))

    org = models.ForeignKey(Org, verbose_name=_("Org"), help_text=_("The organization this trigger belongs to"))

    keyword = models.CharField(verbose_name=_("Keyword"), max_length=16, null=True, blank=True,
                               help_text=_("The first word in the message text"))

    flow = models.ForeignKey(Flow, verbose_name=_("Flow"),
                             help_text=_("Which flow will be started"), related_name="triggers")

    last_triggered = models.DateTimeField(verbose_name=_("Last Triggered"), default=None, null=True,
                                          help_text=_("The last time this trigger was fired"))

    trigger_count = models.IntegerField(verbose_name=_("Trigger Count"), default=0,
                                        help_text=_("How many times this trigger has fired"))

    is_archived = models.BooleanField(verbose_name=_("Is Archived"), default=False,
                                      help_text=_("Whether this trigger is archived"))

    groups = models.ManyToManyField(ContactGroup, verbose_name=_("Groups"),
                                    help_text=_("The groups to broadcast the flow to"))

    contacts = models.ManyToManyField(Contact, verbose_name=_("Contacts"),
                                      help_text=_("Individual contacts to broadcast the flow to"))

    schedule = models.OneToOneField('schedules.Schedule', verbose_name=_("Schedule"),
                                    null=True, blank=True, related_name='trigger',
                                    help_text=_('Our recurring schedule'))

    trigger_type = models.CharField(max_length=1, choices=TRIGGER_TYPES, default=TYPE_KEYWORD,
                                    verbose_name=_("Trigger Type"), help_text=_('The type of this trigger'))

    channel = models.OneToOneField(Channel, verbose_name=_("Channel"), null=True, help_text=_("The associated channel"))

    def __unicode__(self):
        if self.trigger_type == Trigger.TYPE_KEYWORD:
            return self.keyword
        return self.get_trigger_type_display()

    def name(self):
        return self.__unicode__()

    def as_json(self):
        """
        An exportable dict representing our trigger
        """
        return dict(trigger_type=self.trigger_type,
                    keyword=self.keyword,
                    flow=dict(uuid=self.flow.uuid, name=self.flow.name),
                    groups=[dict(uuid=group.uuid, name=group.name) for group in self.groups.all()],
                    channel=self.channel.uuid if self.channel else None)

    def trigger_scopes(self):
        """
        Returns keys that represents the scopes that this trigger can operate against (and might conflict with other triggers with)
        """
        groups = ['**'] if not self.groups else [str(g.id) for g in self.groups.all().order_by('id')]
        return ['%s_%s_%s_%s' % (self.trigger_type, str(self.channel_id), group, str(self.keyword)) for group in groups]

    def restore(self, user):
        self.modified_on = timezone.now()
        self.modified_by = user
        self.is_archived = False
        self.save()

        # archive any conflicts
        self.archive_conflicts(user)

        # if this is new conversation trigger, register for the FB callback
        if self.trigger_type == Trigger.TYPE_NEW_CONVERSATION:
            self.channel.set_fb_call_to_action_payload(Channel.GET_STARTED)

    def archive_conflicts(self, user):
        """
        Archives any triggers that conflict with this one
        """
        now = timezone.now()

        if not self.trigger_type == Trigger.TYPE_SCHEDULE:
            matches = Trigger.objects.filter(org=self.org, is_active=True, is_archived=False, trigger_type=self.trigger_type)

            # if this trigger has a keyword, only archive others with the same keyword
            if self.keyword:
                matches = matches.filter(keyword=self.keyword)

            # if this trigger has a group, only archive others with the same group
            if self.groups.all():
                matches = matches.filter(groups__in=self.groups.all())
            else:
                matches = matches.filter(groups=None)

            # if this trigger has a channel, only archive others with the same channel
            if self.channel:
                matches = matches.filter(channel=self.channel)

            # archive any conflicting triggers
            matches.exclude(id=self.id).update(is_archived=True, modified_on=now, modified_by=user)

    @classmethod
    def import_triggers(cls, exported_json, org, user, same_site=False):
        """
        Import triggers from our export file
        """
        from temba.orgs.models import EARLIEST_IMPORT_VERSION
        if exported_json.get('version', 0) < EARLIEST_IMPORT_VERSION:
            raise ValueError(_("Unknown version (%s)" % exported_json.get('version', 0)))

        # first things first, let's create our groups if necesary and map their ids accordingly
        if 'triggers' in exported_json:
            for trigger_spec in exported_json['triggers']:

                # resolve our groups
                groups = []
                for group_spec in trigger_spec['groups']:

                    group = None

                    if same_site:
                        group = ContactGroup.user_groups.filter(org=org, uuid=group_spec['uuid']).first()

                    if not group:
                        group = ContactGroup.get_user_group(org, group_spec['name'])

                    if not group:
                        group = ContactGroup.create_static(org, user, group_spec['name'])

                    if not group.is_active:
                        group.is_active = True
                        group.save()

                    groups.append(group)

                flow = Flow.objects.get(org=org, uuid=trigger_spec['flow']['uuid'], is_active=True)

                # see if that trigger already exists
                trigger = Trigger.objects.filter(org=org, trigger_type=trigger_spec['trigger_type'])

                if trigger_spec['keyword']:
                    trigger = trigger.filter(keyword__iexact=trigger_spec['keyword'])

                if groups:
                    trigger = trigger.filter(groups__in=groups)

                trigger = trigger.first()
                if trigger:
                    trigger.is_archived = False
                    trigger.flow = flow
                    trigger.save()
                else:

                    # if we have a channel resolve it
                    channel = trigger_spec.get('channel', None)  # older exports won't have a channel
                    if channel:
                        channel = Channel.objects.filter(uuid=channel, org=org).first()

                    trigger = Trigger.objects.create(org=org, trigger_type=trigger_spec['trigger_type'],
                                                     keyword=trigger_spec['keyword'], flow=flow,
                                                     created_by=user, modified_by=user,
                                                     channel=channel)

                    for group in groups:
                        trigger.groups.add(group)

    @classmethod
    def get_triggers_of_type(cls, org, trigger_type):
        return Trigger.objects.filter(org=org, trigger_type=trigger_type, is_active=True, is_archived=False)

    @classmethod
    def catch_triggers(cls, entity, trigger_type, channel):
        if isinstance(entity, Msg):
            contact = entity.contact
            start_msg = entity
        elif isinstance(entity, ChannelEvent) or isinstance(entity, IVRCall):
            contact = entity.contact
            start_msg = Msg(org=entity.org, contact=contact, channel=entity.channel, created_on=timezone.now(), id=0)
        elif isinstance(entity, Contact):
            contact = entity
            start_msg = Msg(org=entity.org, contact=contact, channel=channel, created_on=timezone.now(), id=0)
        else:
            raise ValueError("Entity must be of type msg, call or contact")

        triggers = Trigger.get_triggers_of_type(entity.org, trigger_type)

        if trigger_type in [Trigger.TYPE_FOLLOW, Trigger.TYPE_NEW_CONVERSATION]:
            triggers = triggers.filter(channel=channel)

        # is there a match for a group specific trigger?
        group_ids = contact.user_groups.values_list('pk', flat=True)
        group_triggers = triggers.filter(groups__in=group_ids).order_by('groups__name')

        # if we match with a group restriction, that takes precedence
        if group_triggers:
            triggers = group_triggers

        # otherwise, restrict to triggers that don't filter by group
        else:
            triggers = triggers.filter(groups=None)

        # only fire the first matching trigger
        if triggers:
            triggers[0].flow.start([], [contact], start_msg=start_msg, restart_participants=True)

        return bool(triggers)

    @classmethod
    def find_and_handle(cls, msg):
        from temba.msgs.models import INTERRUPTED, TRIGGERED

        if msg.status == INTERRUPTED:
            return False
        elif msg.status == TRIGGERED:
            matched_object = regex.match('^[\d\*\#]+$', msg.text.strip(), flags=regex.UNICODE)
            keyword = matched_object.string if matched_object else None
        else:
            # get the first word out of our message
            words = regex.split(r"[\W]+", msg.text.strip(), flags=regex.UNICODE | regex.V0)

            while words and not words[0]:
                words = words[1:]

            if not words:
                return False

            keyword = words[0].lower()

        if not keyword:
            return False

        active_run_qs = FlowRun.objects.filter(is_active=True, contact=msg.contact,
                                               flow__is_active=True, flow__is_archived=False)
        active_run = active_run_qs.prefetch_related('steps').order_by("-created_on", "-pk").first()

        if active_run and active_run.flow.ignore_triggers and not active_run.is_completed():
            return False

        groups_ids = msg.contact.user_groups.values_list('pk', flat=True)

        # Check first if we have a trigger for the contact groups
        matching = Trigger.objects.filter(is_archived=False, is_active=True, org=msg.org, keyword__iexact=keyword,
                                          flow__is_archived=False, flow__is_active=True, groups__in=groups_ids).order_by('groups__name').prefetch_related('groups', 'groups__contacts')

        # If no trigger for contact groups find there is a no group trigger
        if not matching:
            matching = Trigger.objects.filter(is_archived=False, is_active=True, org=msg.org, keyword__iexact=keyword,
                                              flow__is_archived=False, flow__is_active=True, groups=None).prefetch_related('groups', 'groups__contacts')

        if not matching:
            return False

        contact = msg.contact
        trigger = matching[0]

        if not contact.is_test:
            trigger.last_triggered = msg.created_on
            trigger.trigger_count += 1
            trigger.save()

        # There is no actual message, it was only used for triggering the flow
        if msg.status == TRIGGERED:
            msg = None

        # if we have an associated flow, start this contact in it
        trigger.flow.start([], [contact], start_msg=msg, restart_participants=True)

        return True

    @classmethod
    def find_flow_for_inbound_call(cls, contact):

        groups_ids = contact.user_groups.values_list('pk', flat=True)

        # Check first if we have a trigger for the contact groups
        matching = Trigger.objects.filter(is_archived=False, is_active=True, org=contact.org,
                                          trigger_type=Trigger.TYPE_INBOUND_CALL, flow__is_archived=False,
                                          flow__is_active=True, groups__in=groups_ids).order_by('groups__name')\
                                  .prefetch_related('groups', 'groups__contacts')

        # If no trigger for contact groups find there is a no group trigger
        if not matching:
            matching = Trigger.objects.filter(is_archived=False, is_active=True, org=contact.org,
                                              trigger_type=Trigger.TYPE_INBOUND_CALL, flow__is_archived=False,
                                              flow__is_active=True, groups=None)\
                                      .prefetch_related('groups', 'groups__contacts')

        if not matching:
            return None

        trigger = matching[0]
        trigger.last_triggered = timezone.now()
        trigger.trigger_count += 1
        trigger.save()

        return trigger.flow

    @classmethod
    def apply_action_archive(cls, user, triggers):
        triggers.update(is_archived=True)

        # for any new convo triggers, clear out the call to action payload
        for trigger in triggers.filter(trigger_type=Trigger.TYPE_NEW_CONVERSATION):
            trigger.channel.set_fb_call_to_action_payload(None)

        return [each_trigger.pk for each_trigger in triggers]

    @classmethod
    def apply_action_restore(cls, user, triggers):
        restore_priority = triggers.order_by('-last_triggered', '-modified_on')
        trigger_scopes = set()

        # work through all the restored triggers in order of most recent used
        for trigger in restore_priority:
            trigger_scope = set(trigger.trigger_scopes())

            # if we haven't already restored a trigger with this scope
            if not trigger_scopes.intersection(trigger_scope):
                trigger.restore(user)
                trigger_scopes = trigger_scopes | trigger_scope

        return [t.pk for t in triggers]

    def release(self):
        """
        Releases this Trigger, use this instead of delete
        """
        self.is_active = False
        self.save()

    def fire(self):
        if self.is_archived or not self.is_active:
            return None

        channels = self.flow.org.channels.all()
        if not channels:
            return None

        groups = list(self.groups.all())
        contacts = list(self.contacts.all())

        if groups or contacts:
            self.last_triggered = timezone.now()
            self.trigger_count += 1
            self.save()

            return self.flow.start(groups, contacts, restart_participants=True)

        return False
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
"""Server module to to take HTTP requests."""

import os
import socket
import sys
import time
from contextlib import closing

BUFFER_LENGTH = 8
ADDRINFO = ('127.0.0.1', 5000)

PARENT_DIR = os.path.dirname(__file__)
GRANDPARENT_DIR = os.path.join(PARENT_DIR, '..')
WEBROOT_PATH = os.path.join(GRANDPARENT_DIR, 'webroot', '')

# Unicode constants for standard re-usable parts.
CRLF = '\r\n'
GET = 'GET'
HTTP1_1 = 'HTTP/1.1'
HOST = 'Host: '
CONTENT_TYPE = 'Content-Type: {}'
CONTENT_LENGTH = 'Content-Length: {}'

TEXT_HTML = 'text/html; charset=utf-8'
TEXT_PLAIN = 'text/plain; charset=utf-8'

# Easy Reference dictionary with int keys for code/message part.
HTTP_CODES = {
    200: '200 OK',
    400: '400 Bad Request',
    404: '404 Not Found',
    405: '405 Method Not Allowed',
    500: '500 Internal Server Error',
    505: '505 HTTP Version Not Supported',
}

# updated HTML templates to use unicode strings
HTML_TEMPLATE = '''
                <!DOCTYPE html>

                    <meta charset="utf-8">
                    <title></title>

{header}

                      {list_items}

                '''
HTML_LI_TEMPLATE = '''

{short_path}

                   '''

# Throw 404 if request is not to one ofthese paths.
PATHS = {
    '/': ('Home page.', 'text/html'),
    '/signup': ('Signup page.', 'text/html'),
    '/login': ('Login page.', 'text/html'),
    '/pictures/python_photo.jpg': (b'', 'image/jpeg'),
}

def server():
    """Start a server using a socket at server.ADDRINFO."""
    print('\nStarting the server...')
    serv_sock = socket.socket(socket.AF_INET,
                              socket.SOCK_STREAM,
                              socket.IPPROTO_TCP)

    serv_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    serv_sock.bind(ADDRINFO)
    serv_sock.listen(1)
    print('Serving at {}:{}.'.format(*ADDRINFO))

    try:
        while True:
            print('Waiting for client connection...')
            conn, addr = serv_sock.accept()
            http_server(conn, addr)
    except KeyboardInterrupt:
        pass
    finally:
        print('\nShutting down the server...\n')
        serv_sock.close()
        sys.exit()

def http_server(conn, addr):
    """Take HTTP requests and return appropriate HTTP response."""
    # with closing(conn):
    request_parts = []
    while True:
        part = conn.recv(BUFFER_LENGTH)
        request_parts.append(part)
        if len(part) < BUFFER_LENGTH:
            break
    # Immediately decode all of incoming request into unicode.
    # In future, may need to check Content-type of incoming request?
    request = b''.join(request_parts).decode('utf-8')
    print('Request received:\n{}'.format(request))

    try:
        try:
            uri = parse_request(request)
            body, content_type = resolve_uri(uri)
        except ValueError as e:
            err_code = e.args[0]
            response_headers = response_error(err_code)
            body = HTTP_CODES[err_code]
        else:
            # Here body might be a bytestring.
            body_length = len(body)
            response_headers = response_ok(content_type, body_length)
    except Exception:
        response_headers = response_error(500)
        body = HTTP_CODES[500]

    # Re-encode into bytes on the way out.
    response_headers = response_headers.encode('utf-8')
    body = body.encode('utf-8')

    response = b''.join([response_headers, body])

    conn.sendall(response)
    conn.close()
    time.sleep(0.01)

def parse_request(request):
    """Parse client request."""
    method = ''
    uri = ''
    protocol = ''
    headers = ''

    # Should be able to separate body from headers at double carriage return.
    try:
        headers, body = request.split(CRLF * 2)
    except ValueError:
        raise ValueError(400)

    # Get list of all headers
    headers = headers.split(CRLF)

    # List of headers should not be empty.
    try:
        first_line = headers[0]
    except IndexError:
        raise ValueError(400)

    # Should be able to cleanly split first line into 3 parts.
    try:
        method, uri, protocol = first_line.split(' ')
    except ValueError:
        raise ValueError(400)

    # if not all((method, uri, protocol)):
    #     raise ValueError(400)

    # At least one of the headers should be the Host header.
    headers = headers[1:]
    for h in headers:
        if h.startswith(HOST):
            break
    else:
        raise ValueError(400)
    if method != GET:
        raise ValueError(405)
    if protocol != HTTP1_1:
        raise ValueError(505)

    return uri

def resolve_uri(uri):
    """Return tuple of content and content type, or raise 404 error."""
    try:
        return PATHS[uri]
    except KeyError:
        raise ValueError(404)

# Response_ok doesn't parse body (might be bytes as for an image).
# References key 200 from HTTP_CODES reference dictionary.
def response_ok(content_type, body_length):
    """Return 'HTTP/1.1 200 OK' for when connection ok."""
    return CRLF.join([
        ' '.join([HTTP1_1, HTTP_CODES[200]]),
        CONTENT_TYPE.format(content_type),
        CONTENT_LENGTH.format(body_length),
        CRLF,
    ])

# Response_error take an int as err_code, to reference HTTP_CODES dict.
def response_error(err_code):
    """Return 'Internal Server Error' for when problem occurs."""
    return CRLF.join([
        ' '.join([HTTP1_1, HTTP_CODES[err_code]]),
        CONTENT_TYPE.format(TEXT_PLAIN),
        CRLF
    ])

if __name__ == '__main__':
    server()
<commit_msg>Removed empty init of http request parts.<commit_after># -*- coding: utf-8 -*-
"""Server module to to take HTTP requests."""

import os
import socket
import sys
import time
from contextlib import closing

BUFFER_LENGTH = 8
ADDRINFO = ('127.0.0.1', 5000)

PARENT_DIR = os.path.dirname(__file__)
GRANDPARENT_DIR = os.path.join(PARENT_DIR, '..')
WEBROOT_PATH = os.path.join(GRANDPARENT_DIR, 'webroot', '')

# Unicode constants for standard re-usable parts.
CRLF = '\r\n'
GET = 'GET'
HTTP1_1 = 'HTTP/1.1'
HOST = 'Host: '
CONTENT_TYPE = 'Content-Type: {}'
CONTENT_LENGTH = 'Content-Length: {}'

TEXT_HTML = 'text/html; charset=utf-8'
TEXT_PLAIN = 'text/plain; charset=utf-8'

# Easy Reference dictionary with int keys for code/message part.
HTTP_CODES = {
    200: '200 OK',
    400: '400 Bad Request',
    404: '404 Not Found',
    405: '405 Method Not Allowed',
    500: '500 Internal Server Error',
    505: '505 HTTP Version Not Supported',
}

# updated HTML templates to use unicode strings
HTML_TEMPLATE = '''
                <!DOCTYPE html>

                    <meta charset="utf-8">
                    <title></title>

{header}

                      {list_items}

                '''
HTML_LI_TEMPLATE = '''

{short_path}

                   '''

# Throw 404 if request is not to one ofthese paths.
PATHS = {
    '/': ('Home page.', 'text/html'),
    '/signup': ('Signup page.', 'text/html'),
    '/login': ('Login page.', 'text/html'),
    '/pictures/python_photo.jpg': (b'', 'image/jpeg'),
}

def server():
    """Start a server using a socket at server.ADDRINFO."""
    print('\nStarting the server...')
    serv_sock = socket.socket(socket.AF_INET,
                              socket.SOCK_STREAM,
                              socket.IPPROTO_TCP)

    serv_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    serv_sock.bind(ADDRINFO)
    serv_sock.listen(1)
    print('Serving at {}:{}.'.format(*ADDRINFO))

    try:
        while True:
            print('Waiting for client connection...')
            conn, addr = serv_sock.accept()
            http_server(conn, addr)
    except KeyboardInterrupt:
        pass
    finally:
        print('\nShutting down the server...\n')
        serv_sock.close()
        sys.exit()

def http_server(conn, addr):
    """Take HTTP requests and return appropriate HTTP response."""
    # with closing(conn):
    request_parts = []
    while True:
        part = conn.recv(BUFFER_LENGTH)
        request_parts.append(part)
        if len(part) < BUFFER_LENGTH:
            break
    # Immediately decode all of incoming request into unicode.
    # In future, may need to check Content-type of incoming request?
    request = b''.join(request_parts).decode('utf-8')
    print('Request received:\n{}'.format(request))

    try:
        try:
            uri = parse_request(request)
            body, content_type = resolve_uri(uri)
        except ValueError as e:
            err_code = e.args[0]
            response_headers = response_error(err_code)
            body = HTTP_CODES[err_code]
        else:
            # Here body might be a bytestring.
            body_length = len(body)
            response_headers = response_ok(content_type, body_length)
    except Exception:
        response_headers = response_error(500)
        body = HTTP_CODES[500]

    # Re-encode into bytes on the way out.
    response_headers = response_headers.encode('utf-8')
    body = body.encode('utf-8')

    response = b''.join([response_headers, body])

    conn.sendall(response)
    conn.close()
    time.sleep(0.01)

def parse_request(request):
    """Parse client request."""

    # Should be able to separate body from headers at double carriage return.
    try:
        headers, body = request.split(CRLF * 2)
    except ValueError:
        raise ValueError(400)

    # Get list of all headers
    headers = headers.split(CRLF)

    # List of headers should not be empty.
    try:
        first_line = headers[0]
    except IndexError:
        raise ValueError(400)

    # Should be able to cleanly split first line into 3 parts.
    try:
        method, uri, protocol = first_line.split(' ')
    except ValueError:
        raise ValueError(400)

    # if not all((method, uri, protocol)):
    #     raise ValueError(400)

    # At least one of the headers should be the Host header.
    headers = headers[1:]
    for h in headers:
        if h.startswith(HOST):
            break
    else:
        raise ValueError(400)
    if method != GET:
        raise ValueError(405)
    if protocol != HTTP1_1:
        raise ValueError(505)

    return uri

def resolve_uri(uri):
    """Return tuple of content and content type, or raise 404 error."""
    try:
        return PATHS[uri]
    except KeyError:
        raise ValueError(404)

# Response_ok doesn't parse body (might be bytes as for an image).
# References key 200 from HTTP_CODES reference dictionary.
def response_ok(content_type, body_length):
    """Return 'HTTP/1.1 200 OK' for when connection ok."""
    return CRLF.join([
        ' '.join([HTTP1_1, HTTP_CODES[200]]),
        CONTENT_TYPE.format(content_type),
        CONTENT_LENGTH.format(body_length),
        CRLF,
    ])

# Response_error take an int as err_code, to reference HTTP_CODES dict.
def response_error(err_code):
    """Return 'Internal Server Error' for when problem occurs."""
    return CRLF.join([
        ' '.join([HTTP1_1, HTTP_CODES[err_code]]),
        CONTENT_TYPE.format(TEXT_PLAIN),
        CRLF
    ])

if __name__ == '__main__':
    server()
<|endoftext|>


<commit_before>from __future__ import absolute_import, unicode_literals

import datetime
import pytz

from mock import patch
from . import TembaClient
from ..exceptions import TembaRateExceededError
from ..tests import TembaTest, MockResponse

@patch('temba_client.clients.request')
class TembaClientTest(TembaTest):
    API_VERSION = 2

    def setUp(self):
        self.client = TembaClient('example.com', '1234567890', user_agent='test/0.1')

    def test_retry_on_rate_exceed(self, mock_request):
        fail_then_success = [
            MockResponse(429, '', {'Retry-After': 1}),
            MockResponse(200, self.read_json('runs'))
        ]
        mock_request.side_effect = fail_then_success

        # no retries means exception right away
        iterator = self.client.get_runs().iterfetches(retry_on_rate_exceed=False)
        self.assertRaises(TembaRateExceededError, iterator.__next__)

        mock_request.side_effect = fail_then_success

        # retries means it will try again after the first 429
        runs = self.client.get_runs().iterfetches(retry_on_rate_exceed=True).next()
        self.assertEqual(len(runs), 2)

        mock_request.side_effect = fail_then_success

        query = self.client.get_runs()
        self.assertRaises(TembaRateExceededError, query.all, retry_on_rate_exceed=False)

        mock_request.side_effect = fail_then_success

        runs = self.client.get_runs().all(retry_on_rate_exceed=True)
        self.assertEqual(len(runs), 2)

        # if requests always return 429, we will hit max retries regardless
        mock_request.side_effect = None
        mock_request.return_value = MockResponse(429, '', {'Retry-After': 1})

        self.assertRaises(TembaRateExceededError, self.client.get_runs().all, retry_on_rate_exceed=False)
        self.assertRaises(TembaRateExceededError, self.client.get_runs().all, retry_on_rate_exceed=True)

    def test_get_runs(self, mock_request):
        # check no params
        mock_request.return_value = MockResponse(200, self.read_json('runs'))

        # check with no params
        query = self.client.get_runs()
        runs = query.all()

        self.assert_request(mock_request, 'get', 'runs')
        self.assertEqual(len(runs), 2)

        self.assertEqual(runs[0].id, 4092373)
        self.assertEqual(runs[0].flow, "ffce0fbb-4fe1-4052-b26a-91beb2ebae9a")
        self.assertEqual(runs[0].contact, "d33e9ad5-5c35-414c-abd4-e7451c69ff1d")
        self.assertEqual(runs[0].responded, True)
        self.assertEqual(len(runs[0].steps), 3)
        self.assertEqual(runs[0].steps[0].node, "ca6c092a-1615-474e-ab64-4e7ce75a808f")
        self.assertEqual(runs[0].steps[0].category, None)
        self.assertEqual(runs[0].steps[0].left_on, datetime.datetime(2015, 8, 26, 10, 4, 10, 6241, pytz.utc))
        self.assertEqual(runs[0].steps[0].text, "Hi Bobby, is your water filter working? Answer with Yes or No.")
        self.assertEqual(runs[0].steps[0].value, None)
        self.assertEqual(runs[0].steps[0].arrived_on, datetime.datetime(2015, 8, 26, 10, 4, 9, 824114, pytz.utc))
        self.assertEqual(runs[0].steps[0].type, "actionset")
        self.assertEqual(runs[0].created_on, datetime.datetime(2015, 8, 26, 10, 4, 9, 737686, pytz.utc))
        self.assertEqual(runs[0].modified_on, datetime.datetime(2015, 8, 26, 10, 5, 47, 516562, pytz.utc))
        self.assertEqual(runs[0].exited_on, datetime.datetime(2015, 8, 26, 10, 5, 47, 516562, pytz.utc))
        self.assertEqual(runs[0].exit_type, "completed")

        self.assertEqual(query.first().id, runs[0].id)
        self.assert_request(mock_request, 'get', 'runs')

        # check with all params
        query = self.client.get_runs(flow="ffce0fbb-4fe1-4052-b26a-91beb2ebae9a",
                                     contact="d33e9ad5-5c35-414c-abd4-e7451c69ff1d",
                                     responded=True,
                                     after=datetime.datetime(2014, 12, 12, 22, 34, 36, 978123, pytz.utc),
                                     before=datetime.datetime(2014, 12, 12, 22, 56, 58, 917123, pytz.utc))
        query.all()

        self.assert_request(mock_request, 'get', 'runs', params={'flow': "ffce0fbb-4fe1-4052-b26a-91beb2ebae9a",
                                                                 'contact': "d33e9ad5-5c35-414c-abd4-e7451c69ff1d",
                                                                 'responded': True,
                                                                 'after': "2014-12-12T22:34:36.978123",
                                                                 'before': "2014-12-12T22:56:58.917123"})

        # check when result is empty
        mock_request.return_value = MockResponse(200, self.read_json('empty'))
        query = self.client.get_runs()

        self.assertEqual(query.all(), [])
        self.assertEqual(query.first(), None)
<commit_msg>No really, fix unit test on Python 3<commit_after>from __future__ import absolute_import, unicode_literals

import datetime
import pytz

from mock import patch
from . import TembaClient
from ..exceptions import TembaRateExceededError
from ..tests import TembaTest, MockResponse

@patch('temba_client.clients.request')
class TembaClientTest(TembaTest):
    API_VERSION = 2

    def setUp(self):
        self.client = TembaClient('example.com', '1234567890', user_agent='test/0.1')

    def test_retry_on_rate_exceed(self, mock_request):
        fail_then_success = [
            MockResponse(429, '', {'Retry-After': 1}),
            MockResponse(200, self.read_json('runs'))
        ]
        mock_request.side_effect = fail_then_success

        # no retries means exception right away
        iterator = self.client.get_runs().iterfetches(retry_on_rate_exceed=False)
        self.assertRaises(TembaRateExceededError, iterator.__next__)

        mock_request.side_effect = fail_then_success

        # retries means it will try again after the first 429
        runs = self.client.get_runs().iterfetches(retry_on_rate_exceed=True).__next__()
        self.assertEqual(len(runs), 2)

        mock_request.side_effect = fail_then_success

        query = self.client.get_runs()
        self.assertRaises(TembaRateExceededError, query.all, retry_on_rate_exceed=False)

        mock_request.side_effect = fail_then_success

        runs = self.client.get_runs().all(retry_on_rate_exceed=True)
        self.assertEqual(len(runs), 2)

        # if requests always return 429, we will hit max retries regardless
        mock_request.side_effect = None
        mock_request.return_value = MockResponse(429, '', {'Retry-After': 1})

        self.assertRaises(TembaRateExceededError, self.client.get_runs().all, retry_on_rate_exceed=False)
        self.assertRaises(TembaRateExceededError, self.client.get_runs().all, retry_on_rate_exceed=True)

    def test_get_runs(self, mock_request):
        # check no params
        mock_request.return_value = MockResponse(200, self.read_json('runs'))

        # check with no params
        query = self.client.get_runs()
        runs = query.all()

        self.assert_request(mock_request, 'get', 'runs')
        self.assertEqual(len(runs), 2)

        self.assertEqual(runs[0].id, 4092373)
        self.assertEqual(runs[0].flow, "ffce0fbb-4fe1-4052-b26a-91beb2ebae9a")
        self.assertEqual(runs[0].contact, "d33e9ad5-5c35-414c-abd4-e7451c69ff1d")
        self.assertEqual(runs[0].responded, True)
        self.assertEqual(len(runs[0].steps), 3)
        self.assertEqual(runs[0].steps[0].node, "ca6c092a-1615-474e-ab64-4e7ce75a808f")
        self.assertEqual(runs[0].steps[0].category, None)
        self.assertEqual(runs[0].steps[0].left_on, datetime.datetime(2015, 8, 26, 10, 4, 10, 6241, pytz.utc))
        self.assertEqual(runs[0].steps[0].text, "Hi Bobby, is your water filter working? Answer with Yes or No.")
        self.assertEqual(runs[0].steps[0].value, None)
        self.assertEqual(runs[0].steps[0].arrived_on, datetime.datetime(2015, 8, 26, 10, 4, 9, 824114, pytz.utc))
        self.assertEqual(runs[0].steps[0].type, "actionset")
        self.assertEqual(runs[0].created_on, datetime.datetime(2015, 8, 26, 10, 4, 9, 737686, pytz.utc))
        self.assertEqual(runs[0].modified_on, datetime.datetime(2015, 8, 26, 10, 5, 47, 516562, pytz.utc))
        self.assertEqual(runs[0].exited_on, datetime.datetime(2015, 8, 26, 10, 5, 47, 516562, pytz.utc))
        self.assertEqual(runs[0].exit_type, "completed")

        self.assertEqual(query.first().id, runs[0].id)
        self.assert_request(mock_request, 'get', 'runs')

        # check with all params
        query = self.client.get_runs(flow="ffce0fbb-4fe1-4052-b26a-91beb2ebae9a",
                                     contact="d33e9ad5-5c35-414c-abd4-e7451c69ff1d",
                                     responded=True,
                                     after=datetime.datetime(2014, 12, 12, 22, 34, 36, 978123, pytz.utc),
                                     before=datetime.datetime(2014, 12, 12, 22, 56, 58, 917123, pytz.utc))
        query.all()

        self.assert_request(mock_request, 'get', 'runs', params={'flow': "ffce0fbb-4fe1-4052-b26a-91beb2ebae9a",
                                                                 'contact': "d33e9ad5-5c35-414c-abd4-e7451c69ff1d",
                                                                 'responded': True,
                                                                 'after': "2014-12-12T22:34:36.978123",
                                                                 'before': "2014-12-12T22:56:58.917123"})

        # check when result is empty
        mock_request.return_value = MockResponse(200, self.read_json('empty'))
        query = self.client.get_runs()

        self.assertEqual(query.all(), [])
        self.assertEqual(query.first(), None)
<|endoftext|>