from keras import backend as K
from keras.models import load_model
from models.keras_ssd7 import build_model
from keras.preprocessing import image

from matplotlib import pyplot as plt
from imageio import imread
import numpy as np
import cv2

import urllib.request
import urllib.parse
import json
import time
import base64

import requests as req
from PIL import Image
from io import BytesIO

import ssl
from flask import Flask, request
from flask_restful import reqparse, Api, Resource
from flask_cors import CORS

import re

ssl._create_default_https_context = ssl._create_unverified_context

class containerNumPredict():

    def __init__(self, image_dir, appcode, url_request):

        self.img_height = 300  # Height of the input images
        self.img_width = 300  # Width of the input images
        self.img_channels = 3  # Image channels
        self.n_classes = 1  # number of class user need to detect.
        self.confi_threshold = 0.2  # parameter for confidence threshold to yeild potential object.
        self.appcode = 'APPCODE ' + appcode  # Appcode for OCR.
        self.image_dir = image_dir  # directory of image file.
        self.url_request = url_request  # url_request for OCR.
        self.headers = {
            'Authorization': self.appcode,
            'Content-Type': 'application/json; charset=UTF-8'
        }  # header param for OCR
        self.xmin = 0
        self.ymin = 0
        self.xmax = 0
        self.ymax = 0

        self.firstTry = True  # 给两次搜索的机会

    def predictObject(self):

        K.clear_session()  # Clear previous models from memory.

        # build object detection model
        model = build_model(image_size=(self.img_height, self.img_width, self.img_channels),
                            n_classes=self.n_classes,
                            mode='inference',
                            l2_regularization=0.0005,
                            scales=[0.08, 0.16, 0.32, 0.64, 0.96],
                            aspect_ratios_global=[0.5, 1.0, 2.0],
                            aspect_ratios_per_layer=None,
                            two_boxes_for_ar1=True,
                            steps=None,
                            offsets=None,
                            clip_boxes=False,
                            variances=[1.0, 1.0, 1.0, 1.0],
                            normalize_coords=True,
                            subtract_mean=127.5,
                            divide_by_stddev=127.5)

        # 2: Optional: Load some weights
        model.load_weights('./trained_weights_ssd7/ssd7_300_300_V1_epoch-70_loss-1.3692_val_loss-1.0694.h5', by_name=True)

        # Two list for storing the original image data and resized image data
        self.orig_images = []  # Store the original images here.
        input_images = []  # Store the resized images here.

        # We'll only load one image in this example.
        try:
            resp = urllib.request.urlopen(self.image_dir)
        except urllib.error.HTTPError as e:
            print("识别图片URL路径错误！")
            return [e.code, e.code]

        img_orig = np.asarray(bytearray(resp.read()), dtype="uint8")
        img_orig = cv2.imdecode(img_orig, cv2.IMREAD_COLOR)

        # self.orig_images.append(img_orig)
        self.orig_images.append(img_orig)

        # img_new = cv2.resize(img_orig, (self.img_width, self.img_height), interpolation=cv2.INTER_CUBIC)
        img_new = cv2.resize(img_orig, (self.img_width, self.img_height), interpolation=cv2.INTER_CUBIC)

        input_images.append(img_new)
        input_images = np.array(input_images)
        # predicting image
        y_pred = model.predict(input_images)

        # using confidence rate to filter the prediction output
        y_pred_thresh = [y_pred[k][y_pred[k, :, 1] > self.confi_threshold] for k in range(y_pred.shape[0])]

        if len(y_pred_thresh) == 1 and len(y_pred_thresh[0]) == 0:
            self.firstTry = False
            img_encode = cv2.imencode('.jpg', self.orig_images[0])[1]
            encodestr = str(base64.b64encode(img_encode), 'utf-8')
            diction = {'image': encodestr}
            return [self.posturl(self.url_request, diction), self.posturl(self.url_request, diction)]

        max_prob = 0

        for box in y_pred_thresh[0]:
            if box[1] > max_prob:
                max_prob = box[1]

        for box in y_pred_thresh[0]:
            if box[1] == max_prob:
                # Transform the predicted bounding boxes for the 300x480 image to the original image dimensions.
                self.xmin = max(0, int(box[2] * self.orig_images[0].shape[1] / self.img_width) - 30)
                self.ymin = max(0, int(box[3] * self.orig_images[0].shape[0] / self.img_height) - 20)
                self.xmax = min(int(box[4] * self.orig_images[0].shape[1] / self.img_width) + 30,
                                self.orig_images[0].shape[1])
                self.ymax = min(int(box[5] * self.orig_images[0].shape[0] / self.img_height) + 20,
                                self.orig_images[0].shape[0])

        #        print(self.xmin, self.ymin, self.xmax, self.ymax)

        # The part of image cropped for containerNum.
        cropImg_conNum = self.orig_images[0][self.ymin:self.ymax, self.xmin:self.xmax]
        # cropImg_weight = self.orig_images[0][min(int(box[5] * self.orig_images[0].shape[0] / self.img_height) + 80,
        #                         self.orig_images[0].shape[0]):self.orig_images[0].shape[0], self.xmin:self.orig_images[0].shape[1]]
        cropImg_weight = self.orig_images[0][min(self.ymax + 60, self.orig_images[0].shape[0]):self.orig_images[0].shape[0], self.xmin:self.orig_images[0].shape[1]]

        if cropImg_conNum is not None:
            print("Object detection successfully!")
        else:
            return [600,600]  # code 600 means a new image is needed

        img_encode_conNum = cv2.imencode('.jpg', cropImg_conNum)[1]
        encodestr_conNum = str(base64.b64encode(img_encode_conNum), 'utf-8')

        diction_conNum = {'image': encodestr_conNum}

        img_encode_weight = cv2.imencode('.jpg', cropImg_weight)[1]
        encodestr_weight = str(base64.b64encode(img_encode_weight), 'utf-8')

        diction_weight = {'image': encodestr_weight}

        result_conNum = self.posturl(self.url_request, diction_conNum)
        result_weight = self.posturl(self.url_request, diction_weight)

        # return [self.posturl(self.url_request, diction_conNum), self.posturl(self.url_request, diction_weight)]
        return [result_weight, result_conNum]

    def posturl(self, url, data={}):
        try:
            params = json.dumps(data).encode(encoding='UTF8')
            req = urllib.request.Request(url, params, self.headers)
            r = urllib.request.urlopen(req)
            html = r.read()
            r.close();
            return html.decode("utf8")
        except urllib.error.HTTPError as e:
            return (e.code)
            print(e.code)
            print(e.read().decode("utf8"))
        time.sleep(1)

    def extractWeightOCR(self, weightOCR):
        words = []
        numbers = []
    # 从箱号下方区域中提取文字信息
        candidates = []

 #       print(weightOCR)
 #       print(type(weightOCR))

        data_dic = json.loads(weightOCR)
        if 'ret' not in data_dic:
            return 700

        for pair in data_dic['ret']:
            candidates.append([pair['rect']['top'], pair['word']])

        if not candidates:
            return 700

        candidates.sort(key = lambda c: c[0])

        for i in candidates:
            words.append(i[1])

        pattern = re.compile(r'\d+')

        print(words)

        for word in words:
            if len(word) > 12:
                if 'KG' in word or 'K.G' in word:
                    numbers += re.split(r'[a-zA-Z]+', word)
            else:
                res = re.findall(pattern, word)
                num = ''
                for i in res:
                    num += i
                if num:
                    numbers.append(num)

        if len(numbers) != 0:
            for i in range(len(numbers)):
                numbers[i] = ''.join(list(filter(str.isdigit, numbers[i])))

            while "" in numbers:
                numbers.remove("")

            while " " in numbers:
                numbers.remove(" ")

        print(numbers)

        # if len(numbers) > 6:
        #     numbers.sort(key = lambda c: int(c))
        for i in range(len(numbers)):
            if len(numbers[i]) == 4:
                for val in range(-20, 21):
                    if str(round(int(numbers[i]) * 2.2046) + val) in numbers:
                        return [numbers[i]+'KG', str(round(int(numbers[i]) * 2.2046) + val) + 'LB']

        numbers.sort(key = lambda c: int(c))

        if len(numbers) > 6:
            for c in numbers[::-1]:
                if len(c) == 4:
                    if int(c) < 5500:
                        return [str(c) + 'KG', str(round(int(c) * 2.2046))+'LB']
                    # return [c+'LB', numbers]
                    else:
                        return [str(round(int(c) / 2.2046)) + 'KG', c+'LB']

        if len(numbers) <= 6:
            for c in numbers:
                if len(c) == 4:
                    return [c+'KG', str(round(int(c) * 2.2046))+'LB']

        print('numbers: ' + str(numbers))

            # numbers_combine.sort(key = lambda c: int(c))

        # if len(numbers) :
        #     return [numbers[2], numbers]
        if len(numbers) == 0:
            return 700

        return ["", ""]

    def extractWordsFromOCRResult(self, ocr_str):
        data_dic = json.loads(ocr_str)
        #        print(data_dic)
        candidates = []
        if 'ret' not in data_dic:
            return 700
        for pair in data_dic['ret']:
            candidates.append([pair['rect']['top'], pair['rect']['left'], \
                               pair['word'], pair['rect']['height'], pair['rect']['width']])

        if not candidates:
            return 700

        candidates.sort(key=lambda x: x[0] + x[1])

        print(candidates)

        if len(candidates) == 1:
            return candidates[0][2]

        if len(candidates) < 8:  # 没有全图搜的情况，特征数没有那么多
            for c in candidates:
                if len(c[2]) >= 10:
                    if c[2].find('<unk>') >= 0:
                        c[2] = c[2].replace('<unk>', '')
                    elif len(c[2].replace(' ', '')) >= 10:
                        return c[2]

                if len(c[2]) >= 6:
                    return candidates[0][2] + c[2]

        i = 1
        while i < len(candidates):
            if ' ' in candidates[i][2]:
                strs = candidates[i][2].split(' ')
                if candidates[i - 1][2].find(strs[0]) >= 0:
                    candidates[i][2] = ''.join(strs[1:])
            # 如果该结果为之前结果的子串，删除掉。例如之前识别了ABCD, 此时出现了BCD，显然OCR识别重复了。
            if len(candidates[i][2]) > 1 and candidates[i - 1][2].find(candidates[i][2]) >= 0:
                candidates.pop(i)
            else:
                i += 1

        res = ''
        for c in candidates:
            res += c[2]
            if len(res) >= 10:
                break
        #        print(res)
        return res

    def checkIfTryAgain(self):
        if not self.firstTry:
            return 700
        self.firstTry = False
        img_encode = cv2.imencode('.jpg', self.orig_images[0])[1]
        encodestr = str(base64.b64encode(img_encode), 'utf-8')
        diction = {'image': encodestr}
        ocr_str = self.posturl(self.url_request, diction)
        res = self.extractWordsFromOCRResult(ocr_str)
        return self.rectifyResult(res)

    def rectifyResult(self, res):
        # res = res.replace(' ', '')
        # res = res.replace('.', '')
        res = re.sub("[^0-9a-zA-Z]", "", res)
        res = res[:11]

        if len(res) < 10:
            return self.checkIfTryAgain()

        # to list, so that easy to revise
        res_list = list(res)

        for i in range(4):
            if res_list[i] == '0':
                res_list[i] = 'O'
            if res_list[i] == 'u':
                res_list[i] = 'U'

        for i in range(4, len(res_list)):
            if res_list[i] == 'o' or res_list[i] == 'O' or res_list[i] == 'Q':
                res_list[i] = '0'
            elif res_list[i] == 'g':
                res_list[i] = '9'
            elif res_list[i] == 'B':
                res_list[i] = '8'
            elif res_list[i] == 'I':
                res_list[i] = '1'
            elif res_list[i] == 'A':
                res_list[i] = '4'

        if not res_list[3].isalpha():  # 只读取了前三个字母，框的区域短了一点
            return self.checkIfTryAgain()

        # check digit
        # create dict for character
        dic = {str(i): i for i in range(10)}
        num = 10
        for i in range(26):
            dic[chr(65 + i)] = num
            num += 1
            if not num % 11:
                num += 1
        # calculate check digit
        tmp_sum = 0
        for i in range(10):
            if res_list[i] not in dic:
                return self.checkIfTryAgain()
            tmp_sum += dic[res_list[i]] * (2 ** i)
        check_digit = tmp_sum % 11
        if check_digit == 10:
            check_digit = 0

        if len(res_list) < 11:
            res_list.append(str(check_digit))
        if res_list[-1] != str(check_digit) or res_list[-1].isalpha():
            print('check digit incorrect!')
            res_list[-1] = str(check_digit)

        res = ''.join(res_list)
        return res

