###################################################################################
# Author: Ricardo Pereira
# Date: 06-06-2021
# Last Modified data: 09-07-2021
# Abstract: GSF2App: Training file
###################################################################################

import numpy as np
import time
import glob
import cv2
import os
import torch
import torch.nn as nn
import torch.nn.functional as F
from torchvision import datasets, models, transforms
import torch.optim as optim
from torch.autograd import Variable
from torch.utils import data
import argparse
import torchsummary
import sys

from GSF2App_data import *
from GSF2App_network import *
from GSF2App_eval import *

def check_GPU():
	device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
	print ("\nDevice: " + str(device))
	return device

def training_gsf2app(trainloader, learning_rate, classes, cnn_model_path, gsf2app_model_path, n_epochs, training_stage):
	# Initializations
	device = check_GPU()

	if training_stage == 1:
		model = load_pre_trained_model(len(classes), training_stage, cnn_model_path)

	elif training_stage == 2:
		model = GSF2App(cnn_model_path, len(classes))

	model.to(device)

	criterion = nn.CrossEntropyLoss()
	optimizer = optim.Adam(model.parameters(), lr = learning_rate, weight_decay = 0.0005)
	#optimizer = optim.SGD(model.parameters(), lr = learning_rate, momentum =0.9, weight_decay = 0.0005)

	print("Training...")
	for epoch in range(n_epochs):
		for local_batch, data in enumerate(trainloader, 0):
			if training_stage == 1:
				images, labels = data
				images, labels = images.to(device), labels.to(device)
				outputs = model(images)

			elif training_stage == 2:
				images, sfv_data, labels = data
				images, sfv_data, labels = images.to(device), sfv_data.to(device), labels.to(device)
				outputs = model(images, sfv_data)

			optimizer.zero_grad()
			loss = criterion(outputs, labels)
			loss.backward()
			optimizer.step()

			# print loss per batch
			if local_batch == 0:
				print ('[%d, %5d] loss: %.3f' % (epoch + 1, local_batch + 1,  loss.item()))

	# Save the model
	if training_stage == 1:
		torch.save(model.state_dict(), cnn_model_path)
		print('\nGSF2App\'s learning stage ' + str(training_stage) + ' was successfully trained!')
		print('Model saved in: ' + cnn_model_path)
	elif training_stage == 2:
		torch.save(model.state_dict(), gsf2app_model_path)
		print('\nGSF2App\'s learning stage ' + str(training_stage) + ' was successfully trained!')
		print('Model saved in: ' + gsf2app_model_path)

if __name__ == '__main__':
	print("PyTorch version = {}".format(torch.__version__))

	parser = argparse.ArgumentParser()
	parser.add_argument("--stage_1_n_epochs", type = int, default=1, help="stage 1 number of epochs") # 75
	parser.add_argument("--stage_1_lr", type=float, default=0.0001, help="stage 1 learning rate")
	parser.add_argument("--stage_2_n_epochs", type = int, default=1, help="stage 2 number of epochs") # 25
	parser.add_argument("--stage_2_lr", type=float, default=0.0001, help="stage 2 learning rate")
	parser.add_argument("--batch_size", type=int, default=32, help="size of each image batch")
	parser.add_argument("--dataset", type=str, default="nyu", help="dataset") #nyu or sun
	opt = parser.parse_args()
	print(opt)

	# --------------- Load Dataset --------------- #
	print("\n# ----- " + opt.dataset + " Dataset ----- #")

	X_train_datapath= os.path.join('folder_data_path')
	X_test_datapath= os.path.join('folder_data_path')

	scene_classes = open(os.path.join('folder_path','file.names'), "r").read().split("\n")[:-1]
	print("Number of classes: " + str(len(scene_classes)))

	# --------------- Prepare Dataset --------------- #
	training_set = Dataset(X_train_datapath)
	test_set = Dataset(X_test_datapath)

	train_loader = data.DataLoader(dataset=training_set, batch_size = opt.batch_size, shuffle = False)
	test_loader = data.DataLoader(dataset=test_set, batch_size = opt.batch_size, shuffle = False)

	# --------------- Training --------------- #

	if not os.path.isdir(os.path.join('models','cnn',opt.dataset)):
		os.makedirs(os.path.join('models','cnn',opt.dataset))
	if not os.path.isdir(os.path.join('models','gsf2app',opt.dataset)):
		os.makedirs(os.path.join('models','gsf2app',opt.dataset))

	cnn_model_path = os.path.join('models','cnn',opt.dataset,opt.dataset+'.ckpt')
	gsf2app_model_path = os.path.join('models','gsf2app',opt.dataset,'gsf2app_'+opt.dataset+'.ckpt')

	# Train CNN model
	training_gsf2app(train_loader, opt.stage_1_lr, scene_classes, cnn_model_path,
		gsf2app_model_path, opt.stage_1_n_epochs, 1)

	# Eval CNN model
	eval_gsf2app(test_loader, scene_classes, cnn_model_path, gsf2app_model_path, 1)

	# Train GSF2App model
	training_set.training_stage = 2
	training_gsf2app(train_loader, opt.stage_2_lr, scene_classes, cnn_model_path,
		gsf2app_model_path, opt.stage_2_n_epochs, 2)

	# Eval GSF2App model
	test_set.training_stage = 2
	eval_gsf2app(test_loader, scene_classes, cnn_model_path, gsf2app_model_path, 2)


#!/usr/bin/env python3
# -*- coding:utf-8 -*-

'''
Given a company with CIK (company ID) XXX (omitting leading zeroes) and
document accession number YYY (acc-no on search results), programmatically
generate the url to get data (http://www.sec.gov/Archives/edgar/data/51143
/000005114313000007/0000051143-13-000007-index.html for IBM for example).
Parse the file to locate the link to the 10q file (quarterly financial
filing form) (https://www.sec.gov/Archives/edgar/data/51143/000005114313
000007/ibm13q3_10q.htm for the above example). Parse this file to
extract “all” tables in this filing and save them as csv files.
'''

import sys
import time
import urllib
import requests
import shutil
import numpy as np
from numpy import *
import pandas as pd
from pandas import *
from scipy.stats import mode
import boto3
from botocore.client import Config
import matplotlib
#matplotlib.use('Agg')
import matplotlib.pyplot as plt

matplotlib.style.use('ggplot')

__author__ = 'Jiali Cheng(001822320)'

