import math

from musicutils import normalize_pitch, CHROMATIC_NOTES_SHARP, CHROMATIC_NOTES_FLAT

class Temperament:
    """
    In musical tuning, temperament is a tuning system that defines the
    notes (semitones) in an octave. Most modern Western musical
    instruments are tuned in the equal temperament system based on the
    1/12 root of 2 (12 semitones per octave). Many traditional
    temperaments are based on ratios.
    """

    # The intervals define which ratios are used to define the notes
    # within a given temperament.
    default_intervals = [
        "perfect 1",
        "minor 2",
        "major 2",
        "minor 3",
        "major 3",
        "perfect 4",
        "diminished 5",
        "perfect 5",
        "minor 6",
        "major 6",
        "minor 7",
        "major 7",
        "perfect 8",
    ]

    twelve_tone_equal_ratios = {
        "perfect 1": 1,
        "minor 2": math.pow(2, 1 / 12),
        "augmented 1": math.pow(2, 1 / 12),
        "major 2": math.pow(2, 2 / 12),
        "augmented 2": math.pow(2, 3 / 12),
        "minor 3": math.pow(2, 3 / 12),
        "major 3": math.pow(2, 4 / 12),
        "augmented 3": math.pow(2, 5 / 12),
        "diminished 4": math.pow(2, 4 / 12),
        "perfect 4": math.pow(2, 5 / 12),
        "augmented 4": math.pow(2, 6 / 12),
        "diminished 5": math.pow(2, 6 / 12),
        "perfect 5": math.pow(2, 7 / 12),
        "augmented 5": math.pow(2, 8 / 12),
        "minor 6": math.pow(2, 8 / 12),
        "major 6": math.pow(2, 9 / 12),
        "augmented 6": math.pow(2, 10 / 12),
        "minor 7": math.pow(2, 10 / 12),
        "major 7": math.pow(2, 11 / 12),
        "augmented 7": math.pow(2, 12 / 12),
        "diminished 8": math.pow(2, 11 / 12),
        "perfect 8": 2,
    }

    just_intonation_ratios = {
        "perfect 1": 1,
        "minor 2": 16 / 15,
        "augmented 1": 16 / 15,
        "major 2": 9 / 8,
        "augmented 2": 6 / 5,
        "minor 3": 6 / 5,
        "major 3": 5 / 4,
        "augmented 3": 4 / 3,
        "diminished 4": 5 / 4,
        "perfect 4": 4 / 3,
        "augmented 4": 7 / 5,
        "diminished 5": 7 / 5,
        "perfect 5": 3 / 2,
        "augmented 5": 8 / 5,
        "minor 6": 8 / 5,
        "major 6": 5 / 3,
        "augmented 6": 16 / 9,
        "minor 7": 16 / 9,
        "major 7": 15 / 8,
        "augmented 7": 2 / 1,
        "diminished 8": 15 / 8,
        "perfect 8": 2,
    }

    pythagorean_ratios = {
        "perfect 1": 1,
        "minor 2": 256 / 243,
        "augmented 1": 256 / 243,
        "major 2": 9 / 8,
        "augmented 2": 32 / 27,
        "minor 3": 32 / 27,
        "major 3": 81 / 64,
        "augmented 3": 4 / 3,
        "diminished 4": 81 / 64,
        "perfect 4": 4 / 3,
        "augmented 4": 729 / 512,
        "diminished 5": 729 / 512,
        "perfect 5": 3 / 2,
        "augmented 5": 128 / 81,
        "minor 6": 128 / 81,
        "major 6": 27 / 16,
        "augmented 6": 16 / 9,
        "minor 7": 16 / 9,
        "major 7": 243 / 128,
        "augmented 7": 2 / 1,
        "diminished 8": 243 / 128,
        "perfect 8": 2,
    }

    third_comma_meantone_ratios = {
        "perfect 1": 1,
        "minor 2": 1.075693,
        "augmented 1": 1.037156,
        "major 2": 1.115656,
        "augmented 2": 1.157109,
        "minor 3": 1.200103,
        "major 3": 1.244694,
        "augmented 3": 1.290943,
        "diminished 4": 1.290943,
        "perfect 4": 1.338902,
        "augmented 4": 1.38865,
        "diminished 5": 1.440247,
        "perfect 5": 1.493762,
        "augmented 5": 1.549255,
        "minor 6": 1.60682,
        "major 6": 1.666524,
        "augmented 6": 1.728445,
        "minor 7": 1.792668,
        "major 7": 1.859266,
        "augmented 7": 1.92835,
        "diminished 8": 1.92835,
        "perfect 8": 2,
    }

    third_comma_meantone_intervals = [
        "perfect 1",
        "augmented 1",
        "minor 2",
        "major 2",
        "augmented 2",
        "minor 3",
        "major 3",
        "diminished 4",
        "perfect 4",
        "augmented 4",
        "diminished 5",
        "perfect 5",
        "augmented 5",
        "minor 6",
        "major 6",
        "augmented 6",
        "minor 7",
        "major 7",
        "diminished 8",
        "perfect 8",
    ]

    quarter_comma_meantone_ratios = {
        "perfect 1": 1,
        "minor 2": 16 / 15,
        "augmented 1": 25 / 24,
        "major 2": 9 / 8,
        "augmented 2": 75 / 64,
        "minor 3": 6 / 5,
        "major 3": 5 / 4,
        "diminished 4": 32 / 25,
        "augmented 3": 125 / 96,
        "perfect 4": 4 / 3,
        "augmented 4": 25 / 18,
        "diminished 5": 36 / 25,
        "perfect 5": 3 / 2,
        "augmented 5": 25 / 16,
        "minor 6": 8 / 5,
        "major 6": 5 / 3,
        "augmented 6": 125 / 72,
        "minor 7": 9 / 5,
        "major 7": 15 / 8,
        "diminished 8": 48 / 25,
        "augmented 7": 125 / 64,
        "perfect 8": 2,
    }

    quarter_comma_meantone_intervals = [
        "perfect 1",
        "augmented 1",
        "minor 2",
        "major 2",
        "augmented 2",
        "minor 3",
        "major 3",
        "diminished 4",
        "augmented 3",
        "perfect 4",
        "augmented 4",
        "diminished 5",
        "perfect 5",
        "augmented 5",
        "minor 6",
        "major 6",
        "augmented 6",
        "minor 7",
        "major 7",
        "diminished 8",
        "augmented 7",
        "perfect 8",
    ]

    freqs = []  # Each temperament contains a list of notes in hertz.
    generic_note_names = []  # An array of names for each note in an octave
    C0 = 16.3516  # By default, we use C in Octave 0 as our base frequency.

    def __init__(self, name="equal"):
        """
        Initialize the class. A temperament will be generated but it can
        subsequently be overriden.

        Parameters
        ----------

        name : str
            The name of a temperament, e.g., "equal", "just intonation". etc.
        """
        self.name = name
        self.octave_length = 12  # in semitones
        self.base_frequency = self.C0
        self.number_of_octaves = 8
        self.ratios = None
        self.intervals = None
        self.generate(self.name)

    def tune(self, pitch_name, octave, frequency):
        """
        Calculate a base frequency based on a pitch and frequency.

        Parameters
        ----------
        pitch_name : str
            Pitch name, e.g. A#

        octave : int
            Octave

        frequency : float
            Frequency from which to calculate the new base frequency

        Results
        -------
        float
            New base frequency for C0
        """
        pitch_name = normalize_pitch(pitch_name)
        if pitch_name in CHROMATIC_NOTES_SHARP:
            i = CHROMATIC_NOTES_SHARP.index(pitch_name)
        elif pitch_name in CHROMATIC_NOTES_FLAT:
            i = CHROMATIC_NOTES_FLAT.index(pitch_name)
        else:
            print("pitch %s not found." % pitch_name)
            return

        if self.ratios is not None and self.intervals is not None:
            self.base_frequency = (frequency / 2 ** octave) / self.ratios[
                self.intervals[i]
            ]
            self.generate(self.name)
        return

    def set_base_frequency(self, base_frequency):
        """
        The base frequency is used as the starting point for generating
        the notes.

        Parameters
        ----------
        base_frequency : float
            The frequency (in Hertz) used to seed the calculation of the
            notes used in the temperament.
        """
        self.base_frequency = base_frequency

    def get_base_frequency(self):
        """
        Returns
        -------
        float
            The base frequency (in Hertz) used to seed the calculations
        """
        return self.base_frequency

    def set_number_of_octaves(self, number_of_octaves=8):
        """
        How many octaves are defined for the temperament?

        Parameters
        ----------
        number_of_octaves : int
            The number of octaves in the temperament. (8 octaves in
            equal temperament would span 96 notes).
        """
        self.number_of_octaves = min(1, int(abs(number_of_octaves)))

    def get_number_of_octaves(self):
        """
        Returns
        -------
        int
            The number of octaves in the temperament.
        """
        return self.number_of_octaves

    def get_name(self):
        """
        Returns
        -------
        str
            The name of the temperament
        """
        return self.name

    def get_freqs(self):
        """
        freqs is the list of all of the frequencies in the temperament.

        Returns
        -------
        list
            A list of frequencies in Hertz (float)
        """
        return self.freqs

    def get_nearest_freq_index(self, target):
        """
        Find the index of the frequency nearest to the target.

        Parameters
        ----------
        target : float
            The target frequency we are looking for.

        Returns
        -------
        int
            The index into the freqs array for the entry nearest to the target.
        """
        min = 10000
        min_index = 0
        for i, f in enumerate(self.freqs):
            if abs(f - target) < min:
                # If we are getting closer, store the index.
                min = abs(f - target)
                # An exact match: no need to keep looking.
                if min == 0:
                    return i
                min_index = i
            else:
                # We've passed the minimum, so return the previous index.
                return min_index
        return min_index

    def get_note_names(self):
        """
        Generic note names are assigned to define a chromatic scale.

        Returns
        -------
        list
            A list of note names (str)
        """
        return self.generic_note_names

    def get_note_name(self, semitone_index):
        """
        Return the generic note name associated with an index.

        Parameters
        __________
        semitone_index : int
            Index into generic note names that define an octave

        Returns
        -------
        str
           The corresponding note name
        """
        return self.generic_note_names[semitone_index]

    def get_modal_index(self, note_name):
        """
        Return the index associated with a generic note name.

        Parameters
        __________
        note_name : str
           The corresponding note name

        Returns
        -------
        int
            Index into generic note names that define an octave
        """
        return self.generic_note_names.index(note_name)

    def get_freq_by_index(self, pitch_index):
        """
        Return the frequency by an index into the frequency list.

        Parameters
        ----------
        pitch_index : int
            The index into the frequency list

        Returns
        -------
        float
            Returns the frequency (in Hertz) of a note by index
        """
        if len(self.freqs) == 0:
            return 0
        if pitch_index < 0:
            pitch_index = 0
        if pitch_index > len(self.freqs) - 1:
            pitch_index = len(self.freqs) - 1
        return self.freqs[int(pitch_index)]

    def get_modal_index_and_octave_from_freq_index(self, pitch_index):
        """
        Convert an index into the frequency list into a modal index
        and an octave.

        Parameters
        ----------
        pitch_index : int
            The index into the frequency list

        Returns
        -------
        int
            the modal index in the scale
        int
            the octave
        """
        return pitch_index % self.octave_length, int(pitch_index / self.octave_length)

    def get_freq_by_modal_index_and_octave(self, modal_index, octave):
        """
        Modal index is an index into the notes in a octave.

        Parameters
        ----------
        modal_index : int
            The index of the note within an octave

        octave : int
            Which octave to access

        Returns
        -------
        float
            The frequency that corresponds to the index and octave (in Hertz)
        """
        if len(self.freqs) == 0:
            return 0
        i = (int(octave) * self.octave_length) + modal_index
        if i < 0:
            return self.freqs[0]
        if i > len(self.freqs) - 1:
            return self.freqs[-1]
        return self.freqs[int(i)]

    def get_freq_by_generic_note_name_and_octave(self, note_name, octave):
        """
        Note name can be used to calculate an index the notes in an octave.

        Parameters
        ----------
        note_name : str
            The name of the note

        octave : int
            Which octave to access

        Returns
        -------
        float
            The frequency that corresponds to the index and octave (in Hertz)
        """
        if len(self.freqs) == 0:
            return 0
        if note_name not in self.generic_note_names:
            print("Note %s not found in generic note names." % (note_name))
            return 0
        ni = self.generic_note_names.index(note_name)
        i = (octave * self.octave_length) + ni
        if i < 0:
            return self.freqs[0]
        if i > len(self.freqs) - 1:
            return self.freqs[-1]
        return self.freqs[int(i)]

    def get_number_of_semitones_in_octave(self):
        """
        Returns
        -------
        int
            The number of notes defined per octave
        """
        return self.octave_length

    def get_number_of_notes_in_temperament(self):
        """
        Returns
        -------
        int
            The number of notes defined by the temperament
        """
        return len(self.freqs)

    def _generate_generic_note_names(self):
        """
        A generic name is defined for each note in the octave.
        The convention is n0, n1, etc. These notes can be used by
        the get_freq_by_generic_note_name_and_octave method to retrieve
        a frequency by note name and octave.
        """
        self.generic_note_names = []
        for i in range(self.octave_length):
            self.generic_note_names.append("n%d" % i)

    def generate(self, name):
        """
        Generate creates one of the predefined temperaments based on the
        rules for generating the frequencies and the selected intervals used
        to determine which frequencies to include in the temperament.
        A rule might be to use a series of ratios between steps, as in the
        Pythagorean temperament, or to use a fixed ratio, such as the
        twelth root of two when calculating equal temperament.

        The base frequency used when applying the rules is defined in
        self.base_frequency
        The number of times to apply the rules is determined by
        self.number_of_octaves
        The resultant frequencies are stored in self.freqs
        The resultant number of notes per octave is stored in
        self.octave_length

        Parameters
        ----------
        name : str
            The name of one of the predefined temperaments
        """
        self.name = name.lower()

        if self.name == "third comma meantone":
            intervals = self.third_comma_meantone_intervals[:]
            ratios = self.third_comma_meantone_ratios
        elif self.name == "quarter comma meantone":
            intervals = self.quarter_comma_meantone_intervals[:]
            ratios = self.quarter_comma_meantone_ratios
        else:
            intervals = self.default_intervals[:]
            if self.name == "equal":
                ratios = self.twelve_tone_equal_ratios
            elif self.name == "just intonation":
                ratios = self.just_intonation_ratios
            elif self.name == "pythagorean":
                ratios = self.pythagorean_ratios
            else:
                print("Unknown temperament %d; using equal temperament" % (name))
                ratios = self.twelve_tone_equal_ratios

        self.ratios = ratios
        self.intervals = intervals
        self.octave_length = len(intervals) - 1
        self.freqs = [self.base_frequency]

        for octave in range(self.number_of_octaves):
            c = self.freqs[-1]
            for i in range(self.octave_length):
                if i == 0:
                    continue
                self.freqs.append(c * ratios[intervals[i]])

        self._generate_generic_note_names()

    def generate_equal_temperament(self, number_of_steps):
        """
        Equal temperaments can be generated for different numbers of steps
        between the notes in an octave. The predefined equal temperament
        defines 12 steps per octave, which is perhaps the most common
        tuning system in modern Western music. But any number of steps can
        be used.

        Parameters
        ----------
        number_of_steps : int
            The number of equal steps into which to divide an octave.
        """
        nsteps = int(number_of_steps)
        if nsteps < 1:
            nsteps = 1

        self.name = "name_%d" % nsteps

        self.octave_length = nsteps
        self.freqs = [self.base_frequency]

        # nth root of 2
        root = 2 ** (1 / nsteps)
        for octave in range(self.number_of_octaves):
            for i in range(self.octave_length):
                if i == 0:
                    continue
                self.freqs.append(self.freqs[-1] * root)

        self._generate_generic_note_names()

    def generate_custom(self, intervals, ratios, name="custom"):
        """
        A custom temperament can be defined with arbitrary rules.

        Parameters
        ----------
        intervals : list
            An ordered list of interval names to define per octave

        ratios : dict
            A dictionary of ratios to apply when generating the note
            frequencies in an octave. The dictionary keys are defined
            in the intervals list. Each ratio (between 1 and 2) is applied
            to the base frequency of the octave. The final frequency
            should always be equal to 2.

        name : str
            The name associated with the custom temperament
        """
        self.name = name

        self.octave_length = len(intervals)
        self.freqs = [self.base_frequency]

        for octave in range(self.number_of_octaves):
            c = self.freqs[-1]
            for i in range(self.octave_length):
                if i == 0:
                    continue
                self.freqs.append(c * ratios[intervals[i]])

        self._generate_generic_note_names()

    def __str__(self):
        """
        Returns
        -------
        str
            The list of frequencies
        """
        freqs = []
        for f in self.freqs:
            freqs.append("%0.2f" % (f + 0.005))
        return "%s temperament:\n\n%s" % (self.name, "\n".join(freqs))