class API_mashiji():

    def __init__(self, image_dir, appcode, url_request):
        self.appcode = 'APPCODE ' + appcode  # Appcode for OCR.
        self.image_dir = image_dir  # directory of image file.
        self.url_request = url_request  # url_request for OCR.
        self.headers = {
            'Authorization': self.appcode,
            'Content-Type': 'application/json; charset=UTF-8'
        }

    def imageToOCR(self):
        with open(self.image_dir, 'rb') as f:  # 以二进制读取本地图片
            data = f.read()
            encodestr = str(base64.b64encode(data), 'utf-8')
        return encodestr

    def urlImage(self):
        resp = urllib.request.urlopen(self.image_dir)
        encodestr = np.asarray(bytearray(resp.read()), dtype="uint8")
        #encodestr = cv2.imdecode(encodestr, cv2.IMREAD_COLOR)
        encodestr = str(base64.b64encode(encodestr), 'utf-8')
        return encodestr

    def posturl(self, data={}):
        try:
            params = json.dumps(data).encode(encoding='UTF8')
            req = urllib.request.Request(self.url_request, params, self.headers)
            r = urllib.request.urlopen(req)
            html = r.read()
            r.close()
            return html.decode("utf8")
        except urllib.error.HTTPError as e:
            print(e.code)
            print(e.read().decode("utf8"))
        time.sleep(1)

    def extractWordsFromOCRResult(self, ocr_str):
        data_dic = json.loads(ocr_str)
#        print(data_dic)
        candidates = []

        if 'ret' not in data_dic:
            return 700
        for pair in data_dic['ret']:
            candidates.append([pair['rect']['top'], pair['rect']['left'], \
                               pair['word'], pair['rect']['height'], pair['rect']['width']])

        if not candidates:
            return 700

        candidates.sort(key=lambda x: x[0] + x[1])

        if len(candidates) == 1:
            output = candidates[0][2].replace(' ','')

            return output

#        if len(candidates) < 8:  # 没有全图搜的情况，特征数没有那么多
        for c in candidates:
            if 13 >= len(c[2]) >= 7:
                if c[2].find('<unk>') >= 0:
                    c[2] = c[2].replace('<unk>', '')
                output = c[2].replace(' ', '')
                if len(output) >= 7 and len(''.join(re.findall(r'\d*', output))) >= 7:
#                    print('diyi')
                    return output

                if len(output) >= 9 and len(''.join(re.findall(r'\d*', output))) >= 6:
                    if output[0].isalpha():
#                        print("dier")
                        return output

                if len(output) >= 8 and len(''.join(re.findall(r'\d*', output))) >= 4:
                    if output[0].isalpha():
#                        print("disan")
                        return output

                if len(output) >= 9 and len(''.join(re.findall(r'\d*', output))) >= 3:
                    if output[0].isalpha():
#                        print("disi")
                        return output

        # i = 1
        # while i < len(candidates):
        #     if ' ' in candidates[i][2]:
        #         strs = candidates[i][2].split(' ')
        #         if candidates[i - 1][2].find(strs[0]) >= 0:
        #             candidates[i][2] = ''.join(strs[1:])
        #     # 如果该结果为之前结果的子串，删除掉。例如之前识别了ABCD, 此时出现了BCD，显然OCR识别重复了。
        #     if len(candidates[i][2]) > 1 and candidates[i - 1][2].find(candidates[i][2]) >= 0:
        #         candidates.pop(i)
        #     else:
        #         i += 1
        #
        for c in candidates:
            if 13 >= len(c[2]) >= 7:
                if c[2].find('<unk>') >= 0:
                    c[2] = c[2].replace('<unk>', '')
                output = c[2].replace(' ', '')
                if len(output) >= 5 and len(''.join(re.findall(r'\d*', output))) >= 5:
                    return output

        # if re.match(r'^[A-Za-z0-9]+', res):
        #     return res
        # else:
        #     return 700
        return 700

parser_put = reqparse.RequestParser()
parser_put.add_argument("path", type=str, required=True, help="need image path")
app = Flask(__name__)
CORS(app, supports_credentials=True)
api = Api(app)

#提取集装箱号的app route.
@app.route('/container', methods=['get', 'post'])
def container():
    if 'path' in request.args:
        infos = request.args['path']
    else:
        tmp = parser_put.parse_args()
        infos = tmp['path']

    url_request = "https://tysbgpu.market.alicloudapi.com/api/predict/ocr_general"
    appcode = '4335919ca03a4714832942f64c6cef2b'

    containerNum = containerNumPredict(image_dir=infos, appcode=appcode, url_request=url_request)

    ocr_str = containerNum.predictObject()
    ocr_str_conNum = ocr_str[1]
    ocr_str_weight = ocr_str[0]

    success_conNum = 0
    success_weight = 0
    msg_conNum = ""
    msg_weight = ""
    res_conNum = ""
    res_weight_kg = ""
    res_weight_lb = ""

    if ocr_str_conNum == 400:
        msg_conNum = "阿里OCR参数错误"
        print("箱号识别：阿里OCR参数错误")
    elif ocr_str_conNum == 401:
        msg_conNum = "您无阿里OCR的权限，请开通后使用"
        print("箱号识别：您无阿里OCR的权限，请开通后使用")
    elif ocr_str_conNum == 403:
        msg_conNum = "阿里OCR购买的容量已用完或者签名错误"
        print("箱号识别:阿里OCR购买的容量已用完或者签名错误")
    elif ocr_str_conNum == 500:
        msg_conNum = "阿里OCR服务器错误，请稍后重试"
        print("箱号识别：阿里OCR服务器错误，请稍后重试")
    elif ocr_str_conNum == 600:
        msg_conNum = "无法识别该图片，请重新上传图片"
        print("箱号识别：无法识别该图片，请重新上传图片")
    elif ocr_str_conNum == 700:
        msg_conNum = "解析出错，请重新上传图片"
        print("箱号识别：解析出错，请重新上传图片")
    elif type(ocr_str_conNum) == int:
        msg_conNum = "网络错误，请稍后再试"
        print("箱号识别：网络错误，请稍后再试")
    elif not ocr_str_conNum:
        msg_conNum = "无法识别该图片，请重新上传图片"
        print("箱号识别：无法识别该图片，请重新上传图片")
    elif ocr_str_conNum == "{" or ocr_str_conNum == "}" or ocr_str_conNum == "\"":
        msg_conNum = "无法识别该图片，请重新上传图片"
        print("箱号识别：无法识别该图片，请重新上传图片")
    else:
        res_conNum = containerNum.extractWordsFromOCRResult(ocr_str_conNum)
        print(res_conNum)
        if res_conNum == 700:
            res_conNum = ""
            msg_conNum = "解析出错，请重新上传图片"
            print("箱号识别：解析出错，请重新上传图片")
            # return json.dumps({"containerNum": res, "msg_conNum": msg_conNum, "success": success})
        else:
            res_conNum = containerNum.rectifyResult(res_conNum)
            print(res_conNum)
            if res_conNum == 700:
                res_conNum = ""
                msg_conNum = "解析出错，请重新上传图片"
                print("箱号识别：解析出错，请重新上传图片")
                # return json.dumps({"containerNum": res, "msg_conNum": msg_conNum, "success": success})
            else:
                success_conNum = 1

    if ocr_str_weight == 400:
        msg_weight = "阿里OCR参数错误"
        print("重量识别：阿里OCR参数错误")
    elif ocr_str_weight == 401:
        msg_weight = "您无阿里OCR的权限，请开通后使用"
        print("重量识别：您无阿里OCR的权限，请开通后使用")
    elif ocr_str_weight == 403:
        msg_weight = "阿里OCR购买的容量已用完或者签名错误"
        print("重量识别：阿里OCR购买的容量已用完或者签名错误")
    elif ocr_str_weight == 500:
        msg_weight = "阿里OCR服务器错误，请稍后重试"
        print("重量识别：阿里OCR服务器错误，请稍后重试")
    elif ocr_str_weight == 600:
        msg_weight = "无法识别该图片，请重新上传图片"
        print("重量识别：无法识别该图片，请重新上传图片")
    elif ocr_str_weight == 700:
        msg_weight = "重量解析出错，请重新上传图片"
        print("重量识别：重量解析出错，请重新上传图片")
    elif type(ocr_str_weight) == int:
        msg_weight = "网络错误，请稍后再试"
        print("重量识别：网络错误，请稍后再试")
    elif not ocr_str_weight:
        msg_weight = "无法识别该图片，请重新上传图片"
        print("重量识别：无法识别该图片，请重新上传图片")
    elif ocr_str_weight == "{" or ocr_str_weight == "}" or ocr_str_weight == "\"":
        msg_weight = "无法识别该图片，请重新上传图片"
        print("重量识别：无法识别该图片，请重新上传图片")
    else:
        res_weight = containerNum.extractWeightOCR(ocr_str_weight)

        if res_weight == 700 or res_weight == ["", ""]:
            msg_weight = "重量解析出错，请重新上传图片"
            print("重量识别：重量解析出错，请重新上传图片")
            # return json.dumps({"containerNum": res, "msg_conNum": msg_conNum, "success": success})
        else:
            success_weight = 1
            res_weight_kg = res_weight[0]
            res_weight_lb = res_weight[1]

    return json.dumps({"res": res_conNum, "msg": msg_conNum, "success": success_conNum, "weight_kg": res_weight_kg, "weight_lb": res_weight_lb, "msg_weight": msg_weight, "success_weight": success_weight})

#提取铅封号的route.
@app.route('/qianfenghao', methods=['get', 'post'])
def mashiji():

    if 'path' in request.args:
        infos = request.args['path']
    else:
        tmp = parser_put.parse_args()
        infos = tmp['path']

    url_request = "https://tysbgpu.market.alicloudapi.com/api/predict/ocr_general"
    appcode = '4335919ca03a4714832942f64c6cef2b'

    mashiji_Num = API_mashiji(image_dir=infos, appcode=appcode, url_request=url_request)

#    encodestr = mashiji_Num.imageToOCR()
    encodestr = mashiji_Num.urlImage()
    encodestr = {'image': encodestr}
    ocr_str = mashiji_Num.posturl(data=encodestr)
#    print(ocr_str)

    success = 0
    msg = ""
    res = ""
    if ocr_str == 400:
        msg = "阿里OCR参数错误"
        print("铅封号识别：阿里OCR参数错误")
    elif ocr_str == 401:
        msg = "您无阿里OCR的权限，请开通后使用"
        print("铅封号识别：您无阿里OCR的权限，请开通后使用")
    elif ocr_str == 403:
        msg = "阿里OCR购买的容量已用完或者签名错误"
        print("铅封号识别：阿里OCR购买的容量已用完或者签名错误")
    elif ocr_str == 500:
        msg = "阿里OCR服务器错误，请稍后重试"
        print("铅封号识别：阿里OCR服务器错误，请稍后重试")
    elif ocr_str == 600:
        msg = "无法识别该图片，请重新上传图片"
        print("铅封号识别：无法识别该图片，请重新上传图片")
    elif not ocr_str:
        msg = "无法识别该图片，请重新上传图片"
        print("铅封号识别：无法识别该图片，请重新上传图片")
    elif type(ocr_str) == int:
        msg = "网络错误，请稍后再试"
        print("铅封号识别：网络错误，请稍后再试")
    elif not ocr_str:
        msg = "无法识别该图片，请重新上传图片"
        print("铅封号识别：无法识别该图片，请重新上传图片")
    elif ocr_str == "{" or ocr_str == "}" or ocr_str == "\"":
        msg = "无法识别该图片，请重新上传图片"
        print("铅封号识别：无法识别该图片，请重新上传图片")
    else:
        res = mashiji_Num.extractWordsFromOCRResult(ocr_str)
        if res == 700:
            res = ""
            msg = "解析出错，请重新上传图片"
            print("铅封号识别：解析出错，请重新上传图片")
            return json.dumps({"res": res, "msg": msg, "success": success})
        else:
            success = 1

    return json.dumps({"res": res, "msg": msg, "success": success})

if __name__ == "__main__":
    app.debug = True
    app.run(host='0.0.0.0', port=8222)


import httplib
import json
import webob.dec
from webob import Response
from pprint import pprint

class Controller(object):
    def __init__(self):
        pass

    def index(self, req):
        match = req.environ['wsgiorg.routing_args'][1]
        #pprint(req.environ)
        response = Response(request=req, status=httplib.MULTIPLE_CHOICES,
                            content_type='application/json')
        ans = int(match['a'])-int(match['b'])
        ans_str = match['a']+"-"+match['b']+"="+str(ans)
        response.body = json.dumps(ans_str)
        response.status = '200 OK'
        return response

    @webob.dec.wsgify
    def __call__(self, request):
        return self.index(request)

