import re
import sqlite3
import os
import glob
import datetime as dt
import bokeh.models
import xarray
import numpy as np
from functools import lru_cache
from forest.exceptions import FileNotFound, IndexNotFound
from forest.old_state import old_state, unique
import forest.util
import forest.map_view
from forest import geo, locate

ENGINE = "h5netcdf"
MIN_DATETIME64 = np.datetime64("0001-01-01T00:00:00.000000")

def _natargmax(arr):
    """Find the arg max when an array contains NaT's"""
    no_nats = np.where(np.isnat(arr), MIN_DATETIME64, arr)
    return np.argmax(no_nats)

class Dataset:
    def __init__(self, pattern=None, database_path=None, minutes=15, **kwargs):
        self.pattern = pattern
        if database_path is None:
            database_path = ":memory:"
        self.database = Database(database_path)
        self.locator = Locator(self.pattern, self.database, minutes=minutes)

    def navigator(self):
        return Navigator(self.locator, self.database)

    def map_view(self, color_mapper):
        loader = Loader(self.locator)
        return forest.map_view.map_view(
            loader, color_mapper, use_hover_tool=False
        )

class Database:
    """Meta-data store for EIDA50 dataset"""

    def __init__(self, path=":memory:"):
        self.fmt = "%Y-%m-%d %H:%M:%S"
        self.path = path
        self.connection = sqlite3.connect(self.path)
        self.cursor = self.connection.cursor()

        # Schema
        query = """
            CREATE TABLE IF NOT EXISTS file (
                      id INTEGER PRIMARY KEY,
                    path TEXT,
                         UNIQUE(path));
        """
        self.cursor.execute(query)
        query = """
            CREATE TABLE IF NOT EXISTS time (
                      id INTEGER PRIMARY KEY,
                    time TEXT,
                 file_id INTEGER,
                 FOREIGN KEY(file_id) REFERENCES file(id));
        """
        self.cursor.execute(query)

    def __enter__(self):
        return self

    def __exit__(self, *args):
        self.close()

    def close(self):
        """Close connection gracefully"""
        self.connection.commit()
        self.connection.close()

    def insert_times(self, times, path):
        """Store times"""
        # Update file table
        query = """
            INSERT OR IGNORE INTO file (path) VALUES (:path);
        """
        self.cursor.execute(query, {"path": path})
        self.connection.commit()

        # Update time table
        query = """
            INSERT INTO time (time, file_id)
                 VALUES (:time, (SELECT id FROM file WHERE path = :path));
        """
        texts = [time.strftime(self.fmt) for time in times]
        args = [{"path": path, "time": text} for text in texts]
        self.cursor.executemany(query, args)
        self.connection.commit()

    def fetch_times(self, path=None):
        """Retrieve times"""
        if path is None:
            query = """
                SELECT time
                  FROM time;
            """
            rows = self.cursor.execute(query)
        else:
            query = """
                SELECT time.time
                  FROM time
                  JOIN file
                    ON file.id = time.file_id
                 WHERE file.path = :path;
            """
            rows = self.cursor.execute(query, {"path": path})
        rows = self.cursor.fetchall()
        texts = [text for text, in rows]
        times = [dt.datetime.strptime(text, self.fmt) for text in texts]
        return list(sorted(times))

    def fetch_paths(self):
        """Retrieve paths"""
        query = """
            SELECT path FROM file;
        """
        rows = self.cursor.execute(query).fetchall()
        texts = [text for text, in rows]
        return list(sorted(texts))

class Locator:
    """Locate EIDA50 satellite images"""

    def __init__(self, pattern, database, minutes=15):
        self.minutes = minutes
        self.pattern = pattern
        self._glob = forest.util.cached_glob(
            dt.timedelta(minutes=self.minutes)
        )
        self.database = database

    def all_times(self, paths):
        """All available times"""
        # Parse times from file names not in database
        unparsed_paths = []
        filename_times = []
        database_paths = set(self.database.fetch_paths())
        for path in paths:
            if path in database_paths:
                continue
            time = self.parse_date(path)
            if time is None:
                unparsed_paths.append(path)
                continue
            filename_times.append(time)

        # Store unparsed files in database
        for path in unparsed_paths:
            times = self.load_time_axis(path)  # datetime64[s]
            self.database.insert_times(times.astype(dt.datetime), path)

        # All recorded times
        database_times = self.database.fetch_times()

        # Combine database/timestamp information
        arrays = [
            np.array(database_times, dtype="datetime64[s]"),
            np.array(filename_times, dtype="datetime64[s]"),
        ]
        return np.unique(np.concatenate(arrays))

    def find(self, paths, date):
        """Find file and index related to date

        .. note:: Find should not write to disk
        """
        # Search file system
        path = self.find_file(paths, date)

        # Load times from database
        if path in self.database.fetch_paths():
            times = self.database.fetch_times(path)
            times = np.array(times, dtype="datetime64[s]")
        else:
            times = self.load_time_axis(path)  # datetime64[s]

        index = self.find_index(
            times, date, dt.timedelta(minutes=self.minutes)
        )
        return path, index

    def glob(self):
        return self._glob(self.pattern)

    @staticmethod
    @lru_cache()
    def load_time_axis(path):
        with xarray.open_dataset(path, engine=ENGINE) as nc:
            values = nc["time"]
        return np.array(values, dtype="datetime64[s]")

    def find_file(self, paths, user_date):
        """ "Find file likely to contain user supplied date

        .. note:: Search based on timestamp only

        .. warning:: Not suitable for searching unparsable file names
        """
        if isinstance(user_date, (dt.datetime, str)):
            user_date = np.datetime64(user_date, "s")
        dates = np.array(
            [self.parse_date(path) for path in paths], dtype="datetime64[s]"
        )
        mask = ~(dates <= user_date)
        if mask.all():
            msg = "No file for {}".format(user_date)
            raise FileNotFound(msg)
        before_dates = np.ma.array(dates, mask=mask, dtype="datetime64[s]")
        i = _natargmax(before_dates.filled())
        return paths[i]

    @staticmethod
    def find_index(times, time, length):
        """Search for index inside array of datetime64[s] values"""
        print(f"FIND_INDEX: {times} {time} {length}")
        dtype = "datetime64[s]"
        if isinstance(times, list):
            times = np.asarray(times, dtype=dtype)
        if isinstance(time, (dt.datetime, str)):
            time = np.datetime64(time, "s")
        bounds = locate.bounds(times, length)
        inside = locate.in_bounds(bounds, time)
        valid_times = np.ma.array(times, mask=~inside)
        if valid_times.mask.all():
            msg = "{}: not found".format(time)
            raise IndexNotFound(msg)
        return _natargmax(valid_times.filled())

    @staticmethod
    def parse_date(path):
        """Parse timestamp into datetime or None"""
        for regex, fmt in [
            (r"([0-9]{8})\.nc", "%Y%m%d"),
            (r"([0-9]{8}T[0-9]{4}Z)\.nc", "%Y%m%dT%H%MZ"),
        ]:
            groups = re.search(regex, path)
            if groups is None:
                continue
            else:
                return dt.datetime.strptime(groups[1], fmt)

class Loader:
    def __init__(self, locator):
        self.locator = locator
        self.empty_image = {"x": [], "y": [], "dw": [], "dh": [], "image": []}
        self.cache = {}
        paths = self.locator.glob()
        if len(paths) > 0:
            with xarray.open_dataset(paths[-1], engine=ENGINE) as nc:
                self.cache["longitude"] = nc["longitude"].values
                self.cache["latitude"] = nc["latitude"].values

    @property
    def longitudes(self):
        return self.cache["longitude"]

    @property
    def latitudes(self):
        return self.cache["latitude"]

    def image(self, state):
        if state.valid_time is None:
            data = self.empty_image
        else:
            try:
                data = self._image(forest.util.to_datetime(state.valid_time))
            except (FileNotFound, IndexNotFound) as e:
                print(f"EIDA50: {e}")
                data = self.empty_image
        return data

    def _image(self, valid_time):
        paths = self.locator.glob()
        path, itime = self.locator.find(paths, valid_time)
        return self.load_image(path, itime)

    def load_image(self, path, itime):
        lons = self.longitudes
        lats = self.latitudes
        with xarray.open_dataset(path, engine=ENGINE) as nc:
            values = nc["data"][itime].values

        # Use datashader to coarsify images from 4.4km to 8.8km grid
        scale = 2
        return geo.stretch_image(
            lons,
            lats,
            values,
            plot_width=int(values.shape[1] / scale),
            plot_height=int(values.shape[0] / scale),
        )

class Navigator:
    """Facade to map Navigator API to Locator"""

    def __init__(self, locator, database):
        self.locator = locator
        self.database = database

    def __call__(self, store, action):
        """Middleware interface"""
        import forest.db.control

        kind = action["kind"]
        if kind == forest.db.control.SET_VALUE:
            key, time = (action["payload"]["key"], action["payload"]["value"])
            if key == "valid_time":
                # Detect missing file
                paths = self.locator.glob()
                path = self.locator.find_file(paths, time)
                if path not in self.database.fetch_paths():
                    times = self.locator.load_time_axis(path)  # datetime64[s]
                    self.database.insert_times(times.astype(dt.datetime), path)

                # Update stale state
                store_times = store.state.get("valid_times", [])
                database_times = self._times()
                if len(store_times) != len(database_times):
                    yield forest.db.control.set_value(
                        "valid_times", database_times
                    )
        yield action

    def variables(self, pattern):
        return ["EIDA50"]

    def initial_times(self, pattern, variable):
        return [dt.datetime(1970, 1, 1)]

    def valid_times(self, pattern, variable, initial_time):
        """Get available times given application state"""
        return self._times()

    def _times(self):
        """Get available times given user selected valid_time

        :param valid_time: application state valid time
        """
        paths = self.locator.glob()
        datetimes = self.locator.all_times(paths)
        return np.array(datetimes, dtype="datetime64[s]")

    def pressures(self, pattern, variable, initial_time):
        return []


import pandas as pd, numpy as np
import re
import pickle
dataset = pd.read_csv("Restaurant_Reviews.csv")

dataset['Review']

import nltk
nltk.download('stopwords')
from nltk.corpus import stopwords
from nltk.stem.porter import PorterStemmer
corpus = []
for i in range(0, 1000):
  review = re.sub('[^a-zA-Z]', ' ', dataset['Review'][i])
  review = review.lower()
  review = review.split()
  ps = PorterStemmer()
  all_stopwords = stopwords.words('english')
  all_stopwords.remove('not')
  review = [ps.stem(word) for word in review if not word in set(all_stopwords)]
  review = ' '.join(review)
  corpus.append(review)

from sklearn.feature_extraction.text import CountVectorizer
cv = CountVectorizer(max_features = 1500)
X = cv.fit_transform(corpus).toarray()
y = dataset.iloc[:, -1].values

from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size = 0.30, random_state=42)

# Creating a pickle file for the CountVectorizer
pickle.dump(cv, open('cv-transform.pkl', 'wb'))

from sklearn.naive_bayes import MultinomialNB
classifier = MultinomialNB(alpha=0.2)
classifier.fit(X_train, y_train)

y_pred = classifier.predict(X_test)
print(np.concatenate((y_pred.reshape(len(y_pred),1), y_test.reshape(len(y_test),1)),1))

from sklearn.metrics import confusion_matrix, accuracy_score
cm = confusion_matrix(y_test, y_pred)
print(cm)
accuracy_score(y_test, y_pred)

pickle.dump(classifier,open('model.pkl','wb'))


from dataclasses import field
from rest_framework import serializers
from django.contrib.auth import get_user_model
import requests
import json
from apps.core.models import UserRole

from apps.core.serializers import MyAccountSerializer, UserAppointment, UserSerializer
from apps.profiles.serializers import CounsellorProfileSerializer, ListCounsellorProfileSerializer

from .models import *
from zoom_api_functions import generateToken

# UserRole = get_user_model()

class AppointmentRequestSerializer(serializers.ModelSerializer):
    class Meta:
        model = AppointmentRequest
        fields = '__all__'

