import random
import warnings
from typing import Any, Iterator, List, Optional

import torch
from torch import Tensor

from avalanche.benchmarks.utils import make_classification_dataset
from avalanche.benchmarks.utils.data import AvalancheDataset
from avalanche.models import avalanche_forward
from avalanche.training.plugins.strategy_plugin import SupervisedPlugin
from avalanche.training.storage_policy import ClassBalancedBuffer
from avalanche.training.utils import cycle

class AGEMPlugin(SupervisedPlugin):
    """Average Gradient Episodic Memory Plugin.

    AGEM projects the gradient on the current minibatch by using an external
    episodic memory of patterns from previous experiences. If the dot product
    between the current gradient and the (average) gradient of a randomly
    sampled set of memory examples is negative, the gradient is projected.
    This plugin does not use task identities.
    """

    def __init__(self, mem_size: int, sample_size: int):
        """
        :param patterns_per_experience: number of patterns per experience in the
            memory.
        :param sample_size: number of patterns in memory sample when computing
            reference gradient.
        """

        super().__init__()

        self.sample_size = int(sample_size)

        self.storage_policy = ClassBalancedBuffer(max_size=mem_size, adaptive_size=True)
        self.buffer_dliter = None

        # Placeholder Tensor to avoid typing issues
        self.reference_gradients: Tensor = torch.empty(0)

    def before_training_iteration(self, strategy, **kwargs):
        """
        Compute reference gradient on memory sample.
        """

        if self.buffer_dliter is not None:
            strategy.model.train()
            strategy.optimizer.zero_grad()
            mb = self.sample_from_memory()
            xref, yref, tid = mb[0], mb[1], mb[-1]
            xref, yref = xref.to(strategy.device), yref.to(strategy.device)

            out = avalanche_forward(strategy.model, xref, tid)
            loss = strategy._criterion(out, yref)
            loss.backward()
            # gradient can be None for some head on multi-headed models
            reference_gradients_list = [
                p.grad.view(-1)
                if p.grad is not None
                else torch.zeros(p.numel(), device=strategy.device)
                for n, p in strategy.model.named_parameters()
            ]
            self.reference_gradients = torch.cat(reference_gradients_list)
            strategy.optimizer.zero_grad()

    @torch.no_grad()
    def after_backward(self, strategy, **kwargs):
        """
        Project gradient based on reference gradients
        """
        if self.buffer_dliter is not None:
            current_gradients_list = [
                p.grad.view(-1)
                if p.grad is not None
                else torch.zeros(p.numel(), device=strategy.device)
                for n, p in strategy.model.named_parameters()
            ]
            current_gradients = torch.cat(current_gradients_list)

            assert (
                current_gradients.shape == self.reference_gradients.shape
            ), "Different model parameters in AGEM projection"

            dotg = torch.dot(current_gradients, self.reference_gradients)
            if dotg < 0:
                alpha2 = dotg / torch.dot(
                    self.reference_gradients, self.reference_gradients
                )
                grad_proj = current_gradients - self.reference_gradients * alpha2

                count = 0
                for n, p in strategy.model.named_parameters():
                    n_param = p.numel()
                    if p.grad is not None:
                        p.grad.copy_(grad_proj[count : count + n_param].view_as(p))
                    count += n_param

    def after_training_exp(self, strategy, **kwargs):
        """Update replay memory with patterns from current experience."""
        self.storage_policy.update_from_dataset(strategy.experience.dataset)
        self.buffer_dliter = torch.utils.data.DataLoader(
            self.storage_policy.buffer, shuffle=True, batch_size=self.sample_size
        )
        self.buffer_dliter = cycle(self.buffer_dliter)

    def sample_from_memory(self):
        """
        Sample a minibatch from memory.
        Return a tuple of patterns (tensor), targets (tensor).
        """
        return next(self.buffer_dliter)


"""
Given newline JSON with order data for each customer
The app read the data into Spark and calculate the net merchandise value of the ordered products).
After transforming the data it writes it into a mongodb file server.
"""

from pyspark.sql import SparkSession
from pyspark.sql import DataFrame
from pymongo import MongoClient
from loguru import logger

from pathlib import Path
from typing import List

from config import config

# Temporary hardcoded filepath. Would be good to fix in future
FILE_PATH = str(Path.cwd() / Path('etl/resources/data.json'))

def main():
    # start Spark application and get Spark session
    spark = SparkSession \
        .builder \
        .appName("net merchandise per customer") \
        .getOrCreate()

    df = load_json_data(FILE_PATH, spark)
    t_df = transform_data(df, spark)
    rows = dataframe_to_rows(t_df)

    # store the rows in mongodb database
    store_rows_in_database(rows)

def transform_data(df: DataFrame, spark) -> DataFrame:
    logger.debug("Starting data transformation...")

    df.createOrReplaceTempView('table')
    transformed = spark.sql(
        """
        with nett AS (
        select customerId
        , o.orderId as orderId,
            CASE
                WHEN b.productType = 'cold food' THEN b.grossMerchandiseValueEur - (b.grossMerchandiseValueEur * 0.15)
                WHEN b.productType = 'hot food' THEN b.grossMerchandiseValueEur - (b.grossMerchandiseValueEur * 0.07)
                WHEN b.productType = 'beverage' THEN b.grossMerchandiseValueEur - (b.grossMerchandiseValueEur * 0.09)
            END as nettMerchandiseValueEur
        from table
        lateral view explode(orders) as o
        lateral view explode(o.basket) as b
        )

        SELECT customerId
        , COUNT(orderId) as orders
        , ROUND(SUM(nettMerchandiseValueEur), 2) as totalNetMerchandiseValueEur
        FROM nett
        GROUP BY 1
        """)
    return transformed

def load_json_data(path: str, spark) -> DataFrame:
    logger.debug(f"Loading data at file path: {path}")
    return spark.read.json(path)

def store_rows_in_database(rows: List[dict]) -> None:
    collection = MongoClient()[config.DATABASE][config.COLLECTION]

    logger.info("Inserting rows into db...")

    collection.insert_many(rows)

def dataframe_to_rows(df: DataFrame) -> List[dict]:
    """
    creates a list of dicts where each dict is a row
    :param df: spark Dataframe
    :return: each row from the dataframe in a dict
    """
    m = map(lambda row: row.asDict(), df.collect())
    rows = [i for i in m]
    return rows

if __name__ == "__main__":
    main()


import streamlit as st
from PIL import Image
import os
import cv2
from mtcnn import MTCNN
import numpy as np
from tensorflow.keras.applications.resnet50 import ResNet50
from tensorflow.keras.applications.resnet50 import preprocess_input
import pickle
from sklearn.metrics.pairwise import cosine_similarity
from tensorflow.keras.layers import GlobalMaxPooling2D
import tensorflow.python.keras

model = ResNet50(weights='imagenet',include_top=False,input_shape=(224,224,3))
model.trainable=False

model = tensorflow.keras.Sequential([
    model,
    GlobalMaxPooling2D()
])

detector = MTCNN()
feature_list = np.array(pickle.load(open('embedding1.pkl','rb')))
filenames = pickle.load(open('filename.pkl','rb'))

def save_uploaded_file(uploaded_image):
    try:
        with open(os.path.join('uploads',uploaded_image.name),'wb') as f:
            f.write(uploaded_image.getbuffer())
        return True
    except:
        return False

def extract_feature(img_path,model,detector):
    img = cv2.imread(img_path)
    results = detector.detect_faces(img)
    x, y, width, height = results[0]['box']

    # for croping the image
    face = img[y:y + width, x:x + width]
    # cv2.imshow('output',face)
    # cv2.waitKey(0)
    # detection done

    # extraction from image start
    image = Image.fromarray(face)
    image = image.resize((224, 224))

    face_array = np.asarray(image)
    face_array = face_array.astype('float32')
    expended_image = np.expand_dims(face_array, axis=0)

    preprocessed_image = preprocess_input(expended_image)
    result = model.predict(preprocessed_image).flatten()
    return result

def recommend(feature_list,features):
    similarity = []

    for i in range(len(feature_list)):
        similarity.append(cosine_similarity(features.reshape(1, -1), feature_list[i].reshape(1, -1))[0][0])
    index_pos = sorted(list(enumerate(similarity)), reverse=True, key=lambda x: x[1])[0][0]
    return index_pos

st.title('which bollywood celebrity are you?')

uploaded_img = st.file_uploader('choose an image')

if uploaded_img is not None:
#     save in directory
    if save_uploaded_file(uploaded_img):
#         load
#           feature extract
#          recomed
        display_img = Image.open(uploaded_img)
        # st.image(display_img)

        features = extract_feature(os.path.join('uploads',uploaded_img.name),model,detector)
        # st.text(features)
        # st.text(features.shape)
        index_pos = recommend(feature_list,features)
        predicted_actor = " ".join(filenames[index_pos].split('\\')[1].split('_'))
        # st.text(index_pos)
        col1,col2 = st.columns(2)
        with col1:
            st.header('Your upload image')
            st.image(display_img,width = 300)
        with col2:

            st.header('seems like '+predicted_actor)
            st.image(filenames[index_pos],width = 300)

# display


import datetime
import traceback

# this is modified code from an older private project