def create_resource():
    return Controller()


# Define your item pipelines here
#
# Don't forget to add your pipeline to the ITEM_PIPELINES setting
# See: https://docs.scrapy.org/en/latest/topics/item-pipeline.html

# useful for handling different item types with a single interface

# Scraped data --> items containers --> json,xml files
# Scraped data --> items containers --> pipelines --> SQL/Mongo database

import sqlite3

from itemadapter import ItemAdapter

class QuotetutorialPipeline:

    def __init__(self):
        self.create_connection()
        self.create_table()

    def create_connection(self):
        self.conn = sqlite3.connect('myquotes.db')
        self.curr = self.conn.cursor()

    def create_table(self):
        self.conn.execute("""DROP TABLE IF EXIST quotes_tb""")
        self.curr.execute("""create table quotes_tb(
                            title text,
                            author text,
                            tag text
                            )""")

    def process_item(self, item, spider):
        self.store_db(item)
        return item

    def store_db(self, item):
        self.conn.execute("""insert into quotes_tb values(?,?,?)""",(
            item['title'][0],
            item['author'][0],
            item['tag'][0]
        ))
        self.conn.commit()


# -*- coding: utf-8 -*-
"""
Created on Mon Feb 10 14:35:38 2020

@author: Mauricio
"""

#import pandas as pd
import numpy as np
import tensorflow as tf
import tensorflow_probability as tfp
from helper import *
import os
from timeit import default_timer as timer
#tf.enable_eager_execution()

tfd = tfp.distributions

learning_rate = 0.01
max_steps = 1500
#para tener resultados m+as estables en el modelo nulo, cambiar iteraciones a 1470...
#max_steps = 1470
batch_size = 32
num_monte_carlo = 50000

tf.reset_default_graph()
tf.compat.v1.random.set_random_seed(1234)
np.random.seed(1234)

logreg_input_matrix1 = np.load(r"SRC/Flores et Al/Arreglos/input1.npy")
logreg_input_matrix2 = np.load(r"SRC/Flores et Al/Arreglos/input2.npy")
logreg_input_matrix3 = np.load(r"SRC/Flores et Al/Arreglos/input3.npy")
logreg_input_matrix4 = np.load(r"SRC/Flores et Al/Arreglos/input4.npy")

logreg_targets = np.reshape(np.load(r"SRC/Flores et Al/Arreglos/lgrg_trgt_Lowenkamp.npy"), (5278,1)).astype('float32')

num_examples = len(logreg_targets)

logreg_input_matrix = logreg_input_matrix4

#para modelos nulos (AIC, chi cuadrada, etc)
#logreg_input_matrix0 = np.ones((logreg_input_matrix1.shape[0],1))
#logreg_input_matrix = logreg_input_matrix0

"""
CÓDIGO PARA RENDIMIENTO Y GENERALIZACIÓN

data = np.concatenate((logreg_input_matrix, logreg_targets), axis = 1)
np.random.shuffle(data)
logreg_input_matrix, logreg_targets = data[:,:(data.shape[1] - 1)], data[:,(data.shape[1] - 1)].reshape((len(data), 1))

X_train = logreg_input_matrix[0:int(num_examples*.8) + 1,:]
X_val = logreg_input_matrix[int(num_examples*.8) + 1:int(num_examples*.9)+1,:]
X_test = logreg_input_matrix[int(num_examples*.9) + 1:num_examples + 1,:]

y_train = logreg_targets[0:int(num_examples*.8) + 1,:]
y_val = logreg_targets[int(num_examples*.8) + 1:int(num_examples*.9)+1,:]
y_test = logreg_targets[int(num_examples*.9) + 1:num_examples + 1,:]

logreg_input_matrix = X_train
logreg_targets = y_train
"""
X, y = logreg_input_matrix, logreg_targets

def build_input_pipeline(x, y, batch_size):
  """Build a Dataset iterator for supervised classification.
  Args:
    x: Numpy `array` of features, indexed by the first dimension.
    y: Numpy `array` of labels, with the same first dimension as `x`.
    batch_size: Number of elements in each training batch.
  Returns:
    batch_features: `Tensor` feed  features, of shape
      `[batch_size] + x.shape[1:]`.
    batch_labels: `Tensor` feed of labels, of shape
      `[batch_size] + y.shape[1:]`.
  """
  training_dataset = tf.data.Dataset.from_tensor_slices((x, y))
  training_batches = training_dataset.repeat().batch(batch_size)
  training_iterator = tf.compat.v1.data.make_one_shot_iterator(training_batches)
  batch_features, batch_labels = training_iterator.get_next()
  return batch_features, batch_labels

# Generate (and visualize) a toy classification dataset.
#w_true, b_true, x, y = toy_logistic_data(num_examples, 2)
#features, labels = build_input_pipeline(x, y, batch_size)
features, labels = build_input_pipeline(X, y, batch_size)

# Define a logistic regression model as a Bernoulli distribution
# parameterized by logits from a single linear layer. We use the Flipout
# Monte Carlo estimator for the layer: this enables lower variance
# stochastic gradients than naive reparameterization.

with tf.compat.v1.name_scope("logistic_regression", values=[features]):
  layer = tfp.layers.DenseFlipout(
      units=1,
      activation= None,
      kernel_posterior_fn = tfp.layers.default_mean_field_normal_fn(),
      bias_posterior_fn = None)
  logits = layer(features)
  labels_distribution = tfd.Bernoulli(logits=logits)

with tf.compat.v1.name_scope("non_bayesian_logistic_regression", values=[features]):
  layer1 = tf.layers.Dense(
          units=1, activation= None,  use_bias = False)
  logits1 = layer1(features)
  labels_distribution1 = tfd.Bernoulli(logits=logits1)

# Compute the -ELBO as the loss, averaged over the batch size.
neg_log_likelihood = -tf.reduce_mean(
    input_tensor=labels_distribution.log_prob(labels))
kl = sum(layer.losses) / num_examples
elbo_loss = neg_log_likelihood + kl

neg_log_likelihood1 = -tf.reduce_mean(
    input_tensor=labels_distribution1.log_prob(labels))
kl1 = sum(layer1.losses) / num_examples
elbo_loss1 = neg_log_likelihood1 + kl1

# Build metrics for evaluation. Predictions are formed from a single forward
# pass of the probabilistic layers. They are cheap but noisy predictions.
predictions = tf.cast(logits > 0, dtype=tf.int32)#predictions1 = tf.cast(logits1 > 0, dtype=tf.int32)
predictions1 = tf.cast(logits1 > 0, dtype=tf.int32)

accuracy, accuracy_update_op = tf.compat.v1.metrics.accuracy(
    labels=labels, predictions=predictions)
accuracy1, accuracy_update_op1 = tf.compat.v1.metrics.accuracy(
    labels=labels, predictions=predictions1)

with tf.compat.v1.name_scope("train"):
  optimizer = tf.compat.v1.train.AdamOptimizer(
          learning_rate = learning_rate)
  train_op = optimizer.minimize(elbo_loss)
  train_op1 = optimizer.minimize(elbo_loss1)

init_op = tf.group(tf.compat.v1.global_variables_initializer(),
                   tf.compat.v1.local_variables_initializer())

with tf.compat.v1.Session() as sess:
    sess.run(init_op)

    # Fit the model to data.
    time_bay, time_opt = 0, 0
    start = timer()
    for step in range(max_steps):
      _ = sess.run([train_op, accuracy_update_op])
      """
      if step % 100 == 0:
       loss_value, accuracy_value = sess.run([elbo_loss, accuracy])
       print("Step: {:.3f} Loss: {:.3f} Accuracy: {:.3f}".format(
               step, loss_value, accuracy_value))
       """
    end = timer()
    time_bay = round((end-start)*1000, 3)
    start = timer()
    for step in range(max_steps):
      _ = sess.run([train_op1, accuracy_update_op1])
      """
      if step % 100 == 0:
       loss_value1, accuracy_value1 = sess.run([elbo_loss1, accuracy1])
       print("Step: {:>3d} Loss: {:.3f} Accuracy: {:.3f}".format(
               step, loss_value1, accuracy_value1))
       """
    end = timer()
    time_opt = round((end-start)*1000, 3)
    time = [time_opt, time_bay]
    variance = layer.kernel_posterior._variance()
    prior_variance = layer.kernel_prior._variance()
    mean = layer.kernel_posterior._mean()
    prior_mean = layer.kernel_prior._mean()
    non_bay_ww = layer1.kernel
    coefs_bay = mean
    coefs_opt = non_bay_ww
    w_samples = []
    p_samples = []
    w_draw = layer.kernel_posterior.sample()
    p_draw = layer.kernel_prior.sample()

    """-------------------------------
    CÓDIGO PARA EJECUCIONES "EAGER"
    ----------------------------------
ytp1 = np.array(log_pred_train_bay)
ytp1l = ytp1 > 0.5
ytp1l = ytp1l.astype('float32')
comp = np.array(ytp1l == y_train)
comp = comp.astype('float32')
unique, counts = np.unique(comp, return_counts = True)
dict(zip(unique, counts))

ytp1 = np.array(log_pred_train_opt)
ytp1l = ytp1 > 0.5
ytp1l = ytp1l.astype('float32')
comp = np.array(ytp1l == y_train)
comp = comp.astype('float32')
unique, counts = np.unique(comp, return_counts = True)
dict(zip(unique, counts))

    """
    """
    pred_train_bay = tf.matmul(X_train1, coefs_bay)
    pred_train_opt = tf.matmul(X_train1, coefs_opt)
    pred_train_freq = tf.matmul(X_train1, coefs_freq)
    pred_val_bay = tf.matmul(X_val, coefs_bay)
    pred_val_opt = tf.matmul(X_val, coefs_opt)
    pred_val_freq = tf.matmul(X_val, coefs_freq)
    pred_tst_bay = tf.matmul(X_test, coefs_bay)
    pred_tst_opt = tf.matmul(X_test, coefs_opt)
    pred_tst_freq = tf.matmul(X_test, coefs_freq)

    log_pred_train_freq = 1/(1 + tf.exp(-pred_train_freq))
    log_pred_train_bay = 1/(1 + tf.exp(-pred_train_bay))
    log_pred_train_opt = 1/(1 + tf.exp(-pred_train_opt))
    log_pred_val_freq = 1/(1 + tf.exp(-pred_val_freq))
    log_pred_val_bay = 1/(1 + tf.exp(-pred_val_bay))
    log_pred_val_opt = 1/(1 + tf.exp(-pred_val_opt))
    log_pred_tst_freq = 1/(1 + tf.exp(-pred_tst_freq))
    log_pred_tst_bay = 1/(1 + tf.exp(-pred_tst_bay))
    log_pred_tst_opt = 1/(1 + tf.exp(-pred_tst_opt))

    sqerr_train_bay = tf.reduce_mean(np.subtract(y_train1, log_pred_train_bay)** 2)
    sqerr_train_opt = tf.reduce_mean(np.subtract(y_train1, log_pred_train_opt)** 2)
    sqerr_train_freq = tf.reduce_mean(np.subtract(y_train1, log_pred_train_freq)** 2)
    sqerr_val_bay = tf.reduce_mean(np.subtract(y_val, log_pred_val_bay) ** 2)
    sqerr_val_opt = tf.reduce_mean(np.subtract(y_val, log_pred_val_opt) ** 2)
    sqerr_val_freq = tf.reduce_mean(np.subtract(y_val, log_pred_val_freq) ** 2)
    sqerr_tst_bay = tf.reduce_mean(np.subtract(y_test, log_pred_tst_bay) ** 2)
    sqerr_tst_opt = tf.reduce_mean(np.subtract(y_test, log_pred_tst_opt) ** 2)
    sqerr_tst_freq = tf.reduce_mean(np.subtract(y_test, log_pred_tst_freq) ** 2)
    """
    var, pvar, mn, pmn, nbww = sess.run((variance, prior_variance, mean, prior_mean, non_bay_ww))

    for i in range(num_monte_carlo):
        w = sess.run(w_draw)
        w_samples.append(w)

    for i in range(num_monte_carlo):
        p = sess.run(p_draw)
        p_samples.append(p)

ws = np.array(w_samples).reshape(num_monte_carlo, len(mn))
ps = np.array(p_samples).reshape(num_monte_carlo, len(mn))

#modelo 1
ORB_black_samples = GetOddsRatio(ws[:, 0], ws[:, 3])
ORB_woman_samples = GetOddsRatio(ws[:, 0], ws[:, 2])
ORB_age_samples = GetOddsRatio(ws[:, 0], ws[:, 1])
ORB_self_samples = GetOddsRatio(ws[:, 0], ws[:, 0])