import boto3
from langchain.document_loaders import UnstructuredWordDocumentLoader
import logging
from botocore.exceptions import ClientError
import tempfile
from langchain.schema.document import Document
from typing import Tuple, List

def process_docx(s3_client: boto3.client, bucket_name: str, file_key: str) -> Tuple[List[Document], str]:
    """
    Download a .docx file from an S3 bucket, process it to extract text, and return the extracted text and temporary file path.

    Parameters:
    s3_client (boto3.client): The S3 client used to interact with Amazon S3.
    bucket_name (str): The name of the S3 bucket where the DOCX file is stored.
    file_key (str): The key of the DOCX file in the S3 bucket.

    Returns:
    tuple: A tuple containing the extracted text data and the temporary file path of the downloaded DOCX.
    """
    try:
        logging.info(f"Downloading DOCX from S3: {file_key}")

        # Download the DOCX from S3
        s3_object = s3_client.get_object(Bucket=bucket_name, Key=file_key)
        docx_file_bytes = s3_object['Body'].read()

        # Create a temporary file to store the DOCX
        with tempfile.NamedTemporaryFile(delete=False, suffix='.docx') as temp_docx_file:
            temp_docx_file.write(docx_file_bytes)
            temp_docx_path = temp_docx_file.name

        logging.info(f"Processing DOCX: {file_key}")

        # Process the DOCX
        loader = UnstructuredWordDocumentLoader(temp_docx_path)
        data = loader.load()

        logging.info(f"DOCX processed successfully: {file_key}")

        # Return both 'data' and 'temp_docx_path'
        return data, temp_docx_path

    except ClientError as e:
        logging.error(f"S3 client error: {e}")
        raise Exception(f"S3 client error: {e}")

    except Exception as e:
        # You can log or re-raise the exception as needed
        logging.error(f"Error processing the DOCX: {e}")
        raise Exception(f"Error processing the DOCX: {e}")


# build cov matrix
import numpy as np
import matplotlib.pylab as plt

data = np.loadtxt('3Ddata.txt')
x = data[:, 0:3]

# build graph

# distance matrix
def distance_matrix(data):
    '''
    tested
    '''
    distances = np.zeros([data.shape[0], data.shape[0]])
    for (i, pointi) in enumerate(data):
        for (j, pointj) in enumerate(data):
            distances[i, j] = np.linalg.norm(pointi - pointj)
    return distances

def set_K_neighboorg(distances, k=10):
    '''
    tested
    '''
    output = np.ones_like(distances)
    # set not connected to inf
    output *= np.inf
    np.fill_diagonal(output, 0)

    for i in range(distances.shape[0]):
        closest = distances[:, i].argsort()[0:k]
        # print i, closest
        output[i, closest] = distances[i, closest]
        # output[closest, i] = distances[closest, i]
    return output

# apply Floyd algotrithm

def shortest_path(distances):
    output = distances.copy()
    # for i in range(output.shape[0]):
    #     output = np.minimum(output, np.add.outer(output[:, i], output[i, :]))

    # for k in range(output.shape[0]):
    #     for j in range(output.shape[0]):
    #         for j in range(output.shape[0]):
    #             output[i,j] = min(output[i,j], output[i,k] + output[k,j])
    for k in xrange(output.shape[0]):
        output = minimum(output, output[newaxis, k, :] + output[:, k, newaxis])

    # for k in xrange(output.shape[0]):
    #     for i in xrange(output.shape[0]):
    #         output[i,:] = minimum(output[i,:], output[i,k] + output[k,:])

    return output