class AnalyseLog:
    ''' This is a table parser that extract all the tables and save them as
        CSV files, the URL generated by a given CID and Acc-no from EDGAR.'''

    def __init__(self, year):

        self.__year = year
        self.__df = DataFrame()

    def getLog(self, month):
        ''' Form the unique URL for a given Accno'''

        qtr = (month+2)//3
        if month<10:
            month = "0"+str(month)
        month = str(month)
        url = ("https://www.sec.gov/dera/data/Public-EDGAR-log-file-data/%s/Qtr%s/log%s01.zip" % (self.__year, qtr, self.__year+month))
        #path = "./log%s01.zip/"%self.__year+month
        #try:

        # Log and print
        operation = "Start retrieving files from "+url
        self.log(operation)
        print(operation)

        # Retrieving
        filename = ("log%s"% self.__year)+month+"01.zip"
        urllib.request.urlretrieve(url,filename=filename)

        # Log and print
        operation = "Done retrieving."
        self.log(operation)
        print(operation)

        # Unzip the file
        shutil.unpack_archive(filename)

        # Log and print
        operation = "Done unpacking."
        self.log(operation)
        print(operation)

        logfile = ("log%s"% self.__year)+month+"01.csv"

        return logfile

        #except:

         #   operation = "Failed"
         #   self.log(operation)
         #   print(operation)
          #  exit(0)

    def analyse(self, filename, m):
        '''Read data, analyse data and deal with missing data'''

        with open(filename, 'r') as f:

            df = pd.read_csv(f)

            self.analyseIp(filename,df["ip"])
            self.analyseTime(filename,df["time"],m)
            self.analyseCik(filename,df["cik"],m)
            self.analyseCode(filename,df["code"],m)
            self.analyseSize(filename,df["size"],m)
            self.analyseIdx(filename,df["idx"])
            self.analyseRef(filename,df["norefer"])
            self.analyseAgt(filename,df["noagent"],m)
            self.analyseCrw(filename,df["crawler"],m)
            self.analyseBrw(filename,df["browser"],m)

        return

    def analyseIp(self, filename, df):###################################################################################
        '''First three octets of the IP address with the fourth octet obfuscated'''

        print("***************Analysing IP***************")
        class_a = 0
        class_b = 0
        class_c = 0

        # Metrics
        # Calculate the numbers of class A, B, C, D, E and missing IP
        number = df.shape[0]
        missing = df.ix[pd.isnull(df),].shape[0]

        for i in df:

            i1 = i.split('.')[0]
            i1 = int(i1)

            if i1 < 128:
                class_a+=1
            elif i1 > 128 & i1 < 192:
                class_b+=1
            elif i1 > 192 & i1 < 224:
                class_c+=1
            else:
                missing+=1

        # Find anomalies
        # Hear we can do nothing towards IP

        # Handle missing data
        # Here we can do nothing towards IP

        # Write summary
        summary = Series([class_a,class_b,class_c,missing,number], index=["class_a","class_b","class_c","missing","total"])
        self.summarize(summary)

        # Log and print
        operation = (
            "Done analysing IP of %s. %s IP addresses totally. %s "
            "class A IPs, %s class B IPs, %s class C IPs and %s "
            "missing IPs.\n" %
            (filename,number,class_a,class_b,class_c,missing))
        self.log(operation)
        print(operation)

    def analyseTime(self, filename, df, m):
        '''Apache log file time'''

        print("***************Analysing Time***************")

        # Find anomalies
        # Sort by time oreder, corecct illegitimate time
        df = df.sort_values(ascending=True)

        # Handle missing data
        # Fill missing time with the former time
        missing = df.ix[pd.isnull(df),].shape[0]
        # if missing != 0:
            #df1.fillna(value=5)

        # Metrics
        count = df.value_counts()
        count = count.sort_index(ascending=True)

        # Log and print
        operation = (
            "Done analysing IP of %s. %s missing values replaced by their adjacent value.\n" %
            (filename,missing))
        self.log(operation)
        print(operation)

        # Plot
        plt.figure("Access volume - time on %s/1, %s" % (m,self.__year))
        count.plot()

        return

    def analyseCik(self, filename, df, m):
        '''SEC Central Index Key (CIK) associated with the document requested'''

        print("***************Analysing CIK***************")

        # Metrics
        number = df.shape[0]
        count = df.value_counts()

        # Find anomalies
        # Hear we can do nothin towards CIK

        # Handle missing data
        # Fill missing CIK with a random one
        missing = df.ix[pd.isnull(df),].shape[0]
        # if missing != 0:
            #df1.fillna(value=5)

        # Plot
        plt.figure("CIK - access volume on %s/1, %s" % (m,self.__year))
        count.plot.hist(logx=True)

    def analyseCode(self, filename, df, m):
        '''Apache log file status code for the request'''

        print("***************Analysing Code***************")

        # Metrics
        number = df.shape[0]
        count = df.value_counts()

        # Find anomalies
        # Hear we can do nothin towards IP

        # Handle missing data
        # Replace all the missing data with 200 OK, which is the most possible case
        missing = df.ix[pd.isnull(df),].shape[0]
        if missing != 0:
            df.fillna(count.index[0])

        # Plot
        plt.figure("Code volume on %s/1, %s" % (m,self.__year))
        count.plot.hist()
        count.plot()

    def analyseSize(self, filename, df, m):
        '''Document file size'''

        number = df.shape[0]

        # Find anomalies
        # Hear we can do nothin towards IP

        # Handle missing data
        # Replace with mean
        missing = df.ix[pd.isnull(df),].shape[0]
        if missing != 0:
            df.fillna(df.mean())

        # Metrics

        # Plot
        plt.figure("File size box on %s/1, %s" % (m,self.__year))
        color = dict(boxes='DarkGreen', whiskers='DarkOrange',
                 medians='DarkBlue', caps='Gray')
        df.plot.box(color=color, sym='r+')

        pass

    def analyseIdx(self, filename, df):###################################################################################

        # Find anomalies
        # Hear we can do nothin towards IP

        # Handle missing data
        # Here we can do nothing toward IP

        # Metrics

        pass

    def analyseRef(self, filename, df):###################################################################################

        # Find anomalies
        # Hear we can do nothin towards IP

        # Handle missing data
        # Here we can do nothing toward IP

        # Metrics

        pass

    def analyseAgt(self, filename, df, m):###################################################################################

        print("***************Analysing Agent***************")

        # Metrics
        # Calculate the numbers of whether or not using agent and missing
        number = df.shape[0]
        count = df.value_counts()
        if count.index.size>1:
            use_agt = count[1]
            not_agt = count[0]
        else:
            use_agt = count[0]
            not_agt = 0

        # Find anomalies
        # Hear we can do nothin towards IP

        # Handle missing data
        # Replace with mode
        missing = df.ix[pd.isnull(df),].shape[0]
        if missing != 0:
            df.fillna(np.nan,caa95f3c0d41)

        # Write summary
        summary = Series([use_agt,not_agt,missing,number], index=["using agent","not using agent","missing","total"])
        self.summarize(summary)
        print(summary)

        # Log and print
        operation = (
            "Done analysing agent of %s. %s items totally. "
            "%s uses agent, %s not and %s missing.\n" %
            (filename,number,use_agt,not_agt,missing))
        self.log(operation)
        print(operation)

        # Plot
        plt.figure("Agnet using distribution on %s/1, %s" % (m,self.__year))
        count.plot.pie(labels=["not using agent","using agent"], autopct='%.2f', fontsize=10, figsize=(6, 6))

    def analyseCrw(self, filename, df, m):

        print("***************Analysing Crawler***************")

        # Metrics
        # Calculate the numbers of whether or not using agent and missing
        number = df.shape[0]
        count = df.value_counts()
        if count.index.size>1:
            use_crw = count[1]
            not_crw = count[0]
        else:
            use_crw = count[0]
            not_crw = 0
        # Find anomalies
        # Hear we can do nothin towards crawler

        # Handle missing data
        # Here we fill missing data with not using crawler
        missing = df.ix[pd.isnull(df),].shape[0]
        if missing != 0:
            df.fillna(value=0)
            count = df.value_counts()
            not_crw = count[0]
            use_crw = count[1]

        # Write summary
        summary = Series([use_crw,not_crw,missing,number], index=["using crawler","not using crawler","missing","total"])
        self.summarize(summary)
        print(summary)

        # Log and print
        operation = (
            "Done analysing agent of %s. %s items totally. "
            "%s uses crawler, %s not and %s missing.\n" %
            (filename,count,use_crw,not_crw,missing))
        self.log(operation)
        print(operation)

        # Plot
        plt.figure("Crawler using distribution on %s/1, %s" % (m,self.__year))
        count.plot.pie(labels=["using crawler","not using crawler"], autopct='%.2f', fontsize=10, figsize=(6, 6))

    def analyseBrw(self, filename, df, m):

        print("***************Analysing Brwoser***************")

        # Metrics
        # Calculate the numbers of which kind of browser and missing
        number = df.shape[0]
        count = df.value_counts()
        count_number = count.shape[0]

        # Find anomalies
        # Hear we can do nothin towards IP

        # Handle missing data
        # Here we fill missing parts with respect to the proportion of each kind accordingly
        missing = df.ix[pd.isnull(df),].shape[0]
        if missing != 0:
            df.replace(np.nan,count.index[0])

        # Write summary
        summary = count
        self.summarize(summary)

        # Log and print
        operation = (
            "Done analysing agent of %s. %s items totally.\n" % (filename,number))
        self.log(operation)
        print(operation)

        # Plot
        plt.figure("Browser using distribution on %s/1, %s" % (m,self.__year))
        count.plot.pie(labels=count.index, autopct='%.2f', fontsize=10, figsize=(6, 6))

    def log(self, operation):
        '''Log all the operations with a time stamp into a log file'''

        self.__final_log = "%s-final-log.txt" % self.__year
        with open(self.__final_log, 'a') as f:
            data = time.ctime() + ": " + operation
            f.write(data)

    def summarize(self, summary):

        if self.__df.empty:
            self.__df = summary
        else:
            self.__df = pd.concat([self.__df,summary],axis=1)

    def concatData(self, log_list):###################################################################################
        '''Data should be concatinated calling pd.concat() since they have the same variables'''

        pieces = []
        for f in log_list:
            df = pd.read_csv(f)
            pieces.append(df)

        merged_data = pd.concat(pieces, keys=log_list)
        final_data = "%s-final-data.csv"%self.__year
        merged_data.to_csv(final_data)

        return final_data

    def concatSummary(self, log_list):
        '''Summary of 12 months should be concatinated calling pd.concat() since they have the same variables'''

        merged_data = pd.concat(log_list)#, keys=log_list)
        final_summary = "%s-final-summary.csv"%self.__year
        merged_data.to_csv(final_summary)

        return final_summary

    def upload_path(self, ACCESS_NAME, ACCESS_KEY):###################################################################################
        '''Path of the AWS S3 bucket to upload the file'''

        print("***************Uploading to AWS S3 bucket***************")

        self.__ACCESS_NAME = ACCESS_NAME
        self.__ACCESS_KEY = ACCESS_KEY
        self.__BUCKET_NAME = "info7390-case1"
        print("Accessing bucket name %s: " %  self.__BUCKET_NAME)

    def upload(self, filename):###################################################################################
        '''Upload the file to the bucket'''

        data = open(filename, 'rb')

        s3 = boto3.resource(
            's3',
            aws_access_key_id=self.__ACCESS_NAME,
            aws_secret_access_key=self.__ACCESS_KEY,
            config=Config(signature_version='s3v4'))

        s3.Bucket(self.__BUCKET_NAME).put_object(Key=filename, Body=data)

        # Log and print
        operation = "Done uploading to: " + self.__BUCKET_NAME + ".\n"
        self.log(operation)
        print(operation)

    def startanalysing(self):###################################################################################
        ''' Start getting and analysing data from EDGAR log file and upload to the bucket'''

        if True:
            log_list=[]
            sum_list=[]
            for m in range(1,13):

                # Get log of the first day of each month in a year
                filename = self.getLog(m)
                # Add each log into a list
                log_list.append(filename)
                self.__df = DataFrame()

                # Analyse this file
                self.analyse(filename,m)
                # df = self.summarize
                sum_list.append(self.__df)

            print("***************Done analysing of %s log file***************" % self.__year)

            self.upload_path(sys.argv[2],sys.argv[3])
            self.__final_data = self.concatData(log_list)
            self.__final_summary = self.concatSummary(sum_list)
            self.upload(self.__final_data)
            self.upload(self.__final_summary)
            self.upload(self.__final_log)
            print("***************Figures of %s***************" % self.__year)
            plt.show()
            print("***************Done uploading of %s***************" % self.__year)