#modelo 2
ORB_DEC_samples = GetOddsRatio(ws[:, 0], ws[:, 3])
ORB_woman_samples = GetOddsRatio(ws[:, 0], ws[:, 2])
ORB_age_samples = GetOddsRatio(ws[:, 0], ws[:, 1])
ORB_self_samples = GetOddsRatio(ws[:, 0], ws[:, 0])

#modelo 3
ORB_DEC_samples = GetOddsRatio(ws[:, 0], ws[:, 4])
ORB_black_samples = GetOddsRatio(ws[:, 0], ws[:, 3])
ORB_woman_samples = GetOddsRatio(ws[:, 0], ws[:, 2])
ORB_age_samples = GetOddsRatio(ws[:, 0], ws[:, 1])
ORB_self_samples = GetOddsRatio(ws[:, 0], ws[:, 0])

#modelo 4
ORB_BxDEC_samples = GetOddsRatio(ws[:, 0], ws[:, 5])
ORB_DEC_samples = GetOddsRatio(ws[:, 0], ws[:, 4])
ORB_black_samples = GetOddsRatio(ws[:, 0], ws[:, 3])
ORB_woman_samples = GetOddsRatio(ws[:, 0], ws[:, 2])
ORB_age_samples = GetOddsRatio(ws[:, 0], ws[:, 1])
ORB_self_samples = GetOddsRatio(ws[:, 0], ws[:, 0])

"""
ORBp_black_samples = GetOddsRatio(ps[:, 0], ps[:, 3])
ORBp_woman_samples = GetOddsRatio(ps[:, 0], ps[:, 2])
ORBp_age_samples = GetOddsRatio(ps[:, 0], ps[:, 1])
ORBp_self_samples = GetOddsRatio(ps[:, 0], ps[:, 0])

ORo_black = GetOddsRatio(nbww[0], nbww[4])
ORo_woman = GetOddsRatio(nbww[0], nbww[1])
ORo_u25 = GetOddsRatio(nbww[0], nbww[3])

OR_black = GetOddsRatio(coefs_freq[0], coefs_freq[4])
OR_woman = GetOddsRatio(coefs_freq[0], coefs_freq[1])
OR_u25 = GetOddsRatio(coefs_freq[0], coefs_freq[3])

"""
"""
Printing out:

Real Sample MIN/MAX
for x in ORB_black_samples:
    print(str(round(np.mean(x), 6)))
    print(str(round(np.var(x), 6)))
    print(str(round(np.min(x), 6)))
    print(str(round(np.max(x), 6)))

3 std dev MIN/MAX
for x in ORB_black_samples:
    print(str(round(np.mean(x), 6)))
    print(str(round(np.var(x), 6)))
    print(str(round(np.percentile(x, 0.15), 6)))
    print(str(round(np.percentile(x, 99.85), 6)))
    break

PERCENTILE MIN/MAX

for x in ORB_black_samples:

for x in ORB_black_samples:
    print(str(round(np.mean(x) - 3*np.sqrt(np.var(x)), 6)))
    print(str(round(np.mean(x) + 3*np.sqrt(np.var(x)), 6)))

    print(str(np.sum(x < (np.mean(x) - 3*np.sqrt(np.var(x)))) + np.sum(x > (np.mean(x) + 3*np.sqrt(np.var(x))))))
"""
"""
PLOTTING STUFF
"""

means = np.array([mn[x][0] for x in range(len(mn))])
means = np.concatenate((means, np.zeros(1)))
variances = np.array([var[x][0] for x in range(len(var))])
variances = np.concatenate((variances, np.ones(1)))

#means1 = means[[12,5,7]]
#variances1 = variances[[12,5,7]]
#range ~ -3:4
points = np.linspace(means - 5*np.sqrt(variances), means + 5*np.sqrt(variances), 100)

#points1 = np.linspace(means1 - 5*np.sqrt(variances1), means1 + 5*np.sqrt(variances1), 100)

#neg = np.linspace(np.ones(len(means))*-3, means - 3*np.sqrt(variances), 10)
#pos = np.linspace(means + 3*np.sqrt(variances), np.ones(len(means))*4, 10)
#points = np.concatenate((neg, points, pos), axis = 0)
pdfs = np.zeros((len(means), len(points)))
with tf.compat.v1.Session() as sess:
    for x in range(len(means)):
        d = tfd.Normal(loc = means[x], scale = np.sqrt(variances[x]))
        pdfs[x] = sess.run(d.prob(points[:, x]))
"""
pdfs1 = np.zeros((len(means1), len(points1)))
with tf.compat.v1.Session() as sess:
    for x in range(len(means1)):
        d = tfd.Normal(loc = means1[x], scale = np.sqrt(variances1[x]))
        pdfs1[x] = sess.run(d.prob(points1[:, x]))
 """
labels = np.array(['Constante $b$',
'Edad',
'Género femenino',
'Raza negra',
'distribución a priori'])

labels = np.array(['Constante $b$',
'Edad',
'Género femenino',
'Puntaje COMPAS',
'distribución a priori'])

labels = np.array(['Constante $b$',
'Edad',
'Género femenino',
'Raza negra',
'Puntaje COMPAS',
'distribución a priori'])

labels = np.array(['Constante $b$',
'Edad',
'Género femenino',
'Raza negra',
'Puntaje COMPAS',
'Puntaje COMPAS X raza negra',
'distribución a priori'])

labels1 = np.array(['distribución a priori',
'Factor de raza: asiático',
'Factor de raza: nativo americano'
])
import matplotlib.colors as col
import matplotlib.pyplot as plt
from matplotlib import figure
from matplotlib.backends import backend_agg

fig = figure.Figure(figsize=(10, 6))
canvas = backend_agg.FigureCanvasAgg(fig)
colors = plt.cm.Spectral(np.linspace(0,1,len(means)))
#Modelo 1
#colors[2] = col.to_rgba_array('grey')
#Modelo 3,4
#colors[3] = col.to_rgba_array('blue')
#colors[5], colors[6], colors[7] = col.to_rgba_array('grey'), col.to_rgba_array('black'), col.to_rgba_array('pink')

ax = fig.add_subplot(1, 1, 1)
ax.set_prop_cycle('color', colors)
#ax.set_prop_cycle(color = ['black', 'red', 'blue'])
#ax.plot(np.linspace(-7, 7, 100), np.ones(100),lw=1, color="black")
for i in range(len(means)):
    if i == len(means) - 1:
        ax.plot(points[:, i], pdfs[i], lw=2, label = labels[i])
        break
    ax.plot(points[:, i], pdfs[i], lw=2,  label = labels[i])
"""
for i in range(len(means1)):
    if i == len(means1) - 1:
        ax.plot(points1[:, i], pdfs1[i], lw=2, label = labels1[i])
        break
    ax.plot(points1[:, i], pdfs1[i], lw=2,  label = labels1[i])
"""

ax.set_xlim([-1.2, 1.2])
ax.set_ylim([-0.1, 15.])
ax.vlines(means, -0.1, 15., linestyles='dashed', lw = 1.5)#np.max(pdfs)])
ax.hlines(0., -2., 2., linestyles='solid', lw = 1.)
ax.set_title('Coeficientes, sus distribuciones a posteriori, y la distribución común a priori')
#ax.set_title('Coeficientes de alta incertidumbre')

"""
plt.rcParams['mathtext.fontset'] = 'custom'
plt.rcParams['mathtext.it'] = 'STIXGeneral:italic'
plt.rcParams['mathtext.bf'] = 'STIXGeneral:bold'
#plt.rcParams['mathtext.it.bf'] = 'STIXGeneral:italic:bold'
"""
ax.set_xlabel('Valor numérico de los coeficientes', fontsize = 12)
ax.set_ylabel('Densidad de probabilidad (PDF)', fontsize = 14)

#ax.set_xticks(np.arange(-7,8,1))
#ax.set_yticks(np.arange(-0.1,1.3,0.1))
ax.xaxis.grid(b=True)
ax.yaxis.grid(b=True)

"""
ax.spines['left'].set_position(('data', 0))
ax.spines['right'].set_color('none')
ax.spines['bottom'].set_position(('data', 0.0))
ax.spines['top'].set_position(('data', 1.0))
#ax.spines['top'].set_color('none')
ax.set_xlabel('x', labelpad=15, fontsize=14)
ax.set_ylabel('p', labelpad = 150, fontsize=14)
"""

ax.legend()
fname=os.path.join(r"SRC/Flores et Al/Figuras/",
                                         "PDF_Lowenkamp_1.png")

canvas.print_figure(fname, format="png")
print("saved {}".format(fname))
#sqetrf, sqetro, sqetrb, sqevf, sqevo, sqevb, sqetf, sqeto, sqetb = sess.run((sqerr_train_freq, sqerr_train_opt, sqerr_train_bay,
  #                                                                              sqerr_val_freq, sqerr_val_opt, sqerr_val_bay,
   #                                                                              sqerr_tst_freq, sqerr_tst_opt, sqerr_tst_bay))

#LL, NLL1 = sess.run((neg_log_likelihood, neg_log_likelihood1))
"""
PLOTTING RELATIONSHIP RACE VS COMPAS
"""
b_data = logreg_input_matrix[np.where(logreg_input_matrix[:,3] == 1)]
w_data = logreg_input_matrix[np.where(logreg_input_matrix[:,3] == 0)]
ssize = 200
w_samps = ws[np.random.choice(np.arange(ws.shape[0]), size = ssize, replace = False)].T
p_samps = ps[np.random.choice(np.arange(ps.shape[0]), size = ssize, replace = False)].T

def sigmoid(x):
    return 1/(1 + np.exp(-x))

b_preds = sigmoid(np.matmul(b_data, w_samps))
w_preds = sigmoid(np.matmul(w_data, w_samps))

b_mn_preds = sigmoid(np.matmul(b_data, mn))
w_mn_preds = sigmoid(np.matmul(w_data, mn))

b_res, w_res = np.zeros((ssize, 10)), np.zeros((ssize, 10))
b_mn_res, w_mn_res = np.zeros(10), np.zeros(10)

for x in range(10):
    b_res[:, x] = np.mean(b_preds[np.where(b_data[:,4] == x + 1)], axis = 0)
    b_mn_res[x] = np.mean(b_mn_preds[np.where(b_data[:,4] == x + 1)], axis = 0)
    w_res[:, x] = np.mean(w_preds[np.where(w_data[:,4] == x + 1)], axis = 0)
    w_mn_res[x] = np.mean(w_mn_preds[np.where(w_data[:,4] == x + 1)], axis = 0)

points = np.arange(11)[1:11]
labels = np.array(['Elementos de raza negra', 'Elementos de raza blanca'])
fig = figure.Figure(figsize=(10, 6))
canvas = backend_agg.FigureCanvasAgg(fig)
ax = fig.add_subplot(1, 1, 1)

for i, y in enumerate(b_res):
    if i == 0:
        ax.plot(points, np.ones(10)*2000, lw=2, label = labels[0], color = 'red')
        ax.plot(points, b_res[i,:], lw=1, alpha = 0.2, color = 'red')
    else:
        ax.plot(points, b_res[i,:], lw=1, alpha = 0.2, color = 'red')

for i, y in enumerate(w_res):
    if i == 0:
        ax.plot(points, np.ones(10)*2000, lw=2, label = labels[1], color = 'blue')
        ax.plot(points, w_res[i,:], lw=1, alpha = 0.2, color = 'blue')
    else:
        ax.plot(points, w_res[i,:], lw=1, alpha = 0.2, color = 'blue')

ax.plot(points, b_mn_res, lw=2, color = 'black', ls = 'dashed', label = 'Media para raza negra')
ax.plot(points, w_mn_res, lw=2, color = 'grey', ls = 'dashed', label = 'Media para raza blanca')

ax.set_xlim([0, 10.1])
ax.set_ylim([0.09, 0.9])
ax.set_xticks(np.arange(11))

ax.set_title('Probabilidad promedio estimada de reincidencia general por raza')
#ax.set_title('Probabilidad promedio estimada de reincidencia violenta por raza (datos filtrados, N = 3377)')
#ax.set_title('Probabilidad promedio estimada de reincidencia violenta por raza (datos sin filtrar, N = 3967)')
ax.set_xlabel('Puntaje COMPAS', fontsize = 15)
ax.set_ylabel('Probabilidad promedio', fontsize = 12)

ax.xaxis.grid(b=True)
ax.yaxis.grid(b=True)

ax.legend()
fname=os.path.join(r"SRC/Flores et Al/Figuras/",
                                         "FloresFig1Bay.png")
canvas.print_figure(fname, format="png")
print("saved {}".format(fname))

fig = figure.Figure(figsize=(10, 6))
canvas = backend_agg.FigureCanvasAgg(fig)
ax = fig.add_subplot(1, 1, 1)