class ListAppointmentRequestSerializer(serializers.ModelSerializer):
    requested_by = MyAccountSerializer()
    # counsellor =
    class Meta:
        model = AppointmentRequest
        fields = '__all__'

class CreateAppointmentSerializer(serializers.ModelSerializer):
    appointment_meeting = serializers.ReadOnlyField()
    class Meta:
        model = Appointment
        fields = '__all__'

class ListAppointmentSerializer(serializers.ModelSerializer):
    class Meta:
        model = Appointment
        fields = '__all__'

class AppointmentDetailsSerializer(serializers.ModelSerializer):
    user = MyAccountSerializer(read_only=True)
    counsellor = ListCounsellorProfileSerializer(read_only=True)

    class Meta:
        model = Appointment
        fields = '__all__'

class LiveMeetingSerializer(serializers.ModelSerializer):
    zoom_meeting = serializers.ReadOnlyField()
    status = serializers.ReadOnlyField(default='upcoming')
    api_url = serializers.ReadOnlyField(required=False)
    start_meeting_url = serializers.ReadOnlyField(required=False)
    invitation_url = serializers.ReadOnlyField(source='get_meeting_invitation')

    class Meta:
        model = AppointmentMeeting
        fields = '__all__'

class GetAppointmentMeetingSerializer(serializers.ModelSerializer):
    zoom_meeting = serializers.SerializerMethodField()
    status = serializers.ReadOnlyField(default='upcoming')
    join_meeting_url = serializers.ReadOnlyField(source='get_join_meeting_url')
    invitation_url = serializers.ReadOnlyField(source='get_meeting_invitation')
    api_url = serializers.ReadOnlyField(source='get_api_url')

    class Meta:
        model = AppointmentMeeting
        exclude = ('created_by', 'created_date',
                   'last_modified_by', 'last_modified_date',)

    def get_zoom_meeting(self, obj):
        meeting_id = obj.zoom_meeting_id
        # Getting the Zoom Meeting
        headers = {'authorization': 'Bearer %s' % generateToken(),
                   'content-type': 'application/json'}
        r = requests.get(
            f'https://api.zoom.us/v2/meetings/{meeting_id}',
            headers=headers)
        # print(r.json())
        return r.json()

class DashboardSerializer(serializers.ModelSerializer):
        total_counsellor_user = serializers.SerializerMethodField()
        total_business_user = serializers.SerializerMethodField()
        total_customer_user = serializers.SerializerMethodField()

        class Meta:
            model = UserRole
            fields = ['total_counsellor_user', 'total_business_user', 'total_customer_user']

        def get_total_counsellor_user(self, obj):
            return UserRole.objects.filter(user_role__title="Counsellor").count()

        def get_total_business_user(self, obj):
            return UserRole.objects.filter(user_role__title="Business").count()

        def get_total_customer_user(self, obj):
            return UserRole.objects.filter(user_role__title="Customer").count()

class ReportSerializer(serializers.ModelSerializer):
    class Meta:
        model = Report
        fields = '__all__'

class ReportInfoSerializer(serializers.ModelSerializer):
    appointment = AppointmentDetailsSerializer(read_only=True)
    counsellor = ListCounsellorProfileSerializer(read_only=True)
    reported_by = MyAccountSerializer(read_only=True)

    class Meta:
        model = Report
        fields = '__all__'

class FeedbackSerializer(serializers.ModelSerializer):
    class Meta:
        model = Feedback
        fields = '__all__'

class FeedbackInfoSerializer(serializers.ModelSerializer):
    appointment = AppointmentDetailsSerializer(read_only=True)
    counsellor = ListCounsellorProfileSerializer(read_only=True)
    feedback_by = MyAccountSerializer(read_only=True)

    class Meta:
        model = Feedback
        fields = '__all__'


from appFile import request, app, ma, cross_origin
from models.index import db, Events, Timeline
from flask import jsonify
from controllers.auth import JWTcheck

class EventSchema(ma.Schema):
  class Meta:
    fields = ('id', 'name', 'info', 'timeline_id', 'day', 'month', 'year', 'time', 'date_created')

event_schema = EventSchema()

@app.route('/addEvent', methods=['POST'])
@cross_origin()
@JWTcheck
def addEvent(data):
  info = request.json['info']
  name = request.json['name']
  timeline_id = request.json['timeline_id']
  day = request.json['day']
  month = request.json['month']
  year = request.json['year']
  time = request.json['time']

  new_event = Events(name, info, timeline_id, day, month, year, time)

  db.session.add(new_event)
  db.session.commit()
  return event_schema.jsonify(new_event)

@app.route('/getEvents', methods=['POST'])
@cross_origin()
@JWTcheck
def getEvents(data):
  timelineID = request.json['timeline_id']

  if (Timeline.query.filter_by(id=timelineID).first().user_id!=data['user']):
    return jsonify(message='You do not own this timeline'), 403

  events = Events.query.filter_by(timeline_id=timelineID).all()

  output = []
  for element in events:
    output.append({'name':element.name, 'id':element.id, 'info':element.info, 'day':element.day, 'month':element.month, 'year':element.year, 'time':element.time})

  return jsonify(output)

@app.route('/eventEdit', methods=['PATCH'])
@cross_origin()
@JWTcheck
def eventEditInfo(data):
  id = request.json['id']
  info = request.json['info']
  name = request.json['name']
  day = request.json['day']
  month = request.json['month']
  year = request.json['year']
  time = request.json['time']

  event = Events.query.filter_by(id=id).first()

  if (Timeline.query.filter_by(id=event.timeline_id).first().user_id!=data['user']):
    return jsonify(message='You do not own this timeline'), 403

  event.info = info
  event.name = name
  event.day = day
  event.month = month
  event.year = year
  event.time = time
  db.session.commit()

  return jsonify({'name':event.name,'info':event.info, 'day':event.day, 'month':event.month, 'year':event.year, 'time':event.time, 'id':event.id})


import re

from utils import logger

BIO_PATTERN = re.compile(r'\[(.+?)\](_[A-Z]{3}_)')
BIO_BEGIN = re.compile(r'_[A-Z]{3}_\[')
BIO_END = re.compile(r'\]_[A-Z]{3}_')

def pre_process(text):
    text = BIO_PATTERN.sub(r'\2[\1]\2', text)
    i = 0
    res = []
    label = ''
    is_begin = False
    is_inner = False
    length = len(text)
    while i < length:
        c = text[i]
        if '_' == c and i + 5 < length and BIO_BEGIN.fullmatch(text, i, i + 6):
            label = text[i + 1:i + 4]
            i += 6
            is_begin = True
            is_inner = True
            continue
        if ']' == c and i + 5 < length and BIO_END.fullmatch(text, i, i + 6) and is_inner:
            i += 6
            is_inner = False
            continue

        if is_begin:
            res.append([c, f'B-{label}'])
            is_begin = False
        elif is_inner:
            res.append([c, f'I-{label}'])
        else:
            res.append([c, 'O'])
        i += 1
    return res

def post_process(text, predict):
    res = []
    end_idx = len(text) - 1
    for i in range(end_idx):
        token = text[i]
        label1 = predict[i]
        label2 = predict[i + 1]
        if 'O' == label1:
            res.append(token)
        else:
            if label1.startswith('B-'):
                res.append('[')
            res.append(token)
            if not label2.startswith('I-'):
                res.append(f']_{label1[2:5]}_')
    token = text[end_idx]
    label1 = predict[end_idx]
    if label1.startswith('B-'):
        res.append('[')
    res.append(token)
    if 'O' != label1:
        res.append(f']_{label1[2:5]}_')
    return ''.join(res)

if __name__ == '__main__':
    text = '我喷了[chanel]_PRO_香水，在[澳大利亚]_LOC_吃着[肯德基]_COM_炸鸡'
    bio = pre_process(text)
    labeled = post_process([char[0] for char in bio], [char[1] for char in bio])
    logger.info(text)
    logger.info(bio)
    logger.info(labeled)
    logger.info(f'check status: {text == labeled}')


import os
import requests
import shutil
import boto3
from PIL import Image
import urllib.request

access_key_src = os.environ.get("AWS_ACCESS_KEY_ID")
secret_key_src = os.environ.get("AWS_SECRET_ACCESS_KEY")
region_name = os.environ.get("AWS_REGION_NAME")
bucket_name = os.environ.get("AWS_MEDIA_BUCKET_NAME")
bucket_folder = os.environ.get("AWS_BUCKET_BASE_FOLDER","")

def convert_image_name(old_name, append_name):
    names = old_name.split(".")
    extension = names[1]
    new_name = names[0] + "-" + append_name + "." + extension
    return new_name

def create_thumb_s3(image_file):
    s3 = boto3.resource('s3', region_name=region_name,
                        aws_access_key_id=access_key_src,
                        aws_secret_access_key=secret_key_src)
    bucket = s3.Bucket(bucket_name)

    if not os.path.exists("__sized__/products"):
        os.makedirs("__sized__/products")

    keys = {
        "products": [
            ("product_gallery", "thumbnail__540x540"),
            ("product_gallery_2x", "thumbnail__1080x1080"),
            ("product_small", "thumbnail__60x60"),
            ("product_small_2x", "thumbnail__120x120"),
            ("product_list", "thumbnail__255x255"),
            ("product_list_2x", "thumbnail__510x510"),
        ]
    }
    for output in keys['products']:
        values = output[1].split("__")
        if len(values) != 2:
            return
        output_size_str = values[1].split("x")
        output_size = tuple(list(map(int, output_size_str)))

        img = Image.open(image_file)

        img.thumbnail(output_size)
        image_path_thumb = convert_image_name(
            image_file.replace("images", "__sized__/products"),
            values[0] + "-" + values[1])

        img.save(image_path_thumb)

        new_image_name = bucket_folder+image_path_thumb.replace(
            "app/media/", "")

        bucket.upload_file(image_path_thumb, new_image_name,
                           ExtraArgs={"ContentType": "image/webp"})

        os.remove(image_path_thumb)

    os.remove(image_file)

    pass

    """download image from url
    """

def download_image(url):
    fileName = url.split("/")[-1]
    fileName = os.path.join("images", fileName)
    path = os.path.dirname(fileName)
    if not os.path.exists(path):
        os.makedirs(path)
    opener = urllib.request.URLopener()
    opener.addheader('User-Agent', 'Upload media service')
    fileName, headers = opener.retrieve(url, fileName)
    # fileName = opener.retrieve(url, fileName)
    return fileName


import json
import pandas as pd
import numpy as np

def processing_title(title):
    return title.upper()

def processing_author(author):
    return author.title()

def processing_discount(discount):
    try:
        discount = discount.replace('%dcto', '')
        return int(discount)
    except ValueError:
        return 0

def processing_price_now(price):
    try:
        price = price.replace('.','')[2:]
        return int(price)
    except ValueError:
        return np.nan

def processing_price_before(row):
    if row['Discount'] == 0:
        return row['Price now']
    else:
        price = row['Price before'].replace('.','')[2:]
        return int(price)

def processing_info(info):
    info = info.split(',')
    info_sorted = [i.strip().upper() for i in info]
    return info_sorted

def extracting_editorial(info_sorted):
    editorial = info_sorted[0]
    return editorial

def extracting_year(info_list):
    for i in info_list:
        if i.isdigit() and len(i) == 4:
            return i
    return np.nan

def process_data(df):

    df['Title'] = df['Title'].apply(processing_title)

    df['Author'] = df['Author'].apply(processing_author)

    df['Discount'] = df['Discount'].apply(processing_discount)

    df['Price now'] = df['Price now'].apply(processing_price_now)

    df['Price before'] = df.apply(processing_price_before, axis=1)

    df['Info_p'] = df['Info'].apply(processing_info)

    df['Editorial'] = df['Info_p'].apply(extracting_editorial)

    df['Year'] = df['Info_p'].apply(extracting_year)
    df['Year'] = df['Year'].fillna(0).astype('int64')

    df = df.drop(['Info', 'Info_p'], axis='columns')

    return df