# Below all give the same mds tested for debugging

# ========= I TRIED DIFFERENT MDS CAUSE IT DID NOT SEEM TO WORK AT FIRST
# do MDS on this
def mds_run(d, dimensions=2):
    '''
    not fully tested yet
    '''
    # create G_tilde from dist

    (n, n) = d.shape
    # create G_tilde − 1/2 P*D*P
    E = (-0.5 * d**2)

    # Use mat to get column and row means to act as column and row means.
    Er = np.mat(np.mean(E, 1))
    Es = np.mat(np.mean(E, 0))

    F = array(E - transpose(Er) - Es + mean(E))

    [U, S, V] = np.linalg.svd(F)

    # create data given the gram matrix using preposition 2

    Y = U * np.sqrt(S)

    return (Y[:, 0:dimensions], S)

# do MDS on this
def mds_run_2(data, dimensions=2):

    # create G_tilde from data

    data -= np.mean(data, axis=0)
    F = np.dot(data, data.T)
    [U, S, V] = np.linalg.svd(F)

    # create data given the gram matrix using preposition 2

    Y = U * np.sqrt(S)

    return (Y[:, 0:dimensions], S)

def cmdscale(D):

    # Number of points
    n = len(D)

    # Centering matrix
    H = np.eye(n) - np.ones((n, n)) / n

    # YY^T
    B = -H.dot(D**2).dot(H) / 2

    # Diagonalize
    evals, evecs = np.linalg.eigh(B)

    # Sort by eigenvalue in descending order
    idx = np.argsort(evals)[::-1]
    evals = evals[idx]
    evecs = evecs[:, idx]

    # Compute the coordinates using positive-eigenvalued components only
    w, = np.where(evals > 0)
    L = np.diag(np.sqrt(evals[w]))
    V = evecs[:, w]
    Y = V.dot(L)

    return Y, evals

def isomap(data, dim=2, k=10):
    # compute l2 distances among points
    dist = distance_matrix(data)
    # convert into a graph with k-neighbourhs
    #  deltaij = -inf if not connected
    dist_k = set_K_neighboorg(dist, k=k)
    # compute shortest path on the graph
    geodesic_distance = shortest_path(dist_k)
    # apply mds to it
    y, s = mds_run(geodesic_distance, dimensions=dim)
    return y


import numpy as np
import nibabel as nib
import matplotlib.pyplot as plt
import scipy.io as scio
import os
import cv2

def temporal_pattern_fig_generation(temporal_path, save_fig_path):
    basises = os.listdir(temporal_path)

    for basis in basises:
        sub_id = basis.split('_')[0]
        part_id = basis.split('_')[1]
        comp_id = basis.split('_')[2]
        print('sub id {}, part_id {}, comp_id {}'.format(sub_id, part_id, comp_id))
        #signal = np.loadtxt(temporal_path+'/'+basis)
        gyri_signal = np.loadtxt(temporal_path+'/'+sub_id + '_gyri_' + comp_id +'_temporal_basis.txt')  #166438_gyri_2_temporal_basis.txt
        sulcii_signal = np.loadtxt( temporal_path+'/'+sub_id + '_sulci_' + comp_id +'_temporal_basis.txt' )

        fig, (ax1, ax2) = plt.subplots(2, 1)
        ax1.plot(gyri_signal, color = 'r',  linewidth = 1, label = sub_id + '_gyri_' + comp_id  )   #'#ffb07c'
        ax1.axis('off')
        ax1.legend( fontsize = 20)
        ax2.plot(sulcii_signal, color = 'b',  linewidth = 1, label = sub_id + '_sulci_' + comp_id  )   #'#ffb07c'
        ax2.axis('off')
        ax2.legend( fontsize = 20)
        pcc = np.corrcoef(gyri_signal, sulcii_signal)[0,1]
        pcc = int(pcc * 1000) / 1000
        plt.title(pcc, fontsize = 20)
        #plt.show()
        fig.savefig(save_fig_path+'/'+str(sub_id)+'_'+str(comp_id)+'_gyri_sulci.png') #166438_1_gyri_sulci.png
        plt.close()

temporal_path = '/media/shawey/SSD8T/GyraSulci_Motor/Twin_Transformers/temporal_basis'
temp_save_path = '/media/shawey/SSD8T/GyraSulci_Motor/Twin_Transformers/VisualizationInBroswer/temporal_pngs'
temporal_pattern_fig_generation(temporal_path, temp_save_path )
gyri_sulci_figs = '/media/shawey/SSD8T/GyraSulci_Motor/Twin_Transformers/VisualizationInBroswer/static'

def gyri_sulci_temp_figs_generateion(temp_save_path, figs_path):
    figs = os.listdir(temp_save_path)

    for fig in figs:
        sub_id = fig.split('_')[0]   #101107_gyri30.png
        if( sub_id == '.DS'):
            continue
        part_id = fig.split('_')[1]
        comp_id = fig.split('_')[2].split('.')[0]
        print('sub id {}, part_id {}, comp_id {}'.format(sub_id, part_id, comp_id))
        gyri_img = sub_id + '_gyri_' + comp_id +'.png'  #211215_sulci_90.png
        sulci_img = sub_id + '_sulci_' + comp_id +'.png'
        img1 = cv2.imread( temp_save_path +'/'+ gyri_img )
        img2 = cv2.imread( temp_save_path +'/'+  sulci_img )
        #dim = ( img1.shape[1], int(img1.shape[0] /2))
        #img2 = cv2.resize(img2, dim) #interpolation= cv2.INTER_AREA
        gyri_sulci_fig = np.vstack((img1, img2))
        cv2.imwrite(figs_path +'/'+sub_id+'_'+comp_id+'_gyri_sulci.png', gyri_sulci_fig)

#gyri_sulci_temp_figs_generateion(temp_save_path, gyri_sulci_figs)


from lingpy import *
import networkx as nx
import igraph
from sys import argv
from lingpy.thirdparty import linkcomm

# preprocessing, load data, and modify the rimes
wl = Wordlist('O_shijing.tsv', col='shijing', row='stanza')

# define colors for vowels
colors = {
    'ə' : ['black', 'red'],
    'a' : ['white', 'green'],
    'u' : ['white', 'blue'],
    'e' : ['white', 'gray'],
    'i' : ['black', 'yellow'],
    'o' : ['black', 'cyan']
    }

D = {}
for k in wl:

    yunbu = wl[k,'ocbsyun']
    yunbu = yunbu.replace('A','a')
    yunbu = yunbu.replace('\u0325', '')
    yunbu = ''.join([x for x in yunbu if x not in '[]()ʔ'])

    yunbu2 = wl[k,'yun']
    if 'ʔ' in wl[k,'yun']:
        shang = yunbu + 'ʔ'
    else:
        shang = yunbu

    if '[' in yunbu2 or '(' in yunbu2:
        yunbu2 = '?'
    else:
        yunbu2 = '!'

    D[k] = (yunbu,yunbu2, shang)

wl.add_entries('rime', D, lambda x: x[0])
wl.add_entries('certainty', D, lambda x: x[1])
wl.add_entries('shangsheng', D, lambda x: x[2])

# make the graph
_G = nx.Graph()
visited = []
excluded = 0

etd = wl.get_etymdict(ref='rhyme')

for row in wl.rows:
    print('[i] Analyzing stanza {0}...'.format(row))
    # get all stanzas
    idxs = wl.get_list(row=row, flat=True)

    # get the rime data
    rimes = [wl[idx, 'rime'] for idx in idxs]
    # get the words judged to be riming
    cogids = [wl[idx,'rhyme'] for idx in idxs]
    # get the readings
    readings = [wl[idx, 'ocbs'] for idx in idxs]
    # get the character
    chars = [wl[idx, 'character'] for idx in idxs]
    # get the sections
    sections = [wl[idx, 'raw_section'] for idx in idxs]
    # mch
    mchs = [wl[idx, 'mchascii'] for idx in idxs]
    shangsheng = [wl[idx,'shangsheng'] for idx in idxs]

    # certainty
    certainties = [wl[idx, 'certainty'] for idx in idxs]

    # visited_tmp to trace multi-chars in one set
    visited_tmp = []

    # now start to populate the graph
    for i,(idxA, rimeA,cogidA,readingA,charA,secA, certA, sA) in enumerate(
            zip(idxs, rimes, cogids, readings, chars, sections, certainties,
                shangsheng)):
        for j,(idxB, rimeB,cogidB,readingB,charB, secB, certB, sB) in enumerate(
                zip(idxs, rimes, cogids, readings, chars, sections,
                    certainties, shangsheng)):
            if i < j:
                if charA not in _G:
                    _G.add_node(
                            charA,
                            rime = rimeA,
                            cogid = cogidA,
                            reading = readingA,
                            color = colors[rimeA[0]][1] if rimeA else '',
                            labelcolor = colors[rimeA[0]][0] if rimeA else '',
                            occurrence = len([k for k in wl if
                                wl[k,'character'] == charA]),
                            mch = mchs[i],
                            certainty = certA,
                            shangsheng=sA,
                            stanza = ','.join([wl[k,'stanza'] for k in wl if
                                wl[k,'character'] == charA]),
                            )

                if charB not in _G:
                    _G.add_node(
                            charB,
                            rime = rimeB,
                            cogid = cogidB,
                            reading = readingB,
                            color = colors[rimeB[0]][1] if rimeB else '',
                            labelcolor = colors[rimeB[0]][0] if rimeB else '',
                            occurrence = len([k for k in wl if
                                wl[k,'character'] == charB]),
                            stanza = ','.join([wl[k,'stanza'] for k in wl if
                                wl[k,'character'] == charB]),
                            mch = mchs[j],
                            certainty = certB,
                            shangsheng=sB
                            )

                # check for section identity
                if (secA, secB) not in visited and charA != charB and cogidA == \
                    cogidB and cogidA and (charA,charB) not in visited_tmp and \
                    (charB,charA) not in visited_tmp:

                    visited += [(secA, secB)]
                    visited_tmp += [(charA, charB), (charB, charA)]
                    try:
                        _G.edge[charA][charB]['occurrence'] += 1
                        _G.edge[charA][charB]['stanza'] += [row]
                        _G.edge[charA][charB]['woccurrence'] += 1 / len(etd[cogidA][0])
                    except KeyError:
                        _G.add_edge(charA, charB, occurrence=1,
                                stanza=[row],
                                woccurrence = 1 / len(etd[cogidA][0])
                                )
                else:
                    excluded += 1

