import numpy as np
import math
import scipy

from Utility.Math.ext.transforms3d.transforms3d.affines import decompose

def decompose_affine_transformation(trans_mat):

    # See Multiple View Geometry p. 39 and p.42

    # https://github.com/matthew-brett/transforms3d
    trans, rot, scales, shear = decompose(trans_mat)
    return trans, rot, scales, shear

def decompose_similarity_transformation(trans_mat):

    # See Multiple View Geometry p. 39 and p.42

    trans = np.transpose(trans_mat)[-1][:-1]
    scales = extract_axis_scales(trans_mat)
    # the normalization is important to remove the scaling
    rot_scale_part = trans_mat[0:-1, 0:-1]
    rot_scale_part_norm = np.linalg.norm(rot_scale_part)
    rot_scale_part_normalized = rot_scale_part / rot_scale_part_norm
    rot = rot_scale_part_normalized
    return trans, rot, scales

def extract_scale(transformation_matrix):
    scales = extract_axis_scales(
        transformation_matrix)

    for scale_1, scale_2 in zip(scales, scales[1:]):
        assert np.isclose(scale_1, scale_2)

    return scales[0]

def extract_axis_scales(transformation_matrix):

    # https://math.stackexchange.com/questions/237369/given-this-transformation-matrix-how-do-i-decompose-it-into-translation-rotati
    #   Rationale behind this approach:
    #       The rotation matrix contains the base vectors of the new coordinate system
    #       If they have a length different than one, scaling is applied

    # https://docs.blender.org/api/2.70/mathutils.html#mathutils.Matrix.to_scale
    #   Note: This method does not return negative a scale on any axis because
    #         it is not possible to obtain this data from the matrix alone.

    # https://www.researchgate.net/publication/238189035_General_Formula_for_Extracting_the_Euler_Angles
    #

    rot_scale_part = transformation_matrix[0:-1, 0:-1]
    rot_scale_part_trans = rot_scale_part.transpose()

    scales = []
    for column_vec in rot_scale_part_trans:
        scales.append(np.linalg.norm(column_vec))

    return scales

def rotation_matrix_2d_to_angle(rotation_mat, degrees):

    # Use the scipy implementation to deal with ill posed input data

    # Note: A 2D image rotation is equivalent to a ration around the axis orthogonal to the image plane
    # Thus: The image rotation corresponds to the rotation around the z euler angle

    angles = rotation_matrix_to_euler_angles(rotation_mat, degrees)
    return angles[2]

def rotation_matrix_to_euler_angles(rotation_mat, degrees):

    # Scipy uses the following algorithm to compute euler angles
    #   https://www.researchgate.net/publication/238189035_General_Formula_for_Extracting_the_Euler_Angles

    assert scipy.__version__ >= '1.2.1'
    from scipy.spatial.transform import Rotation as R  # requires at least scipy 1.2.1

    if rotation_mat.shape == (3, 3):
        scipy_rot = R.from_dcm(rotation_mat)
    elif rotation_mat.shape == (2, 2):
        rotation_mat_3d = np.identity(3)
        rotation_mat_3d[:2,:2] = rotation_mat
        scipy_rot = R.from_dcm(rotation_mat_3d)
    else:
        assert False

    return scipy_rot.as_euler('xyz', degrees=degrees)

def angle_to_rotation_matrix_2d(theta, in_degree=False):
    # https://docs.scipy.org/doc/scipy/reference/generated/scipy.spatial.transform.Rotation.from_euler.html#scipy.spatial.transform.Rotation.from_euler
    if in_degree:
        theta = np.radians(theta)
    cos_theta, sin_theta = np.cos(theta), np.sin(theta)
    return np.array(((cos_theta, -sin_theta),
                     (sin_theta, cos_theta)))

def angles_to_rotation_matrix_3d(theta, phi, psi, in_degree):
    # https://docs.scipy.org/doc/scipy/reference/generated/scipy.spatial.transform.Rotation.from_euler.html#scipy.spatial.transform.Rotation.from_euler
    if in_degree:
        theta = np.radians(theta)
        phi = np.radians(phi)
        psi = np.radians(psi)
    cos_theta, sin_theta = np.cos(theta), np.sin(theta)
    cos_phi, sin_phi = np.cos(phi), np.sin(phi)
    cos_psi, sin_psi = np.cos(psi), np.sin(psi)
    return np.array((
        (cos_phi * cos_psi, cos_psi * sin_theta * sin_phi - cos_theta * sin_psi, cos_theta * cos_psi * sin_phi + sin_theta * sin_psi),
        (cos_phi * sin_psi, cos_theta * cos_psi + sin_theta * sin_phi * sin_psi, cos_theta * sin_phi * sin_psi - cos_psi * sin_theta),
        (-sin_phi,          cos_phi * sin_theta,                                 cos_theta * cos_phi)))

def vector_to_spherical_coords(dir_vec):

    # represent the direction vector in spherical coordinates
    # using the ISO convention (radius r, inclination theta, azimuth phi)

    """
    Theta:
        * is in the interval of in [0, pi]
        * lies between the vector and the z axis

    Phi:
        * phi is in the interval (-pi, pi)
        * lies in the x-y-plane
    :param dir_vec:
    :return:
    """

    # There are special points, for which the angular coordinates are not unique.
    # The angle phi is not defined for points on the z axis.
    # For origin the angle theta is arbitrary.
    # To achieve uniqueness, one might define for
    # points on the z axis phi = 0 and for the origin theta = 0

    x = dir_vec[0]
    y = dir_vec[1]
    z = dir_vec[2]

    r = np.linalg.norm(dir_vec)

    # TODO check the coordinate system
    # Currently we asume that z points upwards

    theta = math.acos(float(z) / float(r))  # theta in [0, pi]
    assert(0 < theta < math.pi)
    phi = math.atan2(float(y), float(x))  # phi in (-pi, pi)
    assert (-math.pi < theta < math.pi)

    return r, theta, phi,

def spherical_coords_to_vector(r, theta, phi):
    """See the description of vector_to_spherical_coords
    for a definition of the angles theta and phi """

    x = r * math.sin(theta) * math.cos(phi)
    y = r * math.sin(theta) * math.sin(phi)
    z = r * math.cos(theta)
    return np.array([x, y, z], dtype=float)

def vsfm_coordinates_to_standard_coordinates(vsfm_coordinates):
    """
    For testing purposes:
    vsfm world coordinate system (at least for some examples):
        * x points backwards
        * y points top
        * z points right
    standard world coordinate system ()
        * x points in front
        * y points to the right
        * z points to the top

    :return:
    """

    x = -1 * vsfm_coordinates[0]
    y = vsfm_coordinates[2]
    z = vsfm_coordinates[1]
    return np.array([x,y,z])

def quaternion_to_rotation_matrix(q):

    """
    Original C++ Method defined in  pba/src/pba/DataInterface.h

    Comments of ChangCHang to visualfsm's coordinate system
    http://www.euclideanspace.com/maths/geometry/rotations/index.htm
    http://www.euclideanspace.com/maths/geometry/rotations/conversions/index.htm

    take a look at the  parallel bundle adjustment (pba) code (used by visualsfm)
    http://grail.cs.washington.edu/projects/mcba/
    in DataInterface.h the corresponding conversion function is 'SetQuaternionRotation'
    """

    qq = math.sqrt(q[0]*q[0]+q[1]*q[1]+q[2]*q[2]+q[3]*q[3])
    qw = qx = qy = qz = 0
    if qq > 0:  # NORMALIZE THE QUATERNION
        qw = q[0]/qq
        qx = q[1]/qq
        qy = q[2]/qq
        qz = q[3]/qq
    else:
        qw = 1
        qx = qy = qz = 0

    m = np.zeros((3, 3), dtype=float)
    m[0][0] = float(qw*qw + qx*qx- qz*qz- qy*qy )
    m[0][1] = float(2*qx*qy -2*qz*qw )
    m[0][2] = float(2*qy*qw + 2*qz*qx)
    m[1][0] = float(2*qx*qy+ 2*qw*qz)
    m[1][1] = float(qy*qy+ qw*qw - qz*qz- qx*qx)
    m[1][2] = float(2*qz*qy- 2*qx*qw)
    m[2][0] = float(2*qx*qz- 2*qy*qw)
    m[2][1] = float(2*qy*qz + 2*qw*qx )
    m[2][2] = float(qz*qz+ qw*qw- qy*qy- qx*qx)
    return m

def rotation_matrix_to_quaternion(m):

    """
    Original C++ Method defined in  pba/src/pba/DataInterface.h
    """

    q = np.array([0, 0, 0, 0], dtype=float)

    q[0] = 1 + m[0][0] + m[1][1] + m[2][2]
    if q[0] > 0.000000001:
        q[0] = math.sqrt(q[0]) / 2.0
        q[1] = (m[2][1] - m[1][2]) / ( 4.0 * q[0])
        q[2] = (m[0][2] - m[2][0]) / ( 4.0 * q[0])
        q[3] = (m[1][0] - m[0][1]) / ( 4.0 * q[0])
    else:
        if m[0][0] > m[1][1] and m[0][0] > m[2][2]:
            s = 2.0 * math.sqrt(1.0 + m[0][0] - m[1][1] - m[2][2])
            q[1] = 0.25 * s
            q[2] = (m[0][1] + m[1][0]) / s
            q[3] = (m[0][2] + m[2][0]) / s
            q[0] = (m[1][2] - m[2][1]) / s

        elif m[1][1] > m[2][2]:
            s = 2.0 * math.sqrt(1.0 + m[1][1] - m[0][0] - m[2][2])
            q[1] = (m[0][1] + m[1][0]) / s
            q[2] = 0.25 * s
            q[3] = (m[1][2] + m[2][1]) / s
            q[0] = (m[0][2] - m[2][0]) / s
        else:
            s = 2.0 * math.sqrt(1.0 + m[2][2] - m[0][0] - m[1][1])
            q[1] = (m[0][2] + m[2][0]) / s
            q[2] = (m[1][2] + m[2][1]) / s
            q[3] = 0.25 * s
            q[0] = (m[0][1] - m[1][0]) / s
    return q

def invert_y_and_z_axis(input_matrix_or_vector):
    """
    This Function inverts the y and the z coordinates in the corresponding matrix and vector entries
    (invert y and z axis <==> rotation by 180 degree around the x axis)

    For the matrix multiplication holds:
    |m11 m12 m13|    |x|       |m11 x + m12 y + m13 z|
    |m21 m22 m23|    |y|   =   |m21 x + m22 y + m23 z|
    |m31 m32 m33|    |z|       |m31 x + m32 y + m33 z|
    Therefore exactly the y and z rows must be inverted!

    For example VisualSFM and Bundler use coordinate systems, which differ in the y and z coordinate
    Both (VisualSfM and Bundler) are left hand coordinate systems
    """

    output_matrix_or_vector = input_matrix_or_vector.copy()
    output_matrix_or_vector[1] = -output_matrix_or_vector[1]
    output_matrix_or_vector[2] = -output_matrix_or_vector[2]

    return output_matrix_or_vector

def convert_camera_matrix_lhs_rhs(camera_matrix):

    """
    This converts a left hand coordinates system to
    a right hand coordinate system and vice versa

    # opengl_camera == blender_camera == vtk_camera

    :param camera_matrix:
    :return:
    """

    gt_camera_rotation_inverse = camera_matrix.copy()[0:3, 0:3]
    gt_camera_rotation = gt_camera_rotation_inverse.T

    # Important: Blender uses a blender_camera coordinate frame system,
    # which looks down the negative z-axis
    # This contradicts the camera coordinate systems used by most SfM camera coordinate systems
    # Thus rotate the camera rotation matrix by 180 degrees (i.e. invert the y and z axis)
    gt_camera_rotation = invert_y_and_z_axis(gt_camera_rotation)
    gt_camera_rotation_inverse = gt_camera_rotation.T

    rotated_camera_matrix_around_x_by_180 = camera_matrix.copy()
    rotated_camera_matrix_around_x_by_180[0:3, 0:3] = gt_camera_rotation_inverse
    return rotated_camera_matrix_around_x_by_180

def convert_opengl_to_computer_vision_camera(opengl_camera_matrix):
    return convert_camera_matrix_lhs_rhs(opengl_camera_matrix)

def convert_computer_vision_to_opengl_camera(computer_vision_camera_matrix):
    return convert_camera_matrix_lhs_rhs(computer_vision_camera_matrix)