if __name__ == "__main__":

    data_json = r"E:\Practicas\Proyects\book_tracker_buddy\data\buscabooks.json"
    with open(data_json) as file:
        books = json.load(file)

    df = pd.DataFrame(books)
    df = process_data(df)

    data_csv = r"E:\Practicas\Proyects\book_tracker_buddy\data\buscabooks_process.csv"
    df.to_csv(data_csv, index=False)


from werkzeug.utils import secure_filename
import sqlite3
import json
import os

class DatabaseHelper:
    """
    This help to manage databases.
    """

    def __init__(self) -> None:
        pass

    def save_image(self, main_dir: str, user_token: str, images: dict) -> bool:
        """
        save_image() will save given images to given path.

        Args:
            main_dir (str): Main directory
            user_token (str): Sub directory (image directory)
            images (dict): Set of images

        Returns:
            bool:  Return code (False -> Failed, True -> Success)
        """
        try:
            os.mkdir(f"{main_dir}/{user_token}")

            for img in images:
                img_name, img_ext = os.path.splitext(
                    secure_filename(images[img].filename)
                )

                images[img].filename = f"{img}{img_ext}"

                location = os.path.join(main_dir, user_token, images[img].filename)

                images[img].save(location)

            return True  # Success

        except Exception as e:
            print(f"An Error occured : {e}")

            return False  # Failure


import heapq
from collections import defaultdict
from typing import List

start_and_end_times = [[4, 7], [2, 5], [1, 3], [5, 8]]

# test_tuples = [(1, 5), (3, 4), (3, 3), (2, 1), (2, 7), (1, 1)]
# heapq.heapify(test_tuples)

def max_concurrency_best(s):
    """Naive method to solve max_concurrency. Passes all tests!"""
    heap = []
    count = 0
    answer = 0
    heapq.heapify(s)
    if s:
        # from the min start time to the max end time
        for i in range(s[0][0], s[len(s) - 1][1]):
            for start_and_end_time in s:
                if i == start_and_end_time[0]:
                    count += 1
                    if len(heap) < 1:
                        heapq.heappush(heap, count)
                    elif len(heap) == 1:
                        # fast push, then pop
                        heapq.heappushpop(heap, count)
                    else:
                        heapq.heappop(heap)
                elif i == start_and_end_time[1]:
                    count -= 1
        if heap:
            answer = heap[0]
    return answer

def max_concurrency_attempt_1(s):
    """Efficient method to solve max_concurrency. Passes 4 tests."""
    heap = []
    start = 0
    end = 1
    heapq.heapify(s[:])
    if s:
        count = len(s)
        heapq.heappush(heap, s[0])
        for i in range(len(s)):
            top = s[0]
            if top[end] < s[i][start]:
                heapq.heappush(heap, s[i])
                count -= 1
            elif top[end] < s[i][end]:
                # top[end] = s[i][end]
                heapq.heappop(heap)
                heapq.heappush(heap, top)
                count += 1
    else:
        count = 0
    return count

def max_concurrency_attempt_2(s):
    """Efficient method to solve max_concurrency. Doesn't even compile."""
    import heapq
    h = []
    heapq.heappush(h, 10000)
    res = 0
    for (start, end) in s:
        while heapq.nsmallest(1, h)[0] < s:
            heapq.heappop(h)
        res = res + len(h) - 1
        heapq.heappush(h, end)
    return res

def max_concurrency_attempt_3(s):
    """Fails 9 tests."""
    es = []
    for start, end in s:
        es.append((start, -1))
        es.append((end, 1))
    es.sort()
    result = 0
    n = 0
    for start, s in es:
        if s == -1:
            result += n
        n -= s
    return result

def max_concurrency_attempt_4(intervals):
    """Fails all tests."""
    intervals = sorted(intervals)
    first_start, first_end = intervals[0]
    current_intervals = []
    heapq.heappush(current_intervals, first_start)
    overlaps = 0

    for start, end in intervals[1:]:
        if current_intervals:
            if current_intervals[0] < start:
                heapq.heappop(current_intervals)
                overlaps += len(current_intervals)
    return overlaps

def max_concurrency_attempt_5(points):
    """
    If len of intervals is 0 or 1 return immediately. Passes 2 tests.
    """
    if len(points) in [0, 1]:
        return len(points)

    """
    Push all intervals to heap
    """
    q = []
    for point in points:
        heapq.heappush(q, (point[0], point[1]))

    """
    Start with the top element in the heap
    """
    cur = heapq.heappop(q)
    cur_max = cur[1]
    res = []

    while q:
        point = heapq.heappop(q)

        """
        check if the last seen max falls in the new point interval. If it is then change the last seen max
        """
        if point[0] <= cur_max:
            cur_max = min(cur_max, point[1])

        else:
            """
            In this case last seen max doesn't fall in the new point interval. Push last seen max to ans and then res assign to new point end
            """
            res.append(cur_max)
            cur_max = point[1]
    res.append(cur_max)
    return len(res)

def max_concurrency(s):
    concurrent_jobs = 0
    max_jobs = 0
    heap = []
    if s:
        for start, end in s:
            heapq.heappush(heap, end)
            while heap[0] <= start:
                heapq.heappop(heap)
                concurrent_jobs -= 1
            concurrent_jobs += 1
            if concurrent_jobs > max_jobs:
                max_jobs = concurrent_jobs
    return max_jobs

def max_concurrency_attempt_7(self, trips: List[List[int]], capacity: int) -> bool:
    """Does not compile."""
    start = defaultdict(int)
    end = defaultdict(int)
    for trip in trips:
        start[trip[1]] += trip[0]
        end[trip[2]] += trip[0]
    curr = 0
    heapStart = []
    for key, value in start.items():
        heapq.heappush(heapStart, (key, value))
    heapEnd = []
    for key, value in end.items():
        heapq.heappush(heapEnd, (key, value))
    while heapStart:
        if heapEnd[0][0] <= heapStart[0][0]:
            curr -= heapq.heappop(heapEnd)[1]
        else:
            if heapStart[0][1] + curr > capacity:
                return False
            curr += heapq.heappop(heapStart)[1]
    return True

def max_concurrency_attempt_8(s):
    """Passes 4 tests."""
    intervalPoints = []
    for interval in s:
        heapq.heappush(intervalPoints, (interval[0], -1))
        heapq.heappush(intervalPoints, (interval[1], 1))
    maxOverlap = 0
    maxOverlapLocation = 0
    overlaps = 0
    while intervalPoints:
        index, val = heapq.heappop(intervalPoints)
        overlaps -= val
        if overlaps > maxOverlap:
            maxOverlap = overlaps
            maxOverlapLocation = index
    return maxOverlapLocation

def max_concurrency_pre_attempt_9(s):
    """Passes 7 tests."""
    s.sort(key=lambda x: x[1])
    f = 0
    heap = []
    for start, end in s:
        heapq.heappush(heap, -start)
        f += start
        if f >= end:
            f += heapq.heappop(heap)
    return len(heap)

def max_concurrency_attempt_9(s):
    pq = []
    start = 0
    for start_end in sorted(s, key=lambda x: x[1]):
        st, end = start_end[0], start_end[1]
        start += st
        heapq.heappush(pq, -st)
        while start >= end:
            start += heapq.heappop(pq)
    return len(pq)

def max_concurrency_attempt_10(s):
    """Fails all tests."""
    starts, ends = zip(*s)
    overlap = range(max(starts), min(ends) + 1)
    if not overlap:
        return ([], range(min(starts), max(ends) + 1), [])

    less_non_overlap = range(*heapq.nsmallest(2, starts))
    end, start = heapq.nlargest(2, ends)
    greater_non_overlap = range(start + 1, end + 1)
    return (overlap, less_non_overlap, greater_non_overlap)

def max_concurrency_attempt_11(s):
    """Fails all tests."""
    meetings = sorted(s, key=lambda k: k[1])

    heap = [(meetings[0][1], 0)]

    overlaps = {}

    for i in range(1, len(meetings)):
        if meetings[i][0] == heap[0][0]:
            heapq.heappush(heap, (meetings[i][1], i))
        else:
            heapq.heappop(heap)
            heapq.heappush(heap, (meetings[i][1], i))

        if meetings[i][0] <= heap[0][0]:
            p = (meetings[i][0], heap[0][0])
            if p in overlaps:
                overlaps[p] = max(overlaps[p], len(heap))
            else:
                overlaps[p] = len(heap)

    return [x for x, y in overlaps.items() if y == max(overlaps.values())]

# print(max_concurrency(start_and_end_times))
# start_time, end_time = start_and_end_time[0], start_and_end_time[1]
# overlap = min(end_time)


from typing import List
from .. import DeviceModel, _template_env
from . import signed_weight

class IntegratorModel(DeviceModel):
    ng_spice_model_name = 'int'

    def __init__(
        self,
        model_name: str,
        in_offset: float = 0.0,
        gain: float = 1.0,
        out_lower_limit: float = -10.0,
        out_upper_limit: float = 10.0,
        limit_range: float = 1e-6,
        out_ic: float = 0.0,
        comments: List[str] = ['integrator'],
    ):
        super().__init__(
            model_name,
            comments=comments,
            in_offset=in_offset,
            gain=gain,
            out_lower_limit=out_lower_limit,
            out_upper_limit=out_upper_limit,
            out_ic=out_ic,
            limit_range=limit_range,
        )
        # indicate that this model has a verilog-ams implementation
        self.verilog_ams = True

    def get_ngspice(self):
        return _template_env.get_template('ngspice/model.cir.j2').render(
            {
                'model_instance_name': self.model_name,
                'model_name': self.ng_spice_model_name,
                'parameters': self.parameters,
            }
        )

    def get_verilog_ams(self):
        return _template_env.get_template('verilog_ams/integrator.vams.j2').render(
            {
                'module_instance_name': self.model_name,
                'model_name': self.ng_spice_model_name,
                'parameters': {
                    'in_offset': {
                        'active': float(self.parameters['in_offset']) != 0.0,
                        'magnitude': float(self.parameters['in_offset']),
                        'sign': ['+', '-'][float(self.parameters['in_offset']) < 0],
                    },
                    'gain': float(self.parameters['gain']),
                    'out_lower_limit': signed_weight(
                        float(self.parameters['out_lower_limit'])
                    ),
                    'out_upper_limit': signed_weight(
                        float(self.parameters['out_upper_limit'])
                    ),
                    'limit_range': signed_weight(float(self.parameters['limit_range'])),
                    'out_ic': {
                        'magnitude': abs(float(self.parameters['out_ic'])),
                        'sign': ['+', '-'][float(self.parameters['out_ic']) < 0],
                    },
                },
                'description': 'A simplistic integrator model',
                'terminals': ['in', 'out'],
            }
        )


"""EJERCICIO 3
El día juliano correspondiente a una fecha es un número entero que indica los días que han
transcurrido desde el 1 de enero del año indicado. Queremos crear un programa principal que
al introducir una fecha nos diga el día juliano que corresponde. Para ello podemos hacer las
siguientes subrutinas:
 LeerFecha: Nos permite leer por teclado una fecha (día, mes y año).
 DiasDelMes: Recibe un mes y un año y nos dice los días de ese mes en ese año.
 EsBisiesto: Recibe un año y nos dice si es bisiesto.
 Calcular_Dia_Juliano: recibe una fecha y nos devuelve el día juliano."""
from datetime import datetime

def leer_fecha():
    try:
        n1=int(input("Introduce un día del mes: "))
        n2=int(input ("Introduce un mes de en formato numérico, 1 para Enero y 12 para diciembre: "))
        n3=int(input ("Introduce un año: "))

        if n1<1 or n1>31 or n2<1 or n2>13:
            return "La fecha introducida no es correcta"

        elif (n2==11 or n2==4 or n2==6 or n2==9 or n2==2) and n1>30:
            return  "La fecha introducida no es correcta"

        elif n2==2 and n1>29:
            return  "La fecha introducida no es correcta"

        elif n2==2 and n3%4!=0 and n1>28:
            return "La fecha introducida no es correcta"

        elif n2==2 and (n3%4==0 and n3%100==0 and n3%400!=0) and n1>28:
            return"La fecha introducida no es correcta"
        else:
            #return "La fecha introducida es correcta"
            return n1,n2,n3

    except:
        "Sólo se admiten enteros"

    #return n1,n2,n3