# check for external and internal edges
N = nx.Graph()
for nA, nB, data in _G.edges(data=True):
    rA = _G.node[nA]['rime']
    rB = _G.node[nB]['rime']

    if rA and rB:

        if rA not in N:
            N.add_node(rA, occurrence=1, color = _G.node[nA]['color'],
                    lcolor=_G.node[nA]['labelcolor'])
        else:
            N.node[rA]['occurrence'] += 1
        if rB not in N:
            N.add_node(rB, occurrence=1, color =_G.node[nB]['color'],
                    lcolor=_G.node[nB]['labelcolor'])
        else:
            N.node[rB]['occurrence'] += 1

        # add edges, if rA not equal to rB
        if rA != rB:
            try:
                N.edge[rA][rB]['occurrence'] += 1
            except KeyError:
                N.add_edge(rA, rB, occurrence = 1)

for nA,nB,data in _G.edges(data=True):

    data['stanza'] = ','.join(data['stanza'])

# convert stuff to igraph
G = igraph.Graph()
for node,data in _G.nodes(data=True):
    G.add_vertex(name=node, **data)
for nodeA, nodeB, data in _G.edges(data=True):
    G.add_edge(nodeA, nodeB, **data)

# do the same for the N graph
_N = igraph.Graph()
for node,data in N.nodes(data=True):
    _N.add_vertex(name=node, **data)
for nodeA, nodeB, data in N.edges(data=True):
    if data['occurrence'] >= 10:
        _N.add_edge(nodeA, nodeB, **data)

nx.write_yaml(N, 'R_rime_graph.yaml')

if 'communities' in argv:
    C = _N.community_infomap(
            edge_weights = 'occurrence',
            vertex_weights = 'occurrence'
            )
    for community,name in zip(C.membership, _N.vs['name']):
        N.node[name]['infomap'] = community

    print('[i] Calculated communities for rhyme groups')

    # check statistics, for example, get

    C = G.community_infomap(
            edge_weights = 'woccurrence',
            vertex_weights = 'occurrence'
            )
    for community,name in zip(C.membership, G.vs['name']):
        _G.node[name]['infomap'] = community

    print('[i] Calculated communities for rhyme words.')

from html import parser
nx.write_gml(N, 'R_rime_transitions.gml')
with open('R_rime_transitions.gml') as f:
    _t = f.read()
with open('R_rime_transitions.gml','w') as f:
    f.write(parser.unescape(_t))

nx.write_gml(_G, 'R_infomap.gml')
with open('R_infomap.gml') as f:
    _t = f.read()
with open('R_infomap.gml','w') as f:
    f.write(parser.unescape(_t))
nx.write_yaml(_G, 'R_infomap.yaml')


# --------------------------------------------------------------
# File:     /my_server_app.py
# Project:  Flask-Demo
# Author:   Adrian Gould <Adrian.Gould@nmtafe.wa.edu.au>
# Created:  14/04/2021
# Purpose:  ...
#
# Renamed app.py to my_server_app.py and removed client based
# code, except for some basic details
# --------------------------------------------------------------
from datetime import date

from flask import Flask, jsonify, render_template
from flask_cors import CORS, cross_origin
from sqlalchemy import create_engine, desc, inspect
from sqlalchemy.orm import sessionmaker

from csv import DictReader
from RainDB import Base, db_data_file, db_filename, Rainfall

MyServerApp = Flask(__name__)
MyServerCors = CORS(MyServerApp,
                    resources={r"/api/*": {"origins": "*"}})
MyServerApp.config['CORS_HEADERS'] = 'Content-Type'

engine = create_engine(f"sqlite:///{db_filename}")
Base.metadata.drop_all(engine)
Base.metadata.create_all(engine)

def object_as_dict(obj):
    """Convert an object to a dictionary

    :param obj:
    :return: dictionary
    """
    return {c.key: getattr(obj, c.key)
            for c in inspect(obj).mapper.column_attrs}

def records_to_list(records):
    """Convert an SQLAlchemy set of records into a list

    :param records:
    :return: list
    """
    result_list = []
    for row in records:
        result_list.append(object_as_dict(row))
    return result_list

# API requests
@MyServerApp.route("/api/rain/<int:days>")
@cross_origin()
def api_rain(days):
    return api_rain_days_from(days, 1)

@MyServerApp.route("/api/rain/<int:days>/<int:start>")
@cross_origin()
def api_rain_days_from(days, start):
    if start > 0:
        start -= 1
    session = sessionmaker(bind=engine)()
    # organise the data in reverse order
    reverse_data_query = session.query(Rainfall) \
        .order_by(desc(Rainfall.date_recorded))
    # now retrieve the number of records required
    result_proxy = reverse_data_query.limit(days).offset(start).all()
    rain_records = records_to_list(result_proxy)
    rain_json = jsonify(rain_records)
    return rain_json

# Normal page requests
@MyServerApp.route("/")  # http://localhost:5000/
@cross_origin()
def hello_world():
    return render_template('index.html',
                           page="home",
                           system_name="Server")

@MyServerApp.route("/about")
def about_app():
    return render_template('server-about.html',
                           page="about",
                           system_name="Server", )

@MyServerApp.route("/seed-rainfall")
@cross_origin()
def seed_rain():
    session = sessionmaker(bind=engine)()
    count = 0
    with open(db_data_file, 'r') as read_handle:
        dictionary_reader = DictReader(read_handle)
        list_of_rows = list(dictionary_reader)
    for data in list_of_rows:
        # Product code,Bureau of Meteorology station number,Year,Month,Day,
        # Rainfall amount (millimetres),Period over which rainfall was
        # measured (days),Quality
        if data["Quality"] != "" \
                and data["Rainfall amount (millimetres)"] != "":
            day = data["Day"]
            month = data["Month"]
            year = data["Year"]
            rain_record = Rainfall()
            rain_record.location = data[
                "Bureau of Meteorology station number"]
            rain_record.rainfall = data["Rainfall amount (millimetres)"]
            rain_record.date_recorded = date(int(year), int(month),
                                             int(day))
            session.add(rain_record)
            session.commit()
            count += 1
    return render_template('server-seed.html',
                           page="seed",
                           records=count,
                           system_name="Server", )

# captures all incorrect page requests
@MyServerApp.route("/<anything>")
def error_404(anything=""):
    return render_template('404.html',
                           page="404",
                           system_name="Server",)

if __name__ == "__main__":  # running our app.py
    # as this is a demo, we prime the database with values then start the
    # application
    MyServerApp.run(debug=True, host="127.0.0.1", port="5050")

#
# To Run the application in DEVELOPMENT mode,
# Open a terminal, and make sure you are in the project folder
# then... execute these two commands:
# set FLASK_ENV=development
# python3 -m flask run --port=5050
#


import math
from itertools import permutations

def check_prime(number: int) -> bool:
    if number == 2:
        return True
    if number == 1 or number % 2 == 0:
        return False
    for i in range(3, math.floor(math.sqrt(number))+1, 2):
        if number % i == 0:
            return False
    return True

perms = [1]

for i in range(2, 10):
    perms.extend([int(''.join([str(y) for y in x]))
                  for x in permutations(range(1, i+1))])

for perm in sorted(perms, reverse=True):
    if check_prime(perm):
        print(perm)
        break


import xml.etree.ElementTree
import sys
import collections
import os
import fnmatch
import pickle

# adapting http://stackoverflow.com/questions/1912434/how-do-i-parse-xml-in-python (ElementTree)

# Constants
kWindowSize = 4

def main():
    '''
    Invocation: python process_xml.py <dirname>
    '''
    #if True: return # comment this out for normal behavior
    dirname = sys.argv[-1]
    full_string = get_dir_string(dirname)
    with open('training_data.p', 'wb') as f:
        pickle.dump(full_string, f)

def parse_directory(dirname):
    '''
    dirname is a path to a directory
    consolidate all the file data
    into one list
    '''
    all_pairs = []
    for dirpath, dirs, files in os.walk(dirname):
        for filename in fnmatch.filter(files, '*.xml'):
            all_pairs += parse_file(os.path.join(dirpath, filename))
    return all_pairs

def parse_file(filename):
    '''
    Return skip prcoessing from given
    xml file
    '''
    root = xml.etree.ElementTree.parse(filename).getroot()
    return create_skipgram(get_word_sequence(root), kWindowSize)

def get_file_string(filename):
    '''
    Convert xml file to a single string,
    gluing together contractions (w/ apostrophes)
    '''
    root = xml.etree.ElementTree.parse(filename).getroot()
    return ' '.join(get_word_sequence(root))

def get_file_sequence(filename):
    '''
    Convert xml file to a single string,
    gluing together contractions (w/ apostrophes)
    '''
    root = xml.etree.ElementTree.parse(filename).getroot()
    return get_word_sequence(root)

def get_dir_string(dirname):
    '''
    Recursively iterate through directories and compile
    all the file strings into one string
    '''
    full_string = ''
    for dirpath, dirs, files in os.walk(dirname):
        for filename in fnmatch.filter(files, '*.xml'):
            full_string += get_file_string(os.path.join(dirpath, filename)) + ' '
    return full_string

def get_dir_sequences(dirname):
    all_sequences = []
    for dirpath, dirs, files in os.walk(dirname):
        for filename in fnmatch.filter(files, '*.xml'):
            all_sequences.append(get_file_sequence(os.path.join(dirpath, filename)))
    return all_sequences

def get_dir_list(dirname, get_file_string):
    '''
    Recursively iterate through directories and compile
    all the file strings into one string
    '''
    full_string = []
    for dirpath, dirs, files in os.walk(dirname):
        for filename in fnmatch.filter(files, '*.xml'):
            full_string += get_file_string(os.path.join(dirpath, filename))
    return full_string