def compute_camera_coordinate_system_translation_vector(center, rotation_mat):

    """
    x_cam = R (X - C) = RX - RC == RX + t
    <==> t = -RC
    """

    t = np.zeros(3, dtype=float)
    for j in range(0, 3):
        t[j] = -float(
            rotation_mat[j][0] * center[0] +
            rotation_mat[j][1] * center[1] +
            rotation_mat[j][2] * center[2])
    return t


import numpy as np
import cv2 as cv
import argparse

#Get position of the mouse
def getPos(event,y,x,flags,param):
    global count,position
    if event == cv.EVENT_LBUTTONDBLCLK:
        print(x,y)
        position.append([[y,x]])
        count+=1

#Init
parser = argparse.ArgumentParser()
parser.add_argument('image', type=str, help='path to image file')
parser.add_argument('tracklines', type=int, help='track the points?')
parser.add_argument('npoints', type=int, help='number of points')

#Get the argument from the console line
args = parser.parse_args()
draw_lines = args.tracklines
n_points = args.npoints

#Start the capture of video
cap = cv.VideoCapture(args.image)

# Parameters for lucas kanade optical flow
lk_params = dict( winSize  = (30,30),
                  maxLevel = 3,
                  criteria = (cv.TERM_CRITERIA_EPS | cv.TERM_CRITERIA_COUNT, 20, 0.03))
# Create some random colors
color = np.random.randint(0,255,(100,3))

# Take first frame
ret, old_frame = cap.read()
#old_frame = cv.rotate(old_frame, cv.ROTATE_90_CLOCKWISE) #I rotate it 90º to se it vertical, you may have to change this
old_gray = cv.cvtColor(old_frame, cv.COLOR_BGR2GRAY)
frame_width = int(old_gray.shape[0])
frame_height = int(old_gray.shape[1])
fps = cap.get(cv.CAP_PROP_FPS)

print("Size: " + str(frame_width) + " x " + str(frame_height) + " at " + str(fps))

win_scale = 0.7 #Scale for the visualization size/scale

#Create window and set the callback function for the double left-click
cv.namedWindow('frame', cv.WINDOW_NORMAL)
cv.resizeWindow('frame', (int(frame_height*win_scale),int(frame_width*win_scale)))
cv.setMouseCallback('frame',getPos)

##START OF THE PROGRAM
print("PICK A POINT IN THE IMAGE")
#Init of points
p0 = None
ix,iy = None,None
position = []
count = 0
#-
#We ask for the n points in the image to track
while(count < n_points):
    cv.resizeWindow('frame', (int(frame_height/2.5),int(frame_width/2.5)))
    cv.imshow('frame',old_frame)
    k = cv.waitKey(10) #Add some delay in the loop
    if k == 27: #If esc is pressed we exit the process
        break
    for point in position:
        old_frame = cv.circle(old_frame,(point[0][0],point[0][1]),10,color[count].tolist(),-1)

p0 = np.float32(position) #Then convert the points into a float32 array. That is the same structure as the UMat from OpenCV

# Create a mask to draw the points and trajectories
mask = np.zeros_like(old_frame)
#Create a Video write to save the output video
out = cv.VideoWriter('outputTr.avi',cv.VideoWriter_fourcc(*'XVID'),fps,(frame_height,frame_width))
counter = 0
##PARAMETER
frames_to_process = 250
max_err = 10
##--
while(counter<frames_to_process): #We process x frames of the video
    active_points = p0.size/2
    counter+=1
    ret,frame = cap.read() #Read the next frame
    #frame = cv.rotate(frame, cv.ROTATE_90_CLOCKWISE)
    frame_gray = cv.cvtColor(frame, cv.COLOR_BGR2GRAY) #create a gray copy for the optical flow

    #If we have any active points we calculate the optical flow.
    if(active_points > 0):
        p1, st, err = cv.calcOpticalFlowPyrLK(old_gray, frame_gray, p0, None, **lk_params)

        #Select good points with an erros smaller than max_err
        good_new = p1[st==1 & (err < max_err)]
        good_old = p0[st==1 & (err < max_err)]
        print(st,err)

        # draw the tracks
        for i,(new,old) in enumerate(zip(good_new, good_old)):
            a,b = new.ravel()
            c,d = old.ravel()
            a,b,c,d = int(a),int(b),int(c),int(d)
            if draw_lines:
                mask = cv.line(mask, (a,b),(c,d), color[i].tolist(), 2) #trajectories
            frame = cv.circle(frame,(a,b),10,color[i].tolist(),-1) #current location

    # Create the current frame with the mask to display
    img = cv.add(frame,mask)
    out.write(img)
    cv.imshow('frame',img)

    k = cv.waitKey(30) & 0xff
    if k == 27: #If esc is pressed we exit the process
        break

    # Now update the previous frame and previous points
    old_gray = frame_gray.copy()
    p0 = good_new.reshape(-1,1,2)


#! /usr/bin/env python3
#
# Sync the data/Processed_NC/ADCP_UHDAS/*.nc to ~Sync and vice versa
#
# June-2022, Pat Welch, pat@mousebrains.com

from argparse import ArgumentParser
from TPWUtils import Logger
import logging
import subprocess
import glob
import socket
import os
import sys

def rsyncit(src:str, tgt:str, args:ArgumentParser) -> None:
    files = glob.glob(src)
    if not files:
        logging.info("No files found for %s", src)
        return

    if not os.path.isdir(tgt):
        logging.info("Creating %s", tgt)
        os.makedirs(tgt, mode=0o766, exist_ok=True)

    cmd = [
            args.rsync,
            "--archive",
            "--verbose",
            "--stats",
            ]
    cmd.extend(files)
    cmd.append(tgt)
    logging.info("Command %s", " ".join(cmd))
    s = subprocess.run(cmd, capture_output=True, shell=False)
    if s.returncode: logging.error("returncode %s", s.returncode)
    if s.stderr: logging.warning("stderr %s", str(s.stderr, "UTF-8"))
    if s.stdout: logging.info("stdout %s", str(s.stdout, "UTF-8"))

parser = ArgumentParser()
Logger.addArgs(parser)
parser.add_argument("--rsync", type=str, default="/usr/bin/rsync", help="rsync command")
parser.add_argument("--platform", type=str, default="/mnt/sci/data/Platform",
        help="Where proc/*/contour files are stored on the science share")
parser.add_argument("--adcp", type=str, default="/mnt/adcp/current_cruise/proc",
        help="Where the UHDAS current cruise files are")
args = parser.parse_args()

Logger.mkLogger(args)

logging.info("Args %s", args)

hostname = socket.gethostname()

if "pe" in hostname:
    platform = "PE"
else:
    platform = "PS"

for src in glob.glob(os.path.join(args.adcp, "*")):
    adcp = os.path.basename(src)
    tgt = os.path.join(args.platform, platform, "ADCP_UHDAS", adcp)
    logging.info("adcp %s %s", src, tgt)
    rsyncit(os.path.join(src, "contour") + "/", tgt, args) # From this host to sync


"""add user field to vote

Revision ID: 2f3daec0d25f
Revises: 42410bd32a0f
Create Date: 2018-04-15 21:27:35.657955

"""
from alembic import op
import sqlalchemy as sa

# revision identifiers, used by Alembic.
revision = '2f3daec0d25f'
down_revision = '42410bd32a0f'
branch_labels = None
depends_on = None

def upgrade():
    # ### commands auto generated by Alembic - please adjust! ###
    op.add_column('vote', sa.Column('user_id', sa.Integer(), nullable=False))
    op.create_foreign_key(None, 'vote', 'user', ['user_id'], ['id'])
    # ### end Alembic commands ###

def downgrade():
    # ### commands auto generated by Alembic - please adjust! ###
    op.drop_constraint(None, 'vote', type_='foreignkey')
    op.drop_column('vote', 'user_id')
    # ### end Alembic commands ###


# Based on ptoject from GitHub page:
# https://github.com/jgamblin/isthisipbad/blob/master/isthisipbad.py
# Modified by MJC to fix errors.

import os
import sys
import urllib
import urllib.request
# import urllib2
import re
import socket
# Requires dnspython AKA python-dns package
import dns.resolver
import socks

from ipaddress import ip_network, ip_address

# net = ip_network("1.1.0.0/16")
# print(ip_address("1.1.2.2") in net)    # True

# blacklists for dns resolving
# bls = ["b.barracudacentral.org", "bl.spamcannibal.org", "bl.spamcop.net",
#        "blacklist.woody.ch", "cbl.abuseat.org", "cdl.anti-spam.org.cn",
#        "combined.abuse.ch", "combined.rbl.msrbl.net", "db.wpbl.info",
#        "dnsbl-1.uceprotect.net", "dnsbl-2.uceprotect.net",
#        "dnsbl-3.uceprotect.net", "dnsbl.cyberlogic.net",
#        "dnsbl.sorbs.net", "drone.abuse.ch", "drone.abuse.ch",
#        "duinv.aupads.org", "dul.dnsbl.sorbs.net", "dul.ru",
#        "dyna.spamrats.com", "dynip.rothen.com",
#        "http.dnsbl.sorbs.net", "images.rbl.msrbl.net",
#        "ips.backscatterer.org", "ix.dnsbl.manitu.net",
#        "korea.services.net", "misc.dnsbl.sorbs.net",
#        "noptr.spamrats.com", "ohps.dnsbl.net.au", "omrs.dnsbl.net.au",
#        "orvedb.aupads.org", "osps.dnsbl.net.au", "osrs.dnsbl.net.au",
#        "owfs.dnsbl.net.au", "pbl.spamhaus.org", "phishing.rbl.msrbl.net",
#        "probes.dnsbl.net.au", "proxy.bl.gweep.ca", "rbl.interserver.net",
#        "rdts.dnsbl.net.au", "relays.bl.gweep.ca", "relays.nether.net",
#        "residential.block.transip.nl", "ricn.dnsbl.net.au",
#        "rmst.dnsbl.net.au", "smtp.dnsbl.sorbs.net",
#        "socks.dnsbl.sorbs.net", "spam.abuse.ch", "spam.dnsbl.sorbs.net",
#        "spam.rbl.msrbl.net", "spam.spamrats.com", "spamrbl.imp.ch",
#        "t3direct.dnsbl.net.au", "tor.dnsbl.sectoor.de",
#        "torserver.tor.dnsbl.sectoor.de", "ubl.lashback.com",
#        "ubl.unsubscore.com", "virus.rbl.jp", "virus.rbl.msrbl.net",
#        "web.dnsbl.sorbs.net", "wormrbl.imp.ch", "xbl.spamhaus.org",
#        "zen.spamhaus.org", "zombie.dnsbl.sorbs.net"]

# blacklists for dns resolving SHORT
bls = ['zen.spamhaus.org',
'dnsbl.sorbs.net',
'bl.spamcop.net']

# URLS to check if our ip in list
URLS = [
    #TOR
    ('http://torstatus.blutmagie.de/ip_list_exit.php/Tor_ip_list_EXIT.csv',
     'is not a TOR Exit Node',
     'is a TOR Exit Node',
     False),

    #EmergingThreats
    ('http://rules.emergingthreats.net/blockrules/compromised-ips.txt',
     'is not listed on EmergingThreats',
     'is listed on EmergingThreats',
     True),

    #AlienVault
    ('http://reputation.alienvault.com/reputation.data',
     'is not listed on AlienVault',
     'is listed on AlienVault',
     True),

    #BlocklistDE
    ('http://www.blocklist.de/lists/bruteforcelogin.txt',
     'is not listed on BlocklistDE',
     'is listed on BlocklistDE',
     True),

    #Dragon Research Group - SSH
    ('http://dragonresearchgroup.org/insight/sshpwauth.txt',
     'is not listed on Dragon Research Group - SSH',
     'is listed on Dragon Research Group - SSH',
     True),

    #Dragon Research Group - VNC
    ('http://dragonresearchgroup.org/insight/vncprobe.txt',
     'is not listed on Dragon Research Group - VNC',
     'is listed on Dragon Research Group - VNC',
     True),

    #OpenBLock
    ('http://www.openbl.org/lists/date_all.txt',
     'is not listed on OpenBlock',
     'is listed on OpenBlock',
     True),

    #NoThinkMalware
    ('http://www.nothink.org/blacklist/blacklist_malware_http.txt',
     'is not listed on NoThink Malware',
     'is listed on NoThink Malware',
     True),

    #NoThinkSSH
    ('http://www.nothink.org/blacklist/blacklist_ssh_all.txt',
     'is not listed on NoThink SSH',
     'is listed on NoThink SSH',
     True),

    #Feodo
    ('http://rules.emergingthreats.net/blockrules/compromised-ips.txt',
     'is not listed on Feodo',
     'is listed on Feodo',
     True),

    #antispam.imp.ch
    ('http://antispam.imp.ch/spamlist',
     'is not listed on antispam.imp.ch',
     'is listed on antispam.imp.ch',
     True),

    #dshield
    ('http://www.dshield.org/ipsascii.html?limit=10000',
     'is not listed on dshield',
     'is listed on dshield',
     True),

    #malc0de
    ('http://malc0de.com/bl/IP_Blacklist.txt',
     'is not listed on malc0de',
     'is listed on malc0de',
     True),

    #MalWareBytes
    ('http://hosts-file.net/rss.asp',
     'is not listed on MalWareBytes',
     'is listed on MalWareBytes',
     True)]