def dias_del_mes():
    treinta=[11,4,6,9]
    treinta_y_uno=[1,3,5,7,8,10,12]
    try:
        dia,mes,anho=leer_fecha()
    except:
        print("Los valores introducidos no son correctos")
    if mes in treinta:
        n1=30
    elif mes in treinta_y_uno:
        n1=31
    elif mes==2:
        if anho%4==0:
            if anho%100==0 and anho%400!=0:
                n1=28
            else:
                n1=29
        else:
            n1=28
    else:
        print("Los meses sólo van del 1 al 12")
        exit()
    print ("Los días de ese mes en ese año son "+ str(n1))
    return n1

def es_bisiesto():
    dia,mes,anho=leer_fecha()
    bandera=True
    if anho%4==0:
         if anho%100==0 and anho%400!=0:
             print ("El año "+str(anho)+" no es bisiesto")
         else:
              print("El año "+str(anho)+" es bisiesto")
              bandera=False
    else:
        print ("El año "+str(anho)+" no es bisiesto")

def calcular_dia_juliano():
    try:
        dia,mes,anho=leer_fecha()
        d1=str(anho)+'-01-01'
        d2=str(anho)+'-'+str(mes)+'-'+str(dia)
        d1 = datetime.strptime(d1, "%Y-%m-%d")
        d2 = datetime.strptime(d2, "%Y-%m-%d")
        resultado= abs((d2 - d1).days)
    except:
        resultado="Los valores introducidos no son correctos"

    return resultado

print (calcular_dia_juliano())


import torch
import gym
from model import Model
from collections import deque
import numpy as np
from env_wrapper import FlappyBirdEnv

env = FlappyBirdEnv(render_mode = "human")

policy = Model(4, 2)
def reinforce(policy, n_training_episodes, max_t, gamma, print_every):
    is_pick = True
    scores_deque = deque(maxlen=100)
    scores = []
    for i_episode in range(1, n_training_episodes+1):
        saved_log_probs = []
        rewards = []
        state, info = env.reset()
        # Line 4 of pseudocode
        for t in range(max_t):
            # print(state)
            # import ipdb; ipdb.set_trace()
            pick = np.random.uniform()

            action, log_prob = policy.act(torch.tensor(state))
            saved_log_probs.append(log_prob)
            # if is_pick and pick <= 0.1:
            #     state, reward, done, _ = env.step(env.action_space.sample())
            # else:
            state, reward, done, _ = env.step(action)

            rewards.append(reward)
            if done:
                break
        scores_deque.append(sum(rewards))
        scores.append(sum(rewards))

        returns = deque(maxlen=max_t)
        n_steps = len(rewards)

        for t in range(n_steps)[::-1]:
            disc_return_t = (returns[0] if len(returns)>0 else 0)
            returns.appendleft( gamma*disc_return_t + rewards[t]   )

        eps = np.finfo(np.float32).eps.item()
        returns = torch.tensor(returns, dtype = torch.float32)
        returns = (returns - returns.mean()) / (returns.std() + eps)

        policy_loss = []
        for log_prob, disc_return in zip(saved_log_probs, returns):
            policy_loss.append(-log_prob * disc_return)

        # print(policy_loss)
        policy_loss = torch.cat(policy_loss).sum()

        policy.optimizer.zero_grad()
        policy_loss.backward()
        policy.optimizer.step()

        if i_episode % print_every == 0:
            print('Episode {}\tAverage Score: {:.2f}'.format(i_episode, np.mean(scores_deque)))
        if i_episode % print_every == 200:
            is_pick = False

    return scores

reinforce(
    policy=policy,
    n_training_episodes=1000,
    max_t = 500,
    gamma = 0.98,
    print_every=100
    )


from PyQt5.QtWidgets import QMainWindow, QAction, qApp, QApplication, QFrame
from PyQt5.QtWidgets import QApplication, QGridLayout, QLabel, QMainWindow, QPushButton, QWidget, QTableWidget, QTableWidgetItem, QMessageBox, QMenuBar, QLineEdit
from PyQt5.QtGui import QBrush, QColor
from PyQt5 import QtCore
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg
from matplotlib.figure import Figure
import matplotlib.pyplot as plt
import matplotlib
from sqlite3 import IntegrityError, OperationalError
import sqlite3
from datetime import datetime
import pandas as pd
import numpy as np
import string
import sys

matplotlib.use('Qt5Agg')

class ExpenseTracker(QMainWindow):
    def __init__(self):
        super().__init__()

        # db

        self.db = sqlite3.connect(
            r'App_projects\expense_tracker\expenses.db')
        self.cursor = self.db.cursor()

        # geometry

        self.setGeometry(0, 0, 900, 900)
        self.setWindowTitle('Expense tracker')
        self.setStyleSheet('background: #FFFFFF')

        # menu bar

        self.menubar = self.menuBar()

        self.statusBar()

        self.budgets = QAction(text='Budgets', parent=self)
        self.budgets.setShortcut('Ctrl+P')
        self.budgets.setStatusTip('Budget overview')
        self.budgets.triggered.connect(self.get_budgets)

        budget_menu = self.menubar.addMenu('&Budgets')
        budget_menu.addAction(self.budgets)

        balance = QAction(text='Balance', parent=self)
        balance.setShortcut('Ctrl+P')
        balance.setStatusTip('Balance overview')
        balance.triggered.connect(self.get_balance)

        statistics_menu = self.menubar.addMenu('&Statistics')
        statistics_menu.addAction(balance)

        exitAction = QAction('&Exit', self)
        exitAction.setShortcut('Ctrl+Q')
        exitAction.setStatusTip('Exit application')
        exitAction.triggered.connect(qApp.quit)

        exit_menu = self.menubar.addMenu('&Exit')
        exit_menu.addAction(exitAction)

        # widgets

        self.central_widget = QWidget()
        self.setCentralWidget(self.central_widget)
        self.central_widget.show()

        self.grid = QGridLayout(self.central_widget)

        self.frame_1 = QFrame(self.central_widget)
        self.grid.addWidget(self.frame_1, 0, 0)

        self.frame_2 = QFrame(self.central_widget)
        self.grid.addWidget(self.frame_2, 1, 0)

        self.grid_1 = QGridLayout(self.frame_1)
        self.grid_2 = QGridLayout(self.frame_2)

        self.label_1 = Label('Expense tracker')
        self.grid_1.addWidget(self.label_1, 0, 1, 1, 2)

        self.label_2 = Label('Balance: ')
        self.grid_1.addWidget(self.label_2, 1, 0)

        self.adjust_button = QPushButton('CHANGE BALANCE')
        self.adjust_button.clicked.connect(self.new_balance)
        self.grid_1.addWidget(self.adjust_button, 1, 1)

        self.label_4 = Label('Account: ')
        self.grid_1.addWidget(self.label_4, 2, 0)

        self.change_acc = QPushButton(self)
        self.change_acc.setText('CHANGE ACCOUNT')
        self.change_acc.clicked.connect(self.change_account)
        self.grid_1.addWidget(self.change_acc, 2, 1)

        self.add_account_button = QPushButton('NEW RECORD')
        self.add_account_button.clicked.connect(self.add_an_expense)
        self.grid_1.addWidget(self.add_account_button, 1, 2)

        self.zapisi_button = QPushButton('RECORDS')
        self.zapisi_button.clicked.connect(self.get_records)
        self.grid_1.addWidget(self.zapisi_button, 2, 2)

        self.label_3 = Label('Expense composition')
        self.grid_2.addWidget(self.label_3, 0, 1)

        try:
            self.account = self.cursor.execute(
                'SELECT user FROM expenses ORDER BY time DESC LIMIT 1;').fetchall()[0][0]
            self.label_4.setText(self.label_4.text() + self.account)
        except IndexError:
            self.account = 'unknown'

        self.get_account_balance(self.account)

        self.button_frame = QFrame(self)
        self.grid.addWidget(self.button_frame, 3, 0)

        self.button_grid = QGridLayout(self.button_frame)

        self.month = QPushButton(self)
        self.month.setText('Last 30 days')
        self.month.clicked.connect(self.show_graph_time)
        self.month.setMaximumWidth(200)
        self.button_grid.addWidget(self.month, 1, 0)
        self.year = QPushButton(self)
        self.year.setText('This year')
        self.year.clicked.connect(self.show_graph_time)
        self.year.setMaximumWidth(200)
        self.button_grid.addWidget(self.year, 1, 1)

        self.show_graph()

    def show_graph(self, table_name='expenses', amount='amount', asset='category', date='Last 30 days'):
        l_of_time = list(self.get_time())

        if date == 'Last 30 days':
            if l_of_time[4:6] == ['0', '1']:
                l_of_time[4] = '1'
                l_of_time[5] = '2'
                l_of_time[3] = str(int(l_of_time[3]) - 1)
            else:
                nums = int(''.join(l_of_time[4:6]))
                nums -= 1
                if nums < 10:
                    l_of_time[4] = '0'
                    l_of_time[5] = str(nums)
                else:
                    l_of_time[4] = str(nums)[0]
                    l_of_time[5] = str(nums)[1]

        else:
            l_of_time[3] = str(int(l_of_time[3]) - 1)

        l_of_time = ''.join(l_of_time)

        try:
            query = self.cursor.execute(
                f'SELECT {asset}, SUM({amount}) FROM "{table_name}" WHERE type = "expense" AND time BETWEEN "{l_of_time}" AND "{self.get_time()}" AND user = "{self.account}" GROUP BY {asset}').fetchall()
        except OperationalError:
            query = self.cursor.execute(
                f'SELECT {asset}, SUM({amount}) FROM "expenses" WHERE type = "expense" AND time BETWEEN "{l_of_time}" AND "{self.get_time()}" AND user = "{self.account}" GROUP BY {asset}').fetchall()

        self.pie = MplCanvasPie(self, width=6, height=5, dpi=100)
        if date == 'Last 30 days':
            time = 'Last 30 days'
        else:
            time = 'This year'

        try:
            self.pie.show_overview(amounts=[float(item[1]) for item in query], assets=[
                item[0] for item in query], table_name=table_name.upper(), table_title=sum([float(item[1]) for item in query]), time_period=time)
        except AttributeError:
            self.pie.show_overview(amounts=[float(item[1]) for item in query], assets=[
                item[0] for item in query], table_name="EXPENSES", table_title=sum([float(item[1]) for item in query]), time_period=time)

        self.grid_2.addWidget(self.pie, 2, 0, 1, 3)
        self.pie.show()

    def show_graph_time(self):
        date = self.sender().text()
        self.chart_1 = self.show_graph(date=date)

    def get_account_balance(self, account, initial=True):
        try:
            self.label_2.setText('Balance: ' + [item[0] for item in self.cursor.execute(
                f'SELECT amount FROM account WHERE name = "{account}";').fetchall()][0] + '€')
            if initial == False:
                self.label_4.setText(self.label_4.text().split(' ')[
                                     0] + ' ' + self.account)
        except IndexError:
            self.label_2.setText('no data')
            self.label_4.setText('no data')

    def get_time(self):
        return ''.join([item for item in datetime.now().strftime(
            '%Y/%m/%d, %H:%M:%S') if item.isalnum()])

    def new_balance(self):
        self.balance_window = NewBalance()
        self.balance_window.show()

    def add_an_expense(self):
        self.expense_window = NewExpense()
        self.expense_window.show()

    def get_records(self):
        self.records_window = RecordsWindow()
        if type(self.records_window.table) == QTableWidget:
            self.records_window.show()
        else:
            self.records_window.close()

    def get_budgets(self):
        self.budget_window = BudgetWindow()
        self.budget_window.show()

    def get_balance(self):
        self.balance_window = BalanceWindow()
        self.balance_window.show()

    def clear_expenses(self):
        self.cursor.execute('DELETE FROM expenses')
        self.db.commit()

    def hide_pie(self):
        self.pie.hide()

    def change_account(self):
        self.change_account_window = AccountPicker(self, initial=True)
        self.change_account_window.show()