class Lesson:
    def __init__(
        self,
        name: str,
        teacher: str,
        ausfall: bool,
        student_id: int,
        student_data: dict,
    ):
        self.data = {}
        self.deletion_candiate = False
        if name == "":
            self.apply_for_deletion()
        elif name in ["Bio", "Chemie", "Informatik"]:
            if student_data[name] is True:
                pass
            else:
                self.apply_for_deletion()
                return
        elif name == "Kreatives Fach":
            name = student_data["Kreatives Fach"]
            teacher = student_data["kLehrer"]
        elif name == "Reli / Philo":
            name = student_data["Reli / Philo"]
            teacher = student_data["rLehrer"]

        self.name = name
        self.teacher = teacher
        self.ausfall = ausfall
        self.student_id = student_id
        self.data = {
            "name": name,
            "teacher": teacher,
            "ausfall": ausfall,
            "student_id": student_id,
        }

    def apply_for_deletion(self):
        self.deletion_candiate = True
        self.data.update({"name": " ", "teacher": " ", "ausfall": True})
        self.name = " "
        self.teacher = " "
        self.ausfall = True

class Day:
    def __init__(
        self,
        fortnight_day: int,
        even_week: bool,
        day_data: list[list[str, str, float]],
        student_id: int,
        student_data: dict,
    ):
        self.lessons = []
        self.fortnight_day = fortnight_day
        self.even_week = even_week
        self.student_id = student_id
        for lesson_data in day_data:
            self.lessons.append(
                Lesson(
                    lesson_data[0],
                    lesson_data[1],
                    lesson_data[2],
                    student_id,
                    student_data,
                )
            )
        self.cleanup()

    def actual_lessons(self):
        lessons = 0
        for lesson in self.lessons:
            if lesson.ausfall == False:
                lessons += 1
        return lessons

    def showself(self, today=True):
        self.cleanup()
        return_string = ""
        cancelled = 0
        for lesson in self.lessons:
            cancelled += 1 if lesson.ausfall == True else 0
        if len(self.lessons) == 0 or cancelled == len(self.lessons):
            if today == True:
                return_string += "Heute ist schulfrei, schon vergessen?"
            else:
                return_string += "Morgen ist schulfrei, schon vergessen?"
        else:
            lessons = self.actual_lessons()
            if today == True:
                date = datetime.datetime.today().strftime("%d.%m.%Y")
                return_string += (
                    f"Heute, am {date}, hat <@{self.student_id}> {lessons} Blöcke.\n"
                )
                return_string += "------------\n"
                for lesson in self.lessons:
                    return_string += "**" + lesson.name + "**\n"
                    return_string += lesson.teacher + "\n"
                    return_string += "------------\n"
                    if self.lessons.index(lesson) == 2 and len(self.lessons) > 3:
                        return_string += "------------\n"
            else:
                datetime_tomorrow = datetime.datetime.today() + datetime.timedelta(
                    days=1
                )
                date = datetime_tomorrow.strftime("%d.%m.%Y")
                return_string += (
                    f"Am {date} hat <@{self.student_id}> {lessons} Blöcke.\n"
                )
                return_string += "------------\n"
                for lesson in self.lessons:
                    return_string += "**" + lesson.name + "**\n"
                    return_string += lesson.teacher + "\n"
                    return_string += "------------\n"
                    if self.lessons.index(lesson) == 2 and len(self.lessons) > 3:
                        return_string += "------------\n"
        return return_string

    def get_lesson(self, lesson, data):
        try:
            i = self.lessons[lesson]
        except IndexError:
            return " "
        else:
            return self.lessons[lesson].data[data]

    def cleanup(self):
        for lesson in self.lessons[::-1]:
            if lesson == self.lessons[-1] and lesson.deletion_candiate == True:
                self.lessons.remove(lesson)

class Stundenplan:
    def __init__(self, member_id, data):
        self.member_id = member_id
        self.days = []
        self.profil = data[member_id]["Profil"]
        self.data = data[self.profil]
        for day_data in self.data:
            even_week = False if self.data.index(day_data) > 6 else True
            self.days.append(
                Day(
                    self.data.index(day_data),
                    even_week,
                    day_data,
                    member_id,
                    data[member_id],
                )
            )

    def heute(self):
        weekday = datetime.datetime.today().weekday()
        if int(datetime.datetime.today().strftime("%V")) % 2 == 0:
            fortnight_day = weekday + 7
        else:
            fortnight_day = weekday
        if weekday > 13:
            weekday -= 14
        return self.days[fortnight_day].showself(today=True)

    def morgen(self):
        weekday_tomorrow = datetime.datetime.today().weekday() + 1
        if int(datetime.datetime.today().strftime("%V")) % 2 == 0:
            fortnight_day = weekday_tomorrow + 7
        else:
            fortnight_day = weekday_tomorrow
        if fortnight_day > 13:
            fortnight_day -= 14
        try:
            return self.days[fortnight_day].showself(today=False)
        except IndexError:
            print(traceback.format_exc())
            return f"Fehler! {fortnight_day} < 13. Pingt mich solange ich den befehl nicht selber eingegeben habe:\n ```{traceback.format_exc()}```"

    def row_splitter(self):
        return (
            "+"
            + 12 * "-"
            + "+"
            + 12 * "-"
            + "+"
            + 12 * "-"
            + "+"
            + 12 * "-"
            + "+"
            + 12 * "-"
            + "+"
            + "\n"
        )
        # +------------+------------+------------+------------+------------+\n

    def show_week(self):
        return_string = ""
        weekdays = [
            "Montag",
            "Dienstag",
            "Mittwoch",
            "Donnerstag",
            "Freitag",
            "Samstag",
            "Sonntag",
        ]
        return_string += f"Heute ist {weekdays[datetime.datetime.today().weekday()]}. "
        even_week = (
            True if int(datetime.datetime.today().strftime("%V")) % 2 == 0 else False
        )
        if datetime.datetime.today().weekday() > 4:
            return_string += f"Hier ist der Stundenplan von <@{self.member_id}> für **nächste** Woche:"
            even_week = not even_week
        else:
            return_string += (
                f"Hier ist der Stundenplan von <@{self.member_id}> für **diese** Woche:"
            )
        return_string += "```\n"
        return_string += self.row_splitter()
        day_numbers = [0, 1, 2, 3, 4] if even_week == False else [7, 8, 9, 10, 11]
        for lesson in [0, 1, 2, 3, 4]:
            if lesson == 3:
                return_string += self.row_splitter()
            for data in ["name", "teacher"]:
                return_string += "|"
                for day in day_numbers:
                    return_string += " "
                    lesson_data = self.days[day].get_lesson(lesson, data)
                    return_string += lesson_data
                    return_string += (11 - len(lesson_data)) * " "
                    return_string += "|"
                return_string += "\n"
            return_string += self.row_splitter()
        return_string += "```"
        return return_string

mats_data = {
    399183833901170689: {
        "Profil": "Physik",
        "Reli / Philo": "Philo",
        "rLehrer": "Priebe",
        "Kreatives Fach": "Musik",
        "kLehrer": "Wellner",
        "Chemie": False,
        "Bio": False,
        "Informatik": True,
    },
    "Physik": [
        [  # mon
            ["Latein", "Derlien", False],
            ["Physik", "Büsser", False],
            ["Deutsch", "Balasus", False],
            ["Bio", "Hudzik", False],
        ],
        [  # tue
            ["Englisch", "Bach", False],
            ["Latein", "Derlien", False],
            ["WiPo", "Becker", False],
            ["Physik", "Büsser", False],
            ["Deutsch", "Balasus", False],
        ],
        [  # wed
            ["Mathe", "Stamm", False],
            ["Chemie", "Weber", False],
            ["Englisch", "Bach", False],
        ],
        [  # thur
            ["Reli / Philo", "rLehrer", False],
            ["Erdkunde", "Boysen", False],
            ["Kreatives Fach", "kLehrer", False],
            ["Informatik", "Stieglitz?", False],
            ["Geschichte", "Heberlein", False],
        ],
        [
            ["", "", True],
            ["Sport", "Pilarczyk", False],
            ["Mathe", "Stamm", False],
        ],  # fri
        [],  # sat
        [],  # sun
        [  # mon
            ["Latein", "Derlien", False],
            ["Physik", "Büsser", False],
            ["Deutsch", "Balasus", False],
            ["Bio", "Hudzik", False],
        ],
        [  # tue
            ["Bio", "Hudzik", True],
            ["BerOri", "Bach", False],
            ["WiPo", "Becker", False],
            ["Physik", "Büsser", False],
        ],
        [  # wed
            ["Mathe", "Stamm", False],
            ["Chemie", "Weber", False],
            ["Englisch", "Bach", False],
            ["Informatik", "Stieglitz?", False],
        ],
        [  # thur
            ["Reli / Philo", "rLehrer", False],
            ["Erdkunde", "Boysen", False],
            ["Kreatives Fach", "kLehrer", False],
            ["Informatik", "Stieglitz?", False],
            ["Geschichte", "Heberlein", False],
        ],
        [
            ["", "", True],
            ["Sport", "Pilarczyk", False],
            ["WiPo", "Becker", False],
        ],  # fri
        [],  # sat
        [],  # sun
    ],
}
mats_stundenplan = Stundenplan(399183833901170689, mats_data)


from chalice import Chalice
import datetime as dt
import time
import socket
import random

app = Chalice(app_name='simulation-chalice')
app.debug = True

@app.route('/')
def index():
    return {'hello': 'world'}

@app.route('/simulate/{scale}/{times}')
def simulate(scale, times):
    start = dt.datetime.now()
    s = 0
    for i in range(int(times)):
        s += random.random()*int(scale)
    return {'scale': int(scale), 'times': int(times), 'sum': s, 'mean': s/int(times), 'time': str(dt.datetime.now() - start)}