#    #Spamhaus DROP (in CIDR format, needs parsing)
#    ('https://www.spamhaus.org/drop/drop.txt',
#     'is not listed on Spamhaus DROP',
#     'is listed on Spamhaus DROP',
#     False),
#    #Spamhaus EDROP (in CIDR format, needs parsing)
#    ('https://www.spamhaus.org/drop/edrop.txt',
#     'is not listed on Spamhaus EDROP',
#     'is listed on Spamhaus EDROP',
#     False)]

URLSCIDR = [
   #Spamhaus DROP (in CIDR format, needs parsing)
   ('https://www.spamhaus.org/drop/drop.txt',
    'is not listed on Spamhaus DROP',
    'is listed on Spamhaus DROP',
    False),
   #Spamhaus EDROP (in CIDR format, needs parsing)
   ('https://www.spamhaus.org/drop/edrop.txt',
    'is not listed on Spamhaus EDROP',
    'is listed on Spamhaus EDROP',
    False)
]

def checIpForDNSResolver(badip):
    global bls
    occurencies = []
    for bl in bls:
        check_addr = None

        try:
            check_host = '.'.join(reversed(str(badip).split("."))) + "." + bl
            check_addr = socket.gethostbyname(check_host)
        # except socket.error:
        except Exception as e:
            check_addr = None

        if check_addr is not None and "127.0.0." in check_addr:
            # print("blacklisted check_addr:%s" % check_addr)
            # print ("[checIpForDNSResolver] %s is listed in %s (%s: %s)" % (badip, bl, check_addr, check_host) )
            occurencies.append(bl)
        else:
            pass
            # print ("[checIpForDNSResolver] %s is NOT listed in %s (%s: %s)" % (badip, bl, check_addr, check_host) )

        # try:
        #     my_resolver = dns.resolver.Resolver()
        #     query = '.'.join(reversed(str(badip).split("."))) + "." + bl

        #     my_resolver.nameservers = [
        #         '8.8.8.8', #google
        #         '8.8.4.4', #google
        #         '8.26.56.26', # Comodo
        #         '8.20.247.20', # Comodo
        #         '84.200.69.80', #DNS.Watch
        #         '84.200.70.40', #DNS.Watch
        #         '77.88.8.88', #yandex
        #         '77.88.8.2' #yandex
        #     ]
        #     my_resolver.timeout = 5
        #     my_resolver.lifetime = 5

        #     print("[checIpForDNSResolver][query]%s" % query)

        #     answers = my_resolver.query(query, "A")
        #     answer_txt = my_resolver.query(query, "TXT")

        #     print(str(answers[0:]))
        #     print(str(answer_txt[0:]))

        #     # for rr in answers:
        #     #     print("[checIpForDNSResolver][aswers]%s" % rr)

        #     # for rr in answer_txt:
        #     #     print("[checIpForDNSResolver][answer_txt]%s" % rr)

        #     print ("[checIpForDNSResolver] %s is listed in %s (%s: %s)" % (badip, bl, answers[0], answer_txt[0]) )
        #     occurencies.append(bl)

        # except dns.resolver.NXDOMAIN:
        #     print ("[checIpForDNSResolver] %s is not listed in %s" % (badip, bl) )
        #     pass

        # except dns.resolver.Timeout:
        #     pass
        #     print ("[checIpForDNSResolver]WARNING: Timeout querying for [%s][%s]" % (badip, bl) )

        # except dns.resolver.NoNameservers:
        #     pass
        #     print ("[checIpForDNSResolver]WARNING: No nameservers for [%s][%s]" % (badip, bl) )

        # except dns.resolver.NoAnswer:
        #     pass
        #     print ("[checIpForDNSResolver]WARNING: No answer for [%s][%s]" % (badip, bl) )

    return occurencies

# def checkIpInCIDRRecord(badip, cidr):
#     net = ip_network(cidr)
#     return ip_address(badip) in net

# blacklists list of dicts from getBlackLists() function
def checkIpInBlackList(badip, blacklists):
    occurencies = []

    for bl in blacklists:
        # treat regular ip
        if bl['type'] == 'ip':
            matches = re.findall(badip, bl['content'])
            if len(matches):
                occurencies.append(bl['url'])
        # treat cidr records
        else:
            # print("cidr list check")
            cidr_list = bl['content'].split("\n")
            for cidr in cidr_list:
                # print("Checking cidr[%s] for ip[%s] " % (cidr, badip))
                net = ip_network(cidr)
                if ip_address(badip) in net:
                    occurencies.append(bl['url'])

    # remove duplicates
    occurencies = list(set(occurencies))

    return occurencies

# predownload all blacklists
def getBlackLists(dl_regular = True, dl_cidr = True):
    global URLS
    blacklists = []
    try:
        # regular ip list
        if dl_regular:
            for url, succ_txt, fail_txt, mal in URLS:
                with urllib.request.urlopen(url) as response:
                    retcode = response.code
                    if retcode == 200:
                        html_content = response.read().decode('utf8')
                        blacklists.append({"url": url, "content": html_content, "type": "ip"})

        # CIDR formatted ip list
        if dl_cidr:
            for url, succ_txt, fail_txt, mal in URLSCIDR:
                with urllib.request.urlopen(url) as response:
                    retcode = response.code
                    if retcode == 200:
                        html_content = response.read().decode('utf8')
                        matches = re.findall("(?:\d{1,3}\.){3}\d{1,3}(?:/\d\d?)?", html_content)
                        blacklists.append({"url": url, "content": "\n".join(matches), "type": "ip_cidr"})

    except Exception as e:
        print( "checkIpInBlackList error: [%s] %s" % (url, str(e)) )

    return blacklists


from hash_encryption import hash_data, encrypt_data
import rsa

def decrypt_data(data, private_key):
    """
    Decrypts the input data using the given private key.
    :param data: The data to be decrypted.
    :param private_key: The private key to be used for decryption.
    :return: The decrypted data.
    """
    return rsa.decrypt(data, private_key).decode('utf-8')

def main():
    # Generate a new public-private key pair
    (public_key, private_key) = rsa.newkeys(2048)

    # Input data
    data = input("Enter a message to encrypt: ")
    hashed_data = hash_data(data)

    # Encrypt the data
    encrypted_data = encrypt_data(hashed_data, public_key)

    # Decrypt the data
    decrypted_data = decrypt_data(encrypted_data, private_key)

    # Compare the decrypted data to the original message
    if decrypted_data == hashed_data:
        print("Decryption successful!")
    else:
        print("Decryption failed.")

if __name__ == '__main__':
    main()


import torch
import torch.optim as optim
from torchcrf import CRF
import torch.distributed as dist
from torch.nn.parallel import DistributedDataParallel
from torch.nn.utils.rnn import pad_sequence
from torch.utils.data import Dataset, DataLoader, DistributedSampler
from torch.nn import functional as F
from torch import nn
from torch.cuda.amp import autocast, GradScaler
import os
import ast
import argparse
import numpy as np
from sklearn.model_selection import train_test_split
from collections import defaultdict
from itertools import chain
from sklearn.metrics import confusion_matrix, precision_recall_curve, roc_curve, accuracy_score, balanced_accuracy_score, precision_score, recall_score, f1_score
from plotting_results import *

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

class GeneDataset(Dataset):
    def __init__(self, data, labels):
        self.data = data
        self.labels = labels

    def __len__(self):
        return len(self.data)

    def __getitem__(self, idx):
        return torch.FloatTensor(self.data[idx]), torch.LongTensor(self.labels[idx])

class CRFClassifier(nn.Module):
    def __init__(self, input_dim, num_tags):
        super(CRFClassifier, self).__init__()
        self.feature_extractor = nn.Sequential(
            nn.Linear(input_dim, 128),
            nn.ReLU(),
            nn.Linear(128, 64),
            nn.ReLU(),
            nn.Linear(64, num_tags)
        )
        self.crf = CRF(num_tags, batch_first=True)

    def forward(self, x):
        features = self.feature_extractor(x)
        output = self.crf.decode(features)
        return output

    def loss(self, x, tags):
        features = self.feature_extractor(x)
        return -self.crf(features, tags)

def get_model_predictions_and_labels(model, dataloader):
    model.eval()
    all_labels = []
    all_predictions = []
    with torch.no_grad():
        for inputs, labels in dataloader:
            inputs, labels = inputs.to(device), labels.to(device)
            outputs = model(inputs)
            all_labels.extend(labels.cpu().numpy())
            all_predictions.extend(outputs)
    return all_labels, all_predictions

def train_crf_classifier(train_dataloader, input_dim, num_tags, num_epochs):
    model = CRFClassifier(input_dim, num_tags).to(device)

    # Wrap the model with DistributedDataParallel
    model = DistributedDataParallel(model, device_ids=[rank])

    optimizer = optim.Adam(model.parameters())
    scaler = GradScaler()

    # Create a list to store the loss at each epoch
    epoch_losses = []
    for epoch in range(num_epochs):
        total_loss = 0
        for i, (inputs, tags) in enumerate(train_dataloader):
            inputs, tags = inputs.to(device), tags.to(device)
            optimizer.zero_grad()
            with autocast():
                loss = model.loss(inputs, tags)
            scaler.scale(loss).backward()
            scaler.step(optimizer)
            scaler.update()
            total_loss += loss.item()
        # Compute average loss for this epoch
        avg_loss = total_loss / len(train_dataloader)
        print(f"Epoch {epoch+1}: Loss = {avg_loss}")
        epoch_losses.append(avg_loss)

    return model, epoch_losses  # return losses along with the model

def plot_loss_curve(epoch_losses, save_path=None):
    plt.plot(epoch_losses)
    plt.title('Training Loss Curve')
    plt.xlabel('Epoch')
    plt.ylabel('Loss')
    if save_path:
        plt.savefig(save_path, dpi=900)
    plt.show()

parser = argparse.ArgumentParser(description='Run the CRF classifier.')
parser.add_argument('train_data_directory', help='The train_data_directory.')
parser.add_argument('test_data_directory', help='The test_data_directory.')
parser.add_argument('output_directory', help='The output directory to write to.')
parser.add_argument('--epochs', type=int, default=100, help='Number of epochs to use.')
args = parser.parse_args()
# Initialize the distributed environment
dist.init_process_group(backend='nccl')

# Get the rank (ID of the current process) and the world size (total number of processes)
rank = dist.get_rank()
world_size = dist.get_world_size()

# Divide the files between the processes
train_directory = args.train_data_directory
test_directory = args.test_data_directory
output_directory = args.output_directory
train_files = sorted(os.listdir(train_directory))
test_files = sorted(os.listdir(test_directory))
train_files = train_files[rank::world_size]
test_files = test_files[rank::world_size]

# Now, load and process the files assigned to this process
train_data = []
train_labels = []
for file in train_files:
    with open(os.path.join(train_directory, file), 'r') as f:
        data = ast.literal_eval(f.read())
        train_data.append(data["features"])
        train_labels.append(data["labels"])

test_data = []
test_labels = []
for file in test_files:
    with open(os.path.join(test_directory, file), 'r') as f:
        data = ast.literal_eval(f.read())
        test_data.append(data["features"])
        test_labels.append(data["labels"])

# Create dataloaders
train_dataset = GeneDataset(train_data, train_labels)
test_dataset = GeneDataset(test_data, test_labels)
train_sampler = DistributedSampler(train_dataset)
test_sampler = DistributedSampler(test_dataset)
train_dataloader = DataLoader(train_dataset, sampler=train_sampler, batch_size=32)
test_dataloader = DataLoader(test_dataset, sampler=test_sampler, batch_size=32)

# Train the model and get the losses for each epoch
model, epoch_losses = train_crf_classifier(train_dataloader, input_dim=128, num_tags=2, num_epochs=100)