ax.plot(points, b_mn_res, lw=2, color = 'red', label = 'Media para raza negra')
ax.plot(points, w_mn_res, lw=2, color = 'blue', label = 'Media para raza blanca')

ax.set_xlim([0, 10.1])
ax.set_ylim([0.09, 0.9])
ax.set_xticks(np.arange(11))

ax.set_title('Probabilidad promedio estimada de reincidencia general por raza')
#ax.set_title('Probabilidad promedio estimada de reincidencia violenta por raza (datos filtrados, N = 3377)')
#ax.set_title('Probabilidad promedio estimada de reincidencia violenta por raza (datos sin filtrar, N = 3967)')
ax.set_xlabel('Puntaje COMPAS', fontsize = 15)
ax.set_ylabel('Probabilidad promedio', fontsize = 12)

ax.xaxis.grid(b=True)
ax.yaxis.grid(b=True)

ax.legend(loc = 'upper left')
fname=os.path.join(r"SRC/Flores et Al/Figuras/",
                                         "FloresFig1BayMN.png")
canvas.print_figure(fname, format="png")
print("saved {}".format(fname))

"""
PRIOR RELATIONSHIPS
"""
pb_preds = sigmoid(np.matmul(b_data, p_samps))
pw_preds = sigmoid(np.matmul(w_data, p_samps))

pb_mn_preds = sigmoid(np.matmul(b_data, pmn))
pw_mn_preds = sigmoid(np.matmul(w_data, pmn))

pb_res, pw_res = np.zeros((ssize, 10)), np.zeros((ssize, 10))
pb_mn_res, pw_mn_res = np.zeros(10), np.zeros(10)

for x in range(10):
    pb_res[:, x] = np.mean(pb_preds[np.where(b_data[:,4] == x + 1)], axis = 0)
    pb_mn_res[x] = np.mean(pb_mn_preds[np.where(b_data[:,4] == x + 1)], axis = 0)
    pw_res[:, x] = np.mean(pw_preds[np.where(w_data[:,4] == x + 1)], axis = 0)
    pw_mn_res[x] = np.mean(pw_mn_preds[np.where(w_data[:,4] == x + 1)], axis = 0)

points = np.arange(11)[1:11]
labels = np.array(['Elementos de raza negra', 'Elementos de raza blanca'])
fig = figure.Figure(figsize=(10, 6))
canvas = backend_agg.FigureCanvasAgg(fig)
ax = fig.add_subplot(1, 1, 1)

for i, y in enumerate(pb_res):
    if i == 0:
        ax.plot(points, np.ones(10)*2000, lw=2, label = labels[0], color = 'red')
        ax.plot(points, pb_res[i,:], lw=1, alpha = 0.2, color = 'red')
    else:
        ax.plot(points, pb_res[i,:], lw=1, alpha = 0.2, color = 'red')

for i, y in enumerate(pw_res):
    if i == 0:
        ax.plot(points, np.ones(10)*2000, lw=2, label = labels[1], color = 'blue')
        ax.plot(points, pw_res[i,:], lw=1, alpha = 0.2, color = 'blue')
    else:
        ax.plot(points, pw_res[i,:], lw=1, alpha = 0.2, color = 'blue')

ax.plot(points, pb_mn_res, lw=2, color = 'black', ls = 'dashed', label = 'Media para raza negra')
ax.plot(points, pw_mn_res, lw=2, color = 'grey', ls = 'dashed', label = 'Media para raza blanca')

ax.set_xlim([0, 10.1])
ax.set_ylim([0.0, 1.0])
ax.set_xticks(np.arange(11))

ax.set_title('Probabilidad promedio estimada de reincidencia general por raza')
#ax.set_title('Probabilidad promedio estimada de reincidencia violenta por raza (datos filtrados, N = 3377)')
#ax.set_title('Probabilidad promedio estimada de reincidencia violenta por raza (datos sin filtrar, N = 3967)')
ax.set_xlabel('Puntaje COMPAS', fontsize = 15)
ax.set_ylabel('Probabilidad promedio', fontsize = 12)

ax.xaxis.grid(b=True)
ax.yaxis.grid(b=True)

ax.legend(loc = 'upper left')
fname=os.path.join(r"SRC/Flores et Al/Figuras/",
                                         "FloresFig1Bay_prior.png")
canvas.print_figure(fname, format="png")
print("saved {}".format(fname))

fig = figure.Figure(figsize=(10, 6))
canvas = backend_agg.FigureCanvasAgg(fig)
ax = fig.add_subplot(1, 1, 1)

ax.plot(points, pb_mn_res, lw=2, color = 'red', label = 'Media para raza negra', alpha = 0.7)
ax.plot(points, pw_mn_res, lw=1, color = 'blue', label = 'Media para raza blanca', alpha = 0.7)

ax.set_xlim([0, 10.1])
ax.set_ylim([0.09, 0.9])
ax.set_xticks(np.arange(11))

ax.set_title('Probabilidad promedio estimada de reincidencia general por raza')
#ax.set_title('Probabilidad promedio estimada de reincidencia violenta por raza (datos filtrados, N = 3377)')
#ax.set_title('Probabilidad promedio estimada de reincidencia violenta por raza (datos sin filtrar, N = 3967)')
ax.set_xlabel('Puntaje COMPAS', fontsize = 15)
ax.set_ylabel('Probabilidad promedio', fontsize = 12)

ax.xaxis.grid(b=True)
ax.yaxis.grid(b=True)

ax.legend(loc = 'upper left')
fname=os.path.join(r"SRC/Flores et Al/Figuras/",
                                         "FloresFig1BayMN_prior.png")
canvas.print_figure(fname, format="png")
print("saved {}".format(fname))


# This application will read roster data in JSON format, parse the file,
# and then produce an SQLite database that contains a User, Course,
# and Member table and populate the tables from the data file.
#
# Store the role column in the Member table.
#
# Run the following SQL command:
# SELECT hex(User.name || Course.title || Member.role )
#     AS X
#     FROM User
#     JOIN Member
#     JOIN Course
#     ON User.id = Member.user_id
#     AND Member.course_id = Course.id
#     ORDER BY X
#
# Find the first row in the resulting record set and enter the long string
# that looks like 53656C696E613333
#
# (Hint: starts with 416)

import json
import sqlite3

db_connection = sqlite3.connect('data-samples/rosterdb.sqlite')
a_cursor = db_connection.cursor()

a_cursor.executescript('''
DROP TABLE IF EXISTS User;
DROP TABLE IF EXISTS Course;
DROP TABLE IF EXISTS Member;

CREATE TABLE User (
    id     INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE,
    name   TEXT UNIQUE
);
CREATE TABLE Course (
    id     INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE,
    title  TEXT UNIQUE
);
CREATE TABLE Member (
    user_id     INTEGER,
    course_id   INTEGER,
    role        INTEGER,
    PRIMARY KEY (user_id, course_id)
);
''')

requested_file = 'data-samples/roster_data.json'
extracted_content = open(requested_file).read()
the_content = json.loads(extracted_content)

for an_item in the_content:
    name = an_item[0]
    title = an_item[1]
    role = an_item[2]

    a_cursor.execute('''INSERT OR IGNORE INTO User (name)
        VALUES ( ? )''', (name, ) )
    a_cursor.execute('SELECT id FROM User WHERE name = ? ', (name, ) )
    user_id = a_cursor.fetchone()[0]

    a_cursor.execute('''INSERT OR IGNORE INTO Course (title)
        VALUES ( ? )''', (title, ) )
    a_cursor.execute('SELECT id FROM Course WHERE title = ? ', (title, ) )
    course_id = a_cursor.fetchone()[0]

    a_cursor.execute('''INSERT OR REPLACE INTO Member (user_id, course_id, role)
        VALUES ( ?, ?, ? )''', (user_id, course_id, role) )

    db_connection.commit()

    #
    #


from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

import random
import numpy as np

# PyTorch imports.
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim

# TensorFlow import.
import tensorflow as tf

# MXNet imports.
from mxnet import nd, autograd, init, gluon
from mxnet.gluon import data as gdata
from mxnet.gluon import nn
from mxnet.gluon import loss as gloss

np.random.seed(71)

class LinearRegression(object):
    """Numpy implementation of Linear Regression."""

    def __init__(self, batch_size=64, lr=0.01, n_epochs=1000):
        self.batch_size = batch_size
        self.lr = lr
        self.n_epochs = n_epochs

    def get_data(self, X_train, y_train, shuffle=True):
        """Get dataset and information."""
        self.X_train = X_train
        self.y_train = y_train

        # Get the numbers of examples and inputs.
        self.n_examples, self.n_inputs = self.X_train.shape

        if shuffle:
            idx = list(range(self.n_examples))
            random.shuffle(idx)
            self.X_train = self.X_train[idx]
            self.y_train = self.y_train[idx]

    def _create_weights(self):
        """Create model weights and bias."""
        self.w = np.zeros(self.n_inputs).reshape(self.n_inputs, 1)
        self.b = np.zeros(1).reshape(1, 1)

    def _model(self, X):
        """Linear regression model."""
        return np.matmul(X, self.w) + self.b

    def _loss(self, y, y_):
        """Squared error loss.

        # squared_error_loss(y, y_)
        #   = - 1/n * \sum_{i=1}^n (y_i - y__i)^2
        """
        self.squared_error = np.square(y - y_)
        return np.mean(self.squared_error)

    def _optimize(self, X, y):
        """Optimize by stochastic gradient descent."""
        m = X.shape[0]

        y_ = self._model(X)
        dw = 1 / m * np.matmul(X.T, y_ - y)
        db = np.mean(y_ - y)

        for (param, grad) in zip([self.w, self.b], [dw, db]):
            param[:] = param - self.lr * grad

    def _fetch_batch(self):
        """Fetch batch dataset."""
        idx = list(range(self.n_examples))
        for i in range(0, self.n_examples, self.batch_size):
            idx_batch = idx[i:min(i + self.batch_size, self.n_examples)]
            yield (self.X_train.take(idx_batch, axis=0),
                   self.y_train.take(idx_batch, axis=0))

    def fit(self):
        """Fit model."""
        self._create_weights()

        for epoch in range(self.n_epochs):
            total_loss = 0
            for X_train_b, y_train_b in self._fetch_batch():
                y_train_b = y_train_b.reshape((y_train_b.shape[0], -1))
                self._optimize(X_train_b, y_train_b)
                batch_loss = self._loss(y_train_b, self._model(X_train_b))
                total_loss += batch_loss * X_train_b.shape[0]

            if epoch % 100 == 0:
                print('epoch {0}: training loss {1}'
                      .format(epoch, total_loss / self.n_examples))

        return self

    def get_coeff(self):
        """Get model coefficients."""
        return self.b, self.w.reshape((-1,))

    def predict(self, X):
        """Predict for new data."""
        return self._model(X).reshape((-1,))

class LinearRegressionTorch(nn.Module):
    """PyTorch implementation of Linear Regression."""

    def __init__(self, batch_size=64, lr=0.01, n_epochs=1000):
        super(LinearRegressionTorch, self).__init__()
        self.batch_size = batch_size
        self.lr = lr
        self.n_epochs = n_epochs

    def get_data(self, X_train, y_train, shuffle=True):
        """Get dataset and information."""
        self.X_train = X_train
        self.y_train = y_train

        # Get the numbers of examples and inputs.
        self.n_examples, self.n_inputs = self.X_train.shape

        if shuffle:
            idx = list(range(self.n_examples))
            random.shuffle(idx)
            self.X_train = self.X_train[idx]
            self.y_train = self.y_train[idx]

    def _create_model(self):
        """Create linear regression model."""
        self.model = nn.Linear(self.n_inputs, 1)

    def forward(self, x):
        """Foward to output model."""
        y = self.model(x)
        return y

    def _create_loss(self):
        """Create squared error loss."""
        self.criterion = nn.MSELoss()

    def _create_optimizer(self):
        """Create optimizer by stochastic gradient descent."""
        self.optimizer = optim.SGD(self.model.parameters(), lr=self.lr)

    def build_graph(self):
        """Build computational graph."""
        self._create_model()
        self._create_loss()
        self._create_optimizer()

    def _fetch_batch(self):
        """Fetch batch dataset."""
        idx = list(range(self.n_examples))
        for i in range(0, self.n_examples, self.batch_size):
            idx_batch = idx[i:min(i + self.batch_size, self.n_examples)]
            yield (self.X_train.take(idx_batch, axis=0),
                   self.y_train.take(idx_batch, axis=0))

    def fit(self):
        """Fit model."""
        for epoch in range(1, self.n_epochs + 1):
            total_loss = 0
            for X_train_b, y_train_b in self._fetch_batch():
                # Convert to Tensor from NumPy array and reshape ys.
                X_train_b, y_train_b = (
                    torch.from_numpy(X_train_b),
                    torch.from_numpy(y_train_b).view(-1, 1))

                y_pred_b = self.model(X_train_b)
                batch_loss = self.criterion(y_pred_b, y_train_b)
                total_loss += batch_loss * X_train_b.shape[0]

                # Zero grads, performs backward pass, and update weights.
                self.optimizer.zero_grad()
                batch_loss.backward()
                self.optimizer.step()

            if epoch % 100 == 0:
                print('Epoch {0}: training loss: {1}'
                      .format(epoch, total_loss / self.n_examples))

    def get_coeff(self):
        """Get model coefficients."""
        # Detach var which require grad.
        return (self.model.bias.detach().numpy(),
                self.model.weight.detach().numpy())

    def predict(self, X):
        """Predict for new data."""
        with torch.no_grad():
            X_ = torch.from_numpy(X)
            return self.model(X_).numpy().reshape((-1,))