if __name__ == '__main__':

    if len(sys.argv) > 0:
        year = sys.argv[1]
        md = AnalyseLog(year)
        md.startanalysing()


# -*- coding: utf-8 -*-
from django.conf.urls.defaults import *
from django.contrib import admin
from django.conf import settings
from patch import sites_flatpages_patch
from django.views.generic.simple import direct_to_template

admin.autodiscover()
sites_flatpages_patch()

urlpatterns = patterns('',
    (r'^admin/', include(admin.site.urls)),
    url(r'^help/$', direct_to_template, {'template': 'help.html'}, name='help'),
    (r'^registration/', include('registration.urls')),
    url(r'^login/$', 'django.contrib.auth.views.login', name='login'),
    url(r'^logout/$', 'django.contrib.auth.views.logout', {'next_page': '/'}, name='logout'),
    (r'^', include('app.main.urls',  namespace='main')),
    (r'^lectures/', include('app.lectures.urls',  namespace='lectures')),
    (r'^statistic/', include('app.statistic.urls',  namespace='statistic')),
    (r'^static/(?P<path>.*)$', 'django.views.static.serve', {'document_root': settings.MEDIA_ROOT}),
)


import sqlite3
import os
from pathlib import Path
"""
Commands:
    add -pub  file_path/dir_path
    add -pri  file_path/dir_path alias/ip
    rm -pub  file_path/dir_path
    rm -pri  file_path/dir_path
    search  file_path/dir_path
"""

class fileHandle:

    def __init__(self):
        self.a = 5
        path = 'fileData.db'
        self.dbConnection = sqlite3.connect(path)
        self.db = self.dbConnection.cursor()

    def createDB(self):
        q = """
                CREATE TABLE pub(
                Name VARCHAR(100) ,
                Path VARCHAR(100) PRIMARY KEY UNIQUE,
                TYPE INTEGER
                );"""
        self.db.execute(q)
        self.dbConnection.commit()
        print('pub Table created')

        q = """
                CREATE TABLE pri(
                Name VARCHAR(100) ,
                Path VARCHAR(100) PRIMARY KEY UNIQUE,
                TYPE INTEGER,
                mac INTEGER
                );"""
        self.db.execute(q)
        self.dbConnection.commit()
        print('pub Table created')

    def handleCMD(self, recData):
        recData = recData.lstrip()
        recData = recData.rstrip()
        index = self.getindex(recData, ' ')
        if index >0:
            cmd = recData[:index]
            opData = recData[index+1:]

            if cmd == "add":
                self.__handleADD__(opData)
            elif cmd == "get":
                self.__handleGET__(opData)
            elif cmd == "rm":
                self.__handleRM__(opData)
        else:
            return "illegalCMD"

    def __handleADD__(self, opData):
        opData = (opData.lstrip()).rstrip()
        index = self.getindex(opData, ' ')
        if index > 0:
            option = opData[:index]
            pS = opData[index + 1:]
            p = Path(pS)
            name = pS[pS.rfind('\\')+1:]
            if p.is_dir():
                type = 1
            elif p.is_file():
                type = 0
            else:
                return 'fdir_not_exist'
            if option == "-pub":
                q = """REPLACE INTO pub (name, path, type) VALUES (?, ?, ?)"""
                try:
                    self.db.execute(q, (name, pS, type))
                except:
                    print('Something Went Wrong')

            elif option == "-pri":
                q = """REPLACE INTO pri (name, path, type)"""
                try:
                    self.db.execute(q, (name, pS, type))
                except:
                    print('Something Went Wrong')
            else:
                return "illegalCMD"
        else:
            return "illegalCMD"

    def __handleGET__(self, opData):
        return 1

    def __handleRM__(self, opData):
        return 1

    def getindex(self, str, indexOf):
        try:
            return str.index(indexOf)
        except:
            return -1