# save the model after training
if rank == 0:  # only save on the main process
    torch.save(model.state_dict(), os.path.join(output_directory,'crf.pth'))

# Plot the training loss curve
plot_loss_curve(epoch_losses, save_path= os.path.join(output_directory,'training_loss_curve.png'))

# Assuming you have trained your model and have your dataloader ready:
y_true, y_pred = get_model_predictions_and_labels(model, test_dataloader)

# Then you can use the data to plot the graphs:
plot_confusion_matrix(y_true, y_pred, save_path=os.path.join(output_directory,'confusion_matrix.png'))


import time,calendar,os,json,sys,datetime,csv,random
from math import log,sqrt
from requests import get
from subprocess import Popen,PIPE

def datetoday(x):
  t=time.strptime(x+'UTC','%Y-%m-%d%Z')
  return calendar.timegm(t)//86400

def daytodate(r):
  t=time.gmtime(r*86400)
  return time.strftime('%Y-%m-%d',t)

def getapidata(req):
  url='https://api.coronavirus.data.gov.uk/v1/data?'
  response = get(url+req, timeout=10)
  if not response.ok:
    raise RuntimeError(f'Request failed: { response.text }')
  if response.status_code!=200: return None
  date=time.strftime('%Y-%m-%d',time.strptime(response.headers['Last-Modified'],'%a, %d %b %Y %H:%M:%S %Z'))# Not currently used
  return response.json()['data']

startdate='2020-11-01'

ltladatadir='ltladatadir'
stpdatadir='stpdatadir'
startday=datetoday(startdate)
nowdate=datetime.datetime.utcnow().strftime('%Y-%m-%d')
endday=datetoday(nowdate)+1

# Fetch and save new case data
os.makedirs(ltladatadir,exist_ok=True)
for day in range(startday,endday):
  date=daytodate(day)
  fn=os.path.join(ltladatadir,date)
  if os.path.isfile(fn): continue
  req='filters=areaType=ltla;date='+date+'&structure={"date":"date","LADCD":"areaCode","LADNM":"areaName","cases":"newCasesBySpecimenDateAgeDemographics"}'
  casedata=getapidata(req)
  if casedata==None: endday=day;break
  # Convert to a map from {LTLA location code} --> list of (min age, max age, case count), where [min age, max age) partition [0,150) in half-open convention
  data={}
  for x in casedata:
    data[x['LADCD']]=[]
    d=data[x['LADCD']]
    # Keep age ranges {xx_yy where xx is a multiple of 5 and <90, and yy=xx+4} and '90+'; discard others
    for y in x['cases']:
      a=y['age']
      if a=='90+':
        amin=90;amax=150
      elif '_' in a:
        amin=int(a[:2]);amax=int(a[-2:])
        if amax!=amin+4: continue
        amax+=1
      else: continue
      d.append((amin,amax,y['cases']))
  with open(fn,'w') as fp:
    json.dump(data,fp)
    print("Wrote",fn)

caseagebands=[(amin,amin+5 if amin<90 else 150) for amin in range(0,95,5)]
enddate=daytodate(endday)
print("Loading case counts from",startdate,"-",enddate,"(excluding end date)")

# Read LTLA->STP mapping
LTLAtoSTP={}
with open('LTLAtoSTP20.csv','r') as fp:
  r=csv.reader(fp)
  headings=next(r)
  (i0,i1)=(headings.index('LAD20CD'),headings.index('STP20NM'))
  for x in r:
    LTLAtoSTP[x[i0]]=x[i1]

# Need to modify conversion to use LAD19CD, because government api uses LAD19 but vaccination database (NIMS) uses STP20
for x in ['E07000004', 'E07000005', 'E07000006', 'E07000007']:
  LTLAtoSTP[x]=LTLAtoSTP['E06000060']

# Convert new LTLA case data to STP and save
os.makedirs(stpdatadir,exist_ok=True)
for day in range(startday,endday):
  date=daytodate(day)
  fnstp=os.path.join(stpdatadir,date)
  if os.path.isfile(fnstp): continue
  fnltla=os.path.join(ltladatadir,date)
  with open(fnltla,'r') as fp:
    ltladata=json.load(fp)
  stpdata={}
  for ltla in ltladata:
    cases=ltladata[ltla]
    stp=LTLAtoSTP[ltla]
    if stp not in stpdata: stpdata[stp]=[]
    for (i,c) in enumerate(cases):
      if len(stpdata[stp])==i: stpdata[stp].append(cases[i])
      else: stpdata[stp][i][2]+=cases[i][2]
  with open(fnstp,'w') as fp:
    json.dump(stpdata,fp)
    print("Wrote",fnstp)

# Load all STP case data
# stpdat[day number from startdate][STP name][age band number] = new case count for that day, place, age band
stpcases=[]
for day in range(startday,endday):
  date=daytodate(day)
  fnstp=os.path.join(stpdatadir,date)
  fnstp=os.path.join(stpdatadir,date)
  with open(fnstp,'r') as fp:
    stpcases.append(json.load(fp))

# Load STP population size data
stppop={}
with open('STPpopulations.csv','r') as fp:
  r=csv.reader(fp)
  headings=next(r)
  for x in r:
    stppop[x[1]]=[]
    for (h,v) in zip(headings[2:],x[2:]):
      if '-' in h:
        z=h.split('-')
        amin,amax=int(z[0]),int(z[1])+1
      elif '+' in h:
        amin,amax=int(h[:-1]),150
      else: assert 0
      stppop[x[1]].append((amin,amax,int(v)))

# Check STP names are compatible
assert set(stppop.keys())==set(LTLAtoSTP.values())
STPNM=sorted(list(set(LTLAtoSTP.values())))

# Read STP -> NHS region mapping
STPtoNHS={}
with open('STP20toNHSER20.csv','r') as fp:
  r=csv.reader(fp)
  headings=next(r)
  (i0,i1)=(headings.index('STP20NM'),headings.index('NHSER20NM'))
  for x in r:
    STPtoNHS[x[i0]]=x[i1]
NHSER=sorted(list(set(STPtoNHS.values())))

# Load vaccination data
# Weekly data from https://www.england.nhs.uk/statistics/statistical-work-areas/covid-19-vaccinations/
# Note that publication date is 4 days after date of last data. Filename uses the latter date.
# Hard code this particular week until we decide we want to do something else.
# vaxnum[STP name] = list of (agemin, agemax, count)
vaxnum={}
vaxagebands=[(0,80),(80,150)]
vaxdate='2021-01-17'
with open(vaxdate+'-vaxdata.csv','r') as fp:
  r=csv.reader(fp)
  headings=next(r)
  # For now assume headings are: Region, STP name, 1st dose 0-80, 1st dose 80+, 2nd dose 0-80, 2nd dose 80+
  for x in r:
    vaxnum[x[1]]=[(0,80,int(x[2])),(80,150,int(x[3]))]
print("Using vaccine data from",vaxdate)

# Check STP names are compatible
assert set(vaxnum)==set(stppop.keys())

# Smooth cases with lagged 7 day average
newstpcases=[]
newstartday=startday+6
for day in range(newstartday,endday):
  newstpcases.append({})
  for stp in STPNM:
    newstpcases[-1][stp]=[(amin,amax,sum(stpcases[d-startday][stp][i][2] for d in range(day-6,day+1))/7) for (i,(amin,amax)) in enumerate(caseagebands)]
stpcases=newstpcases
startday=newstartday
startdate=daytodate(startday)

from scipy.optimize import minimize
import numpy as np

# Add up values in l corresponding to intervals contained in [rmin,rmax)
def totrange(l,rmin,rmax):
  return sum(x[2] for x in l if x[0]>=rmin and x[1]<=rmax)

def sc(xx):
  eff,ff=xx[0],xx[1:]
  err=0
  for stp in STPNM:
    for (amin,amax),f in zip(vaxagebands,ff):
      #if amin==0: continue
      t0=totrange(stpcases[day0-startday][stp],amin,amax)
      t1=totrange(stpcases[day1-startday][stp],amin,amax)
      vax=totrange(vaxnum[stp],amin,amax)
      pop=totrange(stppop[stp],amin,amax)
      v=vax/pop
      #v=randvax[(stp,amin)]
      rawpred=t0
      actual=t1
      pred=f*(1-v*eff)*rawpred
      #print("%-20s   %3d %3d       %5.3f     %6d  %6d  %6.1f"%(stp[:20].replace(' ','_'),amin,amax,v,t0,actual,pred))
      err+=(log(pred/actual))**2
      #err-=-pred+actual*log(pred)
  return err

randvax={}
#for stp in STPNM:
#  for (amin,amax) in vaxagebands:
#    randvax[(stp,amin)]=0.3+0.5*random.random()
for nhs in NHSER:
  for (amin,amax) in vaxagebands:
    r=0.3+0.5*random.random()
    for stp in STPNM:
      if STPtoNHS[stp]==nhs: randvax[(stp,amin)]=r

def sc2(xx):
  eff,f=xx[0],xx[1]
  err=0
  for stp in STPNM:
    casethen=[]
    casenow=[]
    vaxrate=[]
    for (amin,amax) in vaxagebands:
      t0=totrange(stpcases[day0-startday][stp],amin,amax)
      t1=totrange(stpcases[day1-startday][stp],amin,amax)
      vax=totrange(vaxnum[stp],amin,amax)
      pop=totrange(stppop[stp],amin,amax)
      casethen.append(t0)
      casenow.append(t1)
      vaxrate.append(vax/pop)
      #vaxrate.append(randvax[(stp,amin)])
    v=vaxrate[1]
    # Cross-ratio + Poisson model
    pred=casenow[0]/casethen[0]*casethen[1]*f*(1-v*eff)
    actual=casenow[1]
    err-=-pred+actual*log(pred)# I don't think Poisson model is very good here
    #err+=log(pred/actual)**2
  return err

if 0:
  day1=endday-1
  print("Measuring changes in case counts up to",daytodate(day1),"(7 day lagged average)")
  print()
  best=(-1,)
  print("From date    Efficacy coefficient")
  for day0 in range(datetoday(vaxdate)-10,datetoday(vaxdate)+15):
    res=minimize(sc,[0.5, 0.5, 0.5],method="SLSQP",bounds=[(0,0.99),(1e-6,1),(1e-6,1)],options={"maxiter":1000})
    #res=minimize(sc2,[0.5, 1],method="SLSQP",bounds=[(0,0.99),(1e-6,10),],options={"maxiter":1000})
    if not res.success: raise RuntimeError(res.message)
    xx=res.x
    eff,ff=xx[0],xx[1:]
    if eff>best[0]: best=(eff,day0,ff)
    print(daytodate(day0),"  %5.3f"%eff)
  print()

if 0:
  day1s=list(reversed(range(endday-1,datetoday('2020-11-25'),-4)))
  print("From date  "+' '.join(daytodate(x) for x in day1s))
  for day0 in reversed(range(endday-1,datetoday(vaxdate)-50,-2)):
    print(daytodate(day0),end='')
    for day1 in day1s:
      if day1<=day0: print("           ",end='');continue
      res=minimize(sc2,[0.5, 1],method="SLSQP",bounds=[(-9,0.99),(1e-6,10),],options={"maxiter":1000})
      if not res.success: raise RuntimeError(res.message)
      l0=-res.fun
      eff,f=res.x
      if 1:
        eff1=eff
      else:
        beta=0.8
        res=minimize(sc2,[eff*beta, f],method="SLSQP",bounds=[(eff*beta,eff*beta),(1e-6,10)],options={"maxiter":1000})
        if not res.success: raise RuntimeError(res.message)
        l1=-res.fun
        if l0<=l1: eff1=0
        else:
          eff1=eff-((1-beta)*eff)/sqrt((l0-l1)/20)
          if eff*eff1<0: eff1=0
      print("     %6.3f"%(eff1),end='')
    print()
  print()

if 1:
  day1s=list(reversed(range(endday-1,datetoday('2020-11-25'),-4)))
  print("From date  "+' '.join(daytodate(x) for x in day1s))
  for day0 in reversed(range(endday-1,datetoday(vaxdate)-50,-2)):
    print(daytodate(day0),end='')
    for day1 in day1s:
      if day1<=day0: print("           ",end='');continue
      res=minimize(sc,[0.5, 0.5, 0.5],method="SLSQP",bounds=[(0,0.99),(1e-6,10),(1e-6,10)],options={"maxiter":1000})
      if not res.success: raise RuntimeError(res.message)
      l0=-res.fun
      eff,ff=res.x[0],res.x[1:]
      if 0:
        eff1=eff
      else:
        beta=0.8
        res=minimize(sc,[eff*beta, ff[0], ff[1]],method="SLSQP",bounds=[(eff*beta,eff*beta),(1e-6,10),(1e-6,10)],options={"maxiter":1000})
        if not res.success: raise RuntimeError(res.message)
        l1=-res.fun
        if l0<=l1: eff1=0
        else:
          eff1=eff-((1-beta)*eff)/sqrt((l0-l1)/2)
          if eff*eff1<0: eff1=0
      print("     %6.3f"%(eff1),end='')
    print()
  print()