def get_sense_name(element):
    '''
    tag a token with its sense
    '''
    suffix = element.get('sense')
    if suffix is None:
        suffix = ''
    return element.get('text') + suffix

def is_apostrophe_chunk(token):
    return len(token) > 0 and token[0] == '\''

def is_number(s):
    '''
    taken from Python Central
    http://pythoncentral.io/how-to-check-if-a-string-is-a-number-in-python-including-unicode/
    '''
    try:
        float(s)
        return True
    except ValueError:
        pass

    try:
        import unicodedata
        unicodedata.numeric(s)
        return True
    except (TypeError, ValueError):
        pass

    return False

def is_valid(w):
    return (len(w)>1 or w == 'I' or w == 'a' or w == 'i' or w == 'A') and w != "--"

def get_word_sequence(root):
    '''
    Iterate through xml root
    and compile everything into
    a list of strings. Handle apostrophes
    by gluing together to form
    contractions.
    '''
    seq = [get_sense_name(word) for word in root]
    fixed_seq = []
    for i in xrange(len(seq) - 1):
        if is_apostrophe_chunk(seq[i]):
            continue
        to_append = seq[i]
        if is_apostrophe_chunk(seq[i+1]) and 'sense' not in seq[i]:
            to_append += seq[i+1]
        fixed_seq.append(to_append)
    if not is_apostrophe_chunk(seq[-1]) and len(seq[-1]) > 0:
        fixed_seq.append(seq[-1])

    fixed_seq = [word.lower() for word in fixed_seq if is_valid(word)]
    for i, s in enumerate(fixed_seq):
        if is_number(s):
            fixed_seq[i] = 'NUM'

    return fixed_seq

def create_skipgram(word_sequence, window_size):
    '''
    Converts sequence of words to the following form:
    [(<center-word>, <context-word>), ...]
    consider splitting on sentences
    '''
    word_sequence = [word for word in word_sequence if word is not '/']
    length = len(word_sequence)
    center_context_pairs = []
    for i in range(length):
        start = max(0, i - window_size)
        end = min(length, i + window_size + 1)
        context = word_sequence[start:i] + word_sequence[i+1:end]
        for ctxt in context:
            center_context_pairs.append((word_sequence[i], ctxt))
    return center_context_pairs

if __name__ == '__main__':
    main()


import os
import spacy
from spacy import displacy
from typing import Optional, Union, List, Dict

class EntityRecognizer:
    """
    A class that performs entity recognition using a specified model.

    Args:
        model_path (Union[str, os.PathLike]): The path to the entity recognition model.
        doc (Optional[str]): The optional document associated with the entity recognition.

    Attributes:
        model_path (Union[str, os.PathLike]): The path to the entity recognition model.
        model (spacy.language.Language): The loaded entity recognition model.
        COLORS (Dict[str, str]): A dictionary mapping entity types to colors for visualization.
        doc (Optional[spacy.tokens.doc.Doc]): The processed document associated with the entity recognition.

    Methods:
        predict(text: str) -> List[Dict[str, Union[str, int, Dict[str, Union[str, int]]]]]:
            Performs entity recognition on the given text and returns the detected entities.

        display(text: str) -> None:
            Displays the entity recognition visualization for the given text.
    """

    def __init__(self, model_path: Union[str, os.PathLike], doc: Optional[str] = None):
        """
        Initialize the EntityRecognizer object.

        Args:
            model_path (Union[str, os.PathLike]): The path to the entity recognition model.
            doc (Optional[str]): The optional document associated with the entity recognition.
        """
        self.model_path = model_path
        self.model = spacy.load(self.model_path)
        self.COLORS = {
            "TYPE": "#FFCCCC",
            "COLOR": "#CCFFCC",
            "STYLE": "#CCCCFF",
            "APPEARANCE": "#FFEECC",
            "ADDITIONAL_MATERIAL": "#DDCCFF",
            "FEATURE": "#FFFFCC"
        }
        self.doc = None

    def predict(self, text: str) -> List[Dict[str, Union[str, int, Dict[str, Union[str, int]]]]]:
        """
        Performs entity recognition on the given text and returns the detected entities.

        Args:
            text (str): The input text to perform entity recognition on.

        Returns:
            List[Dict[str, Union[str, int, Dict[str, Union[str, int]]]]]: A list of detected entities, each represented as a dictionary.

        Raises:
            None.
        """
        if self.doc is None or self.doc.text != text:
            self.doc = self.model(text)
        return self.doc.to_json()['ents']

    def display(self, text: str) -> None:
        """
        Displays the entity recognition visualization for the given text.

        Args:
            text (str): The input text to visualize.

        Returns:
            None.

        Raises:
            None.
        """
        if self.doc is None or self.doc.text != text:
            self.doc = self.model(text)
        return displacy.render(self.doc, style="ent", jupyter=True, options={'colors': self.COLORS})


"""empty message

Revision ID: c6b96b2c1007
Revises: 3c62dfd278cf
Create Date: 2023-05-16 16:27:57.244815

"""
from alembic import op
import sqlalchemy as sa

# revision identifiers, used by Alembic.
revision = 'c6b96b2c1007'
down_revision = '3c62dfd278cf'
branch_labels = None
depends_on = None

def upgrade():
    # ### commands auto generated by Alembic - please adjust! ###
    op.create_table('t_menus',
    sa.Column('id', sa.Integer(), autoincrement=True, nullable=False),
    sa.Column('name', sa.String(length=32), nullable=False),
    sa.Column('level', sa.Integer(), nullable=True),
    sa.Column('path', sa.String(length=32), nullable=True),
    sa.Column('pid', sa.Integer(), nullable=True),
    sa.ForeignKeyConstraint(['pid'], ['t_menus.id'], ),
    sa.PrimaryKeyConstraint('id'),
    sa.UniqueConstraint('name')
    )
    # ### end Alembic commands ###

def downgrade():
    # ### commands auto generated by Alembic - please adjust! ###
    op.drop_table('t_menus')
    # ### end Alembic commands ###


from utomarket.skip_util import *

from utomarket.util import *
from selenium.webdriver.common.keys import Keys
from .personal_center_util import select_mode

def ad_detail_o(browser, name):  # 进入广告详情
    ad_div = browser.find_element_by_xpath("//a[contains(text(),'%s')]/../../.." % name)
    click_btn = ad_div.find_elements_by_tag_name('td')[-1]
    click_btn.click()
    explicit_wait(browser, 'VOEL', ["ant-card-meta-avatar", 'class'])
    url = get_current_url(browser)
    url_true = 'https://dev.utomarket.com:9094/#/trade/detail'
    name_text = browser.find_element_by_class_name('ant-card-meta-title').text
    assert url_true in url
    assert name_text == name

def create_buy_o(browser, type='法币计价', trading_volume='1,234.24',
                     trading_count='0.2345871', trading_note='这是交易备注'):
    type_button = browser.find_element_by_xpath("//div[@title='法币计价']/..")
    trading_note_input = browser.find_element_by_id('trading_notes')  #创建购买订单
    submit_button = browser.find_elements_by_tag_name('button')[1]
    type_button.click()
    select_mode(browser, type)
    if type == '法币计价':
        trading_input = browser.find_element_by_id('trading_volume')
        trading_input.send_keys(trading_volume)
    else:
        trading_input = browser.find_element_by_id('trading_count')
        trading_input.send_keys(trading_count)
    trading_note_input.send_keys(trading_note)
    submit_button.click()
    popup_o(browser, '操作成功')
    explicit_wait(browser, 'VOEL', ["ant-list-item-content", 'class'])
    url = 'https://dev.utomarket.com:9094/#/trade/step'
    url_true = get_current_url(browser)
    if type == '法币计价':
        verify_div = is_exist_element(browser, 'xpath', "//span[text()='%s CNY ']" % trading_volume)
    else:
        verify_div = is_exist_element(browser, 'xpath', "//span[text()=' %s BTC']" % trading_count)
    assert url in url_true
    assert verify_div

def send_massege_o(browser, massege_content):  # 发送消息
    time.sleep(2)
    explicit_wait(browser, 'VOEL', ["ant-list-item-meta-content", 'class'])
    massege_input = browser.find_element_by_id('message')
    send_button = browser.find_element_by_xpath("//span[text()='发送']/..")
    smart_input(massege_input, massege_content)
    send_button.click()
    explicit_wait(browser, 'VOEL', ["//div[contains(text(),'%s')]" % massege_content, 'xpath'])
    judge_massege_result_o(browser, massege_content)

def judge_massege_result_o(browser, massege_content):  # 判断消息是否在聊天框显示
    result = is_exist_element(browser, 'xpath', "//div[contains(text(),'%s')]" % massege_content)
    assert result

def cancel_order_buyer_o(browser):  # 买家取消订单
    cancel_button = browser.find_element_by_xpath("//span[text()='取消订单']/..")
    cancel_button.click()
    explicit_wait(browser, 'VOEL', ["//textarea[@placeholder='请填写原因']", 'xpath'])
    reason_input = browser.find_element_by_xpath("//textarea[@placeholder='请填写原因']")
    confirm_btn = browser.find_element_by_xpath("//span[text()='确定']/..")
    smart_input(reason_input, '这是取消订单原因')
    confirm_btn.click()
    explicit_wait(browser, 'VOEL', ["//div[text()='买家已取消交易，订单已关闭。']", 'xpath'])
    m = is_exist_element(browser, 'xpath', "//span[text()='已取消']")  # 判断已取消状态的span有没有出现
    assert m

def cancel_order_seller_o(browser):  # 取消订单判断卖家页面
    explicit_wait(browser, 'VOEL', ["//div[text()='买家已取消交易，订单已关闭。']", 'xpath'])
    m = is_exist_element(browser, 'xpath', "//span[text()='已取消']")  # 判断已取消状态的span有没有出现
    assert m