@app.route('/sleep/{seconds}/{uniq_id}')
def sleep(seconds, uniq_id):
    start = dt.datetime.now()
    time.sleep(float(seconds))
    return {"seconds": seconds, "starttime": str(start), "endtime": str(dt.datetime.now()), "hostname": socket.gethostname(), "uniq_id": uniq_id}

@app.lambda_function(name='sleeper')
def sleeper(event, context):
    start = dt.datetime.now()
    time.sleep(event['seconds'])
    return {"seconds": event['seconds'], "starttime": str(start), "endtime": str(dt.datetime.now()), "hostname": socket.gethostname()}

# The view function above will return {"hello": "world"}
# whenever you make an HTTP GET request to '/'.
#
# Here are a few more examples:
#
# @app.route('/hello/{name}')
# def hello_name(name):
#    # '/hello/james' -> {"hello": "james"}
#    return {'hello': name}
#
# @app.route('/users', methods=['POST'])
# def create_user():
#     # This is the JSON body the user sent in their POST request.
#     user_as_json = app.current_request.json_body
#     # We'll echo the json body back to the user in a 'user' key.
#     return {'user': user_as_json}
#
# See the README documentation for more examples.
#


import pandas as pd
from sklearn.linear_model import LinearRegression
import os

def regression_fill(incomplete_filename):
    complete_filename = os.path.abspath("dataset/camera_dataset.csv") #because path as a string didn't work for some reason
    incomplete_data = pd.read_csv(incomplete_filename)
    complete_data = pd.read_csv(complete_filename)
    filled_data = pd.read_csv(incomplete_filename) #creating a copy of incomplete_data to fill its nans with predicted values
    complete_data = complete_data.dropna() #to be sure there is no nan value

    if(len(incomplete_data) == 0 or len(complete_data) == 0):
        print('Brak danych do obliczenia regresji liniowej.')
        return

    X_args = complete_data["Release date"]
    X_args = X_args.values.reshape(-1, 1)

    skipFirst = True
    for (columnName, columnData) in complete_data.iteritems():
        if skipFirst:
            skipFirst=False
            continue

        # counting the linear regression slope for complete data
        model = LinearRegression().fit(X_args, columnData)
        slope = model.coef_
        print(f"The slope coefficient of {columnName} attribute is: {slope}")

        predicted_data = model.predict(X_args) #array of all predicted values

        #putting the predicted values to the matrix if value is nan
        for i in range(len(predicted_data)):
            if pd.isna(incomplete_data.at[i, columnName]):
                filled_data.at[i, columnName] = predicted_data[i]
                #print(filled_data.at[i, columnName])
    filled_data.dropna()
    return filled_data


# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None
from collections import defaultdict
class Solution:
    def verticalOrder(self, root: TreeNode) -> List[List[int]]:
        queue = collections.deque()
        column = defaultdict(list)
        queue.append((root, 0))

        while queue:
            node, col = queue.popleft()
            if node:
                column[col].append(node.val)
                queue.append((node.left, col - 1))
                queue.append((node.right, col + 1))

        ans = [column[k] for k in sorted(column.keys())]
        return ans

class Solution:
    def verticalOrder(self, root: TreeNode) -> List[List[int]]:
        # BFS without sort. The insight is that, the index accompanying
        # each node is within a range, we could return the sub-lists
        # according to the range.
        if not root:
            return []
        queue = collections.deque()
        queue.append((root, 0))
        min_col, max_col = 0, 0
        columnTable = collections.defaultdict(list)

        while queue:
            node, col = queue.popleft()

            if node != None:
                columnTable[col].append(node.val)
                min_col = min(min_col, col)
                max_col = max(max_col, col)
                queue.append((node.left, col - 1))
                queue.append((node.right, col + 1))

        return [columnTable[x] for x in range(min_col, max_col + 1)]

from collections import defaultdict
class Solution:
    def verticalOrder_dfs(self, root: TreeNode) -> List[List[int]]:
        if root is None:
            return []

        columnTable = defaultdict(list)
        min_column = max_column = 0

        def DFS(node, row, column):
            if node is not None:
                nonlocal min_column, max_column
                columnTable[column].append((row, node.val))
                min_column = min(min_column, column)
                max_column = max(max_column, column)

                # preorder DFS
                DFS(node.left, row + 1, column - 1)
                DFS(node.right, row + 1, column + 1)

        DFS(root, 0, 0)

        # order by column and sort by row
        ret = []
        for col in range(min_column, max_column + 1):
            columnTable[col].sort(key=lambda x:x[0])
            colVals = [val for row, val in columnTable[col]]
            ret.append(colVals)

        return ret


# coding: utf-8

"""
This is only valid for Brazilian Real and B3 (Brazil stock exchange).
Todo localization
"""

from decimal import *
import csv, re, copy, json
from calendar import monthrange
from collections import defaultdict
from datetime import datetime, timedelta, date

# Options month codes
CALL = ['A','B','C','D','E','F','G','H','I','J','K','L']
PUT = ['M','N','O','P','Q','R','S','T','U','V','W','X']

MKT = {
        'VIS':{'threshold_exempt':20000},
        'OPV':{'threshold_exempt':0}
        }

class InsufficientStocks(Exception):
    """ Attempt to sell more stocks than in wallet """
    pass

class StockNotFound(Exception):
    """ Attempt to sell more stocks than in wallet """
    pass

class ObjectifyData():
    """
    Opens CSV file and objetify to a sequence of months and a list of stocks
    """
    def __init__(self, mkt_type, file, path="files/"):
        self.mkt_type = mkt_type
        self.file_path = path
        self.file = file
        self.mm = Months(mkt_type)
        self.stocks = {}
        self.running_options = RunningOptions()
        self.stocks_wallet = defaultdict()
        self.illegal_operation = defaultdict(list)

    def two_digits_month(self, month):
        """
        Four digits year plus two digits months for use in dict keys,
        to allows order keys
        """
        if month < 10:
            month = "0%d" % (month)
        return month

    def read_line(self, line):
        # line = [10/07/2019,1-Bovespa,C,VIS,TAEE11 UNT N2,,100,"28,69",2869,D]
        line_dict = {}
        try:
            if self.mkt_type != line[3]:
                return

            # Column 10 filed, to ignore.
            # import pdb; pdb.set_trace()
            if len(line) == 11 and line[10]:
                return

            """
            !!! Compatibilizar as datas. Provavelmente tudo com 2 digitos no ano.
            """
            dt = datetime.strptime(line[0], '%d/%m/%Y').date()
            month = self.two_digits_month(dt.month)
            year_month_id = "%d%s" % (int(dt.year), month)
            year_month_id = int(year_month_id)
            stock = line[4].split()[0]
            qt = int(line[6].replace('.', ''))

            # Comma separator for decimal
            if ',' in line[7]:
                unit_price = Decimal(line[7].replace('.', '').replace(',', '.').replace('"', ''))
            # Dot separator for decimal
            else:
                unit_price = Decimal(line[7].replace('"', ''))
            unit_price = round(unit_price, 2)
#            print(line)
            value = Decimal(line[8].replace('.', '').replace(',', '.').replace('"', ''))
            value = round(value, 2)
            buy_sell = line[2]

            line_dict = {'year_month_id':year_month_id,
                            'dt':dt,
                            'stock':stock,
                            'value':value,
                            'qt':qt,
                            'unit_price': unit_price,
                            'buy_sell': buy_sell,
                            }
            # print(line_dict)
            # import pdb; pdb.set_trace()
            return line_dict
        except Exception as e:
            print(e)
            raise

    def is_stock(self, asset):
        """ Whether is stock or option """
        """ There is a specific field for this on the csv """
        pattern = '^[A-Z]{4}\d+$'
        regexp = re.compile(pattern, re.IGNORECASE)
        if regexp.search(asset):
            return True
        return False

    def file2object(self, csv_only=False):
        """
        Process file line by line using the file's returned iterator
        to permit working with large files.
        Build months object and stocks objects.
        """
        try:
            file_path = '%s%s' % (self.file_path, self.file)
            with open(file_path) as file_handler:
                # import pdb; pdb.set_trace()
                csv_reader = csv.reader(file_handler, quotechar='"', delimiter=',')

                # To remove excel header if exists
                # next(csv_reader, None)
                csv_reader = reversed(list(csv_reader))

                if csv_only:
                    return csv_reader

                while True:
                    try:
                        line = next(csv_reader)
                        line = self.read_line(line)
                        if not line:
                            continue

                        # if not 'ITSA4' in line['stock']:
                        #      continue

                        # Whenever the months change (for each new month),
                        # verify option expiration (OTM)
                        if not line['year_month_id'] in self.mm:
                            if self.running_options:
                                updated_options = self.running_options.chk_running_options(line)
                                self.months_update_option(updated_options)

                        # Objectify stock and months
                        stock_updated_instance = self.objectify_stock(line)
                        if not stock_updated_instance:
                            continue
                        self.objectify_months(stock_updated_instance)

                        # Create or remove a running option.
                        if not self.is_stock(line['stock']): # If is option
                            if stock_updated_instance['qt_total'] == 0:
                                del self.running_options[line['stock']]
                            else:
                                self.running_options[line['stock']] = stock_updated_instance

                    except Exception as e:
                        raise

        except (IOError, OSError):
            print("Error opening / processing file")
        except StopIteration:
            pass

        # After the last operation check if there are OTM options
        updated_options = self.running_options.chk_running_options(None)
        self.months_update_option(updated_options)

        return self.mm

    def months_update_option(self, updated_options):
        if updated_options:
            for uo in updated_options:
                self.objectify_months(uo)

    def objectify_stock(self, line):
        """
        Receives a csv line in dict format and create a stock object.
        Each stock object instance is a stock.
        For each line, create/update the object which returns the object __dict__
        """
        # Create or get instance of a stock account
        # It is populating self.stocks, a still not used dict.
        stock = self.stocks.setdefault(line['stock'], StockCheckingAccount(line['stock']))

        # Set operation attributes
        stock.start_operation(**line)

        # Calculate and set instance values
        if line['buy_sell'] == 'C':
            stock.buy(**line)
        if line['buy_sell'] == 'V':
            try:
                stock.sell(**line)
            except InsufficientStocks:
                line['exception'] = InsufficientStocks('%s: Insufficient stocks to sale (%s)'  % (line['stock'], line['dt']))
                self.illegal_operation[line['stock']].append(line)
                return None

        # deep copy operation values
        cp_stock = copy.deepcopy(stock.__dict__)

        #if cp_stock['qt_total']:
        self.stocks_wallet[cp_stock['stock']] = cp_stock
        return cp_stock

    def objectify_months(self, stock_updated_instance):
        #line_dict = {'year_month_id':year_month_id, 'dt':dt, 'stock':stock, 'value':value, 'buy_sell': buy_sell }
        month = stock_updated_instance['year_month_id']
        month_dict = self.mm[month]
        # Set operation attributes
        self.mm.month_populate(month, stock_updated_instance)