if 0:
  (eff,day0,ff)=best
  print("Strongest signal in changes in case counts is from",daytodate(day0),"-",daytodate(day1),"(in both cases smoothed with 7 day lagging average)")
  print("Coefficient of efficacy : %5.3f"%eff)
  for (amin,amax),f in zip(vaxagebands,ff):
    print("Case count factor for age band %3d - %3d : %5.3f"%(amin,amax,f))
  print()

if 0:
  #day0=datetoday('2020-12-12')
  #day1=datetoday('2021-01-06')
  res=minimize(sc2,[0.5, 1],method="SLSQP",bounds=[(-9,0.99),(1e-6,10),],options={"maxiter":1000})
  if not res.success: raise RuntimeError(res.message)
  xx=res.x
  eff,ff=xx[0],xx[1:]
  for stp in STPNM:
    for (amin,amax),f in zip(vaxagebands,ff):
      t0=totrange(stpcases[day0-startday][stp],amin,amax)
      t1=totrange(stpcases[day1-startday][stp],amin,amax)
      vax=totrange(vaxnum[stp],amin,amax)
      pop=totrange(stppop[stp],amin,amax)
      v=vax/pop
      print("%5d  %6.1f  %5.3f  %5d  %6.3f  %d"%(amin,t0*f,v,t1,log(t1/(t0*f)),NHSER.index(STPtoNHS[stp])))

if 0:
  day0=datetoday('2021-01-24')
  day1=endday-1
  #day0=datetoday('2020-12-12')
  #day1=datetoday('2021-01-06')
  res=minimize(sc2,[0.5, 1],method="SLSQP",bounds=[(-9,0.99),(1e-6,10),],options={"maxiter":1000})
  #res=minimize(sc2,[0, 1],method="SLSQP",bounds=[(0,0),(1e-6,10),],options={"maxiter":1000})
  if not res.success: raise RuntimeError(res.message)
  xx=res.x
  eff,f=xx
  print("eff =",eff)
  print("f =",f)
  totl=0
  fp=open('temp','w')
  for stp in STPNM:
    casethen=[]
    casenow=[]
    vaxrate=[]
    print("%25s"%(STPtoNHS[stp]),end='')
    for (amin,amax) in vaxagebands:
      t0=totrange(stpcases[day0-startday][stp],amin,amax)
      t1=totrange(stpcases[day1-startday][stp],amin,amax)
      vax=totrange(vaxnum[stp],amin,amax)
      pop=totrange(stppop[stp],amin,amax)
      casethen.append(t0)
      casenow.append(t1)
      vaxrate.append(vax/pop)
      print("   %6d  %5.3f"%(vax,vax/pop),end='')
    print()
    v=vaxrate[1]
    rawpred=casenow[0]/casethen[0]*casethen[1]
    pred=rawpred*f*(1-v*eff)
    actual=casenow[1]
    #print("%6.1f  %6.1f   %d"%(pred,actual,NHSER.index(STPtoNHS[stp])))
    l=-pred+actual*log(pred)
    totl+=l
    print("%6.3f  %6.3f   %6.3f   %12g %12g %12g %d   %12g"%(v,actual/rawpred,actual/pred,actual,rawpred,pred,NHSER.index(STPtoNHS[stp]),l),file=fp)
  print("Total likelihood",totl)
  fp.close()

sys.exit(0)

with open('stpcrossratios','w') as fp:
  for stp in STPNM:
    casethen=[]
    casenow=[]
    vaxrate=[]
    for (amin,amax),f in zip(vaxagebands,ff):
      t0=totrange(stpcases[day0-startday][stp],amin,amax)
      t1=totrange(stpcases[day1-startday][stp],amin,amax)
      vax=totrange(vaxnum[stp],amin,amax)
      pop=totrange(stppop[stp],amin,amax)
      casethen.append(t0)
      casenow.append(t1)
      vaxrate.append(vax/pop)
    print('%5.3f   %6.3f  "%s"'%(vaxrate[1],log((casenow[1]/casethen[1])/(casenow[0]/casethen[0])),stp),file=fp)

with open('nhscrossratios','w') as fp:
  for reg in NHSER:
    casethen=[]
    casenow=[]
    vaxrate=[]
    for (amin,amax),f in zip(vaxagebands,ff):
      t0=t1=vax=pop=0
      for stp in STPNM:
        if STPtoNHS[stp]!=reg: continue
        t0+=totrange(stpcases[day0-startday][stp],amin,amax)
        t1+=totrange(stpcases[day1-startday][stp],amin,amax)
        vax+=totrange(vaxnum[stp],amin,amax)
        pop+=totrange(stppop[stp],amin,amax)
      casethen.append(t0)
      casenow.append(t1)
      vaxrate.append(vax/pop)
    print('%5.3f   %6.3f  "%s"'%(vaxrate[1],log((casenow[1]/casethen[1])/(casenow[0]/casethen[0])),reg),file=fp)

# gnuplot> plot "stpcrossratios" u 1:2:(strcol(3)[1:12]) with labels point pt 7 offset char 0,-1.0
# gnuplot> plot "nhscrossratios" u 1:2:3 with labels point pt 7 offset char 0,-1.2


import os
import sys
import tempfile

import click
import pydot
from PyQt5 import QtWidgets
from PyQt5.QtCore import QFile, QTimer, QSize
from PyQt5.QtGui import QIcon
from PyQt5.QtWidgets import QAction, QFileDialog, QMessageBox, QMainWindow

from graphviz_gui.svg import SvgView

class GraphvizCanvas(QMainWindow):
    def __init__(
        self,
        app: QtWidgets.QApplication,
        working_directory: str,
        initial_dot_file: str = None,
    ):
        super().__init__()
        self._view = None
        self._app = app

        self._do_timer = False

        self._output = tempfile.NamedTemporaryFile(suffix=".svg")
        self.output = QFile(self._output.name)

        self.init_ui()

        self._open_file = None
        if initial_dot_file is not None:
            self._open_file_name = os.path.join(working_directory, initial_dot_file)
            self._do_timer = True

    def init_ui(self):
        exit_action = QAction(QIcon("exit.png"), "&Exit", self)
        exit_action.setShortcut("Ctrl+Q")
        exit_action.setStatusTip("Exit GraphViz Editor")
        exit_action.triggered.connect(self._app.quit)

        open_action = QAction(QIcon("SP_DialogOpenButton"), "&Open...", self)
        open_action.setShortcut("Ctrl+O")
        open_action.setStatusTip("Open GraphViz dot file")
        open_action.triggered.connect(self.open_file)
        self._error_dialog = QtWidgets.QErrorMessage()

        self.statusBar()

        menubar = self.menuBar()
        file_menu = menubar.addMenu("&File")
        file_menu.addAction(open_action)
        file_menu.addAction(exit_action)

        self.view = SvgView()
        self.view.outlineItem = False
        self.view.setRenderer(SvgView.OpenGL)
        self.setCentralWidget(self.view)

        self.setWindowTitle("Graphviz Editor")
        self.resize(250, 150)

        self.reload_timer = QTimer()
        self.reload_timer.setInterval(500)
        self.reload_timer.timeout.connect(self.run_timer)
        self.reload_timer.start()

    def run_timer(self):
        try:
            if self._do_timer:
                self.flush()
        except Exception as e:
            self._do_timer = False
            self._error_dialog.showMessage(str(e))

    def open_file(self):
        options = QFileDialog.Options()
        options |= QFileDialog.DontUseNativeDialog
        fileName, _ = QFileDialog.getOpenFileName(
            self,
            "QFileDialog.getOpenFileName()",
            "",
            "Graphviz Files (*.dot);;All Files (*)",
            options=options,
        )
        if fileName:
            self._open_file_name = fileName
            self._open_file = QFile(self._open_file_name)
            if not self._open_file.exists():
                QMessageBox.critical(
                    self, "Open SVG File", "Could not open file '%s'." % self._open_file
                )
            else:
                self._do_timer = True
                self.flush()

    def flush(self):
        (graph,) = pydot.graph_from_dot_file(self._open_file_name)
        graph.write_svg(self._output.name)

        self.view.openFile(self.output)

        self.resize(self.view.sizeHint() + QSize(80, 80 + self.menuBar().height()))

@click.command()
@click.argument("dot_file", default=None)
def main(dot_file=None):
    app = QtWidgets.QApplication(sys.argv)

    canvas = GraphvizCanvas(app, os.getcwd(), dot_file)
    canvas.show()

    exit_code = app.exec_()  # 2. Invoke appctxt.app.exec_()
    sys.exit(exit_code)

if __name__ == "__main__":
    main()


import asyncio, arrow
from forex_python.converter import CurrencyRates, CurrencyCodes
from pyrogram import Client, filters
from pint import UnitRegistry
from .. import config, help_dict, get_entity, log_chat, log_errors, self_destruct, public_log_errors

c = CurrencyRates()
cc = CurrencyCodes()
ureg = UnitRegistry()
Q_ = ureg.Quantity

@Client.on_message(~filters.sticker & ~filters.via_bot & ~filters.edited & filters.me & filters.command(['curr'], prefixes=config['config']['prefixes']))
@log_errors
@public_log_errors
async def currency(client, message):
	if len(message.command) <= 3:
		await self_destruct(message, "Incorrect Syntax")
		return

	value = message.command[1]
	curr1 = message.command[2].upper()
	curr2 = message.command[3].upper()
	try:
		from_sym = cc.get_symbol(curr1)
		to_sym = cc.get_symbol(curr2)
		conv = c.convert(curr1, curr2, float(value))
		await message.edit(f"{value}{from_sym} = {conv:,.2f}{to_sym}")
	except ValueError as err:
		await self_destruct(message, f"{str(err)}")

commands = ['conv', 'convert', 'length', 'len', 'mass', 'vol', 'volume', 'temp']

@Client.on_message(~filters.sticker & ~filters.via_bot & ~filters.edited & filters.me & filters.command(commands, prefixes=config['config']['prefixes']))
@log_errors
@public_log_errors
async def uconvert(client, message):
	if len(message.command) <= 3:
		await self_destruct(message, "Incorrect Syntax")
		return

	value = message.command[1]
	curr1 = message.command[2]
	curr2 = message.command[3]

	# Fix for -temp
	if curr1.lower() == "f":
		curr1 = "degF"
	elif curr1.lower() == "c":
		curr1 = "degC"
	if curr2.lower() == "f":
		curr2 = "degF"
	elif curr2.lower() == "c":
		curr2 = "degC"

	try:
		from_u = ureg(curr1)
		in_u = ureg.Quantity(float(value), from_u)
		conv = in_u.to(ureg(curr2))
		newval = f"{conv.magnitude:,.2f}"
		if newval == "0.00":
			newval = f"{conv.magnitude:f}"
		await message.edit(f"{value} {from_u.units} = {newval} {conv.units}")
	except (TypeError, ValueError) as err:
		await self_destruct(message, f"{str(err)}")

@Client.on_message(~filters.sticker & ~filters.via_bot & ~filters.edited & filters.me & filters.command(['time'], prefixes=config['config']['prefixes']))
@log_errors
@public_log_errors
async def calc_time(client, message):

	try:
		command = message.command
		command.pop(0)
		if command:
			tz = " ".join(command)
		elif not config['config']['timezone']:
			await message.delete()
			return
		else:
			tz = config['config']['timezone']

		now = arrow.utcnow().to(tz)
		time = now.format("hh:mm A (HH:mm)")
		date = now.format(r"MM-DD-YYYY")

		await message.edit(f"Currently it is {time} on {date} in {now.format('ZZZ')}")
	except ValueError as e:
		await self_destruct(message, f"{str(e)}")