class NewBalance(QWidget):
    def __init__(self):
        super().__init__()

        self.setGeometry(0, 0, 300, 300)
        self.setWindowTitle('SET BALANCE')
        self.setStyleSheet('background: #FFFFFF')

        self.grid = QGridLayout(self)

        self.label = QLabel(self)
        self.label.setText('Set balance')
        self.label.setAlignment(QtCore.Qt.AlignCenter)
        self.grid.addWidget(self.label, 0, 0, 1, 2)

        self.edit = QLineEdit(self)
        self.grid.addWidget(self.edit, 1, 0, 1, 2)

        self.insert_button = QPushButton(self)
        self.insert_button.setText('UMETNI')
        self.insert_button.clicked.connect(self.change_balance)
        self.grid.addWidget(self.insert_button, 2, 0)

        self.close_button = QPushButton(self)
        self.close_button.setText('ODUSTANI')
        self.close_button.clicked.connect(self.close)
        self.grid.addWidget(self.close_button, 2, 1)

    def change_balance(self, a='11', account='cele'):
        user_found = e.cursor.execute(
            f'SELECT name FROM account WHERE name = "{account}";').fetchall()
        if user_found != []:
            e.cursor.execute(
                f'UPDATE account SET amount = "{self.edit.text()}" WHERE name = "{account}";')
        else:
            e.cursor.execute(
                f'INSERT INTO account VALUES ("{account}", "{self.edit.text()}");')
        e.db.commit()
        e.get_account_balance(e.account)
        self.close()

class NewExpense(QWidget):
    def __init__(self):
        super().__init__()

        self.setGeometry(0, 0, 300, 300)
        self.setWindowTitle('ADD AN ENTRY')
        self.setStyleSheet('background: #FFFFFF')

        # grid + frames

        self.grid = QGridLayout(self)

        self.frame_1 = QFrame(self)
        self.grid.addWidget(self.frame_1, 0, 0)

        self.frame_2 = QFrame(self)
        self.grid.addWidget(self.frame_2, 1, 0)

        self.frame_3 = QFrame(self)
        self.grid.addWidget(self.frame_3, 2, 0)

        # frame 1

        self.grid_1 = QGridLayout(self.frame_1)

        self.close_button = QPushButton(self.frame_1)
        self.close_button.setText('Close window')
        self.close_button.clicked.connect(self.close)
        self.grid_1.addWidget(self.close_button, 0, 0)

        self.accept = QPushButton(self.frame_1)
        self.accept.setText('Enter an entry')
        self.accept.clicked.connect(self.insert_expense)
        self.grid_1.addWidget(self.accept, 0, 1)

        self.income = QPushButton(self.frame_1)
        self.income.setText('Income')
        self.income.clicked.connect(self.change_to_income)
        self.grid_1.addWidget(self.income, 1, 0)

        self.expense = QPushButton(self.frame_1)
        self.expense.setText('Expense')
        self.expense.clicked.connect(self.change_to_expense)
        self.grid_1.addWidget(self.expense, 1, 1)

        # frame 2

        self.grid_2 = QGridLayout(self.frame_2)

        self.operator = QLabel(self.frame_2)
        self.grid_2.addWidget(self.operator, 0, 0)

        self.amount = QLabel(self.frame_2)
        self.amount.setAlignment(QtCore.Qt.AlignCenter)
        self.amount.setText('')
        self.grid_2.addWidget(self.amount, 0, 1)

        # currency pick function
        self.currency = QLabel(self.frame_2)
        self.currency.setText('EUR')
        self.currency.setAlignment(QtCore.Qt.AlignCenter)
        self.grid_2.addWidget(self.currency, 0, 2)

        self.account = QPushButton(self.frame_2)
        self.account.clicked.connect(self.choose_account)
        self.account_text = 'Račun: '

        self.grid_2.addWidget(self.account, 1, 0)

        self.category = QPushButton(self.frame_2)
        self.category.clicked.connect(self.choose_category)
        self.category_text = 'Kategorija: '
        self.grid_2.addWidget(self.category, 1, 2)

        # frame 3

        self.grid_3 = QGridLayout(self.frame_3)

        self.seven = QPushButton('7')
        self.seven.clicked.connect(self.change_amount)
        self.grid_3.addWidget(self.seven, 0, 0)
        self.eight = QPushButton('8')
        self.eight.clicked.connect(self.change_amount)
        self.grid_3.addWidget(self.eight, 0, 1)
        self.nine = QPushButton('9')
        self.nine.clicked.connect(self.change_amount)
        self.grid_3.addWidget(self.nine, 0, 2)

        self.four = QPushButton('4')
        self.four.clicked.connect(self.change_amount)
        self.grid_3.addWidget(self.four, 1, 0)
        self.five = QPushButton('5')
        self.five.clicked.connect(self.change_amount)
        self.grid_3.addWidget(self.five, 1, 1)
        self.six = QPushButton('6')
        self.six.clicked.connect(self.change_amount)
        self.grid_3.addWidget(self.six, 1, 2)

        self.one = QPushButton('1')
        self.one.clicked.connect(self.change_amount)
        self.grid_3.addWidget(self.one, 2, 0)
        self.two = QPushButton('2')
        self.two.clicked.connect(self.change_amount)
        self.grid_3.addWidget(self.two, 2, 1)
        self.three = QPushButton('3')
        self.three.clicked.connect(self.change_amount)
        self.grid_3.addWidget(self.three, 2, 2)

        self.point = QPushButton('.')
        self.grid_3.addWidget(self.point, 3, 0)
        self.point.clicked.connect(self.change_amount)
        self.zero = QPushButton('0')
        self.zero.clicked.connect(self.change_amount)
        self.grid_3.addWidget(self.zero, 3, 1)
        self.back = QPushButton('<')
        self.back.clicked.connect(self.change_amount)
        self.grid_3.addWidget(self.back, 3, 2)

        self.user = e.account
        self.table_category = 'unknown'
        self.type = 'unknown'

        self.get_inital_values()

    def get_inital_values(self):
        try:
            self.table_category, self.type = e.cursor.execute(
                'SELECT category, type FROM expenses').fetchall()[-1]
            self.account.setText(self.account_text + self.user)
            self.category.setText(self.category_text + self.table_category)
            if self.type == 'expense':
                self.expense.setStyleSheet('background: #FFFF00;')
            else:
                self.income.setStyleSheet('background: #FFFF00;')
        except IndexError:
            self.account.setText('unknown')
            self.category.setText('unknown')

    def change_to_income(self):
        if self.type == 'income':
            pass
        else:
            self.expense.setStyleSheet('background: #FFFFFF;')
            self.income.setStyleSheet('background: #FFFF00;')
            self.type = 'income'

    def change_to_expense(self):
        if self.type == 'expense':
            pass
        else:
            self.income.setStyleSheet('background: #FFFFFF')
            self.expense.setStyleSheet('background: #FFFF00')
            self.type = 'expense'

    def insert_expense(self):

        try:
            new_amount = e.cursor.execute(
                f'SELECT amount FROM account WHERE name = "{self.user}"').fetchall()
            if new_amount != []:
                new_amount = float(new_amount[0][0])
            else:
                msg = QMessageBox(QMessageBox.Warning,
                                  'User not found', f'No user named {self.user}')
                msg.exec_()
                return

            date = datetime.now().strftime('%Y/%m/%d, %H:%M:%S')

            e.cursor.execute(
                f'INSERT INTO expenses VALUES ("{self.user}", "{self.table_category}", "{self.amount.text()}", "{e.get_time()}", "{self.type}")')

            if self.amount.text() == '':
                msg = QMessageBox(
                    QMessageBox.Warning, 'Invalid number', 'Number field cannot be empty')
                msg.exec_()
                return
            if self.type == 'expense':
                new_amount -= float(self.amount.text())
            elif self.type == 'income':
                new_amount += float(self.amount.text())
            else:
                msg = QMessageBox(QMessageBox.Warning, 'Type incorrect',
                                  'Type cannot be unknown, please select a valid type')
                msg.exec_()
                return

            e.cursor.execute(
                f'UPDATE account SET amount = "{new_amount}" WHERE name = "{self.user}";')

            e.db.commit()

        except AttributeError:
            msg = QMessageBox(QMessageBox.Warning, 'No accounts found',
                              'Please make an account to enter an expense')
            msg.exec_()
            return

        e.pie.hide()
        e.show_graph()
        e.get_account_balance(self.user)

    def choose_account(self):
        self.account_window = AccountPicker(parent=self)
        self.account_window.show()

    def choose_category(self):
        self.category_window = CategoryPicker()
        self.category_window.show()

    def change_amount(self):
        current_amount = self.amount.text()
        sender = self.sender().text()
        if sender == '<':
            if len(current_amount) == 0:
                pass
            else:
                self.amount.setText(current_amount[:-1])
        elif sender == '.':
            if '.' not in current_amount:
                if len(current_amount) == 0:
                    pass
                else:
                    self.amount.setText(current_amount + '.')
        else:
            self.amount.setText(current_amount + sender)

class MplCanvasPie(FigureCanvasQTAgg):
    def __init__(self, parent=None, width=5, height=4, dpi=100):
        fig = Figure(figsize=(width, height), dpi=dpi)
        self.axes = fig.add_subplot(111)
        super(MplCanvasPie, self).__init__(fig)

    def show_overview(self, amounts, assets, table_name='overview', table_title='overview', time_period='Last 30 days'):
        def func(pct, allvals):
            absolute = int(np.round(pct/100.*np.sum(allvals)))
            return "{:.1f}%\n({:d} €)".format(pct, absolute)

        wedges, texts, autotexts = self.axes.pie(amounts, autopct=lambda pct: func(pct, amounts),
                                                 textprops=dict(color="w"))

        self.axes.legend(wedges, assets,
                         title='Kategorije troškova',
                         loc="lower right",
                         bbox_to_anchor=(0, 0, 0, 0))

        plt.setp(autotexts, size=8, weight="bold")

        self.axes.set_title(f'{time_period}: {table_title} €')

class AccountPicker(QWidget):
    def __init__(self, parent, initial=False):
        super().__init__()

        self.initial = initial

        self.parent = parent

        self.setGeometry(0, 0, 400, 400)
        self.setWindowTitle('CHOOSE AN ACCOUNT')
        self.setStyleSheet('background: #FFFFFF')

        self.grid = QGridLayout(self)

        self.new_account = QLabel(self)
        self.new_account.setText('Add a new account: ')
        self.grid.addWidget(self.new_account, 0, 0)

        self.new_account_edit = QLineEdit(self)
        self.grid.addWidget(self.new_account_edit, 0, 1)

        self.add_button = QPushButton(self)
        self.add_button.setText('ADD A NEW ACCOUNT')
        self.add_button.clicked.connect(self.add_new)
        self.grid.addWidget(self.add_button, 0, 2)

        self.attempt()

    def add_new(self):
        account = self.new_account_edit.text()
        try:
            if account != '':
                e.cursor.execute(
                    f'INSERT INTO account VALUES ("{account}", "0")')
                e.db.commit()
                self.attempt()
                self.new_account_edit.setText('')
            else:
                msg = QMessageBox(
                    QMessageBox.Warning, 'Account name blank', 'Account name cannot be empty')
                msg.exec_()
        except IntegrityError:
            msg = QMessageBox(QMessageBox.Warning, 'Account already present',
                              'Account already present in the database, cannot duplicate usernames')
            msg.exec_()

    def attempt(self):
        items = e.cursor.execute('SELECT name FROM account').fetchall()
        style_sheet = ""  # napravit stylesheet
        if items != []:
            items = [item[0] for item in items]
            for i in range(len(items)):
                new_button = QPushButton(self)
                new_button.setText(items[i])
                new_button.setStyleSheet(style_sheet)
                new_button.clicked.connect(self.get_account)
                self.grid.addWidget(new_button, i + 1, 1)
        else:
            new_label = QLabel(self)
            new_label.setText(
                'No accounts found, please make an account to proceed')
            self.grid.addWidget(new_label, 0, 0)

    def get_account(self):
        if not self.initial:
            e.expense_window.user = self.sender().text()
            e.expense_window.account.setText(
                e.expense_window.account_text + e.expense_window.user)
        if self.initial:
            e.account = self.sender().text()
            e.get_account_balance(e.account, initial=False)
        e.show_graph()
        self.close()