def confirm_payment_buyer_o(browser):  # 买家确认支付
    confirm_payment_btn = browser.find_element_by_xpath("//span[text()='确认支付']/..")
    confirm_payment_btn.click()
    # explicit_wait(browser, 'VOEL', ["//span[text()='确 定']", 'xpath'])
    time.sleep(2)
    confirm_btn = browser.find_element_by_xpath("//span[text()='确 定']/..")
    print(confirm_btn, 'confirm_btn---------------')
    confirm_btn.click()
    explicit_wait(browser, 'VOEL', ["//div[text()='买家已付款，等待卖家确认']", 'xpath'])
    m = is_exist_element(browser, 'xpath', "//span[text()='申述']")  # 判断申述按钮有没有出现
    assert m

def confirm_payment_seller_o(browser):  # 确认支付后卖家页面判断
    explicit_wait(browser, 'VOEL', ["//div[text()='买家已付款，等待卖家确认']", 'xpath'])
    time.sleep(2)
    cancel_btn = is_exist_element(browser, 'xpath', "//span[text()='申述']")  # 判断申述按钮有没有出现
    confirm_release_btn = is_exist_element(browser, 'xpath', "//span[text()='确认释放']")  # 判断释放按钮有没有出现
    assert cancel_btn
    assert confirm_release_btn

def confirm_release_buyer_o(browser):  # 卖家确认释放
    confirm_release_btn = browser.find_element_by_xpath("//span[text()='确认释放']/..")
    confirm_release_btn.click()
    time.sleep(2)
    confirm_btn = browser.find_element_by_xpath("//span[text()='确 定']/..")
    confirm_btn.click()
    explicit_wait(browser, 'VOEL', ["//div[text()='卖家已释放托管BTC，请留意资产到账。']", 'xpath'])
    released_btn = is_exist_element(browser, 'xpath', "//span[text()='已释放']")
    good_rating_btn = is_exist_element(browser, 'xpath', "//span[text()='好评']")
    assert released_btn
    assert good_rating_btn

def confirm_release_seller_o(browser):  # 释放后卖家页面判断
    explicit_wait(browser, 'VOEL', ["//div[text()='卖家已释放托管BTC，请留意资产到账。']", 'xpath'])
    released_btn = is_exist_element(browser, 'xpath', "//span[text()='已释放']")
    rating_btn = is_exist_element(browser, 'xpath', "//span[text()='好评']")
    assert released_btn
    assert rating_btn

def order_rating_o(browser, content='和你合作真是太愉快了'):
    good_rating_btn = browser.find_element_by_xpath("//img[@alt='good']/..")
    print(good_rating_btn, 'good_rating_btn-----')
    rating_input = browser.find_element_by_id('content')
    submit_btn = browser.find_elements_by_class_name('ant-btn-primary')[0]
    print(submit_btn, 'submit_btn----')
    good_rating_btn.click()
    rating_input.clear()
    rating_input.send_keys(content)
    submit_btn.click()
    popup_o(browser, '操作成功')


"""  """

import numpy as np
import cv2
from time import time

def timeit(func):
    def call(*args, **kwargs):
        t0 = time()
        ret = func(*args, **kwargs)
        t1 = time()
        print(t1 - t0)
        return ret
    return call

class Filter:
    def __init__(self, size):
        pass

class Image:
    def __init__(self, path):
        self.image = cv2.imread(path, 0)
        cv2.imshow('original', self.image)
        cv2.waitKey()
        print(self.image.shape)

    def average(self, kernel, p):
        return np.sum(kernel*p)/(kernel.shape[0]*kernel.shape[1])

    def minimum(self, kernel, p):
        return np.min(kernel*p)

    def maximum(self, kernel, p):
        return np.max(kernel*p)

    def median(self, kernel, p):
        return np.median(kernel*p)

    def first(self, kernel, p):
        return (kernel*p)[0]

    @timeit
    def resize(self, size, method):
        self.old_height, self.old_width = self.image.shape
        self.new_height, self.new_width = size
        self.kernel_height, self.kernel_width = (self.old_height - self.new_height) + 1, (self.old_width - self.new_width) + 1
        print(self.kernel_height, self.kernel_width)
        kernel = np.ones((self.kernel_height, self.kernel_width))

        for i in range(len(kernel)):
            for j in range(len(kernel[0])):
                if i + j > max(len(kernel[0]), len(kernel)):
                    kernel[i, j] = 0

        print(kernel)

        if method == 'min':
            method = self.minimum
        elif method == 'max':
            method = self.maximum
        elif method == 'med':
            method = self.median
        elif method == 'avg':
            method = self.average
        elif method == 'first':
            method = self.first

        return self.convolve(self.image, kernel, method)

    def convolve(self, image, kernel, method):
        new_image = [[0] * self.new_width for i in range(self.new_height)]
        for i in range(image.shape[0] - kernel.shape[0] + 1):
            for j in range(image.shape[1] - kernel.shape[1] + 1):
                p = image[i: i + kernel.shape[0], j: j + kernel.shape[1]]
                new_image[i][j] = method(kernel, p)
        return np.array(new_image, dtype='uint8')

if __name__ == "__main__":
    new_image = Image('download.jpeg').resize((200, 150), 'avg')
    cv2.imshow('resized (150, 85)', new_image)
    cv2.waitKey()
    cv2.destroyAllWindows()


import unittest

from HTMLTestRunner.runner import HTMLTestRunner
from sauce_demo_test import SauceDemoTestCase
from selenium_site_test import SeleniumEasyTest

# Test Suit Class
class TestingClass(unittest.TestCase):

    # Test Suit for all the other tests
    def test_suit(self):
        my_test_suit = unittest.TestSuite()
        my_test_suit.addTests([
            unittest.defaultTestLoader.loadTestsFromTestCase(SauceDemoTestCase),
            unittest.defaultTestLoader.loadTestsFromTestCase(SeleniumEasyTest)
        ])

        # Test Runner HTML Report
        my_test_runnner = HTMLTestRunner(
            output = "report",
            title = "Test report",
            report_name = "report",
            tested_by = "Lazarica Petrut"
        )

        my_test_runnner.run(my_test_suit)


import torch
import torch.nn as nn
from torch.autograd import Variable
import torchvision
import torchvision.datasets as dsets
import torchvision.transforms as transforms
import numpy as np
import matplotlib.pyplot as plt

# Linear Regression:

# Hyper Params:
inputSize       = 1
outputSize      = 1
numEpochs       = 100000
learningRate    = 0.0001

# Toy Dataset
#xTrain = np.array([[3.3], [40.4], [50.5]] , dtype=np.float32)
xTrain = np.array(100*np.random.rand(50,1) , dtype=np.float32)
#xTrain = np.array([[3.3]] , dtype=np.float32)
yTrain = xTrain/5 + 30
yTrain = np.array(yTrain + 5*np.random.randn(yTrain.size,1), dtype=np.float32)

# Linear Regression Model:
class LinearRegression(nn.Module):
    def __init__(self,input_size, output_size):
        super(LinearRegression, self).__init__()
        self.linear = nn.Linear(input_size, output_size)

    def forward(self, x):
        out = self.linear(x)
        return out

model = LinearRegression(inputSize,outputSize)

# Loss: mean square loss:
criterion = nn.MSELoss()

# Train the Model:
# convert numpy array to torch Variable
inputs  = Variable(torch.from_numpy(xTrain))
targets = Variable(torch.from_numpy(yTrain))

for epoch in range(numEpochs):

    # forward + backward + optimize:
    model.zero_grad()
    outputs = model(inputs)
    loss    = criterion(outputs,targets)
    # loss = (sum((outputs-targets)**2))/len(inputs)
    loss.backward()

    # update weights:
    paramNum = 0
    parametersValues = torch.Tensor([[0],[0]])
    parametersValuesGrads = torch.Tensor([[0], [0]])
    for p in model.parameters():
        #print('parameter no.%d value: %.4f' % (paramNum,p.data.numpy() ))
        parametersValues[paramNum] = p.data
        parametersValuesGrads[paramNum] = p.grad.data
        #print('indeed inputs*param - outputs = %.4f' % inputs*p.data - outputs)
        p.data.add_(-learningRate, p.grad.data)
        #p.data = p.data - learningRate*p.grad.data
        paramNum = paramNum + 1

    outputSanityCheck = ((inputs.data * parametersValues[0] + parametersValues[1]) - outputs.data).numpy()
    outputSanityCheck = np.abs(outputSanityCheck).max()
    #print('indeed max(abs((inputs*firstParam + secondParam))) = %.2f' % outputSanityCheck)

    if False:
        # lossSingleInput = (ax + b - target)^2
        # dlossSingleInput/da = 2*(ax + b - target)*x
        # dlossSingleInput/db = 2*(ax + b - target)
        gradWithRespectTo_a = 2*(inputs.data * parametersValues[0] + parametersValues[1] - targets.data)*inputs.data
        gradWithRespectTo_b = 2 * (inputs.data * parametersValues[0] + parametersValues[1] - targets.data)
        print('autograd for a: %.2f' % parametersValuesGrads[0].numpy())
        print('calculated grad for a: %.2f' % gradWithRespectTo_a.numpy())
        print('autograd for b: %.2f' % parametersValuesGrads[1].numpy())
        print('calculated grad for b: %.2f' % gradWithRespectTo_b.numpy())

    if (epoch+1) % 5 == 0:
        #print('Epoch [%d/%d], Loss: %.4f' % (epoch+1, numEpochs, loss.data[0]))
        print('model: out = %.2f x input + %.2f' % (parametersValues[0].numpy(), parametersValues[1].numpy()))

torch.save(model.state_dict(), 'model.pkl')


import pygsheets
import pandas as pd
import tkinter as tk
from tkinter import filedialog
import re
import datetime
from time import time
from DV360_checkedlatecampaign import update_main

today = datetime.datetime.now()
postdate = (str(today.year) + str(today.month).zfill(2))

gc = pygsheets.authorize(service_file=r'C:\Users\sebein\Desktop\Pythonupload\pythonupload-307303-aae13f0bea1f.json')

sht_op = gc.open_by_url(
    # 'https://docs.google.com/spreadsheets/d/12Ts2BP9acoemXb-0bLgdBl8G6PQyMUEoUXrB0b3W8Fo/edit#gid=1390695304'
    'https://docs.google.com/spreadsheets/d/1VUNIGd9wTrKGZgMt9Fo9NWouGgrs9wBzW5aIVoZEJw4/edit#gid=1510624566'
)