class RunningOptions():
    def __init__(self):
        self._running_options = {}

    def __iter__(self):
        return iter(self._running_options)

    def __getitem__(self, option):
        return self._running_options[option]

    def __setitem__(self, option, updated_instance):
        self._running_options[option] = updated_instance

    def __delitem__(self, option):
        self._running_options.pop(option, None)

    def get_expiring_month(self):
        """ Get the month validity on option code.
        It is the fourth letter of the option code, following
        PUT and CALL list codes """
        option_type = ''
        option_code_letter = self.running_option['stock'][4]
        try:
            expiring_month = CALL.index(option_code_letter) + 1
            option_type =  'call'
#            expiring_date = self.expiring_date(expiring_month)
        except ValueError:
            expiring_month = PUT.index(option_code_letter) + 1
            option_type =  'put'
#            expiring_date = self.calc_expiring_date(expiring_month)
        except:
            raise
        return {'option_type':option_type, 'expiring_month':expiring_month}

    def calc_expiring_date(self, expiring_month):
        """ Calculates the expiration date of the option, assuming that
        it is not possible to carry for more than a year. """
        buy_month = self.running_option['dt'].month
        current_year = datetime.today().year
        expiring_year = self.running_option['dt'].year

        """ If the month of the option is smaller than the actual month,
        it refers to the month of the next year. """
        if expiring_month < buy_month:
            expiring_year += 1
        expiring_date = date(expiring_year,
                                expiring_month,
                                monthrange(expiring_year, expiring_month)[1])
        return expiring_date

    def chk_option_expiring(self, reference_date):
        """ Discover option expiring date based on option code and buy date.
        If expired, set loss to the option object. """

        def expired_otm(expiring_date):
            """ Verify if option has lost validity """
            if reference_date > expiring_date:
                return True
            return False

        def set_option_loss():
            """ Create an operation of selling option when it expires """
            sold_option = copy.deepcopy(self.running_option)
            sold_option['loss'] = self.running_option['qt_total'] * self.running_option['avg_price']
            sold_option['value'] = 0
            sold_option['qt_total'] = 0
            sold_option['buy_sell'] = 'V'
            return sold_option

        decoded_option = self.get_expiring_month()
        expiring_date = self.calc_expiring_date(decoded_option['expiring_month'])

        if expired_otm(expiring_date):
            sold_option = set_option_loss()
            return sold_option

    def cleanup_running_option(self, clean_running_option):
        for cro in clean_running_option:
            del self._running_options[cro['name']]

    def chk_running_options(self, line):
        try:
            # Check options status against any date (reference date).
            # Using a negotiation date (year_month_id).
            reference_date = line['dt']
        except:
            # After the end of the loop of the csv file, check against today.
            reference_date = datetime.now().date()

        clean_running_option = []
        sold_options = []
        for key in self._running_options:
            self.running_option = self._running_options[key]
            sold_option = self.chk_option_expiring(reference_date)
            if sold_option:
                sold_options.append(sold_option)
                clean_running_option.append(sold_option)

        self.cleanup_running_option(clean_running_option)
        return sold_options

class StockCheckingAccount():
    def __init__(self, name):
        self.name = name
        self.qt_total = 0
        self._avg_price = 0
        self._qt_total_prev = 0 # for balance
        self._avg_price_prev = 0 # for balance

    @property
    def avg_price(self):
        return self._avg_price

    @avg_price.setter
    def avg_price(self, new_avg_price):
        self._avg_price = new_avg_price

    @property
    def avg_price_prev(self):
        return self._avg_price_prev

    def new_avg_price(self, qt, unit_price):
        current_position = self.qt_total * self.avg_price
        new_dock = qt * unit_price
        new_avg_price = (current_position + new_dock) / (self.qt_total + qt)
        self._avg_price_prev = self.avg_price
        self.avg_price = new_avg_price

        # !!! This is a workaround that was working before p3 migration
        self.__dict__['avg_price'] = self.avg_price
        self.__dict__['avg_price_prev'] = self.avg_price_prev

    def profit_loss(self, qt, unit_price):
        if unit_price <= self.avg_price:
            self.loss += (qt * self.avg_price) - (qt * unit_price)
        elif unit_price >= self.avg_price:
            self.profit += (qt * unit_price) - (qt * self.avg_price)

    def start_operation(self, **kwargs):
        # Pass all csv line values to stockCheckingAccount instance
        self.__dict__.update(kwargs)
        # Init profit and loss for this operation
        # Will calculate this based on the avg_price * sell price.
        self.profit = 0
        self.loss = 0

    def buy(self, **line):
        self.new_avg_price(line['qt'], line['unit_price'])
        self._qt_total_prev = self.qt_total
        self.qt_total += line['qt']

    def sell(self, **line):
        self.profit_loss(line['qt'], line['unit_price'])
        if line['qt'] > self.qt_total:
            raise InsufficientStocks()
        self.qt_total -= line['qt']
        if self.qt_total == 0:
            self._qt_total_prev = 0
            self._avg_price_prev = 0
            self.avg_price = 0

    def __repr__(self):
        return 'name:{}, dt:{}, qt: {}, avg_price: {}, lucro:{}, prejuizo:{}'\
                    .format(self.name, self.dt, self.qt_total, self.avg_price, self.profit, self.loss)

class Months():
    def __init__(self, mkt_type):
        self._months = {}
        self.mkt_type = mkt_type

    def __getitem__(self, month):
        # stocks = {'operations':[], 'totalization':{}}
        return self._months.setdefault(month, {'dt':datetime,
                                                'month_buy':0, 'month_sell':0,
                                                'month_gain':0, 'month_loss':0,
                                                'cumulate_gain':0, 'cumulate_loss':0,
                                                'operations':defaultdict(list),
                                                'tax':{}})

    def __iter__(self):
        return iter(self._months)

    def get_month(self, month):
        return self._months[month]

    def keys(self):
        return sorted(self._months.keys())

    def month_populate(self, month, stock_updated_instance):
        line = stock_updated_instance
        # if 'BMGB' in line['stock']:
        #     import pdb; pdb.set_trace()
        month_dict = self._months[month]
        month_dict['dt'] = line['dt']

        if (line['buy_sell'] == 'C'):
            month_dict['month_buy'] += line['value']
        elif (line['buy_sell'] == 'V'):
            month_dict['month_sell'] += line['value']
        if stock_updated_instance is not None:
            month_dict['operations'][line['stock']].append(stock_updated_instance)

    def subtract_one_month(self, this_month):
        """ receives current month, returns the key of previous active month """
        self._months
        try:
            this_month_index = self.keys().index(this_month)
            if this_month_index:
                return self.keys()[this_month_index - 1]
            return None
        except:
            raise

    def cumulate_loss(self, this_month):
        previous_month = self.subtract_one_month(this_month)
        self._months[this_month]['cumulate_loss'] = self._months[this_month]['month_loss']
        if previous_month:
            self._months[this_month]['cumulate_loss'] += self._months[previous_month]['cumulate_loss']

    def threshold_exempt(self, this_month):
        """
        When the total amount sold on one month is less than R$20,000.00
        no tax are applied over gain
        """
        return True if self._months[this_month]['month_sell'] > MKT[self.mkt_type]['threshold_exempt'] else False

    def tax_calc(self, final_balance):
        #return {'final_balance':final_balance, 'tax_amount': final_balance * 0.15}
        #import pdb; pdb.set_trace()
        return {'final_balance':final_balance, 'tax_amount': final_balance * Decimal(0.15)}

    def month_add_detail(self):
        """
        Add gain, loss for all months.
        Calculate final balance and due tax.
        """
        # import pdb; pdb.set_trace()
        for month in self.keys():
            # print("\n\n"
            # print(month

            balance_current_month = 0
            # if self._months[month]['month_sell'] > 20000:
            # print("%s Total vendas : %s" % (month, self._months[month]['month_sell'])

            for stock, values in self._months[month]['operations'].items():
                for operation in values:
                    if operation['buy_sell'] == 'V':
                        if operation['profit']:
                            balance_current_month += operation['profit']
                        elif operation['loss']:
                            balance_current_month -= operation['loss']

                # print(operation
            if balance_current_month > 0:
                self._months[month]['month_gain'] =  balance_current_month
            elif balance_current_month < 0:
                self._months[month]['month_loss'] =  balance_current_month

            self.cumulate_loss(month)

            if self.threshold_exempt(month):
                if balance_current_month > 0:
                    final_balance = balance_current_month + self._months[month]['cumulate_loss']
                    if final_balance > 0:
                        self._months[month]['cumulate_loss'] = 0
                        self._months[month]['tax'] = self.tax_calc(Decimal(final_balance))
                    elif final_balance <= 0:
                        self._months[month]['cumulate_loss'] = final_balance

            # print(balance_current_month)
            # print(self._months[month]['month_gain'])
            # print(self._months[month]['month_loss'])
            # print(self._months[month]['cumulate_loss'])
            # print(self._months[month]['tax'])