class CategoryPicker(QWidget):
    def __init__(self):
        super().__init__()

        self.setGeometry(0, 0, 400, 400)
        self.setWindowTitle('CHOOSE A CATEGORY')
        self.setStyleSheet('background: #FFFFFF')

        self.grid = QGridLayout(self)

        self.new_category = QLabel(self)
        self.new_category.setText('ADD A NEW CATEGORY: ')
        self.grid.addWidget(self.new_category, 0, 0)

        self.new_category_edit = QLineEdit(self)
        self.grid.addWidget(self.new_category_edit, 0, 1)

        self.add_button = QPushButton(self)
        self.add_button.setText('ADD A NEW CATEGORY')
        self.add_button.clicked.connect(self.add_new)
        self.grid.addWidget(self.add_button, 0, 2)

        self.attempt()

    def add_new(self):
        category = self.new_category_edit.text()
        try:
            if category != '':
                e.cursor.execute(
                    f'INSERT INTO expense_categories VALUES ("{category}")')
                e.db.commit()
                self.attempt()
                self.new_category_edit.setText('')
            else:
                msg = QMessageBox(
                    QMessageBox.Warning, 'Category name blank', 'Category name cannot be empty')
                msg.exec_()
        except IntegrityError:
            msg = QMessageBox(QMessageBox.Warning, 'Category already present',
                              'Category already present in the database, cannot duplicate category names')
            msg.exec_()

    def attempt(self):
        items = e.cursor.execute(
            'SELECT category FROM expense_categories').fetchall()
        style_sheet = ""  # napravit stylesheet
        if items != []:
            items = [item[0] for item in items]
            for i in range(len(items)):
                new_button = QPushButton(self)
                new_button.setText(items[i])
                new_button.setStyleSheet(style_sheet)
                new_button.clicked.connect(self.get_account)
                self.grid.addWidget(new_button, i + 1, 1)
        else:
            new_label = QLabel(self)
            new_label.setText(
                'No categories found, please make a category to proceed')
            self.grid.addWidget(new_label, 0, 0)

    def get_account(self):
        e.expense_window.table_category = self.sender().text()
        e.expense_window.category.setText(
            e.expense_window.category_text + e.expense_window.table_category)
        self.close()

class RecordsWindow(QWidget):
    def __init__(self):
        super().__init__()

        self.setGeometry(0, 0, 900, 900)
        self.setWindowTitle('Records window')
        self.setStyleSheet('background: #FFFFFF')

        self.grid = QGridLayout(self)

        self.table = self.show_table()
        if type(self.table) == QTableWidget:
            self.grid.addWidget(self.table, 0, 0)
        else:
            self.grid.addWidget(QLabel(self), 0, 0)

        self.set_table_width()

        try:
            max_h = sum([self.table.rowHeight(row)
                         for row in range(self.table.rowCount())]) + 40
            self.table.setMaximumHeight(max_h)
        except AttributeError:
            pass

    def get_column_names(self, table_name):
        columns = e.cursor.execute(
            f"SELECT sql FROM sqlite_master WHERE name='{table_name}';").fetchall()[0][0]

        first_sections = columns.split(f'"{table_name}"')[
            1].split('PRIMARY KEY')[0]

        upper = string.ascii_uppercase
        other = ['', ' ', '(', ')', '\n', '\t', '"']

        almost_done = [
            item for item in first_sections if item not in upper and item not in other]

        return [item for item in ''.join(
            almost_done).split(',') if item != '']

    def show_table(self, table_name='expenses'):
        columns = self.get_column_names(table_name)

        try:
            content = e.cursor.execute(
                f'SELECT * FROM {table_name} WHERE user = "{e.account}"').fetchall()
        except OperationalError:
            msg = QMessageBox(QMessageBox.Warning,
                              'Error with columns', f'Columns: {columns}')
            msg.exec_()

        try:
            if len(content) == 0 and len(content[0]) == 0:
                msg = QMessageBox(QMessageBox.Warning,
                                  'Empty table', 'Table has no entries')
                msg.exec_()
                return None
            table = QTableWidget(len(content), len(content[0]), self)
            for i in range(len(content)):
                for j in range(len(content[0])):
                    if j == 3:
                        item = QTableWidgetItem(
                            str(pd.to_datetime(content[i][j])))
                    else:
                        item = QTableWidgetItem(content[i][j])

                    item.setForeground(QBrush(QColor("#000000")))
                    table.setItem(i, j, item)

            table.setHorizontalHeaderLabels(columns)

            return table

        except IndexError:
            msg = QMessageBox(QMessageBox.Warning, 'Table empty',
                              'Table empty, cannot show data')
            msg.exec_()

    def set_table_width(self):
        try:
            width = self.table.verticalHeader().width()
            width += self.table.horizontalHeader().length()
            if self.table.verticalScrollBar().isVisible():
                width += self.table.verticalScrollBar().width()
            width += self.table.frameWidth() * 2
            self.table.setFixedWidth(width)
        except AttributeError:
            pass

class BudgetWindow(QWidget):
    def __init__(self):
        super().__init__()
        self.setGeometry(0, 0, 700, 700)

        self.setStyleSheet('background: #FFFFFF')

        self.grid = QGridLayout(self)

        self.main_label = QLabel(self)
        self.main_label.setText('Budgets')
        self.main_label.setAlignment(QtCore.Qt.AlignCenter)
        self.grid.addWidget(self.main_label, 0, 0, 1, 3)

        self.weekly = QFrame(self)
        self.grid.addWidget(self.weekly, 1, 0)
        self.weekly_grid = QGridLayout(self.weekly)
        self.weekly_label = QLabel(self)
        self.weekly_label.setText('Weekly budgets')
        self.weekly_label.setAlignment(QtCore.Qt.AlignCenter)
        self.weekly_grid.addWidget(self.weekly_label, 0, 0)

        self.monthly = QFrame(self)
        self.grid.addWidget(self.monthly, 1, 1)
        self.monthly_grid = QGridLayout(self.monthly)
        self.monthly_label = QLabel(self)
        self.monthly_label.setText('Monthly budgets')
        self.monthly_label.setAlignment(QtCore.Qt.AlignCenter)
        self.monthly_grid.addWidget(self.monthly_label, 0, 0)

        self.yearly = QFrame(self)
        self.grid.addWidget(self.yearly, 1, 2)
        self.yearly_grid = QGridLayout(self.yearly)
        self.yearly_label = QLabel(self)
        self.yearly_label.setText('Yearly budgets')
        self.yearly_label.setAlignment(QtCore.Qt.AlignCenter)
        self.yearly_grid.addWidget(self.yearly_label, 0, 0)

        self.new_budget = QFrame(self)
        self.grid.addWidget(self.new_budget, 2, 0, 1, 3)
        self.new_budget_grid = QGridLayout(self.new_budget)

        self.make_budget = QPushButton(self)
        self.make_budget.setText('Make a new budget')
        self.make_budget.setMaximumWidth(300)
        self.make_budget.clicked.connect(self.make_a_new_budget)
        self.new_budget_grid.addWidget(self.make_budget, 0, 0)

        self.delete_budget = QPushButton(self)
        self.delete_budget.setText('Delete a budget')
        self.delete_budget.setMaximumWidth(300)
        self.delete_budget.clicked.connect(self.delete_a_budget)
        self.new_budget_grid.addWidget(self.delete_budget, 0, 1)

        self.budgets = []

        self.get_budgets()

        # dodat funkcionalnost za promijenit iznos budgeta kao i dodat novi te izbrisat

    def get_budgets(self):
        budgets = e.cursor.execute(
            f'SELECT * FROM budgets WHERE account = "{e.account}"').fetchall()
        expenses = e.cursor.execute(
            f'SELECT category, SUM(amount) FROM expenses WHERE user = "{e.account}" GROUP BY category;').fetchall()
        expenses_dict = {}
        for expense in expenses:
            expenses_dict[expense[0]] = expense[1]

        weekly = 1
        monthly = 1
        yearly = 1
        for budget in budgets:
            budget_frame = QFrame(self)
            grid = QGridLayout(budget_frame)
            splitted = budget[1].split(', ')
            amount = 0
            for item in splitted:
                amount += float(expenses_dict[item])
            budget_name = Label(budget[0])
            grid.addWidget(budget_name, 0, 0)
            amount_label = Label(str(amount) + '/' + budget[2])
            grid.addWidget(amount_label, 1, 0)
            if amount > float(budget[2]):
                amount_label.setStyleSheet('color: red;')
            elif amount == float(budget[2]):
                amount_label.setStyleSheet('color: yellow')
            if budget[-1] == 'weekly':
                self.weekly_grid.addWidget(budget_frame, weekly, 0)
                weekly += 1
            elif budget[-1] == 'monthly':
                self.monthly_grid.addWidget(budget_frame, monthly, 0)
                monthly += 1
            else:
                self.yearly_grid.addWidget(budget_frame, yearly, 0)
                yearly += 1

    def make_a_new_budget(self):
        self.new_budget_window = NewBudget()
        self.new_budget_window.show()

    def delete_a_budget(self):
        self.delete_a_budget_window = DeleteBudget()
        self.delete_a_budget_window.show()

    def hide_budgets(self):
        [[item.hide() for item in time.children() if type(item) == QFrame]
         for time in [self.weekly, self.monthly, self.yearly]]

class BalanceWindow(QWidget):
    def __init__(self):
        super().__init__()

        self.setGeometry(0, 0, 700, 700)
        self.setWindowTitle('Balance overview')
        self.setStyleSheet('background: #FFFFFF')

        self.balances = self.calc_balance()

        self.canvas = MplCanvas()
        self.canvas.axes.plot(self.balances[0], self.balances[1])

        self.grid = QGridLayout(self)

        self.label_1 = QLabel(self)
        self.label_1.setText('Balance overview')
        self.label_1.setAlignment(QtCore.Qt.AlignCenter)
        self.grid.addWidget(self.label_1, 0, 0)

        self.grid.addWidget(self.canvas, 1, 0)

    def calc_balance(self):
        expenses = e.cursor.execute(
            f'SELECT amount, type, time FROM expenses WHERE user = "{e.account}"').fetchall()
        balances = [float(e.cursor.execute(
            f'SELECT amount FROM account WHERE name = "{e.account}";').fetchall()[0][0])]
        dates = [''.join([item for item in datetime.now().strftime(
            '%Y/%m/%d, %H:%M:%S') if item.isalnum()])]

        for i in range(1, len(expenses) + 1):
            if expenses[-i][1] == 'expense':
                balances.insert(0, float(balances[0]) + float(expenses[-i][0]))
            elif expenses[-i][1] == 'income':
                balances.insert(0, float(balances[0]) - float(expenses[-i][0]))
            dates.append(expenses[-i][2])
        return dates, balances

class MplCanvas(FigureCanvasQTAgg):
    def __init__(self, parent=None, width=5, height=4, dpi=100):
        fig = Figure(figsize=(width, height), dpi=dpi)
        self.axes = fig.add_subplot(111)
        super(MplCanvas, self).__init__(fig)