sht_normal = gc.open_by_url(
    'https://docs.google.com/spreadsheets/d/1J_6W-5VE55M4-hjvP0m-hwU7RFRYgRmhVehS_LG5UWM/edit#gid=1014058029'
)

sht_pg_dv360 = gc.open_by_url(
    'https://docs.google.com/spreadsheets/d/1J3IXxfgy1_EUWLf_hr3vVVpdfn2zAj51sK4UUKIqK6s/edit#gid=1619667887'
)
wks = sht_op.worksheets()
wks_op = wks[1]
wks_apex = wks[0]

df_op = wks_op.get_as_df(start="A1", numerize=False, include_tailing_empty=False)
df_apex = wks_apex.get_as_df(start="B1", numerize=False, include_tailing_empty=False)

wks_normal = sht_normal.worksheets()
df_normal = wks_normal[0].get_as_df(start="A1", numerize=False, include_tailing_empty=False)

wks_pg_dv360 = sht_pg_dv360.worksheets()[0]
df_pg_dv360 = wks_pg_dv360.get_as_df(start="A1", numerize=False, include_tailing_empty=False)

def choose_file(prompt):
    root = tk.Tk()
    root.withdraw()
    file_path = filedialog.askopenfilename(title=prompt)
    return file_path

def dv360_spending_file(skipfooter=0):
    df_monthly = pd.read_excel(choose_file("Please select the DV360 Report File you want to compile"), skipfooter=skipfooter)
    print(df_monthly.columns)
    df_monthly.rename(columns={"Advertiser ID": "AdvertiserID"}, inplace=True)

    columns_to_add = ['MediaCost', 'PlatformFee', 'Invoice_MediaCost_InvalidRefund',
                      'Invoice_PlatformFee_InvalidRefundInvoice_Total', 'TrueView_Adjust_media',
                      'TrueView_Adjust_platform', 'ThridParty', 'CM', 'Perfomics_Invoice', 'Agency_Note',
                      'PFX_Act', 'Startdate', 'EndDate', 'Contact', 'Budget']

    # Initialize the new columns with empty strings only if they don't exist or are empty
    for column in columns_to_add:
        if column not in df_monthly.columns or df_monthly[column].isnull().all():
            df_monthly[column] = ''

    # Move the new columns to the end of df_monthly and keep the order
    original_columns = [col for col in df_monthly.columns if col not in columns_to_add]
    df_monthly = df_monthly[original_columns + columns_to_add]

    df_monthly['AdvertiserID'] = df_monthly['AdvertiserID'].astype('str')
    df_monthly['Insertion Order'] = df_monthly['Insertion Order'].fillna('').apply(str)
    df_monthly = df_monthly.assign(APEX_PRODUCTIONFEE="", PFX_PRODUCTIONFEE="", ADDEDVALUE_PRODUCTIONFEE="")

    return df_monthly

def extract_job(df_monthly):
    regex_list = [
        # r"(?:PGTW\d{6}.*?@)",
        r"(?:PGTW\d{6}.* ?CN~[0-9A-Za-z\s\ / \-\(\)\]]*(?! >: @))",
        r"(?:PFTW.*?\d{6}.*?])",
        r"(?:APEX|PMSC|PMZN|PMTW)\d{6}(?:_.*?_)?",
        r"(?:ZNTWGDV|SCTWGDV|PMTWGDV|SCTWTTD|ZNTWTTD|PM)\d{6}.*?"
    ]
    df_monthly['BCC_KeyIn'] = df_monthly['Insertion Order'].str.extract(
        "(" + "|".join(regex_list) + ")") + "_" + postdate
    return df_monthly

def timer_func(func):
    # This function shows the execution time of
    # the function object passed
    def wrap_func(*args, **kwargs):
        t1 = time()
        result = func(*args, **kwargs)
        t2 = time()
        print(f'Function {func.__name__!r} executed in {(t2 - t1):.4f}s')
        return result

    return wrap_func

@timer_func
def op_apex(df_monthly):
    print("Starting op_apex \n")
    for i in range(len(df_op['Job No'])):
        for j in range(len(df_monthly['Insertion Order'])):
            try:
                if not (df_op['Job No'][i].startswith('000')) and (
                        df_op['Job No'][i] in df_monthly['Insertion Order'][j]):
                    print(df_op['Job No'][i])
                    print(df_op['Star Date'][i])
                    print(df_op['End Date'][i])
                    print(df_op['APEX Budget'][i])
                    print(df_op['Planner\'s Mail'][i])
                    print('*' * 100)
                    df_monthly.loc[j, 'Startdate'] = df_op['Star Date'][i]
                    df_monthly.loc[j, 'EndDate'] = df_op['End Date'][i]
                    df_monthly.loc[j, 'Budget'] = df_op['APEX Budget'][i]
                    df_monthly.loc[j, 'Contact'] = df_op['Planner\'s Mail'][i]
                    break
            except:
                pass
    print("op_apex part completed...")

# function pmp_apex works
@timer_func
def pmp_apex(df_monthly):
    print("Initiating...function'pmp_apex'\n")
    for i in range(len(df_apex['JobNo'])):
        for k in range(len(df_monthly['Insertion Order'])):
            try:
                if (df_apex['JobNo'][i] != '') and (df_apex['JobNo'][i] in df_monthly['Insertion Order'][k]):
                    print(df_apex['JobNo'][i])
                    print(df_apex['Startdate'][i])
                    print(df_apex['EndDate'][i])
                    print(df_apex['Budget'][i])
                    print(df_apex['Contact'][i])
                    print('*' * 100)
                    df_monthly.loc[k, 'Startdate'] = df_apex['Startdate'][i]
                    df_monthly.loc[k, 'EndDate'] = df_apex['EndDate'][i]
                    df_monthly.loc[k, 'Budget'] = df_apex['Budget'][i]
                    df_monthly.loc[k, 'Contact'] = df_apex['Contact'][i]
                    df_monthly.loc[k, 'APEX_PRODUCTIONFEE'] = df_apex['APEX_PRODUCTIONFEE'][i]
                    df_monthly.loc[k, 'PFX_PRODUCTIONFEE'] = df_apex['PFX_PRODUCTIONFEE'][i]
                    df_monthly.loc[k, 'ADDEDVALUE_PRODUCTIONFEE'] = df_apex['ADDEDVALUE_PRODUCTIONFEE'][i]
                    break
            except:
                print('Wrong...', df_apex['JobNo'][i], df_monthly['Insertion Order'][k])
                break
    print("function'pmp_apex' is done...\n")

# function op_normal
@timer_func
def op_normal(df_monthly):
    for i in range(len(df_monthly['Insertion Order'])):
        for l in range(len(df_normal['JOB NO'])):
            try:
                job = re.search(r"\w{4,7}\d{6}", df_monthly['Insertion Order'][i], re.I | re.M).group()
                if job in df_normal['JOB NO'][l]:
                    print(job, ' is in ', df_normal['JOB NO'][l])
                    print('Updating... ', df_normal['START'][l], ' to Monthly Dataframe')
                    print('Updating... ', df_normal['END'][l], ' to Monthly Dataframe')
                    print('Updating... ', df_normal['TOTAL MEDIA BUDGET(TWD)'][l], ' to Monthly Dataframe')
                    print('Updating... ', df_normal['PLANNER MAIL'][l], ' to Monthly Dataframe')
                    print('*' * 100)
                    df_monthly.loc[i, 'Startdate'] = df_normal['START'][l]
                    df_monthly.loc[i, 'EndDate'] = df_normal['END'][l]
                    df_monthly.loc[i, 'Budget'] = df_normal['TOTAL MEDIA BUDGET(TWD)'][l]
                    df_monthly.loc[i, 'Contact'] = df_normal['PLANNER MAIL'][l]
                    break
                else:
                    pass
            except:
                break

# function pg_dv360
@timer_func
def pg_dv360(df_monthly):
    for i in range(len(df_monthly['Insertion Order'])):
        for k in range(len(df_pg_dv360['Job'])):
            try:
                pg_job = re.search(r"\w{4,7}\d{6}", df_monthly['Insertion Order'][i], re.I | re.M).group()
                if pg_job in df_pg_dv360['Job'][k]:
                    print(pg_job, ' is in ', df_pg_dv360['Job'][k])
                    print('updating...,', df_pg_dv360['Start Date'][k])
                    print('updating...,', df_pg_dv360['End Date'][k])
                    print('updating...,', df_pg_dv360['Budget(Local Currency)'][k])
                    print('updating...,', df_pg_dv360['PlanningOwner'][k])
                    print('*' * 100)
                    df_monthly.loc[i, 'Startdate'] = df_pg_dv360['Start Date'][k]
                    df_monthly.loc[i, 'EndDate'] = df_pg_dv360['End Date'][k]
                    df_monthly.loc[i, 'Budget'] = df_pg_dv360['Budget(Local Currency)'][k]
                    df_monthly.loc[i, 'Contact'] = df_pg_dv360['PlanningOwner'][k]
                    break
            except:
                pass

def Merge_TTD(df_monthly):
    TTD_file_location = choose_file("Please select the TTD report file")
    df_TTD = pd.read_excel(TTD_file_location)
    df_TTD.rename(columns={'Advertiser ID': 'AdvertiserID',
                           'Campaign ID': 'Insertion Order ID',
                           'Advertiser Currency Code': 'Advertiser Currency',
                           'Advertiser': 'Advertiser',
                           'Campaign': 'Insertion Order',
                           'Advertiser Cost (Adv Currency)': 'MediaCost'}, inplace=True
                  )
    df_TTD.drop(df_TTD[df_TTD['MediaCost'] <= 10].index, inplace=True)
    df_TTD.sort_values(by=['Advertiser'])
    df_TTD = df_TTD.reset_index(drop=True)
    df_TTD['Partner'] = pd.Series(["TTD" for x in range(len(df_TTD.index))])
    df_TTD['Month'] = pd.Series(["2022/10" for x in range(len(df_TTD.index))])

    df_monthly = df_monthly.reset_index(drop=True)
    df_TTD = df_TTD.reset_index(drop=True)

    Merged_df = pd.concat([df_monthly, df_TTD], axis=0, ignore_index=True)
    Merged_df = Merged_df.reset_index(drop=True)
    return Merged_df