help_dict["calculator"] = ('Calculator',
'''{prefix}curr [value] [from currency] [to currency] - Convert source currency to dest currency

{prefix}time [TZ database name] - Get the time in the timezone

{prefix}convert value <from unit> <to unit> - Convert between units of measure
Aliases: {prefix}conv, {prefix}vol, {prefix}volume, {prefix}temp, {prefix}lenth, {prefix}len, {prefix}mass

Unit conversion can be any of the units defined in this document.
You can also provide expressions in quoted arguments such as {prefix}convert 90 mph "light_year/second" which will convert 90
miles per hour to the equivalent light years per second.

''')


# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from django.db import models, migrations
from django.conf import settings
import datetime

class Migration(migrations.Migration):

    dependencies = [
        migrations.swappable_dependency(settings.AUTH_USER_MODEL),
        ('chores', '0002_auto_20150926_2139'),
    ]

    operations = [
        migrations.RemoveField(
            model_name='chores',
            name='user',
        ),
        migrations.AddField(
            model_name='chores',
            name='frequency_in_days',
            field=models.IntegerField(default=0),
        ),
        migrations.AddField(
            model_name='chores',
            name='last_completed_by',
            field=models.ForeignKey(related_name='last_completed_by', blank=True, to=settings.AUTH_USER_MODEL, null=True),
        ),
        migrations.AddField(
            model_name='chores',
            name='last_completed_date',
            field=models.DateTimeField(default=datetime.datetime.now),
        ),
        migrations.AddField(
            model_name='chores',
            name='primary_assignee',
            field=models.ForeignKey(related_name='primary_assignee', blank=True, to=settings.AUTH_USER_MODEL, null=True),
        ),
        migrations.AddField(
            model_name='chores',
            name='secondary_assignee',
            field=models.ForeignKey(related_name='secondary_assignee', blank=True, to=settings.AUTH_USER_MODEL, null=True),
        ),
    ]


import cv2
import numpy as np

from question_2 import read_image, write_image, display_image

def calc_hist(image, bins):
    histogram = np.zeros(bins)
    for pixel in image:
        histogram[pixel] += 1
    return histogram

def cumulative_freq(histogram):
    b = [histogram[0]]
    for i in histogram:
        b.append(b[-1] + i)
    return np.array(b)

def equalize_hist(img, flat, cs):
    n = (cs - cs.min()) * 255
    N = cs.max() - cs.min()
    cs = n / N
    cs = cs.astype('uint8')
    equalized = cs[flat]
    equalized = np.reshape(equalized, img.shape)
    return equalized

def main():
    img = read_image('../res/img.png', cv2.IMREAD_GRAYSCALE)
    flat_img_array = img.flatten()
    hist = calc_hist(flat_img_array, 256)
    cumulative = cumulative_freq(hist)
    equalized = equalize_hist(img, flat_img_array, cumulative)
    display_image(img, 'original')
    display_image(equalized, 'equalized')
    cv2.waitKey()
    cv2.destroyAllWindows()
    write_image(equalized, '../outputs/Assignment1/question_6.png')

if __name__ == '__main__':
    main()


import pygit2
import logging
import datetime
import sys
from . import errors

class GitRemoteCallbacks(pygit2.remote.RemoteCallbacks):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.lasttime = None
        self.lastreceived = 0
        self.lastspeed = 0
        self.lastlength = 0

    def readableunits(self, value):
        power = 1024
        for unit in ("Bytes", "KiB", "MiB", "GiB", "TiB"):
            if value >= power:
                value = value / power
            else:
                break
        return value, unit

    def transfer_progress(self, stats):
        """
        Args:
          stats.indexed_deltas,   stats.indexed_objects
          stats.local_objects,    stats.received_bytes
          stats.received_objects, stats.total_deltas
          stats.total_objects
        """
        try:
            currenttime = datetime.datetime.now()
            if self.lasttime is not None:
                interval = currenttime - self.lasttime
                interval = interval.total_seconds()
            else:
                interval = 0
                self.lasttime = currenttime
            if interval > 1 or (stats.indexed_objects == stats.total_objects):
                if interval > 1:
                    self.lastspeed = (
                        stats.received_bytes - self.lastreceived) / interval
                speed, speed_unit = self.readableunits(self.lastspeed)
                received_bytes, received_bytes_unit = self.readableunits(
                    stats.received_bytes)
                clear_message = "".join([" " for _ in range(self.lastlength)])
                message = (
                    "Receiving objects:" +
                    " {:>2.2%} ({}/{}), {:.2f} {} | {:.2f} {}/s").format(
                        stats.received_objects / stats.total_objects,
                        stats.received_objects, stats.total_objects,
                        received_bytes, received_bytes_unit, speed, speed_unit)
                print("\r{}\r".format(clear_message), end="", file=sys.stderr)
                if stats.indexed_objects == stats.total_objects:
                    print(message, flush=True, file=sys.stderr)
                else:
                    print(message, end="", flush=True, file=sys.stderr)
                self.lasttime = currenttime
                self.lastreceived = stats.received_bytes
                self.lastlength = len(message)
        except Exception as e:
            logging.error(e)

def clone(repourl, repopath):
    try:
        return pygit2.clone_repository(
            repourl, repopath, callbacks=GitRemoteCallbacks())
    except pygit2.GitError as e:
        raise errors.GitError(e)

def discover(repopath):
    return pygit2.discover_repository(repopath)

def repo(repopath):
    return pygit2.Repository(repopath)


import random
import sqlite3

conn = sqlite3.connect('card.s3db')
cur = conn.cursor()

delete_table = "DROP TABLE card"
cur.execute(delete_table)

sql = '''CREATE TABLE IF NOT EXISTS card(
   id INTEGER,
   number TEXT,
   pin TEXT,
   balance INTEGER DEFAULT 0
);'''
cur.execute(sql)
conn.commit()

row = cur.fetchall()
for row in cur:
    print(f">>> {row}")

z = 0

id = 0
client_base = []
balance = 0
check = True
card_id = 0

show_balance = 0

def customer_balance():
    # global balance
    # balance = 0
    global show_balance
    # balance = str(show_balance)
    # x = balance.strip("(").strip(")").strip(",").strip(" ")

    # print(f"Balance: {balance}")
    # cur.execute('SELECT balance FROM card WHERE card=')
    # row = cur.fetchoneall()
    # show_balanc = "".join(row)
    cur.execute(f'SELECT balance FROM card WHERE id = {card_id}')
    # conn.commit()
    row3 = cur.fetchone()
    show_balance = row3
    balance = str(show_balance)
    x = balance.strip("(").strip(")").strip(",").strip(" ")
    print(show_balance)

    # print(f"Balance: {x}")
    print(f"Balance: {x}")
    in_banking_system_message()
    customer_choice_in_system()

def random_with_n_digits(n):
    range_start = 10 ** (n - 1)
    range_end = (10 ** n) - 1
    return random.randint(range_start, range_end)

def create_account():
    global id
    id += 1
    sum_custom = 0
    check_sum = 0
    card_number = [4, 0, 0, 0, 0, 0]
    card_number_str = 0
    for i in range(10):
        n = random.randint(0, 9)
        card_number.append(n)
        # cur.execute(f"INSERT INTO ")
    del card_number[-1]
    card_number_temp = [x for x in card_number]
    # card_number_temp[0] = 2 * card_number_temp[0]

    for i in range(0, len(card_number_temp)):
        if i % 2 == 0:
            card_number_temp[i] = 2 * card_number_temp[i]

    for i in range(len(card_number_temp)):
        if card_number_temp[i] > 9:
            card_number_temp[i] = card_number_temp[i] - 9

    for i in range(len(card_number_temp)):
        sum_custom = sum_custom + card_number_temp[i]
    if sum_custom % 10 == 0:
        check_sum = 0
    else:
        check_sum = 10 - sum_custom % 10

    card_number.append(check_sum)

    card_number_convert = [str(x) for x in card_number]
    card_number_str = int("".join(card_number_convert))

    pin_number = random_with_n_digits(4)
    global client_base
    client_base.append(card_number_str)
    client_base.append(pin_number)

    cur.execute('INSERT INTO card (id, number, pin) VALUES (?, ?, ?);', (id, card_number_str, pin_number))
    conn.commit()
    # conn.close()

    # print("Your card has been created")
    # print("Your card number:")
    # # print(card_number_str)
    # cur.execute('SELECT number FROM card;')
    # row = cur.fetchone()
    # print(row
    # # conn.commit()
    # print("Your card PIN:")
    # # print()
    # # print(pin_number)
    # cur.execute('SELECT pin FROM card;')
    # # conn.commit()
    # row = cur.fetchone()
    # print(row)
    # print(f">>> {z}")
    # conn.commit()

    # print(client_base)

def read_from_db():
    global z
    global show_balance
    global card_id
    temp_id = str(card_id)
    print("Your card has been created")
    print("Your card number:")
    cur.execute('SELECT number FROM card;')
    row1= cur.fetchall()
    ' '.join(str(y) for y in row1)
    print("".join(row1[z]))
    # conn.commit()
    print("Your card PIN:")
    # print()
    # print(pin_number)
    cur.execute('SELECT pin FROM card;')
    # conn.commit()
    row2 = cur.fetchall()
    print("".join(row2[z]))
    # print("".join(row3[z]))

    z = z + 1

def log_into_account():
    global card_id
    print("Enter your card number:")
    customer_card_number = int(input())
    cur.execute('SELECT number FROM card;')
    row1 = cur.fetchall()
    cur.execute('SELECT pin FROM card;')
    # conn.commit()
    row2 = cur.fetchall()

    if any(str(customer_card_number) in i for i in row1):
        # if customer_card_number in row1:
        print("Enter your PIN:")
        customer_pin_number = int(input())
        if any(str(customer_pin_number) in j for j in row2):
            # if customer_pin_number in row2:
            cur.execute(f'SELECT id FROM card where number={customer_card_number}')
            card_id = cur.fetchone()[0]
            print(card_id)
            print("You have successfully logged in!")

            in_banking_system_message()
            customer_choice_in_system()
        else:
            print("Wrong card number or PIN!")

    # if customer_card_number in client_base:
    #     print("Enter your PIN:")
    #     customer_pin_number = int(input())
    #
    #     if customer_pin_number in client_base:
    #         print("You have successfully logged in!")
    #
    #         in_banking_system_message()
    #         customer_choice_in_system()
    #     else:
    #         print("Wrong card number or PIN!")
    elif customer_card_number not in client_base:
        print("Wrong card number or PIN!")

def customer_out_from_system():
    out_from_banking_system_message()
    global check
    choice = int(input())
    if choice == 1:
        create_account()
        read_from_db()
    elif choice == 2:
        log_into_account()
    elif choice == 0:
        print("Bye!")
        check = False

def add_income():
    global card_id
    temp_id = str(card_id)
    print("Enter  income:")
    custom_input = input()
    cur.execute('UPDATE card SET balance = balance + ? WHERE id = ?', (custom_input, temp_id))
    conn.commit()
    print("Income was added!")
    in_banking_system_message()
    customer_choice_in_system()

def luhn_checksum(card_number):
    def digits_of(n):
        return [int(d) for d in str(n)]
    digits = digits_of(card_number)
    odd_digits = digits[-1::-2]
    even_digits = digits[-2::-2]
    checksum = 0
    checksum += sum(odd_digits)
    for d in even_digits:
        checksum += sum(digits_of(d*2))
    return checksum % 10

def is_luhn_valid(card_number):
    return luhn_checksum(card_number) == 0

def add_transfer():
    print("Transfer")
    print("Enter card number:")
    card_number_transfer = input()
    cur.execute('SELECT number FROM card;')
    row1 = cur.fetchall()
    if is_luhn_valid(card_number_transfer) != True:
        print("Probably you made a mistake in the card number. Please try again!")
        in_banking_system_message()
        customer_choice_in_system()

    elif any(card_number_transfer in row_value for row_value in row1) == False:
        print("Such a card does not exist.")
        in_banking_system_message()
        customer_choice_in_system()

    elif any(str(card_number_transfer)  in i for i in row1):
        cur.execute(f'SELECT id FROM card where number={card_number_transfer}')
        card_id_local = cur.fetchone()[0]
        if  card_id_local == card_id:
            print("You can't transfer money to the same account!")
            in_banking_system_message()
            customer_choice_in_system()
        else:
            print("Enter how much money you want to transfer:")
            money_for_transfer = input()
            cur.execute(f"SELECT balance FROM card WHERE id={card_id}")
            money_from_transfer_balance = cur.fetchone()[0]
            if money_from_transfer_balance < int(money_for_transfer):
                print("Not enough money!")
                in_banking_system_message()
                customer_choice_in_system()
            else:
                cur.execute('UPDATE card SET balance = balance + ? WHERE id = ?', (money_for_transfer, card_id_local))
                cur.execute('UPDATE card SET balance = balance - ? WHERE id = ?', (money_for_transfer, card_id))
                conn.commit()
                print("Success!")
                in_banking_system_message()
                customer_choice_in_system()