class NewBudget(QWidget):
    def __init__(self):
        super().__init__()

        self.setGeometry(0, 0, 700, 700)

        self.setStyleSheet('background: #FFFFFF')

        self.grid = QGridLayout(self)

        self.frame_1 = QFrame(self)
        self.frame_1.setMaximumHeight(100)
        self.grid_1 = QGridLayout(self.frame_1)
        self.grid.addWidget(self.frame_1, 0, 0, 1, 2)

        self.close_button = QPushButton(self)
        self.close_button.setText('Close window')
        self.close_button.clicked.connect(self.close)
        self.close_button.setMaximumWidth(200)
        self.grid_1.addWidget(self.close_button, 0, 0)

        self.save_button = QPushButton(self)
        self.save_button.setText('Save budget')
        self.save_button.clicked.connect(self.save_budget)
        self.save_button.setMaximumWidth(200)
        self.grid_1.addWidget(self.save_button, 0, 1)

        self.name = Label('Name')
        self.name.setFocus()
        self.grid.addWidget(self.name, 1, 0)

        self.name_entry = LineEdit()
        self.grid.addWidget(self.name_entry, 1, 1)

        self.amount = Label('Amount')
        self.grid.addWidget(self.amount, 2, 0)

        self.amount_entry = LineEdit()
        self.grid.addWidget(self.amount_entry, 2, 1)

        self.period = Label('Time period')
        self.grid.addWidget(self.period, 3, 0)

        self.period_entry = LineEdit()
        self.grid.addWidget(self.period_entry, 3, 1)

        self.category = Label('Category (one or more)')
        self.grid.addWidget(self.category, 4, 0)

        self.category_entry = LineEdit()
        self.grid.addWidget(self.category_entry, 4, 1)

        self.account = Label('Account')
        self.grid.addWidget(self.account, 5, 0)

        self.account_entry = LineEdit()
        self.account_entry.setText(e.account)
        self.grid.addWidget(self.account_entry, 5, 1)

    def save_budget(self):
        data = [self.name_entry.text(), self.period_entry.text(),
                self.category_entry.text(), self.account_entry.text(), self.amount_entry.text()]
        budgets = [name[0] for name in e.cursor.execute(f'SELECT name FROM budgets'
                                                        ).fetchall()]
        if data[0] in budgets:
            msg = QMessageBox(QMessageBox.Warning, 'Budget already present',
                              'Budget with this name already exists in the database')
            msg.exec_()
            return
        if '' in data:
            msg = QMessageBox(QMessageBox.Warning, 'Empty entries',
                              'Please fill out all the entries')
            msg.exec_()
            return
        if data[1].lower() not in ['weekly', 'monthly', 'yearly']:
            msg = QMessageBox(QMessageBox.Warning, 'Invalid timeframe',
                              'Please choose either beetwen a weekly, a monthly or a yearly period')
            msg.exec_()
            return
        accounts = [account[0] for account in e.cursor.execute(
            'SELECT name FROM account;').fetchall()]
        if data[3] not in accounts:
            msg = QMessageBox(
                QMessageBox.Warning, 'Account unknown', 'Please select a valid account')
            msg.exec_()
            return
        e.cursor.execute(
            f'INSERT INTO budgets VALUES ("{data[0]}", "{data[2]}", "{data[4]}", "{data[3]}", "{data[1]}")')
        e.db.commit()

        e.budget_window.get_budgets()

class DeleteBudget(QWidget):
    def __init__(self):
        super().__init__()

        self.setGeometry(0, 0, 400, 200)

        self.setStyleSheet('background: #FFFFFF')

        self.grid = QGridLayout(self)

        self.label = Label('Budget name: ')
        self.grid.addWidget(self.label, 0, 0)
        self.edit = LineEdit()
        self.grid.addWidget(self.edit, 0, 1)
        self.button = QPushButton()
        self.button.setText('Delete the budget')
        self.button.clicked.connect(self.delete_budget)
        self.button.setMaximumWidth(200)
        self.grid.addWidget(self.button, 0, 2)

        self.frame = QFrame()
        self.frame_grid = QGridLayout(self.frame)
        self.grid.addWidget(self.frame, 1, 0, 1, 3)

    def delete_budget(self):
        name = self.edit.text()
        e.cursor.execute(f'DELETE FROM budgets WHERE name = "{name}";')
        e.db.commit()
        label = Label('Budget successfuly deleted')
        self.frame_grid.addWidget(label, 0, 0)

        e.budget_window.hide_budgets()
        e.budget_window.get_budgets()

class Label(QLabel):
    def __init__(self, text, width=300):
        super().__init__()
        self.setText(text)
        self.setAlignment(QtCore.Qt.AlignCenter)
        self.setMaximumWidth(width)

class LineEdit(QLineEdit):
    def __init__(self, width=300):
        super().__init__()
        self.setMaximumWidth(width)

if __name__ == '__main__':
    app = QApplication(sys.argv)
    e = ExpenseTracker()
    e.show()
    app.exec()


# before use run in command shell:
# pip install pipenv
# pipenv install requests
# pipenv install BeautifulSoup
# pipenv install lxml
# pip install BeautifulSoup
# pip install beautifulsoup4
# pip install lxml

import requests
import re
from bs4 import BeautifulSoup
import json

# r = requests.get('http://immunet.cn/hmmcas') # this is optional
# url = "http://immunet.cn/hmmcas/index.html"

testgenome = open('NC_016025.faa', 'r').read()
# print(testgenome)
payload = {'sequence': testgenome,
			'submit': 'Run HMMCAS'}

url = "http://immunet.cn/hmmcas/upload2.php"

print("request to "+ url)
headers = {'Host': 'immunet.cn',
'Connection': 'keep-alive',
# 'Content-Length': '400435',
'Cache-Control': 'max-age=0',
'Origin': 'http://immunet.cn',
'Upgrade-Insecure-Requests': '1',
'Content-Type': 'multipart/form-data',
'Save-Data': 'on',
'User-Agent': "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36",
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8',
'Referer': 'http://immunet.cn/hmmcas/index.html',
'Accept-Encoding': 'gzip, deflate',
'Accept-Language': 'en-US,en;q=0.8,ru;q=0.6'
}
r = requests.post(url, data=payload, headers=headers)
# print(r.request.body)
print(r.request.headers)
################# alernate

# files = {'uploadfile': open('NC_016025.faa', 'rb')}
# r = requests.post(url, files=files)

#################
#print(r.text)
text_file = open("Output1.html", "w")
text_file.write(r.text)
text_file.close()
#print(r.status_code)

matches = re.findall("(tables\/ToSlide\d+\.json)", r.text)
if len(matches):
	print("found " + matches[0])
	url2 = "http://immunet.cn/hmmcas/" + matches[0]+ "?limit=10&offset=0&sort=name&order=desc"
	# http://immunet.cn/hmmcas/tables/ToSlide225044.json?limit=10&offset=0&sort=name&order=desc
r = requests.get(url2)

text_file = open("Output1j.html", "w")
text_file.write(r.json())
text_file.close()

# button for:
#url2 = "http://i.uestc.edu.cn/hmmcas/operon.php?qsf=ToSlide175938&tb=Table175938&sp=3"
#url2 = "http://i.uestc.edu.cn/hmmcas/operon.php?qsf=ToSlide031405&tb=Table031405&sp=3"
# Search links on buttons:
matches = re.findall("(http\:\/\/i\.uestc\.edu\.cn\/hmmcas\/operon\.php\?qsf=ToSlide.\d+\&tb=Table\d+\&sp=3)", r.text)
if len(matches):
	print("found " + matches[0])

	url2 = matches[0]

	r = requests.get(url2)

from bs4 import BeautifulSoup

print(r.text)

text_file = open("Output2.html", "w")
text_file.write(r.text)
text_file.close()

soup = BeautifulSoup(r.text, "lxml")

for row in soup.findAll('table')[0].tbody.findAll('tr'):
    first_column = row.findAll('th')[0].contents
    third_column = row.findAll('td')[2].contents
    print (first_column, third_column)

# from lxml import etree
# parser = etree.HTMLParser()
# tree = etree.fromstring(table, parser)
# results = tree.xpath('//tr/td[position()=2]')

# print ('Column 2\n========')
# for r in results:
#     print (r.text)


import praw
import pause

posts = []
url_set = set()
message_user = 'VirtualLeak'
reddit = praw.Reddit('user')
subreddit = reddit.subreddit('friends')

# Output post data in markdown table format
def output():
    format_title = '|'.join(['title', 'author', 'subreddit', 'url', '# of comments\n'])
    format_columns = '|'.join(['-', '-', '-', '-', '-\n'])
    for i in range(len(posts)):
        posts[i] = '|'.join([posts[i].title, str(posts[i].author), str(posts[i].subreddit),
                             posts[i].shortlink, str(posts[i].num_comments)])
    format_table = '\n'.join(posts)
    print(format_title + format_columns + format_table)
    reddit.redditor(message_user).message('New Posts', format_title + format_columns + format_table)
    posts.clear()

for post in subreddit.stream.submissions(pause_after=0):  # Optional parameters: pause_after=0, skip_existing=True
    # Avoids posts that link to the same url in multiple subreddits
    if post is not None and post.url not in url_set:
        url_set.add(post.url)
        posts.insert(0, post)
    # If there are new posts, send message
    if post is None and len(posts) > 0:
        output()
    # If there aren't any new posts, pause for 24 hours
    if len(posts) == 0:
        pause.hours(24)


# -*- coding: utf-8 -*-
import logging
import werkzeug
from odoo import SUPERUSER_ID, api, http, _
from odoo import registry as registry_get
from odoo.addons.web.controllers.main import (login_and_redirect, ensure_db, set_cookie_and_redirect)
from odoo.exceptions import AccessDenied

_logger = logging.getLogger(__name__)

class SSOController(http.Controller):
    @http.route('/web/sso/signin', type='http', auth='none')
    def signin(self, redirect=None, **kwargs):
        login = kwargs.get('userid', False)
        timestamp = kwargs.get('timestamp', False)
        ssoToken = kwargs.get('ssoToken', False)
        dbname = kwargs.get('dbname', 'qis')
        registry = registry_get(dbname)
        with registry.cursor() as cr:
            try:
                env = api.Environment(cr, SUPERUSER_ID, {})
                credentials = env['res.users'].sudo().auth_oauth(login, timestamp, ssoToken)
                cr.commit()
                resp = login_and_redirect(*credentials, redirect_url='/web')
                return resp
            except AttributeError:
                # auth_signup is not installed
                _logger.error("auth_signup not installed on database %s: oauth sign up cancelled." % (dbname,))
                url = "/web/login?oauth_error=1"
            except AccessDenied:
                # oauth credentials not valid, user could be on a temporary session
                _logger.info(
                    'OAuth2: access denied, redirect to main page in case a valid session exists, without setting cookies')
                url = "/web/login?oauth_error=3"
                redirect = werkzeug.utils.redirect(url, 303)
                redirect.autocorrect_location_header = False
                return redirect
            except Exception as e:
                # signup error
                _logger.exception("OAuth2: %s" % str(e))
                url = "/web/login?oauth_error=2"

        return set_cookie_and_redirect(url)


#!/usr/bin/env python
# coding: utf-8

# In[1]:

##############################################
# Hardware
#    - Windows 11
#    - NI USB-6008
#    Erst NI-DAQmx 19.5 installiert
#          (aktuelle Version 2023 Q2 zeigt die Karte nicht an)
#          (Die Unterstützung für Net Framework und C++ nicht installieren wenn kein Compiler auf dem Rechner)
#    Dann nidaqmax installieren: "pip install nidaqmx" in Anaconda Prompt

import time as t
import numpy as np
import nidaqmx
from nidaqmx.constants import AcquisitionType
import pprint

fSampling = 10000 #Hz
nSamples = 1000 #samples
pp = pprint.PrettyPrinter(indent=4)
dtMax = 1 #sec

###############################################
# DAQ

data = []
t_insgesamt = 0