def Merge_yahoo(df_monthly):
    yahoo_file_location = choose_file("Please select the yahoo report file")
    df_yahoo = pd.read_excel(yahoo_file_location, skipfooter=1)
    df_yahoo = df_yahoo.drop(['Total Data Fee', 'Platform Fee'], axis=1)
    df_yahoo.rename(columns={'Currency Code': 'Advertiser Currency',
                             'Campaign Name': 'Insertion Order',
                             'Advertiser Spending': 'MediaCost',
                             'Advertiser Id': 'AdvertiserID',
                             'Campaign ID': 'Insertion Order ID',
                             'Advertiser Name': 'Advertiser'}, inplace=True
                    )
    df_yahoo.drop(df_yahoo[df_yahoo['MediaCost'] <= 10].index, inplace=True)
    df_yahoo.sort_values(by=['Insertion Order'])
    df_yahoo = df_yahoo.reset_index(drop=True)
    df_yahoo['Partner'] = pd.Series(["Yahoo Verizon" for x in range(len(df_yahoo.index))])
    now = datetime.datetime.now()
    if now.day >= 15:
        month = now.strftime('%Y/%m')
    else:
        last_month = now - datetime.timedelta(days=30)
        month = last_month.strftime('%Y/%m')
    df_yahoo['Month'] = pd.Series([month for x in range(len(df_yahoo.index))])

    Merged_df = pd.concat([df_monthly, df_yahoo], axis=0)
    Merged_df = Merged_df.reset_index(drop=True)
    return Merged_df

if __name__ == '__main__':
    updated_df = update_main()
    # 如果有執行上面的updated_df，下面的df_monthly在choose_file時要選擇updated_df最後輸出的那個檔案
    df_monthly = dv360_spending_file(skipfooter=0)
    df_monthly = Merge_TTD(df_monthly)
    df_monthly = Merge_yahoo(df_monthly)
    df_monthly = extract_job(df_monthly)
    op_normal(df_monthly)
    pmp_apex(df_monthly)
    op_apex(df_monthly)
    pg_dv360(df_monthly)
    file_path = filedialog.asksaveasfilename(defaultextension='.xlsx', filetypes=[('Excel files', '*.xlsx')])
    df_monthly.to_excel(file_path, index=False)


import polars as pl
# Візуалізувати дані
import matplotlib.pyplot as plt
import numpy as np
import mplcyberpunk

plt.style.use("cyberpunk")

"""
     Завдання 3 із завантаження даних
     Завантажте дані по вказаному посиланню, відкрийте як csv файл, в параметрах функції
     вкажіть що немає заголовка в першому рядку через додатковий параметр has_header = False, виведіть кілька рядків таблиці.
     https://raw.githubusercontent.com/karlrupp/microprocessor-trend-data/master/50yrs/transistors.dat
"""

# Завантажити дані CSV без заголовка
data = pl.read_csv(
     'https://raw.githubusercontent.com/karlrupp/microprocessor-trend-data/master/50yrs/transistors.dat',
     has_header=False
)

# Перейменувати стовпці та перетворити їх до потрібних типів
df = data.select(pl.col('column_1').str.splitn(' ', 2).struct.rename_fields(['date', 'value'])).unnest("column_1")
df = df.select(
     pl.col('date').cast(pl.Float64, strict=False),
     pl.col('value')
)

plt.figure(figsize=(8, 6))
plt.plot(df['date'], np.asarray(df['value'].to_list(), float), 'o')
plt.yscale("log")
plt.xlabel('Рік')
plt.ylabel('Кількість транзисторів (логарифмічна шкала)')
plt.title('Динаміка кількості транзисторів з часом')
plt.grid(True)
mplcyberpunk.add_glow_effects()
plt.show()


from django.shortcuts import render, redirect

from django.db.models import Q

from .forms import ClientForm, ImmobileForm, RegisterLocationForm
from .models import Immobile, ImmobileImage

# Create your views here.
def list_location(request):
    immobiles = Immobile.objects.filter(is_locate=False)
    context = {'immobiles': immobiles}
    return render(request, 'list-location.html', context)

def form_client(request):
    form = ClientForm()
    if request.method == 'POST':
        form = ClientForm(request.POST)
        if form.is_valid():
            form.save()
            return redirect('list-location')
    return render(request, 'form-client.html', {'form': form})

def form_immobile(request):
    form = ImmobileForm()
    if request.method == 'POST':
        form = ImmobileForm(request.POST, request.FILES)
        if form.is_valid():
            immobile = form.save()
            files = request.FILES.getlist('immobile') ## pega todas as imagens
            if files:
                for f in files:
                    ImmobileImage.objects.create( # cria instance para imagens
                        immobile=immobile,
                        image=f)
            return redirect('list-location')
    return render(request, 'form-immobile.html', {'form': form})

def form_location(request, id):
    get_locate = Immobile.objects.get(id=id) ## pega objeto

    form = RegisterLocationForm()
    if request.method == 'POST':
        form = RegisterLocationForm(request.POST)
        if form.is_valid():
            location_form = form.save(commit=False)
            location_form.immobile = get_locate ## salva id do imovel
            location_form.save()

            ## muda status do imovel para "Alugado"
            immo = Immobile.objects.get(id=id)
            immo.is_locate = True ## passa ser True
            immo.save()

            return redirect('list-location') # Retorna para lista

    context = {'form': form, 'location': get_locate}
    return render(request, 'form-location.html', context)

## Relatório
def reports(request): ## Relatórios
    immobile = Immobile.objects.all()

    get_client = request.GET.get('client')
    get_locate = request.GET.get('is_locate')
    get_type_item = request.GET.get('type_item')

    get_dt_start = request.GET.get('dt_start')
    get_dt_end = request.GET.get('dt_end')
    print(get_dt_start, get_dt_end)

    if get_client: ## Filtra por nome e email do cliente
        immobile = Immobile.objects.filter(
					Q(reg_location__client__name__icontains=get_client) |
					Q(reg_location__client__email__icontains=get_client))

    if get_dt_start and get_dt_end: ## Por data
        immobile = Immobile.objects.filter(
						reg_location__create_at__range=[get_dt_start,get_dt_end])

    if get_locate:
        immobile = Immobile.objects.filter(is_locate=get_locate)

    if get_type_item:
        immobile = Immobile.objects.filter(type_item=get_type_item)

    return render(request, 'reports.html', {'immobiles':immobile})


# author: sunshine
# datetime:2022/3/17 下午5:36
# -*- coding: UTF-8 -*-
import re
import sys
import datetime
import subprocess
from Crypto.Cipher import AES
from binascii import a2b_hex
from binascii import b2a_hex

class LicenseEncode:
    def __init__(self, mac, license_path, expired_date=None):
        self.mac = mac
        self.license_path = license_path
        self.date = (datetime.datetime.now() + datetime.timedelta(days=30)).strftime(
            '%Y%m%d') if expired_date is None else expired_date

    def encrypt(self, content):
        # content length must be a multiple of 16.
        while len(content) % 16:
            content += ' '
        content = content.encode('utf-8')
        # Encrypt content.
        aes = AES.new(b'2021052020210520', AES.MODE_CBC, b'2021052020210520')
        encrypted_content = aes.encrypt(content)
        return (b2a_hex(encrypted_content))

    def gen_license_file(self):
        with open(self.license_path, 'w') as LF:
            LF.write('MAC : %s\n' % (self.mac))

            LF.write('Date : %s\n' % (self.date))

            sign = self.encrypt('%s#%s' % (self.mac, self.date))
            print('Sign : ' + str(sign.decode('utf-8')) + '\n')
            LF.write('Sign : ' + str(sign.decode('utf-8')) + '\n')

class LicenseDecode:
    def __init__(self, license_path):
        self.license_path = license_path

    def license_check(self):
        license_dic = self.parse_license_file()
        sign = self.decrypt(license_dic['Sign'])
        sign_list = sign.split('#')
        mac = sign_list[0].strip()
        date = sign_list[1].strip()
        if (mac != license_dic['MAC']) or (date != license_dic['Date']):
            print('*Error*: License file is modified!')
            sys.exit(1)
        # Check MAC and effective date invalid or not.
        if len(sign_list) == 2:
            macs = self.get_mac()
            current_date = datetime.datetime.now().strftime('%Y%m%d')
            if sign_list[0] not in macs:
                print('*Error*: Invalid host!')
                sys.exit(1)
            # Current time must be before effective date.

            if sign_list[1] < current_date:
                print('*Error*: License is expired!')
                sys.exit(1)
        else:
            print('*Error*: Wrong Sign setting on license file.')
            sys.exit(1)

    def parse_license_file(self):
        license_dic = {}

        with open(self.license_path, 'r') as LF:
            for line in LF.readlines():
                if re.match('^\s*(\S+)\s*:\s*(\S+)\s*$', line):
                    my_match = re.match('^\s*(\S+)\s*:\s*(\S+)\s*$', line)
                    license_dic[my_match.group(1)] = my_match.group(2)
        return (license_dic)

    def decrypt(self, content):
        aes = AES.new(b'2021052020210520', AES.MODE_CBC, b'2021052020210520')
        decrypted_content = aes.decrypt(a2b_hex(content.encode('utf-8')))
        return (decrypted_content.decode('utf-8'))

    def get_mac(self):
        SP = subprocess.Popen('/sbin/ifconfig', shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE,
                              stderr=subprocess.PIPE)
        (stdout, stderr) = SP.communicate()
        macs = []
        for line in str(stdout, 'utf-8').split('\n'):
            if re.match('^\s*ether\s+(\S+)\s+.*$', line):
                my_match = re.match('^\s*ether\s+(\S+)\s+.*$', line)
                mac = my_match.group(1)
                macs.append(mac)
        return macs

if __name__ == '__main__':
    # make license file
    mac = '54:e1:ad:15:38:25'  # 改为需授权的主机的mac地址

    # 第一步，生成license文件
    LicenseEncode(mac, '../License.dat').gen_license_file()

    # 验证license
    # check license file
    # LicenseDecode('./License.dat').license_check()