def close_account():
    cur.execute(f'DELETE FROM card WHERE id = {card_id}')
    conn.commit()
    print("The account has been closed!")

def customer_choice_in_system():
    global check
    check = True
    choice = int(input())
    if choice == 1:
        customer_balance()
        customer_choice_in_system()
    if choice == 2:
        add_income()
    if choice == 3:
        add_transfer()
    if choice == 4:
        close_account()
    elif choice == 5:
        # check = False
        customer_out_from_system()
    elif choice == 0:
        print("Bye!")
        check = False

def in_banking_system_message():
    print("1. Balance")
    print("2. Add income")
    print("3. Do transfer")
    print("4. Close account")
    print("5. Log out")
    print("0. Exit")

def out_from_banking_system_message():
    print("1. Create an account")
    print("2. Log into account")
    print("0. Exit")

while check:
    customer_out_from_system()


__author__ = 'luowen'

"Sqlite3 Demo"

import sqlite3

'''
connector = sqlite3.connect("sqlite3demo.db")  # select database if not exists create it
cursorObj = connector.cursor()  # get cursor object

cursorObj.executescript(
    """
    create table if not exists sqlite3demo_table (id integer, name text, age integer);
    insert into sqlite3demo_table values(1, 'luowen', 23), (2, 'maomao', 21), (3, 'gawin', 23);
    """
)
cursorObj.execute("select * from sqlite3demo_table")
'''

connector = sqlite3.connect('sqlite3demo.db')

for i in connector.execute("select * from {table}".format(table="sqlite3demo_table")):
    print(i)


from flask import abort, json

from data_access import DBClient

unspecified = object()

def read_parks():
    """Handler for /parks endpoint.

    Retrieves data for all parks from database.

    Returns
    -------
    list of dicts

    Raises
    ------
    werkzeug.exceptions.NotFound
        If no park records are found.

    """

    with DBClient() as DB:
        parks_data = DB.read_parks()
    response = []
    for _, data in parks_data.items():
        response.append(json.loads(data))
    if response:
        return response
    else:
        abort(404, f"No park records found.")

def read_park(park_id):
    """Handler for /parks/{park_id} endpoint.

    Retrieves data for the specified park from database.

    Parameters
    ----------
    park_id : str
        A park ID.

    Returns
    -------
    dict

    Raises
    ------
    werkzeug.exceptions.NotFound
        If no match is found for `park_id`.

    """

    with DBClient() as DB:
        park_data = DB.read_park(park_id=park_id)
    if park_data:
        return json.loads(park_data)
    else:
        abort(404, f"Park ID not found.")

def read_experiences(park_id, _type=unspecified):
    """Handler for /parks/{park_id}/experiences endpoint.

    Retrieves all experiences under the specified park from database.

    Parameters
    ----------
    park_id : str
        A park ID.
    _type : str, optional
        Experience type used for filtering.

    Returns
    -------
    list of dicts

    Raises
    ------
    werkzeug.exceptions.NotFound
        If no match is found for `park_id`.
        If `_type` is specified but no match is found.

    """

    with DBClient() as DB:
        experience_data = DB.read_experiences(park_id=park_id)
    if not experience_data:
        abort(404, f"Park ID not found.")
    response = []
    for _, data in experience_data.items():
        experience = json.loads(data)
        if _type is unspecified:
            response.append(experience)
        elif _type is not unspecified and _type.lower() == experience["type"].lower():
            response.append(experience)

    if response:
        return response
    elif _type is not unspecified:
        # park_id returned results but no match for _type.
        abort(404, f"Experience of type '{_type}' not found.")

def read_experience(park_id, experience_id):
    """Handler for /parks/{park_id}/experiences/{experience_id} endpoint

    Retrieves one experience from database.

    Parameters
    ----------
    park_id : str
        A park ID.
    experience_id : str
        An experience ID.

    Returns
    -------
    dict

    Raises
    ------
    werkzeug.exceptions.NotFound
        If no match is found for `park_id` and/or `experience_id`.

    """

    with DBClient() as DB:
        experience_data = DB.read_experience(
            park_id=park_id, experience_id=experience_id
        )
    if experience_data:
        return json.loads(experience_data)
    else:
        abort(404, f"Park and/or experience ID not found.")


#!/usr/bin/env python3

from pathlib import Path
import subprocess
import re
import argparse
import distutils

argparser = argparse.ArgumentParser()
argparser.add_argument("docsroot")
argparser.add_argument("outdir")
args = argparser.parse_args()

output = subprocess.run(["git", "tag", "-l"], capture_output=True, check=True, text=True)
tagRE = re.compile(r"^v(?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)(-rc(?P<rc>\d+))?$")
class Tag:
    orig: str
    major: int
    minor: int
    patch: int
    rc: int

    def __str__(self):
        return self.orig

    def __repr__(self):
        return self.orig

tags = []
for line in output.stdout.split("\n"):
    m = tagRE.match(line)
    if not m:
        continue
    m = m.groupdict()

    t = Tag()
    t.orig = line
    t.major = int(m["major"])
    t.minor = int(m["minor"])
    t.patch = int(m["patch"])
    t.rc = int(m["rc"] if m["rc"] is not None else 0)

    tags.append(t)

by_major_minor = {}

for tag in tags:
    key = (tag.major, tag.minor)
    l = by_major_minor.get(key, [])
    l.append(tag)
    by_major_minor[key] = l

latest_by_major_minor = []
for (mm, l) in by_major_minor.items():
    # sort ascending by patch-level (rc's weigh less)
    l.sort(key=lambda tag: (tag.patch, int(tag.rc == 0), tag.rc))
    latest_by_major_minor.append(l[-1])
latest_by_major_minor.sort(key=lambda tag: (tag.major, tag.minor))

cmdline = [
    "sphinx-multiversion",
    "-D", "smv_tag_whitelist=^({})$".format("|".join([re.escape(tag.orig) for tag in latest_by_major_minor])),
    "-D", "smv_branch_whitelist=^(master|stable)$",
    "-D", "smv_remote_whitelist=^.*$",
    "-D", "smv_latest_version=stable",
    "-D", r"smv_released_pattern=^refs/(tags|heads|remotes/[^/]+)/(?!master).*$", # treat everything except master as released, that way, the banner message makes sense
    # "--dump-metadata", # for debugging
    args.docsroot,
    args.outdir,
]
print(cmdline)
subprocess.run(cmdline, check=True)


import struct
from PyQt5.QtCore import QByteArray, QSize
from qgis.core import QgsPoint, QgsProject

from .conf import debug_mode
from .datamanager import MaterialManager
from .exportlayer import LayerExporter
from .geometry import PolygonGeometry, TriangleMesh, Triangles, dissolvePolygonsOnCanvas
from .propertyreader import DEMPropertyReader
from .rotatedrect import RotatedRect