def reset_tf_graph(seed=71):
    """Reset default TensorFlow graph."""
    tf.reset_default_graph()
    tf.set_random_seed(seed)
    np.random.seed(seed)

class LinearRegressionTF(object):
    """A TensorFlow implementation of Linear Regression."""

    def __init__(self, batch_size=64, learning_rate=0.01, n_epochs=1000):
        self.batch_size = batch_size
        self.n_epochs = n_epochs
        self.learning_rate = learning_rate

    def get_data(self, X_train, y_train, shuffle=True):
        """Get dataset and information.s"""
        self.X_train = X_train
        self.y_train = y_train

        # Get the numbers of examples and inputs.
        self.n_examples, self.n_inputs = self.X_train.shape

        idx = list(range(self.n_examples))
        if shuffle:
            random.shuffle(idx)
        self.X_train = self.X_train[idx]
        self.y_train = self.y_train[idx]

    def _create_placeholders(self):
        """Create placeholder for features and response."""
        self.X = tf.placeholder(tf.float32, shape=(None, self.n_inputs), name='X')
        self.y = tf.placeholder(tf.float32, shape=(None, 1), name='y')

    def _create_weights(self):
        """Create and initialize model weights and bias."""
        self.w = tf.get_variable(shape=[self.n_inputs, 1],
                                 initializer=tf.random_normal_initializer(),
                                 name='weights')
        self.b = tf.get_variable(shape=[1],
                                 initializer=tf.zeros_initializer(),
                                 name='bias')

    def _model(self, X):
        """Linear regression model."""
        return tf.matmul(X, self.w) + self.b

    def _create_model(self):
        """Create linear model."""
        self.y_ = self._model(self.X)

    def _create_loss(self):
        # Create mean squared error loss.
        self.loss = tf.reduce_mean(tf.square(self.y_ - self.y), name='loss')

    def _create_optimizer(self):
        # Create gradient descent optimization.
        self.optimizer = (
            tf.train.GradientDescentOptimizer(learning_rate=self.learning_rate)
            .minimize(self.loss))

    def build_graph(self):
        """Build computational graph."""
        self._create_placeholders()
        self._create_weights()
        self._create_model()
        self._create_loss()
        self._create_optimizer()

    def _fetch_batch(self):
        """Fetch batch dataset."""
        idx = list(range(self.n_examples))
        for i in range(0, self.n_examples, self.batch_size):
            idx_batch = idx[i:min(i + self.batch_size, self.n_examples)]
            yield (self.X_train[idx_batch, :], self.y_train[idx_batch].reshape(-1, 1))

    def fit(self):
        """Fit model."""
        saver = tf.train.Saver()

        with tf.Session() as sess:
            sess.run(tf.global_variables_initializer())

            for epoch in range(1, self.n_epochs + 1):
                total_loss = 0
                for X_train_b, y_train_b in self._fetch_batch():
                    feed_dict = {self.X: X_train_b, self.y: y_train_b}
                    _, batch_loss = sess.run([self.optimizer, self.loss],
                                             feed_dict=feed_dict)
                    total_loss += batch_loss * X_train_b.shape[0]

                if epoch % 100 == 0:
                    print('Epoch {0}: training loss: {1}'
                          .format(epoch, total_loss / self.n_examples))

            # Save model.
            saver.save(sess, 'checkpoints/linreg')

    def get_coeff(self):
        """Get model coefficients."""
        with tf.Session() as sess:
            sess.run(tf.global_variables_initializer())
            # Load model.
            saver = tf.train.Saver()
            saver.restore(sess, 'checkpoints/linreg')
            return self.b.eval(), self.w.eval().reshape((-1,))

    def predict(self, X):
        """Predict for new data."""
        with tf.Session() as sess:
            sess.run(tf.global_variables_initializer())
            # Load model.
            saver = tf.train.Saver()
            saver.restore(sess, 'checkpoints/linreg')
            return self._model(X).eval().reshape((-1,))

def main():
    import sklearn
    from sklearn.datasets import fetch_california_housing
    from sklearn.model_selection import train_test_split
    from sklearn.preprocessing import MinMaxScaler
    from sklearn.linear_model import LinearRegression as LinearRegressionSklearn
    from metrics import mean_squared_error

    # Read California housing data.
    housing = fetch_california_housing()
    data = housing.data
    label = housing.target

    # Normalize features first.
    scaler = StandardScaler()
    data = scaler.fit_transform(data)

    # Split data into training and test datasets.
    X_train_raw, X_test_raw, y_train, y_test = train_test_split(
        X, y, test_size=0.25, random_state=71, shuffle=True)

    # Feature engineering for standardizing features by min-max scaler.
    min_max_scaler = MinMaxScaler()
    X_train = min_max_scaler.fit_transform(X_train_raw)
    X_test = min_max_scaler.transform(X_test_raw)

    # Convert arrays to float32.
    X_train, X_test, y_train, y_test = (
        np.float32(X_train), np.float32(X_test),
        np.float32(y_train), np.float32(y_test)
    )

    # Train Numpy linear regression model.
    linreg = LinearRegression(batch_size=64, lr=0.1, n_epochs=1000)
    linreg.get_data(X_train, y_train, shuffle=True)
    linreg.fit()
    print(linreg.get_coeff())
    y_train_ = linreg.predict(X_train)
    print('Training mean squared error: {}'
           .format(mean_squared_error(y_train, y_train_)))
    y_test_ = linreg.predict(X_test)
    print('Test mean squared error: {}'
           .format(mean_squared_error(y_test, y_test_)))

    # Train PyTorch linear regression model.
    linreg_torch = LinearRegressionTorch(batch_size=64, lr=0.1, n_epochs=1000)
    linreg_torch.get_data(X_train, y_train, shuffle=True)
    linreg_torch.build_graph()
    linreg_torch.fit()
    print(linreg_torch.get_coeff())
    y_train_ = linreg_torch.predict(X_train)
    print('Training mean squared error: {}'
           .format(mean_squared_error(y_train, y_train_)))
    y_test_ = linreg_torch.predict(X_test)
    print('Test mean squared error: {}'
           .format(mean_squared_error(y_test, y_test_)))

    # Train TensorFlow linear regression model.
    linreg_tf = LinearRegressionTF(
        batch_size=64, learning_rate=0.1, n_epochs=1000)
    linreg_tf.get_data(X_train, y_train, shuffle=True)
    linreg_tf.fit()
    print(linreg_tf.get_coeff())
    y_train_ = linreg_tf.predict(X_train)
    print('Training mean squared error: {}'
           .format(mean_squared_error(y_train, y_train_)))
    y_test_ = linreg_tf.predict(X_test)
    print('Test mean squared error: {}'
           .format(mean_squared_error(y_test, y_test_)))

    # Benchmark with sklearn's linear regression model.
    linreg_sk = LinearRegressionSklearn()
    linreg_sk.fit(X_train, y_train)
    y_train_ = linreg_sk.predict(X_train)
    print('Training mean squared error: {}'
           .format(mean_squared_error(y_train, y_train_)))
    y_test_ = linreg_sk.predict(X_test)
    print('Test mean squared error: {}'
           .format(mean_squared_error(y_test, y_test_)))

if __name__ == '__main__':
    main()


"""
3. Массив размером 2m + 1, где m – натуральное число, заполнен случайным образом.
Найдите в массиве медиану. Медианой называется элемент ряда, делящий его на
две равные части: в одной находятся элементы, которые не меньше медианы,
в другой – не больше медианы.

Задачу можно решить без сортировки исходного
массива.

Но если это слишком сложно, то используйте метод сортировки,
который не рассматривался на уроках: Шелла, Гномья, ...

arr[m]
from statistics import median
"""
from statistics import median
import random
import timeit

orig_list_11 = [random.randint(-100, 100) for _ in range(11)]
print(orig_list_11)
print(median(orig_list_11))