# config
with nidaqmx.Task() as task:
    t_vorher = t.time()
    task.ai_channels.add_ai_voltage_chan("Dev1/ai0")
    task.timing.cfg_samp_clk_timing(fSampling, sample_mode=AcquisitionType.CONTINUOUS)

    t_nachher = t.time()
    while t_insgesamt < dtMax:
        value = task.read(number_of_samples_per_channel=nSamples)
        #t.sleep(1)
        data.extend(value)
        t_nachher = t.time()
        dt_buffer = t_nachher-t_vorher
        t_insgesamt += dt_buffer
        t_vorher = t_nachher
        #Verzögerungen haben keinen Einfluss auf die Messung! Super, hier der Beweis:
        #print(dt_buffer)
        #pp.pprint(data)
###############################################
# output to file

# In[2]:

print("Gesamte Messzeit [s]: " + str(t_insgesamt))
print("Anzahl aufgenommener Werte: " + str(len(data)))
print("Abtastfrequenz [Hz]: " + str(len(data) / t_insgesamt))

# In[3]:

import matplotlib.pyplot as plt
plt.rcParams['figure.dpi'] = 300
plt.rcParams['savefig.dpi'] = 300

fig = plt.figure()
plt.plot(data)
plt.tight_layout()
plt.xlabel('Zeit [s]')
plt.ylabel('Spannung [V]')
plt.show()
fig.savefig('..\output_bilder\output_voltage.png', dpi=fig.dpi)

# In[4]:

f = open("..\output_csv\output_voltage.txt", "w")
f.write("t,U")
for i in range(len(data)):
    f.write(str(i/len(data)*t_insgesamt) + ',' + str(data[i]) + '\n')
f.close()

# In[ ]:


import sys
from rdkit import Chem
from rdkit.Chem import AllChem
import glob
import shutil
import os
import multiprocessing as mp

nprocs = 4
except_dir_path = os.path.abspath(os.path.dirname(__file__)) + "\except_mol"

def optimize(mol_file):

	try:
		m = Chem.MolFromMolFile(mol_file)
		m = Chem.AddHs(m)
		AllChem.EmbedMolecule(m)
	except:
		shutil.move(mol_file, except_dir_path)
		return

	try:
		AllChem.UFFOptimizeMolecule(m)
	except ValueError:
		print("Bad Conformer Id")
		print(mol_file)
		shutil.move(mol_file, except_dir_path)

#	m = Chem.RemoveHs(m)
	w = Chem.SDWriter(mol_file)

	w.write(m)
	print(str(mol_file))

	return

if __name__ == "__main__":
	#dir_path = os.path.abspath(os.path.dirname(__file__)) + "\mol_datas"
	mol_files = glob.glob("*.sdf")
	procs = mp.Pool(nprocs)
	procs.map(optimize, mol_files)


from starlette.endpoints import HTTPEndpoint
from starlette.responses import UJSONResponse

from api.utils import is_admin
from db.conn import get_conn
from db.replica import Replica
from db.replica_files import ReplicaFiles

class ReplicasHandlers(HTTPEndpoint):
    async def post(self, request):
        is_admin(request)
        data = await request.json()

        async with get_conn()as conn:
            async with conn.transaction():
                replica = Replica(conn)
                result = await replica.add(data['type'], data)

                master = await replica.master()
                if not master:
                    master_id = result
                    await replica.set_master(result)
                else:
                    master_id = master['id']

                await ReplicaFiles(conn).add_replica(result, master_id)

        return UJSONResponse({'id': result})

    async def get(self, request):
        is_admin(request)
        async with get_conn() as conn:
            replicas = await Replica(conn).get_all()

        return UJSONResponse({'data': replicas})

class ReplicaHandlers(HTTPEndpoint):
    async def post(self, request):
        is_admin(request)
        data = await request.json()
        replica_id = int(request.path_params['id'])

        async with get_conn() as conn:
            if 'master' in data:
                await Replica(conn).set_master(replica_id)
            if 'delay' in data:
                await Replica(conn).update_delay(replica_id, data['delay'])

        return UJSONResponse({})

    async def delete(self, request):
        is_admin(request)
        replica_id = int(request.path_params['id'])

        async with get_conn() as conn:
            await Replica(conn).delete(replica_id)

        return UJSONResponse({})


import argparse
import cv2
import logging as log

import sys
import time
import socket
import paho.mqtt.client as mqtt
import json
import math
import os

from inference import Network

# Set the MQTT server environment variables
HOSTNAME = socket.gethostname()
IPADDRESS = socket.gethostbyname(HOSTNAME)
MQTT_HOST = IPADDRESS
MQTT_PORT = 1884
MQTT_KEEPALIVE_INTERVAL = 60

def connect_mqtt():

    client = mqtt.Client()
    client.connect(MQTT_HOST, MQTT_PORT, MQTT_KEEPALIVE_INTERVAL)

    return client

def get_args():

    # Create the parser object
    parser = argparse.ArgumentParser()

    # Create the arguments
    # Required
    parser.add_argument("-m", "--model", required = True, type = str, help = "The location of model .xml file")
    parser.add_argument("-i", "--input", required = True, type = str, help = "The path to input image or video")

    # Optional
    parser.add_argument("-d", "--device", default = "CPU", type =str, help = "To specify target device used for inference, CPU, GPU, VPU etc.")
    parser.add_argument("-l", "--cpu_extension", required = False, type = str, default = None, help = "MKLDNN (CPU)-targeted custom layers. Absolute path to a shared library")
    parser.add_argument("-pt", "--prob_threshold", default = 0.5, type = float, help = "Probability threshold for detections filtering")
    parser.add_argument("-c", "--color", default = "GREEN", type = str, help = "The color of the bounding boxes to draw; RED, GREEN or BLUE")
    parser.add_argument("-at", "--alert_time", default = 15.0, type = float, help = "The duration people stay in the frame")

    args = parser.parse_args()

    return args

def preprocessing(frame, batch, channel, height, width):

    # Resize to model's input w x h
    p_frame = cv2.resize(frame, (width, height))

    # Transpose the layout from hwc to chw
    p_frame = p_frame.transpose((2, 0, 1))

    # Reshape to model's shape n x c x h x w
    p_frame = p_frame.reshape((batch, channel, height, width))

    return p_frame

def infer_on_stream(args, client):

    # Set flag for the input
    single_image_mode = False

    # Instantiate current inference request
    cur_request_id = 0

    # Initialize the plugin
    plugin = Network()

    # Load model, and get the input shape
    plugin.load_model(args.model, args.device, cur_request_id, args.cpu_extension)
    n, c, h, w = plugin.get_input_shape()

    ### Checking for the feed input file
    # CAM
    if args.input == 'CAM':
        input_stream = 0

    # Image
    elif args.input.endswith('.jpg') or args.input.endswith('.bmp') :
        single_image_mode = True
        input_stream = args.input

    # Video
    else:
        input_stream = args.input
        assert os.path.isfile(args.input), "Specified input file doesn't exist"

    # Create the VideoCapture object, and have it opened
    cap = cv2.VideoCapture(input_stream)

    if input_stream:
        cap.open(args.input)

    if not cap.isOpened():
        log.error("ERROR! Unable to open video source")
    ###

    # Grab the dimension of width and height
    global initial_w, initial_h
    initial_w = cap.get(3)
    initial_h = cap.get(4)

    # Set initial parameters
    temp = 0
    fp = 0
    last_count = 0
    total_count = 0
    duration = 0

    # Iterate the frame until the video ends
    while cap.isOpened():
        flag, frame = cap.read()
        if not flag:
            break
        key_pressed = cv2.waitKey(60)

        # Preprocessing
        p_frame = preprocessing(frame, n, c, h, w)

        # Perform async_inference
        plugin.exec_net(cur_request_id, p_frame)
        inf_start = time.time()

        # Get the output result
        if plugin.wait(cur_request_id) == 0:
            result = plugin.extract_output(cur_request_id)
            det_time = time.time() - inf_start

            # Draw the bounding box
            frame, current_count, d, fp = draw_boxes(frame, result, args, temp, fp)

            # Print the inference time
            inf_time_message = "Inference time: {:.3f}ms".format(det_time * 1000)
            cv2.putText(frame, inf_time_message, (15, 15), cv2.FONT_HERSHEY_COMPLEX, 0.5, (200, 10, 10), 1)

            ### People Counting ###
            # When new person enters into the frame
            if current_count > last_count: # New entry
                start_time = time.time()
                total_count = total_count + current_count - last_count
                client.publish("person", json.dumps({"total": total_count}))

            if current_count < last_count:
                duration = int(time.time() - start_time)
                client.publish("person/duration", json.dumps({"duration": duration}))

            # To check lost frames for false positive cases
            lost_info = "Lost frame: %d" %(fp - 1)
            cv2.putText(frame, lost_info, (15, 30), cv2.FONT_HERSHEY_COMPLEX, 0.5, (200, 10, 10), 1)

            # Warning if people staying time > alert time
            if duration >= args.alert_time:
                cv2.putText(frame, 'CAUTION! Staying longer than ' + str(args.alert_time) + ' seconds', (15, 45), cv2.FONT_HERSHEY_COMPLEX, 0.5, (0, 0, 255), 1)

            client.publish("person", json.dumps({"count": current_count}))
            last_count = current_count

            temp = d
            ###----------------###

            # Break if escape key is pressed
            if key_pressed == 27:
                break

        # Send the information to FFmpeg server
        sys.stdout.buffer.write(frame)
        sys.stdout.flush()

        # Image mode
        if single_image_mode:
            cv2.imwrite('output_image.jpg', frame)

    # Release all. Destroy OpenCV windows, and disconnect the MQTT
    cap.release()
    cv2.destroyAllWindows()
    client.disconnect()

def draw_boxes(frame, result, args, x, k):

    # Customize the color space of bounding box, if not assign default will be 'GREEN'
    colors = {"BLUE": (255, 0, 0), "GREEN": (0, 255, 0), "RED": (0, 0, 255)}
    if args.color:
        draw_color = colors.get(args.color)
    else:
        draw_color = colors["GREEN"]

    current_count = 0
    distance = x

    for obj in result[0][0]:
        conf = obj[2]
        if conf >= args.prob_threshold:
            xmin = int(obj[3] * initial_w)
            ymin = int(obj[4] * initial_h)
            xmax = int(obj[5] * initial_w)
            ymax = int(obj[6] * initial_h)
            class_id = int(obj[1])

            # If the probability threshold is crossed, then count the people, and draw the bounding box
            current_count += 1
            cv2.rectangle(frame, (xmin, ymin), (xmax, ymax), draw_color, 3)

            det_label = str(class_id)
            cv2.putText(frame, "label: " + det_label + ' ' + str(round(obj[2] * 100, 1)) + ' %', (xmin, ymin - 7), cv2.FONT_HERSHEY_COMPLEX, 0.6, draw_color, 2)
            enter_time = time.time()

            # To calculate the distance
            c_y = frame.shape[0] / 2
            c_x = frame.shape[1] / 2

            mid_y = (ymin + ymax) / 2
            mid_x = (xmin + xmax) / 2

            distance =  math.sqrt(math.pow(mid_x - c_x, 2) +  math.pow(mid_y - c_y, 2) * 1.0)
            k = 0

    if current_count < 1:
        k += 1

    if distance > 0 and k < 15:
        current_count = 1
        k += 1
        if k > 100:
            k = 0

    return frame, current_count, distance, k

def main():
    args = get_args()
    client = connect_mqtt()
    infer_on_stream(args, client)

if __name__ == "__main__":
    main()


# %%
# Shi-TOmasi角点检测（一种适应追踪的角点检测方式）
import numpy as np
import cv2
from matplotlib import pyplot as plt

# %%
img = cv2.imread('data/calibresult.png')
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
"""goodFeaturesToTrack函数：
    参数： （1）gray：输入灰度图
           （2） 25：想要检测的角点数量
           （3） 0：角点最低水平 （0到1）
           （4） 10：两个角点的最短欧式距离
"""
corners = cv2.goodFeaturesToTrack(gray,25,0.01,10)
# 返回的结果是[[ 311., 250.]] 两层括号的数组。
corners = np.int0(corners)
for corner in corners:
    x,y = corner.ravel()
    cv2.circle(img,(x,y),3,255,-1)  #汇出角点
plt.imshow(img),plt.show()

# %%