class Report():
    def __init__(self, stock_price_file, stocks_wallet, months):
        self.current_prices = dict
        self.curr_prices_dt = ''
        self.illegal_operation = defaultdict(list)
        self.months = months
        self.stock_price_file = stock_price_file
        self.stocks_wallet = stocks_wallet

    def get_current_quotations(self):
        """
        Whatever market data source you have.
        This is getting form a serialized JSON from Yahoo Finance, from the
        stock_price.py script
        """
        # import pdb; pdb.set_trace()
        with open(self.stock_price_file, 'r') as f:
            stock_price = json.load(f)
            self.current_prices = stock_price[list(stock_price.keys())[0]]
            self.curr_prices_dt = list(stock_price.keys())[0]
            # import pdb; pdb.set_trace()

    def build_statement(self, values):
        statement = {'qt_total_start':0,
                     'avg_price_start':0,
                     'values':[],
                     'qt_total_end':0,
                     'avg_price_end':0 }

        if len(values) == 1:
            operation = values[0]

            statement = {'qt_total_start':operation['_qt_total_prev'],
                         'avg_price_start':operation['_avg_price_prev'],
                         'ops':[operation],
                         'qt_total_end':operation['qt_total'],
                         'avg_price_end':operation['avg_price'] }
        elif len(values) > 1:
            operation_start = values[0]
            operation_end = values[-1]
            statement = {'qt_total_start':operation_start['_qt_total_prev'],
                         'avg_price_start':operation_start['_avg_price_prev'],
                         'ops':values,
                         'qt_total_end':operation_end['qt_total'],
                         'avg_price_end':operation_end['avg_price'] }
        return statement

    def months_build_data(self):
        months = {}
        months_operations = {}
        months_keys = self.months.keys()
        months_keys.reverse()
        for month in months_keys:
            month_data = self.months.get_month(month)
            months[month] = month_data
            months_operations[month] = dict(month_data['operations'])
        return (months, months_operations)

    def report(self):
        """ to refactor - was used for command line tests
        use the months_build_data method as web app """
        months_keys = self.months.keys()
        for month in months_keys:
            month_data = self.months.get_month(month)
            print("\n\n===================================== \n\n\n")
            print('Mês: {0} / Vendas no mes: {1}'.format(month, month_data['month_sell']))
            print('Lucro: {0} / PositionViewPrejuízo: {1} / Prejuizo acumulado: {2}'.format(month_data['month_gain'], month_data['month_loss'], month_data['cumulate_loss']))

            if month_data['tax']:
                print('Balanço mês: {1} / Imposto devido: {0}'.format(month_data['tax']['tax_amount'], month_data['tax']['final_balance']))

            for stock, values in month_data['operations'].items():
                # if stock != 'KLBN11':
                #     continue
                print
                print("Stock: %s" % (stock))
                for ops in values:
                    if ops['buy_sell'] == 'V':
                        if ops['profit']:
                            print("%s: %s " % ('Balance', ops['profit']))
                        if ops['loss']:
                            print("%s: -%s " % ('Balance', ops['loss']))
                statement = self.build_statement(values)
                print(statement)

    def current_position(self):
        """ to refactor - was used for command line tests
        use the months_build_data method as web app """

        """
        On the last day of the month you can sell your stocks where you are
        losing, to reduce from your futures gains.
        """
        position = []
        summary = defaultdict(Decimal)
        def get_stock_position(stock, values):
            try:
                current_price = self.current_prices[stock]
                buy_position = values['qt_total'] * values['avg_price']

                if current_price['price']=='None':
                    raise StockNotFound

                curr_position = values['qt_total'] * round(Decimal(current_price['price']), 2)
                balance = curr_position - buy_position
                balance_pct = round((balance * 100 / buy_position), 2)
                # import pdb; pdb.set_trace()
                return (stock, values, Decimal(buy_position), Decimal(curr_position), Decimal(balance), balance_pct)
            except KeyError as e:
                # If KeyError 'avg_price', means that no buy input was provided.
                import pdb; pdb.set_trace()
                raise StockNotFound

        print("\n\n\nCurrent prices: %s" % (self.curr_prices_dt))
        for stock, values in self.stocks_wallet.items():
            if values['qt_total']:
                try:
                    (stock, values, buy_position, curr_position, balance, balance_pct) = get_stock_position(stock, values)
                    print("%s: Qt:%d - Buy avg: R$%.2f - Cur Price: R$%s - Buy Total: R$%.2f - Cur Total: R$%.2f - Balance: R$%.2f ( %.2f%s )" % (stock, values['qt_total'], values['avg_price'], self.current_prices[stock]['price'], buy_position, curr_position, balance, balance_pct, '%'))
                    position.append({
                        'stock': stock,
                        'qt': values['qt_total'],
                        'buy_avg': values['avg_price'],
                        'curr_price': self.current_prices[stock]['price'],
                        'buy_total':buy_position,
                        'cur_total':curr_position,
                        'balance': balance,
                        'balance_pct': balance_pct
                    })
                    summary['balance'] += balance
                    # import pdb; pdb.set_trace()
                    summary['buy_total'] += buy_position
                    summary['cur_total'] += curr_position

                    # import pdb; pdb.set_trace()

                except StockNotFound:
                    values['exception'] = StockNotFound('%s: StockNotFound (%s)'  % (values['stock'], values['dt']))
                    self.illegal_operation[values['stock']].append(values)
        # import pdb; pdb.set_trace()
        if not summary['buy_total'] == 0:
            summary['balance_pct'] =  (100 * (summary['cur_total'] - summary['buy_total'])) / summary['buy_total']
        return (position, summary)

    def illegal_operations(self, illegal_operations):
        print("\n\n\n")
        print("Illigal operations")
        print("==================")
        for list_io in illegal_operations:
            for stock, values in list_io.items():
                for err in values:
                    print(err['exception'])

if __name__ == "__main__":
    import argparse
    illegal_operations = []
    def get_args():
        """
        To get stock prices from your CSV file, call this script wiht arguments.
        python stock_price.py --path --file
        """
        parser = argparse.ArgumentParser()
        parser.add_argument('--mkt_type', default='VIS')
        parser.add_argument('--path', default='test_sample')
        parser.add_argument('--file', default='sample.csv')
        args = parser.parse_args()
        return args

    def handle_data(args):
        b3_tax_obj = ObjectifyData(mkt_type='VIS', path=args.path, file=args.file)
        return b3_tax_obj

    def gather_illegal_operation(illegal_operation):
        illegal_operations.append(illegal_operation)

    def months_reconcile(b3_tax_obj):
        months = b3_tax_obj.file2object()
        months.month_add_detail()
        return months

    def generate_reports(stocks_wallet, months):
        print("\n\nMercado à vista")
        print("===============")
        # report = Report()
        report = Report('/var/tmp/stock_price.json', stocks_wallet, months)

        report.report()

        report.get_current_quotations()
        report.current_position()
        gather_illegal_operation(report.illegal_operation)
        report.illegal_operations(illegal_operations)

    args = get_args()
    b3_tax_obj = handle_data(args)
    gather_illegal_operation(b3_tax_obj.illegal_operation)
    months = months_reconcile(b3_tax_obj)
    generate_reports(b3_tax_obj.stocks_wallet, months)

    exit(0)

    print("\n\nOpcoes PUT")
    print("==========")
    b3_tax_obj = ObjectifyData('OPV', path=args.path, file=args.file)
    months = b3_tax_obj.file2object()
    months.month_add_detail()
    report.report(months)


# -*- coding: utf-8 -*-
"""
Created on Fri May 10 14:41:29 2019

@author: zhangpingping
"""
# =============================================================================
# 二值化图像cv2.threshold, cv2.adaptiveThreshold
#cv2.threshold() :二值画图像（方法有如下几种）
#cv2.THRESH_BINARY： 黑白二值
#cv2.THRESH_BINARY_INV： 黑白二值反转
#cv2.THRESH_TRUNC: 大于阈值部分被置为threshold，小于部分保持原样
#cv2.THRESH_TOZERO : 小于阈值部分被置为0，大于部分保持不变
#cv2.THRESH_TOZERO_INV:大于阈值部分被置为0，小于部分保持不变
# =============================================================================
import numpy as np
import cv2
import matplotlib.pyplot as plt