def shell_median(data):
    inc = len(data) // 2
    while inc:
        for i, el in enumerate(data):
            while i >= inc and data[i - inc] > el:
                data[i] = data[i - inc]
                i -= inc
            data[i] = el
        inc = 1 if inc == 2 else int(inc * 5.0 / 11)
    return data[len(data) // 2]  # т.к. всегда число будет заканчиваться на .5,
    # будет округление в большую сторону, получаем индекс медианы.

print(shell_median(orig_list_11))

# мой вариант без сортировки
def no_sort_median(data):
    index = len(data) // 2
    for i in range(index):
        data.remove(max(data))
    return max(data)

print(no_sort_median(orig_list_11))

print(timeit.timeit("shell_median(orig_list_11.copy())",
                    setup="from __main__ import shell_median, orig_list_11", number=100))

print(timeit.timeit("no_sort_median(orig_list_11.copy())",
                    setup="from __main__ import no_sort_median, orig_list_11", number=100))


#!/usr/bin/env python

import argparse

def main():
    parser = argparse.ArgumentParser()
    parser.add_argument(
        '-i',
        '--csv',
        help="Provide your tab-delimited file.",
        required=True)
    parser.add_argument(
        '-o',
        '--outfile',
        help="Output file for merged csv filtered for freq <=0.1",
        default="default")
    args = parser.parse_args()
    tmpStr = ""
    if(args.outfile == "default"):
        tmpStr += args.csv.split('/')[-1].split('.')[0] + ".10pctfilt.csv"

    outfile = open(tmpStr, 'w')

    # You could encase this whole thing in a loop that would take all
    # arguments (sys.argv[1], sys.argv[2], etc) each as a file to read in and
    # then write them all out to the same file like
    thiscsv = open(args.csv, 'r')  # this would become thiscsv = open(args.csv, 'r')
    header = "Filename\t" + thiscsv.readline()
    outfile.write(header)

    for line in thiscsv:
        if line.strip().split('\t')[19] == '-':
            outfile.write(
                args.csv +
                '\t' +
                line)  # add the file args.csv as the first column
        elif float(line.strip().split('\t')[19]) <= 0.1:
            outfile.write(args.csv + '\t' + line)

    # Have the outer loop end here

    thiscsv.close()
    outfile.close()
    return

if(__name__ == "__main__"):
    main()


# _*_ coding:utf-8 _*_

from django.conf.urls import url, include
from views import OrgListView, AddAskView, OrgHomeView, OrgCourseView, OrgDescView, OrgTeacherView
from views import AddFavView, TeacherListView, TeacherDetailView
urlpatterns = [
    url(r'^list/$', OrgListView.as_view(), name='orglist'),
    url(r'^addask/$', AddAskView.as_view(), name='addask'),
    url(r'^home/(?P<org_id>\d+)/$', OrgHomeView.as_view(), name='home'),
    url(r'^course/(?P<org_id>\d+)/$', OrgCourseView.as_view(), name='course'),
    url(r'^desc/(?P<org_id>\d+)/$', OrgDescView.as_view(), name='desc'),
    url(r'^teacher/(?P<org_id>\d+)/$', OrgTeacherView.as_view(), name='teacher'),
    url(r'^add_fav/$', AddFavView.as_view(), name='add_fav'),
    url(r'^teacher/list/$', TeacherListView.as_view(), name='teacher_list'),
    url(r'^teacherdatail/(?P<teacher_id>\d+)/$', TeacherDetailView.as_view(), name='teacher_detail'),

]


#!/usr/bin/env python

from __future__ import print_function
import argparse
import atexit
import os
from confluent_kafka import Consumer, KafkaError, TopicPartition, Producer, OFFSET_BEGINNING, OFFSET_END
import sys
import traceback
import time
import threading
from xmlrpclib import _datetime_type

verbose = False
simulated = False

def get_offsets_for_timestamps(client, name, list_partitions, rec_time):
    #print(list_partitions)
    tps = [TopicPartition(name, p, rec_time) for p in list_partitions]
    offset_metadata = client.offsets_for_times(tps)
    offsets = {}
    for met in offset_metadata:
        offsets[met.partition] = met.offset
    return offsets

def wait_for_threads():
    seconds = 0
    while threading.active_count() != 0:
        print ('Waiting for threads to exit.')
        time.sleep(1)
        seconds += 1
        if seconds == 10:
            sys.exit(0)

def main():
    parser = argparse.ArgumentParser(description="Add/remove containers to server.")

    parser.add_argument("-f", "--from-topic", help="Topic name", metavar="NAME", required=True)
    parser.add_argument("-F", "--from-broker", help="Kafka bootstrap broker source", metavar="hostname", required=True)
    parser.add_argument("-k", "--key-filter", help="Only copy messages with these terms in the key", metavar="TERM", action="append")
    parser.add_argument("--port", help="Kafka bootstrap broker port", metavar="port", type=int, default=9092)
    parser.add_argument("-p", "--partition", help="Topic partitions to consume from", metavar="partition", type=int, action="append")
    parser.add_argument("-o", "--offset", help="Copy all data or most recent", choices=['beginning', 'end'], default='beginning')
    parser.add_argument("-O", "--abs-offset", help="Absolute offset", type=int, default=0)
    parser.add_argument("-t", "--to-topic", help="Topic name", metavar="NAME", required=True)
    parser.add_argument("-T", "--to-broker", help="Kafka bootstrap broker destination", metavar="hostname")
    parser.add_argument("-v", "--verbose", help="Verbose output.", action="store_true")
    parser.add_argument("--execute", help="Actual.", action="store_true")

    parser.add_argument("-S", "--start-time", help="Starting timestamp.", type=int)
    parser.add_argument("-E", "--end-time", help="Ending timestamp.", type=int)

    args = parser.parse_args()

    global verbose
    global simulated

    verbose = args.verbose
    simulated = True
    if args.execute:
        simulated = False
    else:
        print("---------------------Simulation only, use --execute to copy for real---------------------")

    if verbose:
        print (args)

    if (not args.to_broker):
        args.to_broker = args.from_broker
    bootstrap_src = '{0}:{1}'.format(args.from_broker, args.port)
    bootstrap_dst = '{0}:{1}'.format(args.to_broker, args.port)

    try:
        consumer_conf = {'bootstrap.servers': bootstrap_src,
                'enable.auto.commit': 'false',
                'session.timeout.ms': 6000,
                'group.id': 'log_copier',
                'api.version.request': True,
                'default.topic.config': {'auto.offset.reset': 'earliest' if args.offset == 'beginning' else 'latest'}}

        consumer = Consumer(consumer_conf)
        partitions = args.partition if args.partition else consumer.list_topics(args.from_topic).topics.get(args.from_topic).partitions.keys()

        if args.abs_offset == 0:
            tps = [TopicPartition(args.from_topic, p, OFFSET_BEGINNING if args.offset == 'beginning' else OFFSET_END) for p in partitions]
        elif args.abs_offset > 0:
            tps = [TopicPartition(args.from_topic, p, args.abs_offset) for p in partitions]
        else:
            end_offs = get_offsets_for_timestamps(consumer, args.from_topic, args.partition, OFFSET_END)
            tps = [TopicPartition(args.from_topic, p, end_offs[p] + args.abs_offset) for p in partitions]
        #print(tps)

        consumer.assign(tps)

        producer = Producer({'bootstrap.servers': bootstrap_dst})

        if verbose:
            print ("Source topic: {0}".format(consumer.list_topics(args.from_topic).topics))
            print ("Destination topic: {0}".format(producer.list_topics(args.to_topic).topics))

        counter = 0
        copy_count = 0
        keep_running = True
        while keep_running:
            messages = consumer.consume(500, 5)
            for message in messages:
                if message is None:
                    continue
                if message.error():
                    if message.error().code() == KafkaError._PARTITION_EOF:
                        continue
                    else:
                        keep_running = False
                        print(msg.error())
                        break
                counter += 1
                print ("Read {:,} messages.".format(counter).rjust(200), end='\r', file=sys.stderr)
                if args.key_filter and message.key():
                    if not any(x in message.key() for x in args.key_filter):
                        continue

                time_type = message.timestamp()[0]
                time_stamp = message.timestamp()[1]
                if args.start_time:
                    if time_stamp < args.start_time:
                        continue

                if args.end_time:
                    if time_stamp > args.end_time:
                        continue

                copy_count += 1

                if verbose:
                    dateStr = time.strftime("%Y-%m-%d %H:%M:%S", time.gmtime(message.timestamp()[1] / 1000))
                    print ("{} - {}".format(message.timestamp()[1], dateStr))
#                     print ("%s:%d:%d: key=%s value=%s" % (message.topic(), message.partition(),
#                                                   message.offset(), message.key(),
#                                                   message.value()))
                if not simulated:
                    producer.produce(topic=args.to_topic
                                  ,value=message.value()
                                  ,partition=message.partition()
                                  ,key=message.key()
                                  #,timestamp=time_stamp
                                  )

            producer.flush()

    except KeyboardInterrupt as e:
        print ("Stopped - copied {} messages".format(copy_count))
        consumer.close()
        producer.flush()

if __name__ == "__main__":
    atexit.register(wait_for_threads)
    main()


# coding: utf-8

# In[1]:

import pyautogui
pyautogui.PAUSE = 2.5
pyautogui.FAILSAFE = True

# In[ ]:

pyautogui.alert(text='Ready to rock the reCAPTCHA?', title='Ready?', button='OK')

# In[ ]:

moveToX = 500
moveToY = 500
pyautogui.rightClick(x=moveToX, y=moveToY)

# In[2]:

im = pyautogui.screenshot(region=(115, 12, 50, 50))
im.save('test.jpg')

# In[3]:

loc = pyautogui.locateOnScreen('test.jpg')

# In[4]:

print(loc)


#!/usr/bin/python3
import json

def save_to_json_file(my_obj, filename):
    """
    Writes an Object to a text file using
    a JSON representation
    """
    with open(filename, mode='w', encoding='utf-8') as file_open:
        f = json.dumps(my_obj)
        file_open.write(f)


# 聚合之前在服务器中进行在训练(baseline number = 100)
import os
import copy
import random
from tqdm import tqdm
import numpy as np
import torch, torchvision
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
from torchvision import datasets, transforms
from torch.utils.data.dataset import Dataset
from torchvision import transforms
from torchvision.transforms import Compose
torch.backends.cudnn.benchmark=True
from configuration import config
from CustomImageDataset import CustomImageDataset     # 图像增强模块
from VGGModule import VGG                   # 神经网络模块
from AlexNetModule import AlexNet
from LeNetModel import LeNet
from dataallocationrandom import dataallocationrandom

device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")

class FLlearningNonIID_1(object):
    def __init__(self):
        self.dataallocation = config.get("DataAllocation_14")     # 每个主机的分配的数据list
        self.classes_pc = 1                             # 每个主机分配的class的大小
        self.num_clients = len(self.dataallocation)          # 环境中主机的个数
        self.num_selected = self.num_clients            # 进行训练的主机的个数
        self.num_rounds = 20                           # 主机和服务器之间进行通信的轮数
        self.epochs = 5                                 # client本地训练的轮数
        self.batch_size = 32                            # 将数据批量加载到数据加载器中
        self.baseline_num = 100                         # 服务器重新训练的基准线
        self.retrain_epochs = 20                        # 服务器重新训练的轮数
        self.del_()                                     # 消除数据为0的节点client

    def del_(self):
        dataallocation_i_temp = [] # 存放元素不为0的元素
        for i in range(len(self.dataallocation)):
            if self.dataallocation[i] != 0:
                dataallocation_i_temp.append(self.dataallocation[i])
            else:
                pass
        self.dataallocation = dataallocation_i_temp
        # 更新self.num_clients和num_selected
        self.num_clients = len(self.dataallocation)
        if self.num_selected > self.num_clients:
            # self.num_selected = random.randint(1,self.num_clients)
            self.num_selected = self.num_clients
        else:
            pass

    #### get cifar dataset in x and y form
    # 得到cifar数据集的数据和标签
    def get_cifar10(self):
        '''Return CIFAR10 train/test data and labels as numpy arrays'''
        data_train = torchvision.datasets.CIFAR10('./data', train=True, download=True)
        data_test = torchvision.datasets.CIFAR10('./data', train=False, download=True)

        x_train, y_train = data_train.data.transpose((0,3,1,2)), np.array(data_train.targets)
        x_test, y_test = data_test.data.transpose((0,3,1,2)), np.array(data_test.targets)

        return x_train, y_train, x_test, y_test

    def print_image_data_stats(self,data_train, labels_train, data_test, labels_test):
        print("\nData: ")
        print(" - Train Set: ({},{}), Range: [{:.3f}, {:.3f}], Labels: {},..,{}".format(
            data_train.shape, labels_train.shape, np.min(data_train), np.max(data_train),
            np.min(labels_train), np.max(labels_train)))
        print(" - Test Set: ({},{}), Range: [{:.3f}, {:.3f}], Labels: {},..,{}".format(
            data_test.shape, labels_test.shape, np.min(data_train), np.max(data_train),
            np.min(labels_test), np.max(labels_test)))

    def clients_rand(self,train_len, nclients):
        '''
        train_len: size of the train data
        nclients: number of clients

        Returns: to_ret

        This function creates a random distribution
        for the clients, i.e. number of images each client
        possess.
        # '''
        # # 随机给client数据比例random
        # client_tmp=[]
        # sum_=0
        # #### creating random values for each client ####
        # for i in range(nclients-1):                     # 把剩余的数据量给最后一个client
        #     tmp=random.randint(10,100)
        #     sum_+=tmp
        #     client_tmp.append(tmp)

        # client_tmp= np.array(client_tmp)
        # #### using those random values as weights ####
        # clients_dist= ((client_tmp/sum_)*train_len).astype(int)
        # num  = train_len - clients_dist.sum()             # 计算剩余的数据量
        # to_ret = list(clients_dist)
        # to_ret.append(num)                          # 将剩余的数据量加入list中
        # return to_ret

        # client的数据比例给定dataallocation
        print("test_1")
        client_dist = []
        for i in range(len(self.dataallocation) -1):
            dtmp = int(train_len * self.dataallocation[i])
            client_dist.append(dtmp)
        client_dist_array = np.array(client_dist)       # 将client_dist转化为np.array类型,使用下面的sum函数计算
        num = train_len - client_dist_array.sum()       # 计算剩余的数据量
        client_dist.append(num)           # 将剩余的数据量加入list中
        to_ret = client_dist
        print("test_2")
        return to_ret

    def split_image_data(self,data, labels, n_clients=100, classes_per_client=10, shuffle=True, verbose=True):
        '''
        Splits (data, labels) among 'n_clients s.t. every client can holds 'classes_per_client' number of classes
        Input:
            data : [n_data x shape]
            labels : [n_data (x 1)] from 0 to n_labels
            n_clients : number of clients
            classes_per_client : number of classes per client
            shuffle : True/False => True for shuffling the dataset, False otherwise
            verbose : True/False => True for printing some info, False otherwise
        Output:
            clients_split : client data into desired format
        '''

        # 判断self.dataallocation中的元素是否都为平均值，即每个client中的数据量为平均分配
        # flag = 1: 平均分配，flag = 0 :非平均分配
        print("self.dataallocation:",self.dataallocation)
        print("ave:",1/self.num_clients)

        flag = 1      # 初始化flag = 1
        for i in self.dataallocation:
            if i != (1 / self.num_clients):
                flag = 0
            else:
                pass

        if flag == 1:
            print("flag = 1,数据平均分配")
        else:
            print("flag = 0，数据非平均分配")

        #### constants ####
        n_data = data.shape[0]                  # 总的数据量
        n_labels = np.max(labels) + 1           # 求序列的最值，即为n_labels的数量

        ### client distribution ####
        data_per_client = self.clients_rand(len(data), n_clients)
        print("data_per_client,环境中主机分配到的数据量大小为:",data_per_client)
        data_per_client_per_class = [np.maximum(1,nd // classes_per_client) for nd in data_per_client]
        print("data_per_client_per_class，环境中每个client中的每个类最多能分配到的数据大小:",data_per_client_per_class)

        data_per_client_temp = copy.deepcopy(data_per_client)

        # data temp
        if flag == 1:
            data_per_client_temp[(len(self.dataallocation) - 1)] -= 1
        else:
            pass
        print("data_per_client_temp:",data_per_client_temp)

        # sort for labels
        data_idcs = [[] for i in range(n_labels)]
        for j, label in enumerate(labels):
            data_idcs[label] += [j]
        if shuffle:
            for idcs in data_idcs:
                np.random.shuffle(idcs)

        # split data among clients
        clients_split = []
        c = 0
        for i in range(n_clients):
            # c = 0
            client_idcs = []

            budget = data_per_client_temp[i]                 # 每个client的数据量

            c = np.random.randint(n_labels)             # 随机获得class
            while budget > 0:
                take = min(data_per_client_per_class[i], len(data_idcs[c]), budget)  # 得到参数最小值

                client_idcs += data_idcs[c][:take]
                data_idcs[c] = data_idcs[c][take:]

                budget -= take

                c = (c + 1) % n_labels                  # 获取新的class
                # c = c + 1

            clients_split += [(data[client_idcs], labels[client_idcs])]
        print("data_per_client:1:",data_per_client)
        print("data_per_client_temp:1:",data_per_client_temp)
        # 内嵌函数，外部无法访问，打印信息
        def print_split(clients_split):
            print("Data split:")
            for i, client in enumerate(clients_split):
                split = np.sum(client[1].reshape(1,-1)==np.arange(n_labels).reshape(-1,1), axis=1)
                print(" - Client {}: {}".format(i,split))
            print()

            if verbose:
                print_split(clients_split)

        clients_split = np.array(clients_split,dtype=object)

        return clients_split

    def shuffle_list(self,data):
        '''
        This function returns the shuffled data
        '''
        # print("len(data)",len(data))
        for i in range(len(data)):
            tmp_len = len(data[i][0])
            index = [i for i in range(tmp_len)]
            random.shuffle(index)
            data[i][0],data[i][1] = self.shuffle_list_data(data[i][0],data[i][1])
        return data

    def shuffle_list_data(self,x, y):
        '''
        This function is a helper function, shuffles an
        array while maintaining the mapping between x and y
        '''
        inds = list(range(len(x)))
        random.shuffle(inds)
        return x[inds],y[inds]

    def get_default_data_transforms(self,train=True, verbose=True):
        transforms_train = {
        'cifar10' : transforms.Compose([
            transforms.ToPILImage(),
            transforms.RandomCrop(32, padding=4),
            transforms.RandomHorizontalFlip(),
            transforms.ToTensor(),
            transforms.Normalize((0.4914, 0.4822, 0.4465), (0.2023, 0.1994, 0.2010))]),#(0.24703223, 0.24348513, 0.26158784)
        }
        transforms_eval = {
        'cifar10' : transforms.Compose([
            transforms.ToPILImage(),
            transforms.ToTensor(),
            transforms.Normalize((0.4914, 0.4822, 0.4465), (0.2023, 0.1994, 0.2010))])
        }
        if verbose:
            print("\nData preprocessing: ")
            for transformation in transforms_train['cifar10'].transforms:
                print(' -', transformation)
            print()

        return (transforms_train['cifar10'], transforms_eval['cifar10'])

    def get_data_loaders(self,nclients,batch_size,classes_pc=10 ,verbose=True ):

        x_train, y_train, x_test, y_test = self.get_cifar10()

        if verbose:
            self.print_image_data_stats(x_train, y_train, x_test, y_test)

        transforms_train, transforms_eval = self.get_default_data_transforms(verbose=False)

        split = self.split_image_data(x_train, y_train, n_clients=nclients,
                classes_per_client=self.classes_pc, verbose=verbose)
        # print("n_client test:",nclients)
        split_tmp = self.shuffle_list(split)

        client_loaders = [torch.utils.data.DataLoader(CustomImageDataset(x, y, transforms_train),
                                                                        batch_size=batch_size, shuffle=True) for x, y in split_tmp]

        test_loader = torch.utils.data.DataLoader(CustomImageDataset(x_test, y_test, transforms_eval), batch_size=100, shuffle=False)

        return client_loaders, test_loader

    def baseline_data(self,num):
        '''
        Returns baseline data loader to be used on retraining on global server
        Input:
                num : size of baseline data
        Output:
                loader: baseline data loader
        '''
        xtrain, ytrain, xtmp, ytmp = self.get_cifar10()
        x , y = self.shuffle_list_data(xtrain, ytrain)

        x, y = x[:num], y[:num]
        transform, _ = self.get_default_data_transforms(train=True, verbose=False)
        loader = torch.utils.data.DataLoader(CustomImageDataset(x, y, transform), batch_size=16, shuffle=True)
        return loader

    def client_update(self, client_model, optimizer, train_loader, epoch, model):
        """
        This function updates/trains client model on client data
        """
        model.train()
        for e in range(epoch):
            for batch_idx, (data, target) in enumerate(train_loader):
                data, target = data.cuda(), target.cuda()
                # data, target = data.to(device), target.to(device)
                optimizer.zero_grad()
                output = client_model(data)
                loss = F.nll_loss(output, target)
                loss.backward()
                optimizer.step()
        return loss.item()

    def client_syn(self, client_model, global_model):
        '''
        This function synchronizes the client model with global model
        '''
        client_model.load_state_dict(global_model.state_dict())

    def server_aggregate(self, global_model, client_models, client_lens):
        """
        This function has aggregation method 'wmean'
        wmean takes the weighted mean of the weights of models
        """
        total = sum(client_lens)
        n = len(client_models)
        global_dict = global_model.state_dict()
        for k in global_dict.keys():
            global_dict[k] = torch.stack([client_models[i].state_dict()[k].float()*(n*client_lens[i]/total) for i in range(len(client_models))], 0).mean(0)
        global_model.load_state_dict(global_dict)
        for model in client_models:
            model.load_state_dict(global_model.state_dict())

    def test(self, global_model, test_loader, model):
        """
        This function test the global model on test
        data and returns test loss and test accuracy
        """
        model.eval()
        test_loss = 0
        correct = 0
        with torch.no_grad():
            for data, target in test_loader:
                data, target = data.cuda(), target.cuda()
                # data, target = data.to(device), target.to(device)
                output = global_model(data)
                test_loss += F.nll_loss(output, target, reduction='sum').item()  # sum up batch loss
                pred = output.argmax(dim=1, keepdim=True)  # get the index of the max log-probability
                correct += pred.eq(target.view_as(pred)).sum().item()

        test_loss /= len(test_loader.dataset)
        acc = correct / len(test_loader.dataset)

        return test_loss, acc

    def main(self):
        print("test")
        ############################################
        #### Initializing models and optimizer  ####
        ############################################

        #### global model ##########
        global_model =  VGG("VGG19").cuda()

        ############# client models ###############################
        client_models = [ VGG("VGG19").cuda() for _ in range(self.num_selected)]
        for model in client_models:
            model.load_state_dict(global_model.state_dict()) ### initial synchronizing with global modle

        ###### optimizers ################
        opt = [optim.SGD(model.parameters(), lr = 0.01) for model in client_models]

        ####### baseline data ############
        loader_fixed = self.baseline_data(self.baseline_num)

        ###### Loading the data using the above function ######
        train_loader, test_loader = self.get_data_loaders(classes_pc=self.classes_pc, nclients= self.num_clients,
                                                            batch_size=self.batch_size,verbose=True)

        losses_train = []
        losses_test = []
        acc_test = []
        losses_retrain=[]

        # Runnining FL
        for r in range(self.num_rounds):    #Communication round
            # select random clients
            client_idx = np.random.permutation(self.num_clients)[:self.num_selected]
            client_lens = [len(train_loader[idx]) for idx in client_idx]

            # client update
            loss = 0
            for i in tqdm(range(self.num_selected)):
                self.client_syn(client_models[i], global_model)
                loss += self.client_update(client_models[i], opt[i], train_loader[client_idx[i]], self.epochs,model)
            losses_train.append(loss)

            # server aggregate
            #### retraining on the global server
            # loss_retrain = 0
            # for i in tqdm(range(self.num_selected)):
            #     loss_retrain+= self.client_update(client_models[i], opt[i], loader_fixed, self.retrain_epochs,model)
            # losses_retrain.append(loss_retrain)

            ### Aggregating the models
            self.server_aggregate(global_model, client_models,client_lens)
            test_loss, acc = self.test(global_model, test_loader,model)
            losses_test.append(test_loss)
            acc_test.append(acc)
            print('%d-th round' % r)
            print('average train loss %0.3g | test loss %0.3g | test acc: %0.3f' % (loss_retrain / self.num_selected, test_loss, acc))
        return acc_test, losses_test

    # 类的销毁
    def __del__(self):
        print("FLLearningNon-IID的对象被收回----\n")

if __name__ == "__main__":
    dataallocation = config.get("DataAllocation_14")
    # dataallocation = dataallocationrandom(10)

    FL = FLlearningNonIID_1()

    FL.num_rounds = 100
    acc_test, losses_test = FL.main()


from pint import models, toa , residuals, fitter
import matplotlib.pyplot as plt
from astropy import units
from astropy.visualization import quantity_support
import numpy as np

#%% Set matplotlib general parameters

#Make everything larger for readability on graphs
plt.rcParams['xtick.labelsize'] = 12
plt.rcParams['ytick.labelsize'] = 12
plt.rcParams['font.size'] = 14
#Set same fonts as my LaTeX document
plt.rcParams['font.family'] = 'STIXGeneral'
plt.rcParams['mathtext.fontset'] = 'stix'
#other plotting params
plt.rcParams['axes.grid'] = True
plt.rcParams["figure.figsize"] = [10,10]

#%%

# Import model
model = models.model_builder.get_model('J0332+5434_Grahams_initial_parameters.par')

# Import TOAs
TOAs = toa.get_TOAs('J0332+5434_Grahams_Observations.tim', planets=True)

# Compute pre-fit residuals
prefit_residuals = residuals.Residuals(TOAs, model)

# Plot them, with the jumps
fig3 = plt.figure()
ax3 = plt.gca()
ax3.errorbar(
    TOAs.get_mjds().value,
    prefit_residuals.time_resids.to(units.us),
    yerr=TOAs.get_errors().to(units.us),
    marker='o',markersize=0.5,
    color='black', elinewidth=0.5, capsize=0, linestyle='none')
ax3.vlines(56769, np.min(prefit_residuals.time_resids.to(units.us)),  np.max(prefit_residuals.time_resids.to(units.us)), linewidth=0.5, color='red', label='Jumps')
ax3.vlines(56794, np.min(prefit_residuals.time_resids.to(units.us)),  np.max(prefit_residuals.time_resids.to(units.us)),  linewidth=0.5, color='red')
ax3.vlines(57048, np.min(prefit_residuals.time_resids.to(units.us)),  np.max(prefit_residuals.time_resids.to(units.us)), linewidth=0.5, color='red')
ax3.vlines(57575, np.min(prefit_residuals.time_resids.to(units.us)),  np.max(prefit_residuals.time_resids.to(units.us)),  linewidth=0.5, color='red')
ax3.set_title("%s Pre-fit Timing Residuals" % model.PSR.value)
ax3.set_xlabel("MJD")
ax3.set_ylabel("Residual ($\mu$s)")
plt.legend()
plt.savefig('Pre_fit_residuals_Graham.pdf')
plt.close()

# Perform the fit
WLS_fit = fitter.WLSFitter(TOAs, model)
WLS_fit.set_fitparams('F0','F1', 'RAJ', 'DECJ', 'JUMP1', 'JUMP2', 'JUMP3')
WLS_fit.fit_toas()

fig4 = plt.figure()
ax4 = plt.gca()

# Plot the post-fit residuals
ax4.errorbar(TOAs.get_mjds().value, WLS_fit.resids.time_resids.to(units.us), yerr=TOAs.get_errors().to(units.us), color='black', elinewidth=0.05, capsize=0, marker='o',markersize=0.8, linestyle='none')
ax4.vlines(56769,  np.min(WLS_fit.resids.time_resids.to(units.us)),  np.max(WLS_fit.resids.time_resids.to(units.us)), linewidth=0.5, color='red', label='Jumps')
ax4.vlines(56794, np.min(WLS_fit.resids.time_resids.to(units.us)),  np.max(WLS_fit.resids.time_resids.to(units.us)), linewidth=0.5, color='red')
ax4.vlines(57048, np.min(WLS_fit.resids.time_resids.to(units.us)),  np.max(WLS_fit.resids.time_resids.to(units.us)), linewidth=0.5, color='red')
ax4.vlines(57575, np.min(WLS_fit.resids.time_resids.to(units.us)),  np.max(WLS_fit.resids.time_resids.to(units.us)), linewidth=0.5, color='red')
ax4.set_title("%s Post-Fit Timing Residuals" % model.PSR.value)
ax4.set_xlabel("MJD")
ax4.set_ylabel("Residual ($\mu$s)")
plt.legend()
#plt.savefig('Post_fit_residuals_Graham.pdf')
#plt.close()