class DEMLayerExporter(LayerExporter):

  def __init__(self, settings, imageManager, layer, pathRoot=None, urlRoot=None, progress=None):
    """if both pathRoot and urlRoot are None, object is built in all_in_dict mode."""
    LayerExporter.__init__(self, settings, imageManager, layer, pathRoot, urlRoot, progress)
    self.provider = settings.demProviderByLayerId(layer.layerId)
    self.prop = DEMPropertyReader(layer.layerId, layer.properties)

  def build(self, export_blocks=False):
    if self.provider is None:
      return None

    d = {
      "type": "layer",
      "id": self.layer.jsLayerId,
      "properties": self.layerProperties()
      }

    if debug_mode:
      d["PROPERTIES"] = self.properties

    # DEM block
    if export_blocks:
      d["data"] = [block.build() for block in self.blocks()]
    else:
      d["data"] = []

    return d

  def layerProperties(self):
    p = LayerExporter.layerProperties(self)
    p["type"] = "dem"
    p["shading"] = self.properties.get("checkBox_Shading", True)
    return p

  def blocks(self):
    mapTo3d = self.settings.mapTo3d()
    baseExtent = self.settings.baseExtent
    center = baseExtent.center()
    rotation = baseExtent.rotation()
    base_grid_size = self.prop.demSize(self.settings.mapSettings.outputSize())

    # clipping
    clip_geometry = None
    clip_option = self.properties.get("checkBox_Clip", False)
    if clip_option:
      clip_layerId = self.properties.get("comboBox_ClipLayer")
      clip_layer = QgsProject.instance().mapLayer(clip_layerId) if clip_layerId else None
      if clip_layer:
        clip_geometry = dissolvePolygonsOnCanvas(self.settings, clip_layer)

    # surroundings
    surroundings = self.properties.get("checkBox_Surroundings", False)    #TODO: [GSIElevProvider] if prop.layerId else False
    roughening = self.properties["spinBox_Roughening"] if surroundings else 1
    size = self.properties["spinBox_Size"] if surroundings else 1
    size2 = size * size

    blks = []
    for i in range(size2):
      sx = i % size - (size - 1) // 2
      sy = i // size - (size - 1) // 2
      dist2 = sx * sx + sy * sy
      blks.append([dist2, i, sx, sy])

    for dist2, blockIndex, sx, sy in sorted(blks):
      #self.progress(20 * i / size2 + 10)
      is_center = (sx == 0 and sy == 0)

      if is_center:
        extent = baseExtent
        grid_size = base_grid_size
      else:
        block_center = QgsPoint(center.x() + sx * baseExtent.width(), center.y() + sy * baseExtent.height())
        extent = RotatedRect(block_center, baseExtent.width(), baseExtent.height()).rotate(rotation, center)
        grid_size = QSize(max(2, (base_grid_size.width() - 1) // roughening + 1),
                          max(2, (base_grid_size.height() - 1) // roughening + 1))

      block = DEMBlockExporter(self.settings,
                               self.imageManager,
                               self.layer,
                               blockIndex,
                               self.provider,
                               grid_size,
                               extent,
                               mapTo3d.planeWidth,
                               mapTo3d.planeHeight,
                               offsetX=mapTo3d.planeWidth * sx,
                               offsetY=mapTo3d.planeHeight * sy,
                               edgeRougheness=roughening if is_center else 1,
                               clip_geometry=clip_geometry if is_center else None,
                               pathRoot=self.pathRoot,
                               urlRoot=self.urlRoot)
      yield block

class DEMBlockExporter:

  def __init__(self, settings, imageManager, layer, blockIndex, provider, grid_size, extent, planeWidth, planeHeight, offsetX=0, offsetY=0, edgeRougheness=1, clip_geometry=None, pathRoot=None, urlRoot=None):
    self.settings = settings
    self.imageManager = imageManager
    self.materialManager = MaterialManager(settings.materialType())

    self.layer = layer
    self.properties = layer.properties

    self.blockIndex = blockIndex
    self.provider = provider
    self.grid_size = grid_size
    self.extent = extent
    self.planeWidth = planeWidth
    self.planeHeight = planeHeight
    self.offsetX = offsetX
    self.offsetY = offsetY
    self.edgeRougheness = edgeRougheness
    self.clip_geometry = clip_geometry
    self.pathRoot = pathRoot
    self.urlRoot = urlRoot

  def build(self):
    if self.edgeRougheness == 1:
      ba = self.provider.read(self.grid_size.width(), self.grid_size.height(), self.extent)
    else:
      grid_values = list(self.provider.readValues(self.grid_size.width(), self.grid_size.height(), self.extent))
      self.processEdges(grid_values, self.edgeRougheness)
      ba = struct.pack("{0}f".format(self.grid_size.width() * self.grid_size.height()), *grid_values)

    # write grid values to an external binary file
    if self.pathRoot is not None:
      with open(self.pathRoot + "{0}.bin".format(self.blockIndex), "wb") as f:
        f.write(ba)

    # block data
    g = {"width": self.grid_size.width(),
         "height": self.grid_size.height()}

    if self.urlRoot is None:
      g["binary"] = QByteArray(ba)
      # g["array"] = grid_values
    else:
      g["url"] = self.urlRoot + "{0}.bin".format(self.blockIndex)

    # material
    material = self.material()

    mapTo3d = self.settings.mapTo3d()
    b = {"type": "block",
         "layer": self.layer.jsLayerId,
         "block": self.blockIndex,
         "grid": g,
         "width": self.planeWidth,
         "height": self.planeHeight,
         "translate": [self.offsetX, self.offsetY, mapTo3d.verticalShift * mapTo3d.multiplierZ],
         "zShift": mapTo3d.verticalShift,
         "zScale": mapTo3d.multiplierZ,
         "material": material}

    # clipped with polygon layer
    if self.clip_geometry:
      b["clip"] = self.clipped()

    # sides and bottom
    if self.properties.get("checkBox_Sides", False):
      b["sides"] = True

    # frame
    if self.properties.get("checkBox_Frame", False) and not self.properties.get("checkBox_Clip", False):
      b["frame"] = True

    return b

  def material(self):
    # properties
    texture_scale = self.properties.get("comboBox_TextureSize", 100) // 100
    opacity = self.properties.get("spinBox_Opacity", 100) / 100
    transp_background = self.properties.get("checkBox_TransparentBackground", False)

    # display type
    canvas_size = self.settings.mapSettings.outputSize()
    if self.properties.get("radioButton_MapCanvas", False):
      #if texture_scale == 1:
      #  mi = self.materialManager.getCanvasImageIndex(opacity, transp_background)
      #else:
      mi = self.materialManager.getMapImageIndex(canvas_size.width() * texture_scale, canvas_size.height() * texture_scale, self.extent, opacity, transp_background)

    elif self.properties.get("radioButton_LayerImage", False):
      layerids = self.properties.get("layerImageIds", [])
      mi = self.materialManager.getLayerImageIndex(layerids, canvas_size.width() * texture_scale, canvas_size.height() * texture_scale, self.extent, opacity, transp_background)

    elif self.properties.get("radioButton_ImageFile", False):
      filepath = self.properties.get("lineEdit_ImageFile", "")
      mi = self.materialManager.getImageFileIndex(filepath, opacity, transp_background, True)

    else:   #.get("radioButton_SolidColor", False)
      mi = self.materialManager.getMeshMaterialIndex(self.properties.get("lineEdit_Color", ""), opacity, True)

    #elif self.properties.get("radioButton_Wireframe", False):
    #  mi = self.materialManager.getWireframeIndex(self.properties["lineEdit_Color"], opacity)

    # build material
    filepath = None if self.pathRoot is None else "{0}{1}.png".format(self.pathRoot, self.blockIndex)
    url = None if self.urlRoot is None else "{0}{1}.png".format(self.urlRoot, self.blockIndex)
    return self.materialManager.build(mi, self.imageManager, filepath, url, self.settings.base64)

  def clipped(self):
    mapTo3d = self.settings.mapTo3d()
    z_func = lambda x, y: 0
    transform_func = lambda x, y, z: mapTo3d.transform(x, y, z)

    # create triangle mesh
    hw = 0.5 * mapTo3d.planeWidth
    hh = 0.5 * mapTo3d.planeHeight
    tmesh = TriangleMesh(-hw, -hh,
                         hw, hh,
                         self.grid_size.width() - 1, self.grid_size.height() - 1)

    # split polygons with triangle mesh
    geom = PolygonGeometry.fromQgsGeometry(self.clip_geometry, z_func, transform_func)
    geom.splitPolygon(tmesh, z_func)

    triangles = Triangles()
    split_polygons = []
    for polygon in geom.split_polygons:
      boundary = polygon[0]
      if len(polygon) == 1 and len(boundary) == 4:
        triangles.addTriangle(boundary[0], boundary[2], boundary[1])    # vertex order should be counter-clockwise
      else:
        bnds = [[[pt.x, pt.y] for pt in bnd] for bnd in polygon]
        split_polygons.append(bnds)

    return {"polygons": geom.asList2(),
            "triangles": {"v": [[pt.x, pt.y] for pt in triangles.vertices],
                          "f": triangles.faces},
            "split_polygons": split_polygons}

  def processEdges(self, grid_values, roughness):
    grid_width = self.grid_size.width()
    grid_height = self.grid_size.height()
    rg_grid_width = (grid_width - 1) // roughness + 1
    rg_grid_height = (grid_height - 1) // roughness + 1
    ii = range(roughness)[1:]

    for x0 in range(rg_grid_width - 1):
      # top edge
      ix0 = x0 * roughness
      z0 = grid_values[ix0]
      z1 = grid_values[ix0 + roughness]
      for i in ii:
        grid_values[ix0 + i] = (z1 - z0) * i / roughness + z0

      # bottom edge
      iy0 = grid_width * (grid_height - 1)
      z0 = grid_values[iy0 + ix0]
      z1 = grid_values[iy0 + ix0 + roughness]
      for i in ii:
        grid_values[iy0 + ix0 + i] = (z1 - z0) * i / roughness + z0

    rw = roughness * grid_width
    for y0 in range(rg_grid_height - 1):
      # left edge
      iy0 = y0 * grid_width
      z0 = grid_values[iy0]
      z1 = grid_values[iy0 + rw]
      for i in ii:
        grid_values[iy0 + i * grid_width] = (z1 - z0) * i / roughness + z0

      # right edge
      iy0 += grid_width - 1
      z0 = grid_values[iy0]
      z1 = grid_values[iy0 + rw]
      for i in ii:
        grid_values[iy0 + i * grid_width] = (z1 - z0) * i / roughness + z0

  def getValue(self, x, y):

    def _getValue(gx, gy):
      return self.grid_values[gx + self.grid_width * gy]

    if 0 <= x and x <= self.grid_width - 1 and 0 <= y and y <= self.grid_height - 1:
      ix, iy = int(x), int(y)
      sx, sy = x - ix, y - iy

      z11 = _getValue(ix, iy)
      z21 = 0 if x == self.grid_width - 1 else _getValue(ix + 1, iy)
      z12 = 0 if y == self.grid_height - 1 else _getValue(ix, iy + 1)
      z22 = 0 if x == self.grid_width - 1 or y == self.grid_height - 1 else _getValue(ix + 1, iy + 1)

      return (1 - sx) * ((1 - sy) * z11 + sy * z12) + sx * ((1 - sy) * z21 + sy * z22)    # bilinear interpolation

    return 0    # as safe null value

  def gridPointToPoint(self, x, y):
    x = self.rect.xMinimum() + self.rect.width() / (self.grid_width - 1) * x
    y = self.rect.yMaximum() - self.rect.height() / (self.grid_height - 1) * y
    return x, y

  def pointToGridPoint(self, x, y):
    x = (x - self.rect.xMinimum()) / self.rect.width() * (self.grid_width - 1)
    y = (self.rect.yMaximum() - y) / self.rect.height() * (self.grid_height - 1)
    return x, y

class DEMBlocks:

  def __init__(self):
    self.blocks = []

  def appendBlock(self, block):
    self.blocks.append(block)

  def appendBlocks(self, blocks):
    self.blocks += blocks

  def processEdges(self):
    """for now, this function is designed for simple resampling mode with surroundings"""
    count = len(self.blocks)
    if count < 9:
      return

    ci = (count - 1) // 2
    size = int(count ** 0.5)

    center = self.blocks[0]
    blocks = self.blocks[1:ci + 1] + [center] + self.blocks[ci + 1:]

    grid_width, grid_height, grid_values = center.grid_width, center.grid_height, center.grid_values
    for istop, neighbor in enumerate([blocks[ci - size], blocks[ci + size]]):
      if grid_width == neighbor.grid_width:
        continue

      y = grid_height - 1 if not istop else 0
      for x in range(grid_width):
        gx, gy = center.gridPointToPoint(x, y)
        gx, gy = neighbor.pointToGridPoint(gx, gy)
        grid_values[x + grid_width * y] = neighbor.getValue(gx, gy)

    for isright, neighbor in enumerate([blocks[ci - 1], blocks[ci + 1]]):
      if grid_height == neighbor.grid_height:
        continue

      x = grid_width - 1 if isright else 0
      for y in range(grid_height):
        gx, gy = center.gridPointToPoint(x, y)
        gx, gy = neighbor.pointToGridPoint(gx, gy)
        grid_values[x + grid_width * y] = neighbor.getValue(gx, gy)

  def stats(self):
    if len(self.blocks) == 0:
      return {"max": 0, "min": 0}

    block = self.blocks[0]
    stats = {"max": block.orig_stats["max"], "min": block.orig_stats["min"]}
    for block in self.blocks[1:]:
      stats["max"] = max(block.orig_stats["max"], stats["max"])
      stats["min"] = min(block.orig_stats["min"], stats["min"])
    return stats

def dummyProgress(progress=None, statusMsg=None):
  pass


"""Define class to import Quotes from a Docx-file."""

from typing import List
import docx

from .IngestorInterface import IngestorInterface
from QuoteEngine import QuoteModel

class DocxIngestor(IngestorInterface):
    """Define ingest of Quotes from Docx files."""

    def __repr__(self) -> str:
        """Define representation of object as string."""
        return "Docx Ingestor"

    allowed_extensions = ["docx"]

    @classmethod
    def parse(cls, path: str) -> List[QuoteModel]:
        """Parse Quotes from a Docx-file to a list of QuoteModel objects."""
        if not cls.can_ingest(path):
            raise Exception("cannot ingest exception")

        quotes = []
        doc = docx.Document(path)

        for para in doc.paragraphs:
            if para.text != "":
                parse = para.text.split(" - ")
                body = parse[0].strip('"')
                new_quote = QuoteModel(body, parse[1])
                quotes.append(new_quote)

        return quotes


#
# Save datagrams into a database
#
# June-2021, Pat Welch, pat@mousebrains.com

from argparse import ArgumentParser
from TPWUtils.Thread import Thread
import logging
import queue
import sqlite3
try:
    import psycopg3
    qPSQL = True
except:
    qPSQL = False

class Raw2DB(Thread):
    def __init__(self, args:ArgumentParser) -> None:
        Thread.__init__(self, "R2DB", args)
        self.__queue = queue.Queue()

    @staticmethod
    def addArgs(parser:ArgumentParser) -> None:
        grp = parser.add_argument_group(description="Raw2DB related options")
        grp.add_argument("--r2dbTable", type=str, default="raw", help="Database table name")
        gg = grp.add_mutually_exclusive_group()
        gg.add_argument("--r2dbSQLite3", type=str, help="SQLite3 database to write to")
        if qPSQL:
            gg.add_argument("--r2dbPostgreSQL", type=str, help="PostgreSQL database to write to")

    @staticmethod
    def qUse(args:ArgumentParser) -> bool:
        if args.r2dbSQLite3: return True
        return qPSQL and self.args.r2dbPostgreSQL

    def put(self, payload:tuple[float, str, int, bytes]) -> None:
        self.__queue.put(payload)

    def runIt(self): # Called on thread start
        args = self.args
        qSQLite3 = args.r2dbSQLite3
        dbname = args.r2dbSQLite3 if qSQLite3 else args.r2dbPostgreSQL
        logging.info("Starting %s %s", dbname, args.r2dbTable)
        with sqlite3.connect(dbname) \
                if qSQLite3 else \
                psycopg3.connect(dbname) \
                as db:
                    self.__mkTable(db)
                    self.__process(db)

    def __mkTable(self, db) -> None:
        tbl = self.args.r2dbTable
        sql = f"CREATE TABLE IF NOT EXISTS {tbl} (\n"
        sql+=  "  t DOUBLE PRECISION, -- UTC seconds\n"
        sql+=  "  ipAddr TEXT,\n"
        sql+=  "  port INTEGER,\n"
        sql+=  "  msg TEXT, -- Datagram\n"
        sql+=  "  PRIMARY KEY(t, msg)\n"
        sql+= f"); -- {tbl}"
        logging.debug("Creating table:\n%s", sql)
        db.cursor().execute("BEGIN;")
        db.cursor().execute(sql)
        db.cursor().execute("COMMIT;")

    def __process(self, db) -> None:
        tbl = self.args.r2dbTable
        q = self.__queue
        sql = f"INSERT INTO {tbl} VALUES(?,?,?,?) ON CONFLICT DO NOTHING;"
        logging.debug("%s", sql)
        cur = db.cursor()
        while True:
            payload = q.get()
            logging.debug("Received %s", payload)
            cur.execute("BEGIN;")
            cur.execute(sql, payload)
            cur.execute("COMMIT;")
            q.task_done()


from . import views
from django.contrib import admin
from django.urls import path

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', views.index, name = 'home'),
    path('register', views.register, name = 'register'),
    path('show', views.show, name = 'datashow'),
    path('login', views.login, name = 'login'),
    path('logout', views.logout, name = 'logout'),
    path('posts', views.posts, name = 'posts'),
    path('post/<str:pk>', views.post, name = 'post'),
]