import numpy as np
import matplotlib.pyplot as py
from math import pi

def euler(xs, fs, h):
    return xs + h * fs(xs)

def solver(method, x0, fs, tmax, h = 1E-3, maxsteps = 1E+5, skip_t = 0.1):
    steps = 0
    xs = [x0] # list of values of the solution
    last_t = 0 # last recorded time
    x = x0 # current value of the solution
    while x[0] <= tmax and steps <= maxsteps:
        steps += 1 # increment the value of steps
        x = method(x, fs, h)
        if abs(x[0] - last_t) > skip_t:
            xs.append(x)
            last_t = x[0]
    if steps > maxsteps:
        print("Warning: max number of steps reached")
    return xs
# x = [x, p]
# dx/dt = p, dp/dt = - omega**2 * x
def oscillator(x, omega = 1):
    return np.array([1, x[2], - omega**2 * x[1]])

sol = solver(
    euler, # method
    np.array([0, 1, 0]), # initial state t=0, x=1, p=0
    oscillator, # fs - vector of first derivatives
    10, # tmax
    h = 1E-3
)

py.figure()

T, X, P = zip(*sol)
py.plot(T, X, color='black', label='position')
py.plot(T, P, '--', color='orange', label='velocity')
py.legend()
py.show()


"""Classes for buffered rendering of 2D arrays of (colored) characters to the terminal

Windows provide buffered rendering of 2d arrays of (colored) charactors.

FullscreenWindow will only render arrays the size of the terminal
or smaller, and leaves no trace on exit (like top or vim). It never
scrolls the terminal. Changing the terminal size doesn't do anything,
but 2d array rendered needs to fit on the screen.

CursorAwareWindow provides the ability to find the
location of the cursor, and allows scrolling.
Sigwinches can be annotated with how the terminal scroll changed
Changing the terminal size breaks the cache, because it
is unknown how the window size change affected scrolling / the cursor.
Leaving the context of the window deletes everything below the cursor
at the beginning of the its current line.
(use scroll_down() from the last rendered line if you want to save all history)

All windows write only unicode to the terminal - that's what blessings does, so
we match it.
"""

import locale
import logging
import os
import re
import signal
import sys

import blessings

from .formatstring import fmtstr
from .formatstringarray import FSArray
from .termhelpers import Cbreak, Nonblocking
from . import events

logger = logging.getLogger(__name__)

SCROLL_DOWN = u"\x1bD"
FIRST_COLUMN = u"\x1b[1G"

#BIG TODO!!!
#TODO How to get cursor position? It's a thing we need!
#
# Option 1: Window has a reference to the corresponding stdin
#           For now, going to HCF when we read input that isn't a reponse to a cursor query
#           Later will figure out how to deal with this (ungetc, use the same input gen, etc.)
#
# Is seems ungetc would require compiling code to install - that's a bit of a barrier
# for something you want to install all the time like bpython