img = np.zeros([100, 100, 3], np.uint32)
img.size
a = np.array(np.arange(0,255, 2.55))

## 应用矩阵生成灰度图
a = np.arange(0,255, 2.55).reshape(1,100)
b = np.array(np.arange(0,255, 2.55).reshape(1,100))
for i in range(99):
    b = np.concatenate([b, a], axis = 0)
plt.imshow(b, cmap='gray')

c = np.reshape(b,(100, 100, 1))
d = np.concatenate([c,c],2)
e = np.concatenate([d,c],2)

e = np.array(e, dtype=np.uint32)
plt.imshow(e)

img = b
# 进行二值化处理
ret,thresh1 = cv2.threshold(img,127,255,cv2.THRESH_BINARY)
ret,thresh2 = cv2.threshold(img,127,255,cv2.THRESH_BINARY_INV)
ret,thresh3 = cv2.threshold(img,127,255,cv2.THRESH_TRUNC)
ret,thresh4 = cv2.threshold(img,127,255,cv2.THRESH_TOZERO)
ret,thresh5 = cv2.threshold(img,127,255,cv2.THRESH_TOZERO_INV)
# ret,thresh6 = cv2.threshold(img,0,255,cv2.THRESH_OTSU)

# 图像展示
titles = ['img','BINARY','BINARY_INV','TRUNC','TOZERO','TOZERO_INV']
images = [img,thresh1,thresh2,thresh3,thresh4,thresh5]
for i in range(6):
    plt.subplot(2,3,i+1),plt.imshow(images[i],'gray')
    plt.title(titles[i])
    plt.xticks([]),plt.yticks([])
plt.show()

# 图像展示
plt.subplot(231),plt.imshow(img,'gray') # 几行几列图中的第几个
plt.subplot(232),plt.imshow(thresh1,'gray')
plt.subplot(233),plt.imshow(thresh2,'gray')
plt.subplot(234),plt.imshow(thresh3,'gray')
plt.subplot(235),plt.imshow(thresh4,'gray')
plt.subplot(236),plt.imshow(thresh5,'gray')

"""
Otsu’s二值化
"""
img_bird = cv2.imread(r"E:\ZhiYuan\opencv\bird.jpg", 0)
plt.imshow(img_bird, cmap='gray')
#简单滤波
ret1,th1 = cv2.threshold(img_bird,127,255,cv2.THRESH_BINARY)
plt.imshow(th1, cmap='gray')