class BaseWindow(object):
    """ Renders 2D arrays of characters and cursor position """
    def __init__(self, out_stream=sys.stdout, hide_cursor=True):
        logger.debug('-------initializing Window object %r------' % self)
        self.t = blessings.Terminal(stream=out_stream, force_styling=True)
        self.out_stream = out_stream
        self.hide_cursor = hide_cursor
        self._last_lines_by_row = {}
        self._last_rendered_width = None
        self._last_rendered_height = None

    def scroll_down(self):
        logger.debug('sending scroll down message w/ cursor on bottom line')
        with self.t.location(x=0, y=1000000):# since scroll-down only moves the screen if cursor is at bottom
            self.write(SCROLL_DOWN) #TODO will blessings do this?

    def write(self, msg):
        #logger.debug('writing %r' % msg)
        self.out_stream.write(msg)
        self.out_stream.flush()

    def __enter__(self):
        logger.debug("running BaseWindow.__enter__")
        if self.hide_cursor:
            self.write(self.t.hide_cursor)
        return self

    def __exit__(self, type, value, traceback):
        logger.debug("running BaseWindow.__exit__")
        if self.hide_cursor:
            self.write(self.t.normal_cursor)

    def on_terminal_size_change(self, height, width):
        self._last_lines_by_row = {}
        self._last_rendered_width = width
        self._last_rendered_height = height

    def render_to_terminal(self, array, cursor_pos=(0,0)):
        """Renders array to terminal"""
        raise NotImplemented

    def get_term_hw(self):
        """Returns current terminal width and height"""
        return self.t.height, self.t.width

    width  = property(lambda self: self.t.width)
    height = property(lambda self: self.t.height)

    def array_from_text(self, msg):
        rows, columns = self.t.height, self.t.width
        return self.array_from_text_rc(msg, rows, columns)

    @classmethod
    def array_from_text_rc(cls, msg, rows, columns):
        arr = FSArray(0, columns)
        i = 0
        for c in msg:
            if i >= rows * columns:
                return arr
            elif c in '\r\n':
                i = ((i // columns) + 1) * columns - 1
            else:
                arr[i // arr.width, i % arr.width] = [fmtstr(c)]
            i += 1
        return arr

    def fmtstr_to_stdout_xform(self):
        if sys.version_info[0] == 2:
            if hasattr(self.out_stream, 'encoding'):
                encoding = self.out_stream.encoding
            else:
                encoding = locale.getpreferredencoding()

            def for_stdout(s):
                return unicode(s).encode(encoding, 'replace')
        else:
            def for_stdout(s):
                return str(s)
        return for_stdout

class FullscreenWindow(BaseWindow):
    """A 2d-text rendering window that dissappears when its context is left

    Uses blessings's fullscreen capabilities"""
    def __init__(self, out_stream=sys.stdout, hide_cursor=True):
        BaseWindow.__init__(self, out_stream=out_stream, hide_cursor=hide_cursor)
        self.fullscreen_ctx = self.t.fullscreen()

    def __enter__(self):
        self.fullscreen_ctx.__enter__()
        return BaseWindow.__enter__(self)

    def __exit__(self, type, value, traceback):
        self.fullscreen_ctx.__exit__(type, value, traceback)
        BaseWindow.__exit__(self, type, value, traceback)

    def render_to_terminal(self, array, cursor_pos=(0, 0)):
        """Renders array to terminal and places (0-indexed) cursor

        If array received is of width too small, render it anyway
        if array received is of width too large, render the renderable portion
        if array received is of height too small, render it anyway
        if array received is of height too large, render the renderable portion (no scroll)
        """
        #TODO there's a race condition here - these height and widths are
        # super fresh - they might change between the array being constructed and rendered
        # Maybe the right behavior is to throw away the render in the signal handler?
        height, width = self.height, self.width

        for_stdout = self.fmtstr_to_stdout_xform()
        if not self.hide_cursor:
            self.write(self.t.hide_cursor)
        if height != self._last_rendered_height or width != self._last_rendered_width:
            self.on_terminal_size_change(height, width)

        current_lines_by_row = {}
        rows = list(range(height))
        rows_for_use = rows[:len(array)]
        rest_of_rows = rows[len(array):]

        # rows which we have content for and don't require scrolling
        for row, line in zip(rows_for_use, array):
            current_lines_by_row[row] = line
            if line == self._last_lines_by_row.get(row, None):
                continue
            self.write(self.t.move(row, 0))
            self.write(for_stdout(line))
            if len(line) < width:
                self.write(self.t.clear_eol)

        # rows onscreen that we don't have content for
        for row in rest_of_rows:
            if self._last_lines_by_row and row not in self._last_lines_by_row: continue
            self.write(self.t.move(row, 0))
            self.write(self.t.clear_eol)
            self.write(self.t.clear_bol)
            current_lines_by_row[row] = None

        logger.debug('lines in last lines by row: %r' % self._last_lines_by_row.keys())
        logger.debug('lines in current lines by row: %r' % current_lines_by_row.keys())
        self.write(self.t.move(*cursor_pos))
        self._last_lines_by_row = current_lines_by_row
        if not self.hide_cursor:
            self.write(self.t.normal_cursor)

class CursorAwareWindow(BaseWindow):
    """

    Cursor diff depends on cursor never being touched by the application
    Only use the render_to_terminal interface for moving the cursor"""
    def __init__(self, out_stream=sys.stdout, in_stream=sys.stdin,
                 keep_last_line=False, hide_cursor=True, extra_bytes_callback=None):
        """

        extra_bytes_callback is a function that will be called with extra bytes
        read from in_stream if they are inadvertantly read during a cursor_position call
        """
        BaseWindow.__init__(self, out_stream=out_stream, hide_cursor=hide_cursor)
        self.in_stream = in_stream
        self._last_cursor_column = None
        self._last_cursor_row = None
        self.keep_last_line = keep_last_line
        self.cbreak = Cbreak(self.in_stream)
        self.extra_bytes_callback = extra_bytes_callback
        self.another_sigwinch = False   # whether another SIGWINCH is queued up
        self.in_get_cursor_diff = False # in the cursor query code of cursor diff

    def __enter__(self):
        if hasattr(self.in_stream, 'file_no'): #fake files aren't buffered, no need for cbreak
            self.cbreak.__enter__()
        self.top_usable_row, _ = self.get_cursor_position()
        self._orig_top_usable_row = self.top_usable_row
        logger.debug('initial top_usable_row: %d' % self.top_usable_row)
        return BaseWindow.__enter__(self)

    def __exit__(self, type, value, traceback):
        if self.keep_last_line:
            self.write(SCROLL_DOWN) # just moves cursor down if not on last line

        self.write(FIRST_COLUMN)
        self.write(self.t.clear_eos)
        self.write(self.t.clear_eol)
        if hasattr(self.in_stream, 'file_no'): #fake files aren't buffered
            self.cbreak.__exit__(type, value, traceback)
        BaseWindow.__exit__(self, type, value, traceback)

    def get_cursor_position(self):
        """Returns the terminal (row, column) of the cursor

        0-indexed, like blessings cursor positions"""
        in_stream = self.in_stream # TODO would this be cleaner as a parameter?

        QUERY_CURSOR_POSITION = u"\x1b[6n"
        self.write(QUERY_CURSOR_POSITION)

        def retrying_read():
            while True:
                try:
                    c = in_stream.read(1)
                    if c == '':
                        raise ValueError("Stream should be blocking - should't return ''. Returned %r so far", (resp,))
                    return c
                except IOError:
                    raise ValueError('cursor get pos response read interrupted')
                    # to find out if this ever really happens
                    logger.debug('read interrupted, retrying')

        resp = ''
        while True:
            c = retrying_read()
            resp += c
            m = re.search('(?P<extra>.*)'
                          '(?P<CSI>\x1b\[|\x9b)'
                          '(?P<row>\\d+);(?P<column>\\d+)R', resp, re.DOTALL)
            if m:
                row = int(m.groupdict()['row'])
                col = int(m.groupdict()['column'])
                extra = m.groupdict()['extra']
                if extra:
                    if self.extra_bytes_callback:
                        self.extra_bytes_callback(extra.encode(in_stream.encoding))
                    else:
                        raise ValueError(("Bytes preceding cursor position query response thrown out:\n%r\n"
                                          "Pass an extra_bytes_callback to CursorAwareWindow to prevent this") % (extra,))
                return (row-1, col-1)

    def get_cursor_vertical_diff(self):
        """Returns the how far down the cursor moved

        If another get_cursor_vertical diff call is already in progress,
        immediately returns zero.

        Does cursory querying until a SIGWINCH doesn't happen during
        the query. Calls to the function from a signal handler COULD STILL
        HAPPEN out of order - they just can't interrupt the actual cursor query.
        """
        if self.in_get_cursor_diff:
            self.another_sigwinch = True
            return 0

        cursor_dy = 0
        while True:
            self.in_get_cursor_diff = True
            self.another_sigwinch = False
            cursor_dy += self._get_cursor_vertical_diff_once()
            self.in_get_cursor_diff = False
            if not self.another_sigwinch:
                return cursor_dy

    def _get_cursor_vertical_diff_once(self):
        """Returns the how far down the cursor moved"""
        old_top_usable_row = self.top_usable_row
        row, col = self.get_cursor_position()
        if self._last_cursor_row is None:
            cursor_dy = 0
        else:
            cursor_dy = row - self._last_cursor_row
            logger.info('cursor moved %d lines down' % cursor_dy)
            while self.top_usable_row > -1 and cursor_dy > 0:
                self.top_usable_row += 1
                cursor_dy -= 1
            while self.top_usable_row > 1 and cursor_dy < 0:
                self.top_usable_row -= 1
                cursor_dy += 1
        logger.info('top usable row changed from %d to %d', old_top_usable_row, self.top_usable_row)
        logger.info('returning cursor dy of %d from curtsies' % cursor_dy)
        self._last_cursor_row = row
        return cursor_dy

    def render_to_terminal(self, array, cursor_pos=(0,0)):
        """Renders array to terminal, returns the number of lines
            scrolled offscreen
        outputs:
         -number of times scrolled

        If array received is of width too small, render it anyway
        if array received is of width too large, render it anyway
        if array received is of height too small, render it anyway
        if array received is of height too large, render it, scroll down,
            and render the rest of it, then return how much we scrolled down
        """
        for_stdout = self.fmtstr_to_stdout_xform()
        # caching of write and tc (avoiding the self. lookups etc) made
        # no significant performance difference here
        if not self.hide_cursor:
            self.write(self.t.hide_cursor)
        height, width = self.t.height, self.t.width #TODO race condition here?
        if height != self._last_rendered_height or width != self._last_rendered_width:
            self.on_terminal_size_change(height, width)

        current_lines_by_row = {}
        rows_for_use = list(range(self.top_usable_row, height))

        # rows which we have content for and don't require scrolling
        shared = min(len(array), len(rows_for_use)) #TODO rename shared
        for row, line in zip(rows_for_use[:shared], array[:shared]):
            current_lines_by_row[row] = line
            if line == self._last_lines_by_row.get(row, None):
                continue
            self.write(self.t.move(row, 0))
            self.write(for_stdout(line))
            if len(line) < width:
                self.write(self.t.clear_eol)

        # rows already on screen that we don't have content for
        rest_of_lines = array[shared:]
        rest_of_rows = rows_for_use[shared:]
        for row in rest_of_rows: # if array too small
            if self._last_lines_by_row and row not in self._last_lines_by_row: continue
            self.write(self.t.move(row, 0))
            self.write(self.t.clear_eol)
            self.write(self.t.clear_bol) #TODO probably not necessary - is first char cleared?
            current_lines_by_row[row] = None

        # lines for which we need to scroll down to render
        offscreen_scrolls = 0
        for line in rest_of_lines: # if array too big
            self.scroll_down()
            if self.top_usable_row > 0:
                self.top_usable_row -= 1
            else:
                offscreen_scrolls += 1
            current_lines_by_row = dict((k-1, v) for k, v in current_lines_by_row.items())
            logger.debug('new top_usable_row: %d' % self.top_usable_row)
            self.write(self.t.move(height-1, 0)) # since scrolling moves the cursor
            self.write(for_stdout(line))
            current_lines_by_row[height-1] = line

        logger.debug('lines in last lines by row: %r' % self._last_lines_by_row.keys())
        logger.debug('lines in current lines by row: %r' % current_lines_by_row.keys())
        self._last_cursor_row = cursor_pos[0]-offscreen_scrolls+self.top_usable_row
        self._last_cursor_column = cursor_pos[1]
        self.write(self.t.move(self._last_cursor_row, self._last_cursor_column))
        self._last_lines_by_row = current_lines_by_row
        if not self.hide_cursor:
            self.write(self.t.normal_cursor)
        return offscreen_scrolls

def demo():
    handler = logging.FileHandler(filename='display.log')
    logging.getLogger(__name__).setLevel(logging.DEBUG)
    logging.getLogger(__name__).addHandler(handler)
    from . import input
    with FullscreenWindow(sys.stdout) as w:
        with input.Input(sys.stdin) as input_generator:
            rows, columns = w.t.height, w.t.width
            while True:
                c = input_generator.next()
                if c == "":
                    sys.exit() # same as raise SystemExit()
                elif c == "h":
                    a = w.array_from_text("a for small array")
                elif c == "a":
                    a = [fmtstr(c*columns) for _ in range(rows)]
                elif c == "s":
                    a = [fmtstr(c*columns) for _ in range(rows-1)]
                elif c == "d":
                    a = [fmtstr(c*columns) for _ in range(rows+1)]
                elif c == "f":
                    a = [fmtstr(c*columns) for _ in range(rows-2)]
                elif c == "q":
                    a = [fmtstr(c*columns) for _ in range(1)]
                elif c == "w":
                    a = [fmtstr(c*columns) for _ in range(1)]
                elif c == "e":
                    a = [fmtstr(c*columns) for _ in range(1)]
                elif c == '\x0c': # ctrl-L
                    [w.write('\n') for _ in range(rows)]
                    continue
                else:
                    a = w.array_from_text("unknown command")
                w.render_to_terminal(a)

def main():
    handler = logging.FileHandler(filename='display.log', level=logging.DEBUG)
    logging.getLogger(__name__).setLevel(logging.DEBUG)
    logging.getLogger(__name__).addHandler(handler)
    from .termhelpers import Cbreak
    print('this should be just off-screen')
    w = FullscreenWindow(sys.stdout)
    rows, columns = w.t.height, w.t.width
    with w:
        a = [fmtstr((('.row%r.' % (row,)) * rows)[:columns]) for row in range(rows)]
        w.render_to_terminal(a)

if __name__ == '__main__':
    demo()


import pytest

from napier.operators import Addition, Subtraction, Multiplication, Division
from napier.numbers import NaturalNumber

def test_constructors():
    x, y = NaturalNumber(1), NaturalNumber(2)

    s = Addition(x, y)
    d = Subtraction(x, y)
    p = Multiplication(x, y)
    q = Division(x, y)

def test_typecheck():
    x, y = NaturalNumber(1), NaturalNumber(2)

    for C in [Addition, Subtraction, Multiplication, Division]:
        C(x, y)

        with pytest.raises(TypeError):
            C(1, y)

        with pytest.raises(TypeError):
            C(x, 1)

def test_addition_simplification():
    s = Addition(NaturalNumber(4), NaturalNumber(5))
    assert s.simplify() == NaturalNumber(9)

    s = Addition(NaturalNumber(12), NaturalNumber(6))
    assert s.simplify() == NaturalNumber(18)

def test_subtraction_simplification():
    d = Subtraction(NaturalNumber(12), NaturalNumber(2))
    assert d.simplify() == NaturalNumber(10)

    d = Subtraction(NaturalNumber(5), NaturalNumber(4))
    assert d.simplify() == NaturalNumber(1)

def test_multiplication_simplification():
    p = Multiplication(NaturalNumber(3), NaturalNumber(4))
    assert p.simplify() == NaturalNumber(12)

    p = Multiplication(NaturalNumber(5), NaturalNumber(5))
    assert p.simplify() == NaturalNumber(25)

def test_division_simplification():
    q = Division(NaturalNumber(12), NaturalNumber(3))
    assert q.simplify() == NaturalNumber(4)

    q = Division(NaturalNumber(5), NaturalNumber(5))
    assert q.simplify() == NaturalNumber(1)

def test_complex_addition_simplifications():
    e = Addition(
        Addition(NaturalNumber(5), NaturalNumber(6)),
        NaturalNumber(7)
    )

    assert e.simplify() == NaturalNumber(18)

    e = Addition(
        NaturalNumber(5),
        Addition(NaturalNumber(2), NaturalNumber(1)),
    )

    assert e.simplify() == NaturalNumber(8)

def test_complex_subtraction_simplifications():
    e = Subtraction(
        NaturalNumber(10),
        Multiplication(NaturalNumber(3), NaturalNumber(3)),
    )

    assert e.simplify() == NaturalNumber(1)

    e = Subtraction(
        Multiplication(NaturalNumber(4), NaturalNumber(3)),
        NaturalNumber(10),
    )

    assert e.simplify() == NaturalNumber(2)

def test_complex_multiplication_simplifications():
    e = Multiplication(
        Division(NaturalNumber(4), NaturalNumber(2)),
        NaturalNumber(5),
    )

    assert e.simplify() == NaturalNumber(10)

    e = Multiplication(
        NaturalNumber(5),
        Division(NaturalNumber(4), NaturalNumber(2)),
    )

    assert e.simplify() == NaturalNumber(10)

def test_complex_division_simplifications():
    e = Division(
        NaturalNumber(10),
        Subtraction(NaturalNumber(5), NaturalNumber(3)),
    )

    assert e.simplify() == NaturalNumber(5)

    e = Division(
        Addition(NaturalNumber(5), NaturalNumber(5)),
        NaturalNumber(5),
    )

    assert e.simplify() == NaturalNumber(2)

def test_complex_nested_simplifications():
    e = Addition(
        NaturalNumber(2),
        Multiplication(
            NaturalNumber(3),
            Subtraction(
                NaturalNumber(10),
                Division(
                    NaturalNumber(12),
                    Addition(
                        NaturalNumber(1),
                        NaturalNumber(2),
                    ),
                ),
            ),
        ),
    )

    assert e.simplify() == NaturalNumber(20)


# import necessary packages
from pyimagesearch.transform import four_point_transform
from skimage.filters import threshold_local
import numpy as np
import argparse
import os, sys
import cv2
import imutils
from hand_writing import pred_matrix

def add_second_image(origin, sec_image):
    x_offset = origin.shape[1] - 20
    y_offset = origin.shape[0] - 20
    origin[y_offset - sec_image.shape[0]:y_offset, x_offset - sec_image.shape[1]:x_offset] = sec_image

def contour_detect(edge):
    # find the contours in the edged image, keeping only the
    # largest ones, and initialize the screen contour
    cnts = cv2.findContours(edged.copy(), cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
    cnts = imutils.grab_contours(cnts)
    cnts = sorted(cnts, key=cv2.contourArea, reverse=True)[:5]
    screenCnt = None
    h,w = edge.shape[0], edge.shape[1]
    min_square_size = h * w / 3
    print('height: ' + str(h) + ' width = ' + str(w))
    print('min_square= ' + str(min_square_size))

    # loop over the contours
    for c in cnts:
        # approximate the contour
        peri = cv2.arcLength(c, True)
        approx = cv2.approxPolyDP(c, 0.02 * peri, True)
        area = cv2.contourArea(c)
        area = np.int(area)
        print('contour_detect area = ' + str(area))
        print('approx = ' + str(len(approx)))

        # if our approximated contour has four points, then we
        # can assume that we have found our screen
        if len(approx) == 4 and (area > min_square_size):
            screenCnt = approx
            print(area)
            #cv2.drawContours(edge, [screenCnt], -1, (255, 255, 0), 2)
            #cv2.imshow('origin', edge)
            #cv2.waitKey()
            break
    print('final result: ')
    print('contour_detect area = ' + str(area))
    print('approx = ' + str(len(approx)))
    return screenCnt

def edge_detect(image):
    # convert the image to grayscale, blur it, and find edges
    # in the image
    #gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
    #gray = cv2.bilateralFilter(gray, 11, 17, 17)
    gray = cv2.GaussianBlur(gray, (5, 5), 0)
    edged = cv2.Canny(gray, 75, 200)

    return edged

def text_area_detect(img):
    #rgb = cv2.pyrDown(img)
    #rgb = img
    small = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

    locs = []

    kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3, 3))
    grad = cv2.morphologyEx(small, cv2.MORPH_GRADIENT, kernel)

    _, bw = cv2.threshold(grad, 0.0, 255.0, cv2.THRESH_BINARY | cv2.THRESH_OTSU)

    kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (9, 1))
    connected = cv2.morphologyEx(bw, cv2.MORPH_CLOSE, kernel)
    # using RETR_EXTERNAL instead of RETR_CCOMP
    contours, hierarchy = cv2.findContours(connected.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
    #For opencv 3+ comment the previous line and uncomment the following line
    #_, contours, hierarchy = cv2.findContours(connected.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)

    mask = np.zeros(bw.shape, dtype=np.uint8)

    for idx in range(len(contours)):
        x, y, w, h = cv2.boundingRect(contours[idx])
        # compute the bounding box of the contour, then use the
        # bounding box coordinates to derive the aspect ratio
        ar = w / float(h)

        # since credit cards used a fixed size fonts with 4 groups
        # of 4 digits, we can prune potential contours based on the
        # aspect ratio
        if ar > 2.5 and ar < 4.0:
            # contours can further be pruned on minimum/maximum width
            # and height
            print(ar)
            print('w is ' + str(w) + ' and height is ' + str(h))
            if (w > 90 and w < 100) and (h > 28 and h < 35):
                locs.append((x, y, w, h))
                #cv2.rectangle(img, (x, y), (x+w-1, y+h-1), (0, 255, 0), 2)
                #roi = img[y:y+h, x:x+w]
                #cv2.imshow(str(idx), roi)

    return locs

def sort_contours(cnts, method="left-to-right"):
    # initialize the reverse flag and sort index
    reverse = False
    i = 0

    # handle if we need to sort in reverse
    if method == "right-to-left" or method == "bottom-to-top":
        reverse = True

    # handle if we are sorting against the y-coordinate rather than
    # the x-coordinate of the bounding box
    if method == "top-to-bottom" or method == "bottom-to-top":
        i = 1

    # construct the list of bounding boxes and sort them from top to
    # bottom
    boundingBoxes = [cv2.boundingRect(c) for c in cnts]
    (cnts, boundingBoxes) = zip(*sorted(zip(cnts, boundingBoxes),
    key=lambda b:b[1][i], reverse=reverse))

    # return the list of sorted contours and bounding boxes
    return (cnts, boundingBoxes)

def extract_text_images(gray, locs):
    # initialize the list of group digits
    rois = []
    pad = 5
    for (i, (gX, gY, gW, gH)) in enumerate(locs):
        # extract the group ROI of 4 digits from the grayscale image,
        # then apply thresholding to segment the digits from the
        # background of the credit card
        group = gray[gY - 5:gY + gH + 5, gX - 5:gX + gW + 5]
        #group = cv2.threshold(group, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]
        group = cv2.threshold(group, 127, 255, cv2.THRESH_BINARY_INV)[1]

        # detect the contours of each individual digit in the group,
        # then sort the digit contours from left to right
        digitCnts = cv2.findContours(group.copy(), cv2.RETR_EXTERNAL,
        	cv2.CHAIN_APPROX_SIMPLE)
        digitCnts = imutils.grab_contours(digitCnts)
        digitCnts = sort_contours(digitCnts,method="left-to-right")[0]
        # loop over the digit contours
        for cnt, c in enumerate(digitCnts):
            print(cv2.contourArea(c))
            # compute the bounding box of the individual digit, extract
            # the digit, and resize it to have the same fixed size as
            # the reference OCR-A images
            (x, y, w, h) = cv2.boundingRect(c)
            roi = group[y - pad :y + h + pad, x - pad:x + w + pad]
            #cv2.imshow(str(i) + '-' + str(cnt) + '-----.jpg', roi)
            roi = cv2.resize(roi.copy(), (28, 28))
            cv2.imshow(str(i) + '-' + str(cnt) + '-----.jpg', roi)
            rois.append(roi)

    return rois

"""For perspective transformation, you need a 3x3 transformation matrix. Straight lines will remain straight even after the transformation.
To find this transformation matrix, you need 4 points on the input image and corresponding points on the output image.
Among these 4 points, 3 of them should not be collinear. Then transformation matrix can be found by the function cv2.getPerspectiveTransform.
Then apply cv2.warpPerspective with this 3x3 transformation matrix."""
def draw_contour(contour, img):
    min_square_size = 987
    images = []
    area = cv2.contourArea(contour)
    rect = cv2.minAreaRect(screenCnt)
    # If the contour is not really small, or really big
    h,w = img.shape[0], img.shape[1]
    print('area == ' + str(area))
    if area > min_square_size and area < h*w-(2*(h+w)):
        # Get the four corners of the contour
        epsilon = .1 * cv2.arcLength(contour, True)
        approx = cv2.approxPolyDP(contour, epsilon, True)

        print("========================================================")

        # now that we have our screen contour, we need to determine
        # the top-left, top-right, bottom-right, and bottom-left
        # points so that we can later warp the image -- we'll start
        # by reshaping our contour to be our finals and initializing
        # our output rectangle in top-left, top-right, bottom-right,
        # and bottom-left order
        pts = screenCnt.reshape(4, 2)
        rect = np.zeros((4, 2), dtype = "float32")

        # the top-left point has the smallest sum whereas the
        # bottom-right has the largest sum
        s = pts.sum(axis = 1)
        rect[0] = pts[np.argmin(s)]
        rect[2] = pts[np.argmax(s)]

        # compute the difference between the points -- the top-right
        # will have the minumum difference and the bottom-left will
        # have the maximum difference
        diff = np.diff(pts, axis = 1)
        rect[1] = pts[np.argmin(diff)]
        rect[3] = pts[np.argmax(diff)]

        # now that we have our rectangle of points, let's compute
        # the width of our new image
        (tl, tr, br, bl) = rect
        widthA = np.sqrt(((br[0] - bl[0]) ** 2) + ((br[1] - bl[1]) ** 2))
        widthB = np.sqrt(((tr[0] - tl[0]) ** 2) + ((tr[1] - tl[1]) ** 2))

        # ...and now for the height of our new image
        heightA = np.sqrt(((tr[0] - br[0]) ** 2) + ((tr[1] - br[1]) ** 2))
        heightB = np.sqrt(((tl[0] - bl[0]) ** 2) + ((tl[1] - bl[1]) ** 2))

        # take the maximum of the width and height values to reach
        # our final dimensions
        maxWidth = max(int(widthA), int(widthB))
        maxHeight = max(int(heightA), int(heightB))

        # construct our destination points which will be used to
        # map the screen to a top-down, "birds eye" view
        dst = np.array([
        	[0, 0],
        	[maxWidth - 1, 0],
        	[maxWidth - 1, maxHeight - 1],
        	[0, maxHeight - 1]], dtype = "float32")

        # calculate the perspective transform matrix and warp
        # the perspective to grab the screen
        M = cv2.getPerspectiveTransform(rect, dst)
        warp = cv2.warpPerspective(frame, M, (maxWidth, maxHeight))

        #cv2.imshow('warp', warp)
        #cv2.waitKey()
        locs = text_area_detect(warp)

        print("========================================================")

        images.append(warp.copy())
    return images, locs

# Capture frame-by-frame
#frame = cv2.imread("./images/receipt.jpg")
# frame = cv2.imread("./images/credit_1.png")
# frame = cv2.imread("./images/credit_2.png")
frame = cv2.imread("./images/vcard.png")
#frame = cv2.imread("./images/credit.jpg")
frame = imutils.resize(frame, height=500)

# convert the image to grayscale, blur it, and find edges
# in the image
#gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
#gray = cv2.bilateralFilter(gray, 11, 17, 17)
#edged = cv2.Canny(gray, 0, 200)
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
gray = cv2.GaussianBlur(gray, (5, 5), 0)
edged = cv2.Canny(gray, 75, 200)

cv2.imshow('edged', edged)
cv2.waitKey()

# find contours
screenCnt = contour_detect(edged)

if (screenCnt is not None):
    print('>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>There is contour...')
    images, locs = draw_contour(screenCnt, frame)
    img_arr = []
    if(len(images) > 0):
        add_second_image(frame, images[0])
        gray = cv2.cvtColor(images[0], cv2.COLOR_BGR2GRAY)
        if(len(locs) > 0):
            rois = extract_text_images(gray, locs)
            if(len(rois) > 0):
                for i, roi in enumerate(rois):
                    h,w = roi.shape[0], roi.shape[1]
                    # print('height: ' + str(h) + ' width = ' + str(w))
                    img_arr.append(pred_matrix(roi , "../hand_writing/param/cnn_params.pkl")[0])
                    cv2.imshow(str(i) + '.jpg' , roi)
            cv2.waitKey()
            print("img_arr", img_arr)
else:
    print('>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>There is no contour...')

# Display the resulting frame
cv2.imshow('origin', frame)

cv2.waitKey()

# When everything done, release the capture
cv2.destroyAllWindows()


from django.urls import path
from . import views

urlpatterns =[
    path('line8', views.Line_8.as_view(), name='line8'),
    path('line9', views.Line_9.as_view(), name='line9'),
    path('line10', views.Line_10.as_view(), name='line10'),
    # path('detail', views.detail),

]


# -*- coding: utf-8 -*-
"""Daily bonus handler module.

"""
from tornado import gen
from translation import _T
from web import RequestHandler, set_path
from eventshandler import EventsHandler
from datetime import date
import logger
import redisutils
import time

NOT_RECEIVED = 0
RECEIVED = 1
FUTURE = 2
IGNORED = -1

@set_path(r"/dailybonus")
class GetDailyBonusHandler(EventsHandler):

    """Get daily bonus handler class.

    This class implements HTTP GET request '/dailybonus'.
    The query for this handler are:

        - id: facebook id user

        - cookie: user cookie

    Examples::

        /dailybonus?id=83273827&cookie=83u2832iu3h23hu32hu3

    """

    @gen.coroutine
    def get(self):
        """Implements HTTP GET method."""

        try:
            fid = self.get_argument('id')
            cookie = self.get_argument('cookie', '')

            verify_cookie = self.rd.get(redisutils.user_cookie(fid))

            if self.config.server['verify'] and cookie != verify_cookie:
                self.write(
                    {"error_code": -1, "error_msg": _T("Permission denied or your transaction is expired")})
                raise StopIteration

            available = yield self.events_available()
            if not available:
                self.write(
                    {"error_code": 1, "error_msg": _T("Can not get daily bonus")})
                raise StopIteration

            query = """SELECT d.`day`, d.`coin`, d.`status`, d.`active_date`
                FROM daily_bonus_users d, events e
                WHERE d.fid = '%s'
                AND e.`status` = 1 AND e.event_id = 'DAILYBONUS'
                AND e.start_time <= NOW() AND e.end_time >= NOW() """ % fid
            data = yield self.db.query(query)
            if data:
                today = date.today()

                async_data = []

                for bonus in data:
                    if bonus['status'] != NOT_RECEIVED:
                        continue

                    if bonus.active_date < today:
                        bonus['status'] = IGNORED
                        async_data.append(bonus)
                    elif bonus.active_date > today:
                        bonus['status'] = FUTURE

                if async_data:
                    values = ", ".join(
                        "('%s',%d,%d)" % (fid, bonus.day, bonus.status) for bonus in async_data)
                    query = """INSERT INTO daily_bonus_users(`fid`, `day`, `status`) VALUES %s
                            ON DUPLICATE KEY UPDATE `status` = VALUES(`status`)""" % values
                    yield self.db.insert(query)

                self.write({"error_code": 0, "data": data})
            else:
                self.write({
                    "error_code": 1,
                    "error_msg": _T("Can not get daily bonus")
                })
        except StopIteration:
            pass
        except:
            self.log_stack_trace()
            self.write({
                "error_code": 1,
                "error_msg": _T("Can not get daily bonus")
            })

@set_path(r"/receivedailybonus")
class ReceiveDailyBonusHandler(RequestHandler):

    """Receive daily bonus handler class.

    This class implements HTTP GET request '/receivedailybonus'.
    The query for this handler are:

        - id: facebook id user

        - cookie: user cookie

        - day: day of daily-bonus

        - coin: coin of daily-bonus

    Examples::

        /receivedailybonus?id=83273827&cookie=218921&day=3&coin=2121

    """

    @gen.coroutine
    def get(self):
        """Implements HTTP GET method."""

        try:
            fid = self.get_argument('id')
            cookie = self.get_argument('cookie', '')

            verify_cookie = self.rd.get(redisutils.user_cookie(fid))

            if self.config.server['verify'] and cookie != verify_cookie:
                self.write(
                    {"error_code": -1, "error_msg": _T("Permission denied or your transaction is expired")})
                raise StopIteration

            day = int(self.get_argument('day'))
            coin = int(self.get_argument('coin'))

            query = """UPDATE daily_bonus_users SET `status` = %d
                WHERE fid = '%s' AND `day` = %d AND `coin` = %d
                AND active_date = CURRENT_DATE()
            """ % (RECEIVED, fid, day, coin)

            data = yield self.db.update(query)

            if not data:
                self.write(
                    {"error_code": 1, "error_msg": _T("This day is received or expired")})
                raise StopIteration

            user_coin = self.rd.incrby(redisutils.user_coin(fid), coin)

            self.write({
                "error_code": 0,
                "data": {
                    "add_coin": coin,
                    "user_coin": user_coin,
                    "day": day,
                }
            })
            logger.trans_log.put({
                'time': time.strftime('%Y-%m-%d %H:%M:%S'),
                'fid': fid,
                'gameid': 'DAILY_BONUS',
                'room_coin': 0,
                'bet_coin': 0,
                'win_coin': coin,
                'win_type': 0,
                'user_coin': user_coin,
            })
            redisutils.async_user(self.rd, fid)

        except StopIteration:
            pass
        except:
            self.log_stack_trace()
            self.write({
                "error_code": 2,
                "error_msg": _T("Can not receive daily bonus")
            })