#Otsu 滤波
ret2,th2 = cv2.threshold(img_bird, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
print(ret2)
plt.imshow(th2, cmap='gray')

# =============================================================================
# 自适应阈值
# 第一个原始图像
# 第二个像素值上限
# 第三个自适应方法Adaptive Method:
#   — cv2.ADAPTIVE_THRESH_MEAN_C ：领域内均值
#   —cv2.ADAPTIVE_THRESH_GAUSSIAN_C ：领域内像素点加权和，权 重为一个高斯窗口
# 第四个值的赋值方法：只有cv2.THRESH_BINARY 和cv2.THRESH_BINARY_INV
# 第五个Block size:规定领域大小（一个正方形的领域）
# 第六个常数C，阈值等于均值或者加权值减去这个常数（为0相当于阈值 就是求得领域内均值或者加权值）
# 这种方法理论上得到的效果更好，相当于在动态自适应的调整属于自己像素点的阈值，而不是整幅图像都用一个阈值。
# =============================================================================
th2 = cv2.adaptiveThreshold(img_bird, 100, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY,3,2)


'''Module DocString'''
from http import HTTPStatus
from flask import current_app as app
from app.main.model.user import User
from ..service.blacklist_service import save_token

class Auth:
    '''Contains Authentication functions'''
    @staticmethod
    def login_user(data):
        '''User Auth'''
        try:
            # fetch the user data
            user = User.query.filter_by(email=data.get('email')).first()
            app.logger.info(f"User with email {data.get('email')} found...checking password...")
            if user and user.check_password(data.get('password')):
                app.logger.info(f"User with email {data.get('email')} password confirmed.")
                auth_token = user.encode_auth_token(user.id)
                if auth_token:
                    response_object = {
                        'status': 'success',
                        'message': 'Successfully logged in.',
                        'Authorization': auth_token,
                        'username': user.username
                    }
                    return response_object, HTTPStatus.OK
            else:
                response_object = {
                    'status': 'fail',
                    'message': 'Email or password does not match.'
                }
                return response_object, HTTPStatus.UNAUTHORIZED

        except Exception as err:
            print(err)
            response_object = {
                'status': 'fail',
                'message': 'Try again'
            }
            return response_object, HTTPStatus.INTERNAL_SERVER_ERROR

    @staticmethod
    def logout_user(data):
        '''Logs out the user and blacklists the token'''
        if data:
            auth_token = data.split(" ")[1]
        else:
            auth_token = ''
        if auth_token:
            resp = User.decode_auth_token(auth_token)
            if not isinstance(resp, str):
                # mark the token as blacklisted
                return save_token(token=auth_token)
            else:
                response_object = {
                    'status': 'fail',
                    'message': resp
                }
                return response_object, HTTPStatus.UNAUTHORIZED
        else:
            response_object = {
                'status': 'fail',
                'message': 'Provide a valid auth token.'
            }
            return response_object, HTTPStatus.FORBIDDEN

    @staticmethod
    def get_logged_in_user(new_request):
        '''get the auth token'''
        auth_token = new_request.headers.get('Authorization')
        if auth_token:
            app.logger.info(f"Auth token received {auth_token} decoding and checking...")
            resp = User.decode_auth_token(auth_token)
            if not isinstance(resp, str):
                app.logger.info(f"Auth token {auth_token} decoded finding user {resp}")
                user = User.query.filter_by(id=resp).first()
                app.logger.info("User found...")
                response_object = {
                    'status': 'success',
                    'data': {
                        'user_id': user.id,
                        'email': user.email,
                        'username': user.username,
                        'admin': user.admin,
                        'registered_on': str(user.registered_on)
                    }
                }
                return response_object, HTTPStatus.OK
            response_object = {
                'status': 'fail',
                'message': resp
            }
            return response_object, HTTPStatus.UNAUTHORIZED
        else:
            response_object = {
                'status': 'fail',
                'message': 'Provide a valid auth token.'
            }
            return response_object, HTTPStatus.UNAUTHORIZED

    @staticmethod
    def get_logged_in_user_object(request) -> User|None:
        '''Returns the logged in user'''
        auth_token = request.headers.get('Authorization')
        if auth_token:
            resp = User.decode_auth_token(auth_token)
            if not isinstance(resp, str):
                user = User.query.filter_by(id=resp).first()
                if user:
                    return user


import inspect
from typing import Any
import langfun.core as lf
from langfun.core.coding.python import errors
from langfun.core.coding.python import execution
import pyglove as pg

class CodeWithError(pg.Object):
  """Python code with error."""

  code: str
  error: str

class CodeCorrection(pg.Object):
  """Structure used for code correction."""

  latest_code: CodeWithError
  correction_history: list[CodeWithError]
  corrected_code: str

def run_with_correction(
    code: str,
    error: str | None = None,
    *,
    global_vars: dict[str, Any] | None = None,
    lm: lf.LanguageModel = lf.contextual(),
    examples: list[CodeCorrection] | None = None,
    max_attempts: int = 5,
    sandbox: bool | None = None,
    timeout: int | None = 5,
    returns_code: bool = False,
) -> Any | tuple[Any, str]:
  """Correct code with a language model via self-play.

  Args:
    code: The source code that may or may not be problematic.
    error: An optional initial error for `code` when it's problematic, usually
      caught from elsewhere when it ran. If None, code will be executed once to
      verify if its good and obtain a feedback error message.
    global_vars: A dict of str to value as the global variables that could be
      accessed within the corrected code.
    lm: Language model to be used. If not specified, it will try to use the `lm`
      under `lf.context`.
    examples: Code correction examples to use.
    max_attempts: Max number of attempts for the correction.
    sandbox: If True, run code in sandbox; If False, run code in current
      process. If None, run in sandbox first, if the output could not be
      serialized and pass to current process, run the code again in current
      process.
    timeout: The timeout for running the corrected code. If None, there is no
      timeout. Applicable only when sandbox is set to True.
    returns_code: If True, the return value is a tuple of (result, final code).
      Otherwise the return value is the result only.

  Returns:
    Run result if `returns_code` is set to False (default), otherwise a tuple
      of (result, final code str).

  Raises:
    `lf.CodeError`: If code cannot be corrected after `max_attempts`.
  """
  # Delay import at runtime to avoid circular depenency.
  # pylint: disable=g-import-not-at-top
  from langfun.core.structured import completion  # pytype: disable=import-error
  # pylint: enable=g-import-not-at-top

  if max_attempts == 0:
    result = execution.run(
        code, global_vars=global_vars, sandbox=sandbox, timeout=timeout
    )
    return (result, code) if returns_code else result

  def result_and_error(code: str) -> tuple[Any, str | None]:
    try:
      result = execution.run(
          code,
          global_vars=global_vars,
          sandbox=sandbox,
          timeout=timeout,
      )
      return (result, None)
    except Exception as e:  # pylint: disable=broad-exception-caught
      return (None, _error_feedback_str(e))

  if error is None:
    result, error = result_and_error(code)
    if error is None:
      return (result, code) if returns_code else result

  examples = examples or DEFAULT_CODE_CORRECTION_EXAMPLES
  examples = [  # pylint: disable=g-complex-comprehension
      completion.completion_example(
          CodeCorrection.partial(ex.latest_code, ex.correction_history),
          ex,
      )
      for ex in examples
  ]

  history = []
  num_attempts = 0
  for _ in range(max_attempts):
    correction = CodeCorrection.partial(
        CodeWithError(code=code, error=error), history
    )
    num_attempts += 1

    # Catch completion error when the LM could not formulate a completion
    # structure.
    try:
      # Disable autofix for code correction to avoid recursion.
      correction = completion.complete(
          correction, lm=lm, examples=examples, autofix=0
      )
    except errors.CodeError:
      break

    history.append(CodeWithError(code=code, error=error))
    code = correction.corrected_code
    result, error = result_and_error(code)
    if error is None:
      return (result, code) if returns_code else result

  raise errors.CodeError(
      code,
      RuntimeError(
          f"Cannot correct code after {num_attempts} attempts. "
          f"Last error: {error}."
      ),
  )

def correct(
    code: str,
    error: str | None = None,
    *,
    global_vars: dict[str, Any] | None = None,
    lm: lf.LanguageModel = lf.contextual(),
    examples: list[CodeCorrection] | None = None,
    max_attempts: int = 5,
    sandbox: bool | None = None,
    timeout: int | None = 5,
) -> str:
  """Correct code with a language model via self-play.

  Args:
    code: The source code that may or may not be problematic.
    error: An optional initial error for `code` when it's problematic, usually
      caught from elsewhere when it ran. If None, code will be executed once to
      verify if its good and obtain a feedback error message.
    global_vars: A dict of str to value as the global variables that could be
      accessed within the corrected code.
    lm: Language model to be used. If not specified, it will try to use the `lm`
      under `lf.context`.
    examples: Code correction examples to use.
    max_attempts: Max number of attempts for the correction.
    sandbox: If True, run code in sandbox; If False, run code in current
      process. If None, run in sandbox first, if the output could not be
      serialized and pass to current process, run the code again in current
      process.
    timeout: The timeout for running the corrected code. If None, there is no
      timeout. Applicable only when sandbox is set to True.

  Returns:
    The final correct code if corrections are successful.

  Raises:
    `lf.CodeError`: If code cannot be corrected after `max_attempts`.
  """
  return run_with_correction(
      code,
      error=error,
      global_vars=global_vars,
      lm=lm,
      examples=examples,
      max_attempts=max_attempts,
      sandbox=sandbox,
      timeout=timeout,
      returns_code=True,
  )[1]

def _error_feedback_str(error: Exception) -> str:
  """Returns the error str for feedback."""
  if isinstance(error, errors.CodeError):
    return lf.text_formatting.decolored(
        error.format(include_complete_code=False)
    )
  else:
    return f"Encountered {error.__class__.__name__}: {error}"

DEFAULT_CODE_CORRECTION_EXAMPLES = [
    CodeCorrection(
        CodeWithError(
            inspect.cleandoc("""
                class A(pg.Object):
                  x: str

                a = A('1')
                b = a + 'foo'
                """),
            (
                "TypeError: unsupported operand type(s) for +: 'A' and "
                "'str' (<unknown>, line 5)\n"
                "  b = a + 'foo'"
            ),
        ),
        correction_history=[
            CodeWithError(
                inspect.cleandoc("""
                    class A(pg.Object):
                      x: str

                    a = A(1
                    b = a + 'foo'
                    """),
                (
                    "SyntaxError: '(' was never closed (<unknown>, line 4)\n"
                    "  a = A(1"
                ),
            ),
            CodeWithError(
                inspect.cleandoc("""
                    class A(pg.Object):
                      x: str

                    a = A(1)
                    b = a + 'foo'
                    """),
                (
                    "TypeError: Expect <class 'str'> but encountered "
                    "<class 'int'>: 1. (path=x) (<unknown>, line 4)\n"
                    "  a = A(1)"
                ),
            ),
        ],
        corrected_code=inspect.cleandoc("""
            class A(pg.Object):
              x: str

            a = A('1')
            b = a.x + 'foo'
            """),
    ),
]


from subprocess import check_call, CalledProcessError, TimeoutExpired
from runpy import run_module
from pathlib import Path
from typing import Optional
import argparse
import logging
logger = logging.getLogger(__name__)

class UpdateError(Exception):
    pass

def rm_tree(path: Path):
    for child in path.iterdir():
        if child.is_file():
            child.unlink()
        else:
            rm_tree(child)
    path.rmdir()

def get_new_nodeset(timeout: float = 120, tag: Optional[str] = None, branch: Optional[str] = None):
    cwd = Path(".")
    target_v1 = cwd / "UA-Nodeset"
    backup_v1 = target_v1.parent / (target_v1.name + "_backup")
    target_v2 = cwd / "UA-Nodeset-master"
    backup_v2 = target_v2.parent / (target_v2.name + "_backup")
    if target_v1.is_dir():
        target_v1.rename(str(target_v2))
    elif target_v2.is_dir():
        target_v2.rename(str(backup_v2))
    try:
        args = [
            "git",
            "clone",
            "--depth=1",
            "https://github.com/OPCFoundation/UA-Nodeset.git",
            "UA-Nodeset-master"
        ]
        if tag is not None:
            args.append(f"tags/{tag}")
        if branch is not None:
            args.append(branch)
        print(args)
        check_call(args, timeout=timeout)
    except CalledProcessError:
        if backup_v1.is_dir():
            backup_v1.rename(str(target_v1))
        elif backup_v2.is_dir():
            backup_v2.rename(str(target_v2))
            logger.warning("Failed to clone repository. Try continue with old Nodeset folder.")
        else:
            raise UpdateError("Nodeset neither existed nor was downloaded. Abort update.")
    except TimeoutExpired:
        raise UpdateError(f"Timeout expired - Waited {timeout} seconds to clone from repo."
                          f" Either change value or check network connection. Abort update.")
    if backup_v1.is_dir():
        rm_tree(backup_v1)
    elif backup_v2.is_dir():
        rm_tree(backup_v2)
    rm_tree(target_v2 / ".git")
    rm_tree(target_v2 / ".github")
    (target_v2 / "PublishNodeSets.bat").unlink()

def generate_standard_nodesets():
    run_module("generate_address_space", run_name="__main__")
    run_module("generate_event_objects", run_name="__main__")
    run_module("generate_ids", run_name="__main__")
    run_module("generate_protocol_python", run_name="__main__")
    run_module("generate_model_event", run_name="__main__")
    run_module("generate_event_objects", run_name="__main__")
    run_module("generate_statuscode", run_name="__main__")
    run_module("generate_uaerrors", run_name="__main__")

if __name__ == "__main__":
    parser = argparse.ArgumentParser(
        description="Update the ua nodeset from https://github.com/OPCFoundation/UA-Nodeset"
    )
    parser.add_argument("-b", "--branch", help="which git branch is used default UA-Nodeset-master")
    parser.add_argument("-t", "--tag", help="git tag is used default: no tag is used")
    args = parser.parse_args()
    logger.debug("Try creating new Nodeset from github source")
    get_new_nodeset(branch=args.branch, tag=args.tag)
    generate_standard_nodesets()
    logger.debug("UA Nodeset created successfully")


"""Flask Application
"""
from multiprocessing import Process
from flask import Flask, render_template
from get_info import read_info, read_old_info
from run_spider import run_spider

Process(target=run_spider).start()

app = Flask(__name__)

@app.route("/")
def crawl_info():
    """crawl inforamation
    """
    try:
        info: dict = read_info()
    except IOError:
        info: dict = read_old_info()
    table: dict = info['table_info']
    return render_template(
        "index.html",
        cases=info.get("cases"),
        deaths=info.get("deaths"),
        recovered=info.get("recovered"),
        country=table.get("country"),
        total_cases=table.get("total_cases"),
        new_cases=table.get("new_cases"),
        total_deaths=table.get("total_deaths"),
        new_deaths=table.get("new_deaths"),
        total_recovered=table.get("total_recovered"),
        new_recovered=table.get("new_recovered"),
        active_cases=table.get("active_cases"),
        population=table.get("population")
    )

if __name__ == "__main__":
    app.run()


from PIL import Image
import time
img =  Image.open("C:\\Users\\tiepl\\Desktop\\images.jpg")
img = img.convert('1')
row,col = img.size
img_data = img.getdata()
dem=0

for i in img_data:
	if(i==0):
		print("  ",end="")
	if(i==255):
		print("♥♥",end="")
	dem+=1
	if(dem %row ==0):
		print("")
time.sleep(9999)


import cv2
import numpy as np
import argparse

src1 = cv2.imread('./image/scr_home_0.bmp', cv2.IMREAD_COLOR)
crop = src1.copy()
crop = src1[0:300, 75:725]

cv2.imshow('origin', src1)
cv2.imshow('crop', crop)

cv2.waitKey()
cv2.destroyAllWindows()


"""
/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/
"""

__author__ = '3liz'
__date__ = '2019-02-15'
__copyright__ = '(C) 2019 by 3liz'

from typing import Any, List, Tuple, Union

from qgis.core import QgsProviderConnectionException, QgsProviderRegistry

def fetch_data_from_sql_query(connection_name: str, sql: str) -> Union[Tuple[Any, None], Tuple[List[Any], str]]:
    """Execute SQL and return the result."""
    metadata = QgsProviderRegistry.instance().providerMetadata('postgres')
    connection = metadata.findConnection(connection_name)
    try:
        result = connection.executeSql(sql)
        return result, None
    except QgsProviderConnectionException as e:
        return [], str(e)

def validateTimestamp(timestamp_text):
    from dateutil.parser import parse
    valid = True
    msg = ''
    try:
        parse(timestamp_text)
    except ValueError as e:
        valid = False
        msg = str(e)
    return valid, msg


import pygame
import pygame.freetype

pygame.init()

class Display:
    def __init__(self,y):
        self.y = y
        self.font_name = "assets/8-BIT WONDER.TTF"

    def draw_text(self, text, size, x, y, screen):
        font = pygame.font.Font(self.font_name, size)
        text_surface = font.render(text, True, (255, 255, 255))
        text_rect = text_surface.get_rect()
        text_rect.center = (x, y)
        screen.blit(text_surface, text_rect)


from os.path import join, dirname
from typing import List, Optional, Set, Tuple

Instructions = List[Tuple[str, int]]

class VM:
    def __init__(self, code: str) -> None:
        instructionsRaw = code.strip().split('\n')
        self.acc = 0
        self.oc = 0
        self.invert: Optional[int] = None
        self.instructions: Instructions = []
        for instruction in instructionsRaw:
            op, value = instruction.split(' ')
            self.instructions.append((op, int(value)))

    def reset(self):
        self.acc = 0
        self.oc = 0

    def exec(self):
        op, value = self.instructions[self.oc]
        if self.oc == self.invert:
            op = 'jmp' if op == 'nop' else 'nop'
        if op == 'nop':
            self.oc += 1
        elif op == 'acc':
            self.acc += value
            self.oc += 1
        elif op == 'jmp':
            self.oc += value

    def run(self) -> Tuple[int, bool]:
        self.reset()
        already_visited: Set[int] = set()
        m = len(self.instructions)
        while True:
            if self.oc in already_visited:
                return (self.acc, True)
            if not self.oc < m:
                return (self.acc, False)
            already_visited.add(self.oc)
            self.exec()

    def fix(self):
        for i, instruction in enumerate(self.instructions):
            op, _ = instruction
            if op == 'nop' or op == 'jmp':
                self.invert = i
                acc, error = self.run()
                if not error:
                    return acc

data = join(dirname(__file__), '../data.txt')
with open(data) as f:
    vm = VM(f.read())

    acc, err = vm.run()
    print(acc)

    print(vm.fix())


import boto3
from datetime import datetime

def lambda_handler(event, context):
    ec2 = boto3.resource('ec2', region_name='us-west-2')

    # Volume 'Name' Tag
    nametag = ''

    # List only unattached volumes ('available' vs. 'in-use')
    volumes = ec2.volumes.filter(
        Filters=[{'Name': 'status', 'Values': ['available']}])

    for volume in volumes:
        v = ec2.Volume(volume.id)

        # Get name tag
        nametag = ''
        for tag in v.tags:
            if tag['Key'] == 'Name':
                nametag = tag['Value']

        if not nametag:
            nametag = 'Volume did not have Name tag'

        print('Creating snapshot on: {} for {}'.format(datetime.today().strftime('%Y-%m-%d-%H:%M:%S'), v.id))
        # snap = v.create_snapshot(Description='Created by ebs-reaper lambda, Tagged: ' +  nametag)


# this script loads json to relational model
import json
import sqlite3
from argparse import ArgumentParser
from datetime import datetime

# faker required for generating user names etc.
from faker import Faker

# load the constants and configurations
import configuration as conf
import sqlHelpers as SQL

# function to check how many records are present by inspecting the first line
def extractCount(firstLine):
    # starting of string
    findStr1 = '_count":'
    findStr2 = '}'

    # get position1, then add length of str to get end pos
    pos1 = firstLine.find(findStr1)
    pos1 += len(findStr1)

    # find closing bracket from end position
    pos2 = firstLine.find(findStr2, pos1)

    # convert the string to integer
    return int(firstLine[pos1:pos2])

# get formatted sql for inserting into tables
def genInsertSql(props):
    # name: from prop name
    # insert props: from prop insert list
    # param styles: list of ? size of the insert props
    sqlStr = SQL.insert_rel_table.format(
        table_name=props['name'],
        insert_props=', '.join(props['insert']),
        param_styles=', '.join(['?' for _ in range(len(props['insert']))])
    )

    return sqlStr

# prepare and email address from a name
def genEmailFromName(name):
    email = name.lower()

    # spaces replaced by underscores, dots or just removed.
    email = email.replace(' ', fake.random_choices(elements=('_', '.', ''), length=1)[0])

    # concatenate with a random email domain
    email = f'{email}@{fake.domain_name()}'

    return email

# create new user and insert into db with provided id
def insertUser(db, user_id):
    insertSql = genInsertSql(SQL.user_table_props)

    # get random name and generate email
    name = fake.name()
    email = genEmailFromName(name)

    insertTuple = (user_id, name, email)

    # execute insert and commit
    db.execute(insertSql, insertTuple)
    db.commit()

# create new agent and insert into db with provided id
def insertAgent(db, agent_id):
    insertSql = genInsertSql(SQL.agent_table_props)

    # get random name and TFN
    name = fake.name()
    tfn = fake.bothify(text='###-###-###')

    insertTuple = (agent_id, name, tfn)

    # execute insert and commit
    db.execute(insertSql, insertTuple)
    db.commit()

# insert ticket according to provided instance
def insertTicket(db, instance):
    insertSql = genInsertSql(SQL.ticket_table_props)

    activity = instance['activity']

    # insert values
    insertTuple = (
        instance['ticket_id'],
        instance['performer_type'],
        instance['performer_id'],
        activity['shipping_address'],
        activity['shipment_date'],
        conf.CATEGORY_LIST.index(activity['category']),
        conf.ISSUE_LIST.index(activity['issue_type']),
        conf.GROUP_LIST.index(activity['group']),
        conf.PRODUCT_LIST.index(activity['product'])
    )

    # execute insert and commit
    db.execute(insertSql, insertTuple)
    db.commit()

def insertActivity(db, activity_id, instance):
    insertSql = genInsertSql(SQL.activity_table_props)

    activity = instance['activity']

    date_string = instance['performed_at']

    # convert string to datetime, later converted to Unix Timestamp
    dt = datetime.strptime(date_string, conf.PERFORMED_DATE_FORMAT)

    # insert values
    insertTuple = (
        activity_id,
        instance['ticket_id'],
        dt.timestamp(),
        activity['contacted_customer'],
        activity['source'],
        conf.STATUS_LIST.index(activity['status']),
        activity['priority'],
        activity['agent_id'],
        activity['requester']
    )

    # execute insert and commit
    db.execute(insertSql, insertTuple)
    db.commit()

# driver function for reading and writing
def convert(inFileName, db):
    with open(inFileName, 'r') as inFile:
        # read first line and get number of values
        firstLine = inFile.readline()
        count = extractCount(firstLine)

        # declare empty sets for tracking new IDs
        userIdSet = set()
        agentIdSet = set()
        ticketIdSet = set()

        for activity_id in range(count):
            # read one line and convert to string
            line = inFile.readline()

            instance = json.loads(line[:-2])
            activity = instance['activity']

            # check if user is new, insert to db
            if instance['performer_id'] not in userIdSet:
                insertUser(db, instance['performer_id'])
                userIdSet.add(instance['performer_id'])

            # check if agent is new, insert to db
            if activity['agent_id'] not in agentIdSet:
                insertAgent(db, activity['agent_id'])
                agentIdSet.add(activity['agent_id'])

            # check if ticket_id is new, insert to db
            if instance['ticket_id'] not in ticketIdSet:
                insertTicket(db, instance)
                ticketIdSet.add(instance['ticket_id'])

            # insert the activity to db, with loop counter as id
            insertActivity(db, activity_id, instance)

def main():
    argParser = ArgumentParser()
    argParser.add_argument('-i', type=str, default=conf.DEFAULT_JSON_FILE)
    argParser.add_argument('-o', type=str, default=conf.DEFAULT_SQLITE_FILE)

    parsed = argParser.parse_args()
    inFileName = parsed.i
    outFileName = parsed.o

    print(f"{inFileName} -> {outFileName}")

    db = SQL.create_connection(outFileName)

    if db is None:
        print(f'Error establishing connection with DB.')
        return

    try:
        convert(inFileName, db)
        print(f'Database conversion done!')

    except sqlite3.Error as e:
        print(e)
        print(f'DB error occurred, Did you run create script first?')

if __name__ == '__main__':
    fake = Faker(conf.FAKER_LOCALE)
    main()


from django.db import models
from users.models import User
from drevo.models.category import Category

class CategoryExpert(models.Model):
    """
    Класс для описания привязки экспертов к категориям
    """
    title = 'Вид "Эксперта"'
    expert = models.ForeignKey(
        User,
        on_delete=models.CASCADE,
        verbose_name='Эксперт',
        related_name='expert'
    )
    categories = models.ManyToManyField(
        Category,
        related_name='category_list',
        verbose_name='Список категорий'
    )

    def __str__(self):
        return f'{self.expert}'

    def save(self, *args, **kwargs):
        """
        Переопределяем метод save для того, чтобы у пользователя установить флаг is_expert
        """
        user = User.objects.get(id=self.expert.id)
        user.is_expert = True
        user.save()
        super().save(*args, **kwargs)

    class Meta:
        verbose_name = 'Эксперта'
        verbose_name_plural = 'Эксперты'