from pathlib import Path
from datetime import datetime, timedelta
import time
import csv
from pins import *

class Alarmmod():
    def __init__(self, Main):
        self.Main = Main

        self.alarms = []
        self.alarm_path = Path(__file__).resolve().parent / "alarms.txt"
        self.alarm_format = "%d.%m.%y %H:%M"

        self.selected = 0       # index of selected rel to alarms
        self.displayed = []     # index of displayed lines from disp_list
        # complete list of alarms, ordered, [[{alarm: opt}, selected?, active?], ...]

        self.mod_alarm = None       # holds alarm which is getting modifyied
        self.sound_selected = 0      # index of dict
        self.light_selected = 0
        self.light_opt = ["Aus", "Sonnenaufgang"]
        self.light_delay = 0

        self.load_alarms()

    def is_set24(self):
        '''Whether an alarm is set for the next 24h.
        '''
        for alarm in self.alarms:
            if alarm.get_armed24():
                return True
        return False

    def get_set24(self):
        '''Returns list of all alarms set for the next 36h.
        '''
        armed = []
        for alarm in self.alarms:
            if alarm.get_armed24():
                armed.append(alarm)
        return armed

    def load_alarms(self):
        with self.alarm_path.open(newline='') as alarm_file:
            alarm_reader = csv.reader(alarm_file, skipinitialspace=True)
            for alarm_line in alarm_reader:
                alarm_date = datetime.strptime(
                    alarm_line[0], self.alarm_format)
                active = bool(alarm_line[1] == "True")
                self.alarms.append(Alarm(
                    alarm_date, sound=alarm_line[2],
                    light=alarm_line[3], light_delay=alarm_line[4], active=active))
        print("befor sorting: \n", self.alarms)
        self.alarms.sort()
        print("after sorting: \n", self.alarms)

    def save_alarms(self):
        print("Saving alarms..")
        with self.alarm_path.open("w", newline='') as alarm_file:
            for alarm in self.alarms:
                alarm_date_str = alarm.get_a_datetime().strftime(
                    self.alarm_format)
                row = "{}, {}, {}, {}, {}\n".format(
                    alarm_date_str, alarm.active, alarm.sound, alarm.light,
                    int(alarm.light_delay.seconds/60))
                print("saving alarms: ", row)
                alarm_file.write(row)

    def get_disp_list(self):
        if self.displayed == []:     # fill initally the list "displayed"
            self.displayed.extend(
                [i for i, _ in enumerate(self.alarms) if i < 3])
            self.alarms[0].set_selected()

        self.updated_displayed()
        return [self.alarms[i] for i in self.displayed]

    def updated_displayed(self):
        while self.selected not in self.displayed:
            if self.selected < min(self.displayed):
                self.displayed = [i - 1 for i in self.displayed]
            elif self.selected > max(self.displayed):
                self.displayed = [i + 1 for i in self.displayed]

            self.displayed = [len(self.alarms) +
                              i if i < 0 else i for i in self.displayed]
            self.displayed = [i - len(self.alarms)
                              if i > len(self.alarms) - 1
                              else i for i in self.displayed]

    def del_mod(self):
        self.alarms.remove(self.mod_alarm)

# -----Multiline button handling-----
    def sel_up(self):
        print("Selection UP")
        self.selected -= 1
        if self.selected < 0:
            self.selected = len(self.alarms) - 1
        self.alarms[self.selected].set_selected(True, self.alarms)
        self.Main.Disp.disp_update = True

    def sel_down(self):
        print("Selection Down")
        self.selected += 1
        if self.selected > len(self.alarms) - 1:
            self.selected = 0
        self.alarms[self.selected].set_selected(True, self.alarms)
        self.Main.Disp.disp_update = True

    def sel_ok(self):
        print("Selection OK")
        if not self.alarms[self.selected].get_armed24():   # inactive -> active
            print("inactive --> active")
            self.alarms[self.selected].set_armed24()
            self.save_alarms()
        else:
            self.Main.menu_mode = "Main"                # active -> settings
            self.Main.Menu.set_crr_menu("mod_alarm")
            self.Main.Disp.set_disp_mode(1)
            self.mod_alarm = self.alarms[self.selected]

        self.Main.Disp.disp_update = True

    def sel_back(self):
        print("Selection BACK")
        self.displayed = []
        self.Main.menu_mode = "Main"
        self.Main.Menu.set_crr_menu("main")
        self.Main.Disp.set_disp_mode(1)
        self.Main.Disp.disp_update = True

# -----alarm clock setting-----
    def set_clock(self, ch):
        ''' Handles buttons if menu_mode = "alarm_set"
        '''
        # -----hours-----
        if self.mod_alarm.mod_part == "h":
            repeats = 1
            while True:
                if ch == UP:
                    print("mod a_datetime: ", self.mod_alarm.a_datetime)
                    self.mod_alarm.a_datetime -= timedelta(hours=1)
                    self.Main.Disp.disp_update = True
                    print("mod a_datetime + 1h: ", self.mod_alarm.a_datetime)
                    if GPIO.input(UP):
                        time.sleep(1 / repeats)
                        if GPIO.input(UP):
                            if repeats <= 4:
                                repeats += 1
                            continue
                elif ch == DOWN:
                    self.mod_alarm.a_datetime += timedelta(hours=1)
                    self.Main.Disp.disp_update = True
                    if GPIO.input(DOWN):
                        time.sleep(1 / repeats)
                        if GPIO.input(DOWN):
                            if repeats <= 4:
                                repeats += 1
                            continue

                elif ch == BACK:
                    print("Canceled!")
                    self.Main.menu_mode = "alarm_multiL"
                    self.Main.Disp.set_disp_mode(3)

                elif ch == OK:
                    self.mod_alarm.mod_part = "min"
                    self.Main.Disp.disp_update = True
                break
        # -----minutes-----
        elif self.mod_alarm.mod_part == "min":
            repeats = 1
            while True:
                if ch == UP:
                    self.mod_alarm.a_datetime -= timedelta(minutes=1)
                    self.Main.Disp.disp_update = True
                    if GPIO.input(UP):
                        time.sleep(1 / repeats)
                        if GPIO.input(UP):
                            if repeats <= 10:
                                repeats += 1
                            continue
                elif ch == DOWN:
                    self.mod_alarm.a_datetime += timedelta(minutes=1)
                    self.Main.Disp.disp_update = True
                    if GPIO.input(DOWN):
                        time.sleep(1 / repeats)
                        if GPIO.input(DOWN):
                            if repeats <= 10:
                                repeats += 1
                            continue

                elif ch == BACK:
                    self.mod_alarm.mod_part = "h"
                    self.Main.Disp.disp_update = True
                elif ch == OK:
                    self.mod_alarm.mod_part = "sound"
                    self.Main.Disp.disp_update = True
                break
        # -----sound-----
        elif self.mod_alarm.mod_part == "sound":
            if ch == UP:
                self.sound_selected -= 1
            elif ch == DOWN:
                self.sound_selected += 1
            elif ch == BACK:
                self.mod_alarm.mod_part = "min"

            if self.sound_selected < 0:
                self.sound_selected = len(self.Main.Speakers.sounds_list) - 1
            elif self.sound_selected > len(self.Main.Speakers.sounds_list) - 1:
                self.sound_selected = 0
            #self.mod_alarm.sound = self.Main.Speakers.sounds_list[self.sound_selected]

            if ch == OK:
                print("Alarm old: ", self.mod_alarm.sound)
                self.mod_alarm.sound = self.Main.Speakers.sounds_list[self.sound_selected]
                print("ALARM new: ",
                      self.Main.Speakers.sounds_list[self.sound_selected])
                self.sound_selected = 0
                self.mod_alarm.mod_part = "light"
            self.Main.Disp.disp_update = True
        # -----light-----
        elif self.mod_alarm.mod_part == "light":
            if ch == UP:
                print("light: UP")
                self.light_selected += 1
            elif ch == DOWN:
                print("light: DOWN")
                self.light_selected -= 1
            elif ch == BACK:
                print("light: Back")
                self.mod_alarm.mod_part = "sound"

            if self.light_selected < 0:
                self.light_selected = len(self.light_opt) - 1
            elif self.light_selected > len(self.light_opt) - 1:
                self.light_selected = 0

            if ch == OK:
                self.mod_alarm.light = self.light_opt[self.light_selected]
                if self.mod_alarm.light != "Aus":
                    self.mod_alarm.mod_part = "light_delay"
                else:
                    self.mod_alarm.mod_part = None
                    self.mod_alarm.set_armed24()
                    self.save_alarms()
                    self.Main.tasks.put(["Blink", -1, "green"])
                    self.Main.menu_mode = "alarm_multiL"
                    self.Main.Disp.set_disp_mode(3)
            self.Main.Disp.disp_update = True

        # -----light delay-----
        elif self.mod_alarm.mod_part == "light_delay":
            if ch == UP:
                self.light_delay -= 1 if self.light_delay > 0 else 0
            elif ch == DOWN:
                self.light_delay += 1 if self.light_delay < 30 else 0
            elif ch == BACK:
                self.mod_alarm.mod_part = "light"
            elif ch == OK:
                self.mod_alarm.light_delay = timedelta(
                    minutes=self.light_delay)
                self.mod_alarm.mod_part = None
                self.mod_alarm.set_armed24()
                self.save_alarms()
                self.Main.tasks.put(["Blink", -1, "green"])
                self.Main.menu_mode = "alarm_multiL"
                self.Main.Disp.set_disp_mode(3)

            self.Main.Disp.disp_update = True

# -----Ringing-----
    def ring(self, alarm):
        '''Handles alarm ringing. Sets the light and sound. \n
        Gets called from taskmanager 30 min befor the alarm.
        '''
        print("START: ring() ", alarm)
        # Wait for light
        while not alarm.a_datetime - alarm.light_delay < datetime.now():
            time.sleep(10)

        # Start light if set:
        if alarm.light == "Sonnenaufgang":
            self.Main.tasks.put(
                ["sunrise", -1, alarm.light_delay.total_seconds()])

        # Wait for alarm
        while not alarm.a_datetime < datetime.now() and not self.Main.abort_alarm:
            time.sleep(10)

        if self.Main.abort_alarm:
            self.Main.abort_alarm = False
            self.Main.ringing = False
            return

        # waking display up
        if self.Main.menu_mode == "night":
            self.Main.menu_mode = "full_screen"
            self.Main.Disp.set_disp_mode(2)

        # start alarm sound
        print("Start Alarm")
        if alarm.sound in self.Main.Speakers.stations:
            print("..in 'stations'")
            self.Main.Speakers.radio(alarm.sound)
        elif alarm.sound in self.Main.Speakers.musik:
            print("..in 'musik'")
            file_uri = self.Main.Speakers.musik[alarm.sound]
            print(file_uri)
            self.Main.Speakers.play_file(file_uri)
        self.Main.Menu.make_submenus("Radio_on")

        time.sleep(10)

        if self.Main.abort_alarm:
            self.Main.abort_alarm = False
            self.Main.ringing = False
            return
        if not self.Main.Speakers.is_playing:
            print("Alarm sound not playing after 5s, playing local track.")
            self.Main.Speakers.play_file(
                self.Main.Speakers.get_audio_files().popitem()[1])

# -----Scheduler-----

    def alarm_scheduler(self):
        ''' Checks every 60s for set alarms. Activates a alarm 30 min befor
        the alarmtime by passing it to ring().
        '''

        print("alarm_scheduler initialized")
        while not self.Main.exit_flag:
            time.sleep(60)
            if not self.is_set24():
                # print("alarm_scheduler: No alarm active, sleeping...")
                pass
            else:
                act_alarms = self.get_set24()
                # print("alarm_scheduler: ", act_alarms)
                soonest = act_alarms[0]
                for alarm in act_alarms:
                    if alarm.a_datetime < soonest.a_datetime:
                        soonest = alarm

                if not soonest.a_datetime < datetime.now() + timedelta(minutes=35):
                    # print("alarm_scheduler: No alarm the next 40 min, sleeping...")
                    pass
                elif not self.Main.ringing:
                    self.Main.tasks.put(("ring", [-1], soonest))
                    soonest.active = False
                else:
                    print("Already ringing!! ", soonest)

        print("alarm_sceduler: EXIT")

class Alarm():
    def __init__(self, a_datetime, sound="SWR3", light="Aus", light_delay=0, active=True):
        self.a_datetime = a_datetime
        self.sound = sound
        self.light = light
        self.light_delay = timedelta(minutes=int(light_delay))
        self.active = active
        self.selected = False
        self.mod_part = None    # "h", "min", "sound", "light"

    def __lt__(self, other):  # self < other?
        if self.get_armed24() == other.get_armed24():
            if self.a_datetime < other.a_datetime:
                return True
            return False

        return self.get_armed24()

    def __eq__(self, other):
        return (self.get_armed24() == other.get_armed24()) and self.a_datetime == other.a_datetime

    def __str__(self):
        return "--".join((self.a_datetime.strftime("%d.%m.%y %H:%M"), str(self.active), "\n"))

    def set_active(self, active=True):
        if active:
            self.active = True
        else:
            self.active = False

    def get_selected(self):
        return self.selected

    def set_selected(self, selected=True, all_alarms=None):
        if all_alarms is not None:
            for alarm in all_alarms:
                alarm.set_selected(False)
        self.selected = selected

    def get_armed24(self):
        '''Whether the alarm is set and active in the next 36 h.
        '''
        if self.active:
            tomorrow = datetime.now() + timedelta(days=1, hours=12)
            if (datetime.now() < self.a_datetime) and (self.a_datetime < tomorrow):
                return True
        return False

    def set_armed24(self):
        '''Activates the alarm, if the date is in the past, sets it to tomorrow.
        '''
        print("set_armed24")
        self.active = True

        self.a_datetime = datetime.combine(
            datetime.today(), self.a_datetime.time())
        if self.a_datetime < datetime.now():
            # set date to tomorrow, keep time
            tomorrow = datetime.now() + timedelta(days=1)
            self.a_datetime = datetime.combine(
                tomorrow, self.a_datetime.time())
        print("a_datetime was changed: ", self.a_datetime)
        print("get_armed24: ", self.get_armed24())
        # else:
        #     print("set_armed24: a_datetime not changed.")

    def get_text_line(self):
        return [self.a_datetime, self.active, self.selected]

    def set_a_datetime(self, a_datetime):
        self.a_datetime = a_datetime

    def get_a_datetime(self):
        return self.a_datetime

    __repr__ = __str__


#!/usr/bin/env python3
"""RTL-SDR P2000 Receiver for Home Assistant."""

# See README for installation instructions
import calendar
import configparser
import fnmatch
import json
import os
import re
import subprocess
import sys
import threading
import time
from datetime import datetime

import requests

VERSION = "0.0.1"

class MessageItem:
    """Contains all the Message data."""

    def __init__(self):
        self.timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        self.message_raw = ""
        self.timestamp = ""
        self.timereceived = time.monotonic()
        self.groupid = ""
        self.receivers = ""
        self.capcodes = []
        self.body = ""
        self.location = ""
        self.postcode = ""
        self.city = ""
        self.address = ""
        self.street = ""
        self.region = ""
        self.priority = 0
        self.disciplines = ""
        self.remarks = ""
        self.is_posted = False

def load_config():
    """Create default or load existing config file."""
    config = configparser.ConfigParser()
    if config.read("config.ini"):
        print("Loading configuration from 'config.ini'")
        return config

    config["main"] = {"debug": False}
    config["rtl-sdr"] = {
        "cmd": "rtl_fm -f 169.65M -M fm -s 22050 | multimon-ng -a FLEX -t raw -"
    }
    config["home-assistant"] = {
        "baseurl": "http://192.168.2.123:8123",
        "token": "Place Your Long-Lived Access Token Here",
        "sensorname": "P2000",
    }
    with open("config.ini", "w") as configfile:
        config.write(configfile)
    print("Created config file 'config.ini', edit it and restart the program.")
    sys.exit(0)

def check_requirements():
    """Check if required software is installed."""
    print("Checking if required software is installed")
    # Check if rtl_fm is installed
    process = subprocess.Popen(
        "rtl_fm", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE
    )
    # Wait for the process to finish
    dummy, err = process.communicate()
    error_str = err.decode("utf8")
    if "not found" in error_str or "not recognized" in error_str:
        print("rtl_fm command not found, please install RTL-SDR software")
        return False

    print("rtl_fm is found")

    # Check if multimon-ng is installed
    process = subprocess.Popen(
        "multimon-ng -h", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE
    )
    # Wait for the process to finish
    dummy, err = process.communicate()
    error_str = err.decode("utf8")
    if "not found" in error_str:
        print("multimon-ng not found, please install the multimon-ng package")
        return False

    print("multimon-ng is found")
    return True

def load_capcodes_dict(filename):
    """Load capcodes to dictionary."""
    capcodes = {}
    try:
        print("Loading data from '{}'".format(filename))
        with open(filename, "r") as csv_file:
            csv_list = [
                [val.strip() for val in r.split(",")] for r in csv_file.readlines()
            ]

        (_, *header), *data = csv_list
        for row in data:
            key, *values = row
            capcodes[key] = {key: value for key, value in zip(header, values)}
        print("{} records loaded".format(len(capcodes)))
    except KeyError:
        print(f"Could not parse file contents of: {filename}")
    except OSError:
        print(f"Could not open/read file: {filename}, ignore filter")

    return capcodes

def load_capcodes_filter_dict(filename):
    """Load capcodes ignore or match data to dictionary."""
    capcodes = dict()
    try:
        print("Loading data from '{}'".format(filename))
        with open(filename, "r") as text_file:
            lines = text_file.readlines()
            for item in lines:
                if item[0] == "#":
                    continue

                fields = item.split(",")
                if len(fields) == 2:
                    capcodes[fields[0].strip()] = fields[1].strip()
                elif len(fields) == 1:
                    capcodes[fields[0].strip()] = 'NO DESCR'
        print("{} records loaded".format(len(capcodes)))
        return capcodes
    except KeyError:
        print(f"Could not parse file contents of: {filename}")
    except OSError:
        print(f"Could not open/read file: {filename}, ignore filter")

    return capcodes

def load_list(filename):
    """Load data in list."""
    tmplist = []
    try:
        print("Loading data from '{}'".format(filename))
        with open(filename, "r") as text_file:
            lines = text_file.readlines()
            lines_strip = map((lambda line: line.strip()), lines)
            tmplist = list(
                filter(
                    lambda line: len(line) > 0
                    and line[0:1] != "#"
                    and line[0:1] != ";",
                    lines_strip,
                )
            )
        print("{} records loaded".format(len(tmplist)))
        return tmplist
    except KeyError:
        print(f"Could not parse file contents of: {filename}")
    except OSError:
        print(f"Could not open/read file: {filename}")

    return tmplist

def check_filter(mylist, text):
    """Check filter data."""
    # If list is not loaded or empty allow all
    if len(mylist) == 0:
        return True

    # Check if text applied matches at least one filter
    for f_str in mylist:
        if fnmatch.fnmatch(text, f_str):
            return True

    return False

def to_local_datetime(utc_dt):
    """Convert utc to local time."""
    time_tuple = time.strptime(utc_dt, "%Y-%m-%d %H:%M:%S")
    return time.ctime(calendar.timegm(time_tuple))

def p2000_get_prio(message):
    """Look for priority strings and return level."""
    priority = 0

    regex_prio1 = r"^A\s?1|\s?A\s?1|PRIO\s?1|^P\s?1"
    regex_prio2 = r"^A\s?2|\s?A\s?2|PRIO\s?2|^P\s?2"
    regex_prio3 = r"^B\s?1|^B\s?2|^B\s?3|PRIO\s?3|^P\s?3"
    regex_prio4 = r"^PRIO\s?4|^P\s?4"

    if re.search(regex_prio1, message, re.IGNORECASE):
        priority = 1
    elif re.search(regex_prio2, message, re.IGNORECASE):
        priority = 2
    elif re.search(regex_prio3, message, re.IGNORECASE):
        priority = 3
    elif re.search(regex_prio4, message, re.IGNORECASE):
        priority = 4

    return priority

class Main:
    """Main class, start of application."""

    def __init__(self):
        self.running = True
        self.messages = []

        print(f"RTL-SDR P2000 Receiver for Home Assistant Version {VERSION}\n")
        # Set current folder so we can find the config files
        os.chdir(os.path.dirname(os.path.abspath(__file__)))

        # Check if required software is installed
        if not check_requirements():
            print("Application stopped, required software was not found!")
            sys.exit(0)

        # Load configuration
        self.config = load_config()
        self.debug = self.config.getboolean("main", "debug")
        self.rtlfm_cmd = self.config.get("rtl-sdr", "cmd")
        self.baseurl = self.config.get("home-assistant", "baseurl")
        self.token = self.config.get("home-assistant", "token")
        self.sensorname = self.config.get("home-assistant", "sensorname")

        # Load capcodes data
        self.capcodes = load_capcodes_dict("db_capcodes.txt")

        # Load plaatsnamen data
        self.plaatsnamen = load_list("db_plaatsnamen.txt")

        # Load plaatsnamen afkortingen data
        self.pltsnmn = load_capcodes_dict("db_pltsnmn.txt")

        # Load capcodes ignore data
        self.ignorecapcodes = load_capcodes_filter_dict("ignore_capcodes.txt")

        # Load text ignore data
        self.ignoretext = load_list("ignore_text.txt")

        # Load match text filter data
        self.matchtext = load_list("match_text.txt")

        # Load match capcodes filter data
        self.matchcapcodes = load_capcodes_filter_dict("match_capcodes.txt")

        # Start thread to get data from RTL-SDR stick
        data_thread = threading.Thread(target=self.data_thread_call)
        data_thread.start()

        # Start thread to post messages to Home Assistant
        post_thread = threading.Thread(target=self.post_thread_call)
        post_thread.start()

        # Run the wait loop
        while True:
            try:
                time.sleep(1)
            except KeyboardInterrupt:
                break

        # Application is interrupted and is stopping
        self.running = False
        print("Application stopped")

    def post_to_homeassistant(self, msg):
        """Post data to Home Assistant via Rest API."""
        data = {
            "state": msg.body,
            "attributes": {
                "time received": msg.timestamp,
                "group id": msg.groupid,
                "receivers": msg.receivers,
                "capcodes": msg.capcodes,
                "priority": msg.priority,
                "disciplines": msg.disciplines,
                "raw message": msg.message_raw,
                "region": msg.region,
                "location": msg.location,
                "postcode": msg.postcode,
                "city": msg.city,
                "address": msg.address,
                "street": msg.street,
                "remarks": msg.remarks,
            },
        }

        try:
            headers = {
                "Authorization": "Bearer " + self.token,
                "content-type": "application/json",
            }

            response = requests.post(
                self.baseurl + "/api/states/sensor." + self.sensorname,
                headers=headers,
                data=json.dumps(
                    data, default=lambda o: o.__dict__, sort_keys=True, indent=4
                ),
            )
            response.raise_for_status()
            if self.debug:
                print(f"POST status: {response.status_code} {response.reason}")
                print(f"POST text: {response.text}")
        except requests.HTTPError:
            print(
                f"HTTP Error while trying to post data, check your baseurl and token in config.ini: {response.status_code} {response.reason}"
            )
        except requests.exceptions.SSLError as err:
            print(
                f"SSL Error occurred while trying to post data, check baseurl in config.ini:\n{err}"
            )
        except requests.exceptions.ConnectionError as err:
            print(
                f"Connection Error occurred while trying to post data, check your baseurl in config.ini:\n{err}"
            )
        finally:
            # Mark as posted to prevent race conditions
            msg.is_posted = True

    def data_thread_call(self):
        """Thread for parsing data from RTL-SDR."""
        print(f"RTL-SDR process started with: {self.rtlfm_cmd}")
        multimon_ng = subprocess.Popen(
            self.rtlfm_cmd, stdout=subprocess.PIPE, shell=True
        )
        try:
            while self.running:
                # Read line from process
                line = multimon_ng.stdout.readline()
                try:
                    line = line.decode("utf8", "backslashreplace")
                except UnicodeDecodeError:
                    if self.debug:
                        print(f"Error while decoding utf8 string: {line}")
                    line = ""
                multimon_ng.poll()
                if line.startswith("FLEX") and line.__contains__("ALN"):
                    line_data = line.split("|")
                    timestamp = line_data[1]
                    groupid = line_data[3].strip()
                    capcodes = line_data[4].strip()
                    message = line_data[6].strip()
                    priority = p2000_get_prio(message)
                    location = ""
                    postcode = ""
                    city = ""
                    address = ""
                    street = ""

                    if self.debug:
                        print(line.strip())

                    # Get address info if any, look for valid postcode and get the two words around them
                    # A2 (DIA: ja) AMBU 17106 Schiedamseweg 3134BA Vlaardingen VLAARD bon 8576
                    regex_address = r"(\w*.) ([1-9][0-9]{3}[a-zA-Z]{2}) (.\w*)"
                    addr = re.search(regex_address, message)
                    if addr:
                        street = addr.group(1)
                        postcode = addr.group(2)
                        city = addr.group(3)
                        address = f"{street} {postcode} {city}"

                    # Try to get city only when there is one after a prio
                    # A1 Breda
                    else:
                        regex_prio_loc = r"(^A\s?1|\s?A\s?2|B\s?1|^B\s?2|^B\s?3|PRIO\s?1|^P\s?1|PRIO\s?2|^P\s?2) (.\w*)"
                        loc = re.search(regex_prio_loc, message)
                        if loc and loc.group(2) in self.plaatsnamen:
                            city = loc.group(2)
                        else:
                            # Find all uppercase words and check if there is a valid city name amoung them
                            # A2 Ambulancepost Moordrecht Middelweg MOORDR V
                            regex_afkortingen = "[A-Z]{2,}"
                            afkortingen = re.findall(regex_afkortingen, message)
                            for afkorting in afkortingen:
                                if afkorting in self.pltsnmn:
                                    city = self.pltsnmn[afkorting]["plaatsnaam"]

                    if not check_filter(self.matchtext, message):
                        if self.debug:
                            print(
                                f"Message '{message}' ignored (didn't match match_text)")
                    else:
                        if check_filter(self.ignoretext, message):
                            if self.debug:
                                print(
                                    f"Message '{message}' ignored (matched ignore_text)")
                        else:
                            # There can be several capcodes in one message
                            ignore = False
                            for capcode in capcodes.split(" "):
                                # Apply filter
                                if not capcode in self.matchcapcodes and self.matchcapcodes:
                                    if self.debug:
                                        print(
                                            f"Message '{message}' ignored (didn't match match_capcodes)"
                                        )
                                    ignore = True
                                    break
                                if capcode in self.ignorecapcodes and self.ignorecapcodes:
                                    if self.debug:
                                        print(
                                            f"Message '{message}' to '{capcode}' ignored (capcode in ignore_capcodes)"
                                        )
                                    ignore = True
                                    break

                            if not ignore:
                                for capcode in capcodes.split(" "):
                                    # Get data from capcode, if exist
                                    if capcode in self.capcodes:
                                        receiver = "{} ({})".format(
                                            self.capcodes[capcode]["description"], capcode
                                        )
                                        discipline = "{} ({})".format(
                                            self.capcodes[capcode]["discipline"], capcode
                                        )
                                        region = self.capcodes[capcode]["region"]
                                        location = self.capcodes[capcode]["location"]
                                        remark = self.capcodes[capcode]["remark"]
                                    else:
                                        receiver = capcode
                                        discipline = ""
                                        region = ""
                                        remark = ""

                                    # If this message was already received, only add extra info
                                    if len(self.messages) > 0 and self.messages[0].body == message:
                                        if self.messages[0].receivers == "":
                                            self.messages[0].receivers = receiver
                                        elif receiver:
                                            self.messages[0].receivers += ", " + receiver

                                        if self.messages[0].disciplines == "":
                                            self.messages[0].disciplines = discipline
                                        elif discipline:
                                            self.messages[0].disciplines += ", " + discipline
                                        if self.messages[0].remarks == "":
                                            self.messages[0].remarks = remark
                                        elif remark:
                                            self.messages[0].remarks += ", " + remark

                                        self.messages[0].capcodes.append(capcode)
                                        self.messages[0].location = location
                                        self.messages[0].postcode = postcode
                                        self.messages[0].city = city
                                        self.messages[0].street = street
                                        self.messages[0].address = address
                                    else:
                                        msg = MessageItem()
                                        msg.groupid = groupid
                                        msg.receivers = receiver
                                        msg.capcodes = [capcode]
                                        msg.body = message
                                        msg.message_raw = line.strip()
                                        msg.disciplines = discipline
                                        msg.priority = priority
                                        msg.region = region
                                        msg.location = location
                                        msg.postcode = postcode
                                        msg.city = city
                                        msg.street = street
                                        msg.address = address
                                        msg.remarks = remark
                                        msg.timestamp = to_local_datetime(timestamp)
                                        msg.is_posted = False
                                        self.messages.insert(0, msg)

                                # Limit the message list size
                                if len(self.messages) > 100:
                                    self.messages = self.messages[:100]

        except KeyboardInterrupt:
            os.kill(multimon_ng.pid, 9)

        if self.debug:
            print("Data thread stopped")

    # Thread for posting data to Home Assistant
    def post_thread_call(self):
        """Thread for posting data."""
        if self.debug:
            print("Post thread started")
        while True:
            if self.running is False:
                break

            now = time.monotonic()
            for msg in self.messages:
                if msg.is_posted is False and now - msg.timereceived >= 1.0:
                    self.post_to_homeassistant(msg)
            time.sleep(1.0)
        if self.debug:
            print("Post thread stopped")

# Start application
Main()


#!/usr/bin/env python
#Copyright ReportLab Europe Ltd. 2000-2017
#see license.txt for license details
#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/graphics/widgets/grids.py
__version__='3.3.0'

from reportlab.graphics.widgetbase import Widget
from reportlab.graphics.charts.textlabels import Label
from reportlab.graphics import shapes
from reportlab.lib import colors
from reportlab.lib.validators import *
from reportlab.lib.attrmap import *

from reportlab.graphics.shapes import Drawing

class TableWidget(Widget):
    """A two dimensions table of labels
    """

    _attrMap = AttrMap(
        x = AttrMapValue(isNumber, desc="x position of left edge of table"),
        y = AttrMapValue(isNumber, desc="y position of bottom edge of table"),
        width = AttrMapValue(isNumber, desc="table width"),
        height = AttrMapValue(isNumber, desc="table height"),
        borderStrokeColor = AttrMapValue(isColorOrNone, desc="table border color"),
        fillColor = AttrMapValue(isColorOrNone, desc="table fill color"),
        borderStrokeWidth = AttrMapValue(isNumber, desc="border line width"),
        horizontalDividerStrokeColor = AttrMapValue(isColorOrNone, desc="table inner horizontal lines color"),
        verticalDividerStrokeColor = AttrMapValue(isColorOrNone, desc="table inner vertical lines color"),
        horizontalDividerStrokeWidth = AttrMapValue(isNumber, desc="table inner horizontal lines width"),
        verticalDividerStrokeWidth = AttrMapValue(isNumber, desc="table inner vertical lines width"),
        dividerDashArray = AttrMapValue(isListOfNumbersOrNone, desc='Dash array for dividerLines.'),
        data = AttrMapValue(None, desc="a list of list of strings to be displayed in the cells"),
        boxAnchor = AttrMapValue(isBoxAnchor, desc="location of the table anchoring point"),
        fontName = AttrMapValue(isString, desc="text font in the table"),
        fontSize = AttrMapValue(isNumber, desc="font size of the table"),
        fontColor = AttrMapValue(isColorOrNone, desc="font color"),
        alignment = AttrMapValue(OneOf("left", "right"), desc="Alignment of text within cells"),
        textAnchor = AttrMapValue(OneOf('start','middle','end','numeric'), desc="Alignment of text within cells"),
    )

    def __init__(self, x=10, y=10, **kw):

        self.x = x
        self.y = y
        self.width = 200
        self.height = 100
        self.borderStrokeColor = colors.black
        self.fillColor = None
        self.borderStrokeWidth = 0.5
        self.horizontalDividerStrokeColor = colors.black
        self.verticalDividerStrokeColor = colors.black
        self.horizontalDividerStrokeWidth = 0.5
        self.verticalDividerStrokeWidth = 0.25
        self.dividerDashArray = None
        self.data = [['North','South','East','West'],[100,110,120,130],['A','B','C','D']] # list of rows each row is a list of columns
        self.boxAnchor = 'nw'
        #self.fontName = None
        self.fontSize = 8
        self.fontColor = colors.black
        self.alignment = 'right'
        self.textAnchor = 'start'

        for k, v in kw.items():
            if k in list(self.__class__._attrMap.keys()):
                setattr(self, k, v)
            else:
                raise ValueError('invalid argument supplied for class %s'%self.__class__)

    def demo(self):
        """ returns a sample of this widget with data
        """
        d = Drawing(400, 200)
        t = TableWidget()
        d.add(t, name='table')
        d.table.dividerDashArray = (1, 3, 2)
        d.table.verticalDividerStrokeColor = None
        d.table.borderStrokeWidth = 0
        d.table.borderStrokeColor = colors.red
        return d

    def draw(self):
        """ returns a group of shapes
        """
        g = shapes.Group()

        #overall border and fill
        if self.borderStrokeColor or self.fillColor: # adds border and filling color
            rect = shapes.Rect(self.x, self.y, self.width, self.height)
            rect.fillColor = self.fillColor
            rect.strokeColor = self.borderStrokeColor
            rect.strokeWidth = self.borderStrokeWidth
            g.add(rect)

        #special case - for an empty table we want to avoid divide-by-zero
        data = self.preProcessData(self.data)
        rows = len(self.data)
        cols = len(self.data[0])
        #print "(rows,cols)=(%s, %s)"%(rows,cols)
        row_step = self.height / float(rows)
        col_step = self.width / float(cols)
        #print "(row_step,col_step)=(%s, %s)"%(row_step,col_step)
        # draw the grid
        if self.horizontalDividerStrokeColor:
            for i in range(rows): # make horizontal lines
                x1 = self.x
                x2 = self.x + self.width
                y = self.y + row_step*i
                #print 'line (%s, %s), (%s, %s)'%(x1, y, x2, y)
                line = shapes.Line(x1, y, x2, y)
                line.strokeDashArray = self.dividerDashArray
                line.strokeWidth = self.horizontalDividerStrokeWidth
                line.strokeColor = self.horizontalDividerStrokeColor
                g.add(line)
        if self.verticalDividerStrokeColor:
            for i in range(cols): # make vertical lines
                x = self.x+col_step*i
                y1 = self.y
                y2 = self.y + self.height
                #print 'line (%s, %s), (%s, %s)'%(x, y1, x, y2)
                line = shapes.Line(x, y1, x, y2)
                line.strokeDashArray = self.dividerDashArray
                line.strokeWidth = self.verticalDividerStrokeWidth
                line.strokeColor = self.verticalDividerStrokeColor
                g.add(line)

        # since we plot data from down up, we reverse the list
        self.data.reverse()
        for (j, row) in enumerate(self.data):
            y = self.y + j*row_step + 0.5*row_step - 0.5 * self.fontSize
            for (i, datum) in enumerate(row):
                if datum:
                    x = self.x + i*col_step + 0.5*col_step
                    s = shapes.String(x, y, str(datum), textAnchor=self.textAnchor)
                    s.fontName = self.fontName
                    s.fontSize = self.fontSize
                    s.fillColor = self.fontColor
                    g.add(s)
        return g

    def preProcessData(self, data):
        """preprocess and return a new array with at least one row
        and column (use a None) if needed, and all rows the same
        length (adding Nones if needed)

        """
        if not data:
            return [[None]]
        #make all rows have similar number of cells, append None when needed
        max_row = max( [len(x) for x in data] )
        for rowNo, row in enumerate(data):
            if len(row) < max_row:
                row.extend([None]*(max_row-len(row)))
        return data

#test
if __name__ == '__main__':
    d = TableWidget().demo()
    import os
    d.save(formats=['pdf'],outDir=os.getcwd(),fnRoot=None)


from __future__ import print_function

import json
import pandas
import sys

from sklearn.model_selection import train_test_split, cross_val_score
from sklearn.pipeline import Pipeline
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.multiclass import OneVsRestClassifier
from sklearn.svm import LinearSVC

# TITLE_WEIGHT 1 BODY_WEIGHT 0 gives good, fast results
# TITLE_WEIGHT 1 BODY_WEIGHT 1 gives baseline results
# TITTLE_WEIGHT 1000 BODY_WEIGHT 1 gives best results (but slow) might be coincidence

USE_HIGHEST_PROBABILITY_IF_EMPTY = False  # if True, will always predict its guess for most likely label
PRINT_SCORE_PREDICTIONS = False  # print the actual and predicted labels when scoring

TITLE_WEIGHT = 1  # how many times the title is replicated in the training data
BODY_WEIGHT = 1  # how many times the body is replicated in the training data
TRAINING_SEED = None  # use int for repeatable results. Use NONE to truly randomize the test/train split

BEST_GUESS_PREFIX = '* '

VECTORIZER = TfidfVectorizer()
CLASSIFIER = OneVsRestClassifier(LinearSVC())
PIPELINE = Pipeline([
    ('vectorizer', VECTORIZER),
    ('clf', CLASSIFIER)
])
LABEL_VALUES = []

def _process_key(obj, key):
    val = obj[key]
    val = val.replace(',', ' ')
    val = val.replace('\n', ' ')
    val = val.replace('\r', ' ')
    return val

def load_json(src):
    """ Dispense with CSV format """
    json_data = None
    all_labels = set()
    transformed = []

    with open(src) as f:
        json_data = json.load(f)

    # build master labels set
    for record in json_data:
        labels = set(record['Labels'])
        for label in labels:
            all_labels.add(label)

    # now build up the transformed data
    for record in json_data:
        title = _process_key(record, 'Title')
        body = _process_key(record, 'Body')
        text = ('{} '.format(title) * TITLE_WEIGHT) + ('{} '.format(body) * BODY_WEIGHT)
        new_record = {
            '_id': record['Id'],
            '_text': text,
        }
        for label in all_labels:
            new_record[label] = label in record['Labels']
        transformed.append(new_record)

    df = pandas.read_json(path_or_buf=json.dumps(transformed), orient='records')
    return df

def split_data(x, y, test_split=0.5, random_state=None):
    return train_test_split(x, y, test_size=test_split, random_state=random_state)

def train_classifier(x_train, y_train):
    VECTORIZER.fit(x_train, y_train)
    PIPELINE.fit(x_train, y_train)

def get_best_guess(decision_array):
    # if no predicted labels, take the highest probability label
    max_label = None
    max_decision = -10
    max_decision_j = 0
    ZERO_BUFFER = 0.05
    for j, val in enumerate(decision_array):
        # don't consider features with probabilities at or near zero?
        if val > max_decision and (val < -ZERO_BUFFER or val > ZERO_BUFFER):
            max_decision = val
            max_decision_j = j
    most_probable_label = LABEL_VALUES[max_decision_j]
    best_guess = '{}{}'.format(BEST_GUESS_PREFIX, most_probable_label)
    return best_guess

def strip_best_guess(val):
    if val.startswith(BEST_GUESS_PREFIX):
        return val[len(BEST_GUESS_PREFIX):]
    else:
        return val

def make_prediction(text):
    if not isinstance(text, str):
        TypeError("'make_prediction' only designed to work with a single string.")
    text = pandas.DataFrame({'_id': 0, '_text': text}, index=[0])['_text']
    prediction = PIPELINE.predict(text)[0]
    decision = PIPELINE.decision_function(text)[0]

    label_str = ''
    for i, text in enumerate(prediction):
        label_str = label_str + ('{} '.format(LABEL_VALUES[i]) if text else '')
    label_list = sorted(label_str.split())
    if not label_list and USE_HIGHEST_PROBABILITY_IF_EMPTY:
        label_list.append(get_best_guess(decision))
    return label_list

def score_classifer(x_test, y_test):

    X_TEST = x_test
    Y_TEST = y_test
    predicted = PIPELINE.predict(X_TEST)
    decision_values = PIPELINE.decision_function(X_TEST)

    total_actual = 0
    total_predicted = 0
    total_best_guess = 0
    true_predicted = 0
    true_best_guess = 0
    true_actual = 0

    for i, zip_tuple in enumerate(zip(X_TEST, predicted)):
        item = zip_tuple[0]
        predicted_array = zip_tuple[1]
        decision_array = decision_values[i]

        short_item = item[:25]
        label_str = ''
        for j, x in enumerate(predicted_array):
            label_str = label_str + ('{} '.format(LABEL_VALUES[j]) if x else '')
        label_list = sorted(label_str.split())

        if not label_list and USE_HIGHEST_PROBABILITY_IF_EMPTY:
            label_list.append(get_best_guess(decision_array))

        actual_label_str = ''
        for j, x in enumerate(Y_TEST.values[i]):
            actual_label_str = actual_label_str + ('{} '.format(LABEL_VALUES[j]) if x else '')
        actual_list = sorted(actual_label_str.split())

        if PRINT_SCORE_PREDICTIONS:
            print('{} => (P) {}\t\t(A) {}'.format(short_item, label_list, actual_list), file=sys.stderr)

        for x in label_list:
            if x.startswith(BEST_GUESS_PREFIX):
                x = strip_best_guess(x)
                total_best_guess += 1
                if x in actual_list:
                    true_best_guess += 1
            else:
                total_predicted += 1
                if x in actual_list:
                    true_predicted += 1

        for x in actual_list:
            total_actual += 1
            if x in [strip_best_guess(x) for x in label_list]:
                true_actual += 1
    print('OVERALL: {}/{} ({}%) predicted labels correct'.format(true_predicted, total_predicted, round(100.0 * true_predicted / total_predicted, 2)), file=sys.stderr)
    print('         {}/{} ({}%) predicted labels wrong'.format(total_predicted - true_predicted, total_predicted, round(100.0 * (total_predicted - true_predicted) / total_predicted, 2)), file=sys.stderr)
    if USE_HIGHEST_PROBABILITY_IF_EMPTY:
        print('         {}/{} ({}%) best guess labels correct'.format(true_best_guess, total_best_guess, round(100.0 * true_best_guess / total_best_guess, 2)), file=sys.stderr)
        print('         {}/{} ({}%) best guess labels wrong'.format(total_best_guess - true_best_guess, total_best_guess, round(100.0 * (total_best_guess - true_best_guess) / total_best_guess, 2)), file=sys.stderr)
        print('         {}/{} ({}%) actual labels predicted correctly'.format(true_actual, total_actual, round(100.0 * true_actual / total_actual, 2)), file=sys.stderr)
        print('         {}/{} ({}%) actual labels missed'.format(total_actual - true_actual, total_actual, round(100.0 * (total_actual - true_actual) / total_actual, 2)), file=sys.stderr)
    else:
        print('         {}/{} ({}%) actual labels predicted correctly'.format(true_actual, total_actual, round(100.0 * true_actual / total_actual, 2)), file=sys.stderr)
        print('         {}/{} ({}%) actual labels missed'.format(total_actual - true_actual, total_actual, round(100.0 * (total_actual - true_actual) / total_actual, 2)), file=sys.stderr)

def analyze_data(df):
    if not isinstance(df, pandas.DataFrame):
        raise TypeError('Expected pandas.DataFrame. Got {}'.format(type(df)))

    x_values = df['_text']
    y_values = df.drop(['_id', '_text'], axis=1)

    x_train, x_test, y_train, y_test = split_data(x_values, y_values, test_split=0.2, random_state=TRAINING_SEED)
    train_classifier(x_train, y_train)
    score_classifer(x_test, y_test)

def main(args):

    global LABEL_VALUES
    global USE_HIGHEST_PROBABILITY_IF_EMPTY

    if len(args) < 2 or len(args) > 3:
        print('Incorrect usage: python multilabel_classifier.py <model_file_path> <json_issue_file_path> [use_best_guess]', file=sys.stderr)
        sys.exit(1)

    model_path = args[0]
    issue_path = args[1]
    try:
        USE_HIGHEST_PROBABILITY_IF_EMPTY = args[2].lower() == 'true'
    except:
        pass

    print('** BUILDING MODEL FROM {} **'.format(model_path), file=sys.stderr)
    data = load_json(model_path)
    LABEL_VALUES = [str(x) for x in list(data) if not str(x).startswith('_')]
    analyze_data(data)

    issue_json = None
    with open(issue_path) as f:
        issue_json = json.load(f)
    issue_title = issue_json['Title']
    issue_body = issue_json.get('Body', '')
    issue_text = '{} {}'.format(issue_title, issue_body)

    predictions = make_prediction(issue_text)
    print('\nCLASSIFY NEW TEXT:', file=sys.stderr)
    print('\t{}\nLABELS:\n\t{}'.format(issue_text, predictions), file=sys.stderr)
    return predictions

args = sys.argv[1:]
predictions = main(args)
print(json.dumps([strip_best_guess(p) for p in predictions]))
sys.exit(0)


# See https://colab.research.google.com/github/tensorflow/examples/blob/master/courses/udacity_intro_to_tensorflow_for_deep_learning/l05c01_dogs_vs_cats_without_augmentation.ipynb#scrollTo=oSdjGwVWGshH

import tensorflow as tf

# Used for working with data on disk to interface with our model.
from tensorflow.keras.preprocessing.image import ImageDataGenerator

import os
import matplotlib.pyplot as plt
import numpy as np

import logging
logger = tf.get_logger()
logger.setLevel(logging.ERROR)

import pathlib

# Constants
EPOCHS = 100
BATCH_SIZE = 100  # Number of training examples to process before updating our models variables
IMG_SHAPE  = 150  # Our training data consists of images with width of 150 pixels and height of 150 pixels

def create_rgb_image_model() -> tf.keras.models.Sequential :
    model = tf.keras.models.Sequential([
        tf.keras.layers.Conv2D(32, (3,3), activation='relu', input_shape=(150, 150, 3)),
        tf.keras.layers.MaxPooling2D(2, 2),

        tf.keras.layers.Conv2D(64, (3,3), activation='relu'),
        tf.keras.layers.MaxPooling2D(2,2),

        tf.keras.layers.Conv2D(128, (3,3), activation='relu'),
        tf.keras.layers.MaxPooling2D(2,2),

        tf.keras.layers.Conv2D(128, (3,3), activation='relu'),
        tf.keras.layers.MaxPooling2D(2,2),

        tf.keras.layers.Flatten(),
        tf.keras.layers.Dense(512, activation='relu'),
        tf.keras.layers.Dense(2)
    ])

    return model

def compile_rgb_image_model(model: tf.keras.models.Sequential) -> tf.keras.models.Sequential :
    model.compile(optimizer='adam',
                loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),
                metrics=['accuracy'])

    return model

def visualize_history(history):
    acc = history.history['accuracy']
    val_acc = history.history['val_accuracy']

    loss = history.history['loss']
    val_loss = history.history['val_loss']

    epochs_range = range(EPOCHS)

    plt.figure(figsize=(8, 8))
    plt.subplot(1, 2, 1)
    plt.plot(epochs_range, acc, label='Training Accuracy')
    plt.plot(epochs_range, val_acc, label='Validation Accuracy')
    plt.legend(loc='lower right')
    plt.title('Training and Validation Accuracy')

    plt.subplot(1, 2, 2)
    plt.plot(epochs_range, loss, label='Training Loss')
    plt.plot(epochs_range, val_loss, label='Validation Loss')
    plt.legend(loc='upper right')
    plt.title('Training and Validation Loss')
    plt.savefig('./foo.png')
    plt.show()

class MachineLearning04:
    def __init__(self):
        self._URL = ''
        self.zip_dir = None
        self.base_dir = None
        self.train_dir = None
        self.validation_dir = None

        self.train_cats_dir = None
        self.train_dogs_dir = None
        self.validation_cats_dir = None
        self.validation_dogs_dir = None

        self.num_cats_tr = None
        self.num_dogs_tr = None

        self.num_cats_val = None
        self.num_dogs_val = None

        self.total_train = None
        self.total_val = None

        self.train_image_generator      = None
        self.validation_image_generator = None

        self.train_data_gen = None
        self.val_data_gen = None

        self.model = None

    def load_data(self):
        current_directory = os.path.dirname(os.path.realpath(__file__))
        desired_file = os.path.join(current_directory, 'data', 'cats_and_dogs_filtered.zip')
        print(desired_file)
        if os.path.exists(desired_file):
            print('Using local copy of cats & dogs data.')
            self._URL = pathlib.Path(desired_file).as_uri()
        else:
            print('Using remote copy of cats & dogs data.')
            self._URL = 'https://storage.googleapis.com/mledu-datasets/cats_and_dogs_filtered.zip'
        self.zip_dir = tf.keras.utils.get_file('cats_and_dogs_filtered.zip', origin=self._URL, extract=True)

        self.base_dir = os.path.join(os.path.dirname(self.zip_dir), 'cats_and_dogs_filtered')
        self.train_dir = os.path.join(self.base_dir, 'train')
        self.validation_dir = os.path.join(self.base_dir, 'validation')

        self.train_cats_dir = os.path.join(self.train_dir, 'cats')  # directory with our training cat pictures
        self.train_dogs_dir = os.path.join(self.train_dir, 'dogs')  # directory with our training dog pictures
        self.validation_cats_dir = os.path.join(self.validation_dir, 'cats')  # directory with our validation cat pictures
        self.validation_dogs_dir = os.path.join(self.validation_dir, 'dogs')  # directory with our validation dog pictures

        self.num_cats_tr = len(os.listdir(self.train_cats_dir))
        self.num_dogs_tr = len(os.listdir(self.train_dogs_dir))

        self.num_cats_val = len(os.listdir(self.validation_cats_dir))
        self.num_dogs_val = len(os.listdir(self.validation_dogs_dir))

        self.total_train = self.num_cats_tr + self.num_dogs_tr
        self.total_val = self.num_cats_val + self.num_dogs_val

    def print_metadata(self):
        print('total training cat images:', self.num_cats_tr)
        print('total training dog images:', self.num_dogs_tr)

        print('total validation cat images:', self.num_cats_val)
        print('total validation dog images:', self.num_dogs_val)
        print("--")
        print("Total training images:", self.total_train)
        print("Total validation images:", self.total_val)

    """
    Images must be formatted into appropriately pre-processed floating point tensors before being fed into the network. The steps involved in preparing these images are:
    1) Read images from the disk
    2) Decode contents of these images and convert it into proper grid format as per their RGB content
    3) Convert them into floating point tensors
    4) Rescale the tensors from values between 0 and 255 to values between 0 and 1, as neural networks prefer to deal with small input values.

    All these tasks can be completed with ImageDataGenerator.
    """
    def load_image_data_generators(self):
        self.train_image_generator      = ImageDataGenerator(rescale=1./255)  # Generator for our training data
        self.validation_image_generator = ImageDataGenerator(rescale=1./255)  # Generator for our validation data

        # We can read the data from disk, rescale, and resize with flow_from_directory().
        self.train_data_gen = self.train_image_generator.flow_from_directory(batch_size=BATCH_SIZE,
                                                           directory=self.train_dir,
                                                           shuffle=True,
                                                           target_size=(IMG_SHAPE, IMG_SHAPE), #(150,150)
                                                           class_mode='binary')

        self.val_data_gen = self.validation_image_generator.flow_from_directory(batch_size=BATCH_SIZE,
                                                              directory=self.validation_dir,
                                                              shuffle=False,
                                                              target_size=(IMG_SHAPE, IMG_SHAPE), #(150,150)
                                                              class_mode='binary')

    # This function will plot images in the form of a grid with 1 row and 5 columns where images are placed in each column.
    def plotImages(self, images_arr):
        fig, axes = plt.subplots(1, 5, figsize=(20,20))
        axes = axes.flatten()
        for img, ax in zip(images_arr, axes):
            ax.imshow(img)
        plt.tight_layout()
        plt.show()

    def visualize_training_images(self):
        sample_training_images, _ = next(self.train_data_gen)
        self.plotImages(sample_training_images[:5])  # Plot images 0-4

    def create_and_train_model(self):
        self.model = create_rgb_image_model()
        self.model = compile_rgb_image_model(self.model)

        # Print the model summary.
        print("Summary of RGB Image Model")
        print("--------------------------")
        self.model.summary()

        history = self.model.fit_generator(
            self.train_data_gen,
            steps_per_epoch=int(np.ceil(self.total_train / float(BATCH_SIZE))),
            epochs=EPOCHS,
            validation_data=self.val_data_gen,
            validation_steps=int(np.ceil(self.total_val / float(BATCH_SIZE)))
        )

        return history

def main():
    ml04 = MachineLearning04()
    ml04.load_data()
    ml04.print_metadata()
    ml04.load_image_data_generators()

    # Uncomment to see some sample training images.
    # ml04.visualize_training_images()

    history = ml04.create_and_train_model()
    visualize_history(history)

if __name__ == "__main__":
    main()


from tkinter import *
from tkinter import ttk
from datetime import date
import csv
import time
import matplotlib
matplotlib.use("TkAgg")
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2TkAgg
from matplotlib.figure import Figure

date = date.today() #Get today's date
date = str(date) #Convert to string
month = date[5:7] #Get the month
year = date[0:4] #Get the year
day = date[8:10]
currentBGDS = True
currentLGDS = False
currentBGMI = False
currentBGPS = False
currentBGSI = False

#Convert numerical month to letterical month
Months = {"01":"January",
          "02":"February",
          "03":"March",
          "04":"April",
          "05":"May",
          "06":"June",
          "07":"July",
          "08":"August",
          "09":"September",
          "10":"October",
          "11":"November",
          "12":"December"}

MonthsReverse =  {"January":"01",
                  "February":"02",
                  "March":"03",
                  "April":"04",
                  "May":"05",
                  "June":"06",
                  "July":"07",
                  "August":"08",
                  "September":"09",
                  "October":"10",
                  "November":"11",
                  "December":"12"}

dayInMonths ={"01":31,
              "02":{0:28,
                 1:29},
              "03":31,
              "04":30,
              "05":31,
              "06":30,
              "07":31,
              "08":31,
              "09":30,
              "10":31,
              "11":30,
              "12":31}

DateOfCurrentExpenses = "MonthlyExpense" + month + year + ".txt"
UsedExpensesFile = "UsedExpenses" + month + year + ".txt"
MonthlyItemsFile = "Items" + month + year + ".csv"
TotalDailyExpenditureFile = "DailyExpenditure" + month + year + ".csv"
Title = "Finance Tracker: " + Months[month] + " " + year
CurrentCurrency = "Currency" + month + year + ".txt"

def ReadMonthlyExpense(fileName):
    with open(fileName,"r") as file:
        monthlyExpense = file.read()
        file.close()
    return monthlyExpense

def ReadUsedExpenses(fileName):
    with open(fileName,"r") as file:
        usedExpenses = file.read()
        file.close()
    return usedExpenses

def ReadTDEF(fileName):
    with open(fileName, "r") as TDEF:
        a = csv.reader(TDEF)
        TDEFlist = list(a)
        TDEF.close()
    return TDEFlist

def WriteTDEF(List):
    with open (TotalDailyExpenditureFile, "w", newline='') as TDEF:
        Save = csv.writer(TDEF)
        Save.writerows(List)
        TDEF.close()

def ReadMonthlyItems(fileName):
    with open (fileName, "r") as MIF:
        a = csv.reader(MIF)
        MIFlist = list(a)
        MIF.close()
    return MIFlist

def WriteMonthlyItems(List):
    with open (MonthlyItemsFile, "w", newline='') as MIF:
        Save = csv.writer(MIF)
        Save.writerows(List)
        MIF.close()

def UpperFirsts(s):
    ProperVal = ""
    Skip = True
    ProperVal += s[0].upper()
    Length = len(s)
    for x in range(Length):
        if not Skip:
            ProperVal += s[x]
            if s[x] == " " or s[x] == "/" or s[x] == "-" or s[x] == "_":
                Skip = True
                ProperVal += s[x+1].upper()
        else:
            Skip = False
    return ProperVal

def StringToFloat(List):
    for x in range(len(List)):
        List[x] = float(List[x])
    return List

def maxMessage(List, day):
    print(List[1])
    List[1] = StringToFloat(List[1])
    expensivestIndex = List[1].index(max(List[1]))
    expensivest = List[0][expensivestIndex]
    if day == True:
        expensivestMessage = "The most expensive day \n this month is: "+ "Day " + expensivest +"!"
    else:
        expensivest = UpperFirsts(expensivest)
        expensivestMessage = "The most expensive item you \n bought this month is: "+ expensivest +"!"
    return expensivestMessage

def plotGraph(List, plots, xtitle, ytitle, mainTitle, BGDS, LGDS, BGMI, BGPS, BGSI, barTrue, lineTrue):
    global currentBGDS
    global currentLGDS
    global currentBGMI
    global currentBGPS
    global currentBGSI
    print("behairi")
    print(List[1])
    maxCost = int(max(List[1]))
    remainder = maxCost % 20
    highestYTick = maxCost + (20-remainder)
    increments = int(highestYTick / 20)
    if barTrue == True:
        plots.bar(range(len(List[0])), List[1])
    else:
        plots.plot(range(len(List[0])), List[1])
    plots.set_xticks(range(len(List[0])), minor=False)
    plots.set_xticklabels(List[0], fontdict=None, minor=False)
    if maxCost > 20:
        plots.set_yticks(range(0,highestYTick+1,increments))
    plots.set_xlabel(xtitle)
    plots.set_ylabel(ytitle)
    plots.set_title(mainTitle)
    currentBGDS = BGDS
    currentLGDS = LGDS
    currentBGMI = BGMI
    currentBGPS = BGPS
    currentBGSI = BGSI

def makeGraph(self, BGDS, LGDS, BGMI, BGPS, BGSI, col, rw, rowsp, colsp, TDEfileName, MIfileName, chosenStore):
    graphFigure = Figure(figsize=(10,5), dpi=100)
    dataPlots = graphFigure.add_subplot(111)
    canvas = FigureCanvasTkAgg(graphFigure, self)
##    toolbar = NavigationToolbar2TkAgg(canvas, self)
##    toolbar.update()
##    toolbar.pack()
    canvas.show()
    canvas.get_tk_widget().grid(column=col,row=rw, rowspan=rowsp, columnspan=colsp)

    dataPlots.clear()
    if BGDS == True:
        TDEFlist = ReadTDEF(TDEfileName)
        TDEFlist[1] = StringToFloat(TDEFlist[1])
        plotGraph(TDEFlist, dataPlots, "Day", "Expenditure", "Daily Spendings", True, False, False, False, False, True, False)
        return canvas

    elif LGDS == True:
        TDEFlist = ReadTDEF(TDEfileName)
        TDEFlist[1] = StringToFloat(TDEFlist[1])
        plotGraph(TDEFlist, dataPlots, "Day", "Expenditure", "Daily Spendings", False, True, False, False, False, False, True)
        return canvas

    elif BGMI == True:
        MIFlist = ReadMonthlyItems(MIfileName)
        itemsList = []
        for x in range(len(MIFlist[0])):
            item = UpperFirsts(MIFlist[0][x])
            itemsList.append(item)
        MIFlist[0] = itemsList
        MIFlist[1] = StringToFloat(MIFlist[1])
        plotGraph(MIFlist, dataPlots, "Items", "Expenditure", "Monthly Spendings per Item", False, False, True, False, False, True, False)
        return canvas

    elif BGPS == True:
        MIFlist = ReadMonthlyItems(MIfileName)
        storeList = [[],[]]
        for x in range(2, len(MIFlist)):
            store = UpperFirsts(MIFlist[x][0])
            storeTotalPrice = MIFlist[x][1]
            storeList[0].append(store)
            storeList[1].append(storeTotalPrice)
        storeList[1] = StringToFloat(storeList[1])
        print(storeList)
        plotGraph(storeList, dataPlots, "Stores", "Expenditure", "Monthly Spendings per Store", False, False, False, True, False, True, False)
        return canvas

    elif BGSI == True:
        MIFlist = ReadMonthlyItems(MIfileName)
        itemsList = [[],[]]
        for x in range(2,len(MIFlist)):
            if MIFlist[x][0] == chosenStore:
                for y in range(2, len(MIFlist[x]), 2):
                    itemsList[0].append(MIFlist[x][y])
                    itemsList[1].append(MIFlist[x][y+1])
                break
        itemsList[1] = StringToFloat(itemsList[1])
        xtitle = "Items From " + chosenStore
        plotGraph(itemsList, dataPlots, xtitle, "Expenditure", "Monthly Spendings per Store Item", False, False, False, False, True, True, False)
        return canvas

def NewData(itemEntry, storeEntry, costEntry, passController, RElabel, UElabel, EDlabel, EIlabel, self, BGDS, LGDS, BGMI, BGPS, BGSI):
    item = itemEntry.get()
    store = storeEntry.get()
    totalCost = costEntry.get()

    item = item.lower()
    store = store.lower()

    textfile = open(CurrentCurrency, "r")
    currence = textfile.read()

    try:
        totalCost = float(totalCost)
        try:
            MIFlist = ReadMonthlyItems(MonthlyItemsFile)
            rows = len(MIFlist)
            if item not in MIFlist[0]:
                MIFlist[0].append(item)
                MIFlist[1].append(totalCost)
                for x in range(rows):
                    if store == MIFlist[x][0]:
                        if item not in MIFlist[x]:
                            MIFlist[x].append(item)
                            MIFlist[x].append(totalCost)
                            MIFlist[x][1] = float(MIFlist[x][1]) + totalCost

                        break
                    else:
                        if x == rows-1:
                            MIFlist.append([store, totalCost, item, totalCost])

            else:
                itemIndex = MIFlist[0].index(item)
                for x in range(rows):
                    if store == MIFlist[x][0]:
                        if item not in MIFlist[x]:
                            MIFlist[x].append(item)
                            MIFlist[x].append(totalCost)
                            MIFlist[x][1] = float(MIFlist[x][1]) + totalCost
                        break
                    else:
                        if x == rows-1:
                            MIFlist.append([store, totalCost, item, totalCost])
                MIFlist[1][itemIndex] = float(MIFlist[1][itemIndex]) + totalCost

            WriteMonthlyItems(MIFlist)

            ######

            TDEFlist = ReadTDEF(TotalDailyExpenditureFile)
            dayIndex = TDEFlist[0].index(str(int(day)))
            TDEFlist[1][dayIndex] = float(TDEFlist[1][dayIndex]) + totalCost
            WriteTDEF(TDEFlist)

            #######

            usedExpense = float(ReadUsedExpenses(UsedExpensesFile))
            newUsedExpense = usedExpense + totalCost
            with open(UsedExpensesFile, "w") as UEF:
                UEF.write(str(newUsedExpense))
                UEF.close()

            remainingExpenses = str(float(ReadMonthlyExpense(DateOfCurrentExpenses)) - newUsedExpense)
            remainingExpensesMessage = "You have: " + currence + remainingExpenses + "\n remaining this month!"
            usedExpensesMessage = "You have spent: " + currence + str(newUsedExpense) + "\n this month!"
            TDEFlist = ReadTDEF(TotalDailyExpenditureFile)
            expensivestDayMessage = maxMessage(TDEFlist, True)
            MIFlist = ReadMonthlyItems(MonthlyItemsFile)
            expensivestItemMessage = maxMessage(MIFlist, False)

            #Resets the Entry fields and also updates the info labels at bottom of graph
            itemEntry.delete(0,END)
            storeEntry.delete(0,END)
            costEntry.delete(0,END)

            UpdateItems = []
            for x in range(len(MIFlist[0])):
                UpdateItems.append(UpperFirsts(MIFlist[0][x]))
            itemEntry['values'] = UpdateItems
            UpdateStores = []
            for x in range(2, len(MIFlist)):
                UpdateStores.append(UpperFirsts(MIFlist[x][0]))
            storeEntry['values'] = UpdateStores

            RElabel.config(text=str(remainingExpensesMessage))
            UElabel.config(text=usedExpensesMessage)
            EDlabel.config(text=expensivestDayMessage)
            EIlabel.config(text=expensivestItemMessage)

            makeGraph(self, BGDS, LGDS, BGMI, BGPS, BGSI, 2, 0, 17, 10, TotalDailyExpenditureFile, MonthlyItemsFile, "Kappa")

        except:
            newList = [[item], [totalCost], [store, totalCost, item, totalCost]]
            WriteMonthlyItems(newList)

            newList2 = [[],[]]
            if month == "02":
                isItLeapYear = int(year) % 4
                if isItLeapYear == 0:
                    numberOfDays = dayInMonths[month][1]
                else:
                    numberOfDays = dayInMonths[month][0]
            else:
                numberOfDays = dayInMonths[month]
            for x in range(1, (numberOfDays + 1)):
                newList2[0].append(x)
                newList2[1].append(0)
            dayIndex = newList2[0].index(int(day))
            newList2[1][dayIndex] = float(newList2[1][dayIndex]) + totalCost
            WriteTDEF(newList2)

            ###

            remainingExpensesMessage = "You have: " + currence + str(float(ReadMonthlyExpense(DateOfCurrentExpenses)) - totalCost) + "\n remaining this month!"
            usedExpensesMessage = "You have spent: " + currence + str(totalCost) + "\n this month!"
            TDEFlist = ReadTDEF(TotalDailyExpenditureFile)
            expensivestDayMessage = maxMessage(TDEFlist, True)
            MIFlist = ReadMonthlyItems(MonthlyItemsFile)
            expensivestItemMessage = maxMessage(MIFlist, False)

            with open (UsedExpensesFile, "w") as UEF:
                UEF.write(str(totalCost))
                UEF.close()

            #Resets the Entry fields and also updates the info labels at bottom of graph
            itemEntry.delete(0,END)
            storeEntry.delete(0,END)
            costEntry.delete(0,END)

            UpdateItems = []
            for x in range(len(MIFlist[0])):
                UpdateItems.append(UpperFirsts(MIFlist[0][x]))
            itemEntry['values'] = UpdateItems
            UpdateStores = []
            for x in range(2, len(MIFlist)):
                UpdateStores.append(UpperFirsts(MIFlist[x][0]))
            storeEntry['values'] = UpdateStores

            RElabel.config(text=remainingExpensesMessage)
            UElabel.config(text=usedExpensesMessage)
            EDlabel.config(text=expensivestDayMessage)
            EIlabel.config(text=expensivestItemMessage)

            makeGraph(self, BGDS, LGDS, BGMI, BGPS, BGSI, 2, 0, 17, 10, TotalDailyExpenditureFile, MonthlyItemsFile, "Kappa")

    except:
        messagebox.showerror(title="Input Error!",message="Please input correctly!")

class MainFrame(Tk):

    def __init__(self):
        Tk.__init__(self)

        Tk.title(self, Title)
        Tk.iconbitmap(self, default="fticon2.ico")

        window = Frame(self)
        window.pack(side="top",fill="both",expand=True)
        window.grid_rowconfigure(0, weight=1)
        window.grid_columnconfigure(0, weight=1)

        self.frames = {}

        for x in (NewMonth, Standard, LoadOldDataPage):
            frame = x(window, self)
            self.frames[x] = frame
            frame.grid(row=0, column=0, sticky="nsew")

        try:
            monthlyExpense = ReadMonthlyExpense(DateOfCurrentExpenses)
            self.show_frame(Standard)
        except:
            self.show_frame(NewMonth)

    def show_frame(self, cont):
        frame = self.frames[cont]
        frame.tkraise()

class NewMonth(Frame): #Completed 100%, perhaps make it look nice

    def __init__(self, parent, controller):
        Frame.__init__(self, parent)
        standardCurrency = ["$","$", "£", "€", "₹", "¥", "₩", "IDR", "OMR", "AED"]
        currency2 = StringVar()
        currency2.set(standardCurrency[0])
        self.currencyOption = ttk.OptionMenu(self, currency2, *standardCurrency)

        self.label = ttk.Label(self, text="What is your monthly expenses?").pack()
        self.entry = ttk.Entry(self)
        self.label2 = ttk.Label(self, text="Please enter your currency:")

        self.button = ttk.Button(self, text="ENTER",command=lambda: self.GET(controller, currency2))
        self.entry.pack()
        self.label2.pack()
        self.currencyOption.pack()
        self.button.pack()

    def GET(self,controller, currency2):
        monthlyExpense = self.entry.get()
        standardCurrency = ["$", "£", "€", "IDR", "OMR"]
        currency = currency2.get()
        with open(DateOfCurrentExpenses,"w") as file:
            file.write(monthlyExpense)
            file.close()
        if monthlyExpense == "" and currency == "":
            Pass = False
        else:
            Pass = True
        if Pass:
            try:
                monthlyExpenseINT = int(monthlyExpense)
                if monthlyExpenseINT <= 0:
                    messagebox.showerror(title="Input Error!",message="You can't have minus or zero monthly expenses!")
                else:
                    with open(DateOfCurrentExpenses,"w") as file:
                        file.write(monthlyExpense)
                        file.close()
                    controller.show_frame(Standard)
                    textfile = open(CurrentCurrency, "w")
                    textfile.write(currency)
                    textfile.close()
                    changeInitialLabels()
                    controller.show_frame(Standard)

            except:
                messagebox.showerror(title="Input Error!",message="That is not a number!")
        else:
            messagebox.showerror(title="Input Error!",message="You have left a field blank!")

def changeInitialLabels():
    textfile = open(CurrentCurrency, "r")
    currence = textfile.read()
    monthlyExpense = ReadMonthlyExpense(DateOfCurrentExpenses)
    monthlyExpense = "This month's expenses: " + currence + monthlyExpense
    label2.config(text=monthlyExpense)
    originalMoneys = ReadMonthlyExpense(DateOfCurrentExpenses)
    expenditureLeft = "The expenses you have remaining this month:\n" + currence + originalMoneys +"!"
    RElabel.config(text=expenditureLeft)
    aaa = "The expenses you have used this month:\n" + currence + "0" +"!"
    UElabel.config(text=aaa)

class Standard(ttk.Frame):
    def __init__(self, parent, controller):
        global label2
        global RElabel
        global UElabel
        ttk.Frame.__init__(self, parent)

        try:
            textfile = open(CurrentCurrency, "r")
            currence = textfile.read()
        except:
            currence = "tr"

        #Label texts
        try:
            monthlyExpense = ReadMonthlyExpense(DateOfCurrentExpenses)
            monthlyExpense = "This month's expenses: " + currence + monthlyExpense
        except:
            monthlyExpense="dsa"

        todayDate = "Today is: " + day + "/" + month + "/" + year

        empty = 40*"_"
        #value = StringVar()

        label = ttk.Label(self, text=todayDate)
        label.grid(column=0,row=1, columnspan=2)

        label2 = ttk.Label(self, text=monthlyExpense)
        label2.grid(column=0,row=2, columnspan=2)

        itemLabel = ttk.Label(self, text="Item Name")
        itemLabel.grid(column=0,row=3, columnspan=2)

        StoreLabel = ttk.Label(self, text="Store Name")
        StoreLabel.grid(column=0,row=5, columnspan=2)

        try:
            MIFlist = ReadMonthlyItems(MonthlyItemsFile)
            NewVal = []
            itemEntry = ttk.Combobox(self, textvariable=StringVar())
            for x in range(len(MIFlist[0])):
                NewVal.append(UpperFirsts(MIFlist[0][x]))
            itemEntry['values'] = NewVal
            itemEntry.grid(column=0,row=4, columnspan=2)
            NewVal = []
            storeEntry = ttk.Combobox(self, textvariable=StringVar())
            for x in range(2, len(MIFlist)):
                NewVal.append(UpperFirsts(MIFlist[x][0]))
            storeEntry['values'] = NewVal
            storeEntry.grid(column=0, row=6, columnspan=2)

        except:
            itemEntry = ttk.Combobox(self, textvariable=StringVar())
            itemEntry.grid(column=0,row=4, columnspan=2)
            print("gay fucker")

            storeEntry = ttk.Combobox(self, textvariable=StringVar())
            storeEntry.grid(column=0, row=6, columnspan=2)

        costLabel = ttk.Label(self, text="Total Cost")
        costLabel.grid(column=0,row=7, columnspan=2)

        costEntry = ttk.Entry(self)
        costEntry.grid(column=0,row=8, columnspan=2)

        enterButton = ttk.Button(self, text="ENTER", command=lambda: NewData(itemEntry, storeEntry, costEntry, controller, RElabel, UElabel, EDlabel, EIlabel, self, currentBGDS, currentLGDS, currentBGMI, currentBGPS, currentBGSI))
        enterButton.grid(column=0,row=9, columnspan=2)

        ####bottom left corner
        label12 = ttk.Label(self, text=empty)
        label12.grid(column=0,row=10, columnspan=2)

        GraphControlsLabel = ttk.Label(self, text="Graph Controls")
        GraphControlsLabel.grid(column=0,row=11, columnspan=2)

        GSBbutton = ttk.Button(self, text="Show General Spendings (BG)", command=lambda: self.GraphWork(True, False, False, False, False, 2, 0, 17, 10, TotalDailyExpenditureFile, MonthlyItemsFile, "Kappa"))
        GSBbutton.grid(column=0,row=12, columnspan=2)

        GSLbutton = ttk.Button(self, text="Show General Spendings (LG)", command=lambda: self.GraphWork(False, True, False, False, False, 2, 0, 17, 10, TotalDailyExpenditureFile, MonthlyItemsFile, "Kappa"))
        GSLbutton.grid(column=0,row=13, columnspan=2)

        SPIbutton = ttk.Button(self, text="Show Spendings per Item", command=lambda: self.GraphWork(False, False, True, False, False, 2, 0, 17, 10, TotalDailyExpenditureFile, MonthlyItemsFile, "Kappa"))
        SPIbutton.grid(column=0,row=14, columnspan=2)

        SPSbutton = ttk.Button(self, text="Show Spendings per Store",command=lambda: self.GraphWork(False, False, False, True, False, 2, 0, 17, 10, TotalDailyExpenditureFile, MonthlyItemsFile, "Kappa"))
        SPSbutton.grid(column=0,row=15,columnspan=2)

        SSISbutton = ttk.Button(self, text="Show Spendings per Store Item",command=lambda: self.StorePerItem())
        SSISbutton.grid(column=0,row=16,columnspan=2)

        LPSbutton = ttk.Button(self, text="Load Past Spendings", command=lambda: controller.show_frame(LoadOldDataPage))
        LPSbutton.grid(column=0,row=17, columnspan=2)

        #### end blc

        ####bottom most row

        dash = ttk.Label(self, text=5*"|\n")
        dash.grid(column=2, row=17, rowspan=2, sticky="w")

        try:
            remainingExpenses = str(float(ReadMonthlyExpense(DateOfCurrentExpenses)) - float(ReadUsedExpenses(UsedExpensesFile)))
            remainingExpensesMessage = "You have: " + currence + remainingExpenses + "\n remaining this month!"
            RElabel = Label(self, text=remainingExpensesMessage, borderwidth=2, relief="solid", bg="yellow")
            RElabel.grid(column=3, row=17, rowspan=2)
        except:
            try:
                remainingExpenses = str(float(ReadMonthlyExpense(DateOfCurrentExpenses)))
                remainingExpensesMessage = "You have: " + currence + remainingExpenses + "\n remaining this month!"
                RElabel = Label(self, text=remainingExpensesMessage, borderwidth=2, relief="solid", bg="yellow")
                RElabel.grid(column=3, row=17, rowspan=2)
            except:
                originalMoneys = "32"
                expenditureLeft = "You have: " + currence + originalMoneys + "\n remaining this month!"
                RElabel = Label(self, text=expenditureLeft, borderwidth=2, relief="solid", bg="yellow")
                RElabel.grid(column=3, row=17, rowspan=2)

        try:
            usedExpenses = ReadUsedExpenses(UsedExpensesFile)
            usedExpensesMessage = "You have spent: " + currence + str(usedExpenses) + "\n this month!"
            UElabel = Label(self, text=usedExpensesMessage, borderwidth=2, relief="solid", bg="yellow")
            UElabel.grid(column=5, row=17, rowspan=2)
        except:
            usedExpenses = "0"
            usedExpensesMessage = "You have spent: " + currence + str(usedExpenses) + "\n this month!"
            UElabel = Label(self, text=usedExpensesMessage, borderwidth=2, relief="solid", bg="yellow")
            UElabel.grid(column=5, row=17, rowspan=2)

        try:
            TDEFlist = ReadTDEF(TotalDailyExpenditureFile)
            expensivestDayMessage = maxMessage(TDEFlist, True)
            EDlabel = Label(self, text=expensivestDayMessage, borderwidth=2, relief="solid", bg="yellow")
            EDlabel.grid(column=7, row=17, rowspan=2)
        except:
            expensivestDayMessage = "You haven't spent anything this month!"
            EDlabel = Label(self, text=expensivestDayMessage, borderwidth=2, relief="solid", bg="yellow")
            EDlabel.grid(column=7, row=17, rowspan=2)

        try:
            MIFlist = ReadMonthlyItems(MonthlyItemsFile)
            expensivestItemMessage = maxMessage(MIFlist, False)
            EIlabel = Label(self, text=expensivestItemMessage, borderwidth=2, relief="solid", bg="yellow")
            EIlabel.grid(column=9, row=17, rowspan=2)
        except:
            expensivestItemMessage = "You haven't bought anything this month!"
            EIlabel = Label(self, text=expensivestItemMessage, borderwidth=2, relief="solid", bg="yellow")
            EIlabel.grid(column=9, row=17, rowspan=2)

        ######Canvas and graph time!
        try:
            self.canvas = makeGraph(self, True, False, False, False, False, 2, 0, 17, 14, TotalDailyExpenditureFile, MonthlyItemsFile, "Kappa")
        except:
            self.canvas = Canvas(self, height=500,width=1000,bg="white")
            self.canvas.grid(column=2,row=0, rowspan=17, columnspan=14)

    def GraphWork(self, BGDS, LGDS, BGMI, BGPS, BGSI, col, rw, rowsp, colsp, TDEfileName, MIfileName, lolz):
        self.canvas.get_tk_widget().destroy()
        self.canvas = makeGraph(self, BGDS, LGDS, BGMI, BGPS, BGSI, 2, 0, 17, 14, TotalDailyExpenditureFile, MonthlyItemsFile, lolz)

    def StorePerItem(self):
        self.canvas.get_tk_widget().destroy()

        self.SPIlabel = Label(self, text="Select Store to View")
        MIFlist = ReadMonthlyItems(MonthlyItemsFile)
        storeList = [MIFlist[2][0]]
        for x in range(2, len(MIFlist)):
            storeList.append(MIFlist[x][0])
        print(storeList)
        ShowingStore = StringVar()
        ShowingStore.set(storeList[0])
        self.StoreMenu = ttk.OptionMenu(self, ShowingStore, *storeList)
        self.SPIlabel.grid(column=7, row=6, rowspan=2)
        self.StoreMenu.grid(column=7, row=8, rowspan=2)
        self.SPIbutton = ttk.Button(self, text="ENTER", command=lambda: self.GetStoreAndProceed(ShowingStore))
        self.SPIbutton.grid(column=7, row=10, rowspan=2)

    def GetStoreAndProceed(self, ShowingStore):
        chosenStore = ShowingStore.get()
        self.SPIlabel.destroy()
        self.StoreMenu.destroy()
        self.SPIbutton.destroy()
        self.GraphWork(False, False, False, False, True, 2, 0, 17, 10, TotalDailyExpenditureFile, MonthlyItemsFile, chosenStore)

class LoadOldDataPage(Frame):
    def __init__(self, parent, controller):
        Frame.__init__(self, parent)
        self.Options(controller)

    def Options(self, controller):
        MonthsPure = ["January","January","February","March","April","May", "June", "July", "August",
                      "September", "October", "November", "December"]
        YearsPure = ["2013"]
        Years = 2013
        yearINT = int(year)
        for x in range(yearINT - Years + 1):
            YearsPure.append(str(Years))
            Years += 1
        ShowingMonth = StringVar()
        ShowingMonth.set(MonthsPure[0])
        ShowingYear = StringVar()
        ShowingYear.set(YearsPure[0])
        self.label = Label(self, text="Which data would you like to see from?")
        self.MonthMenu = ttk.OptionMenu(self, ShowingMonth, *MonthsPure)
        self.YearMenu = ttk.OptionMenu(self, ShowingYear, *YearsPure)
        self.button = ttk.Button(self, text="ENTER", command=lambda: self.loadGraph(ShowingMonth, ShowingYear, controller))
        self.backButton = ttk.Button(self, text="BACK",command=lambda: controller.show_frame(Standard))
        self.label.pack()
        self.MonthMenu.pack()
        self.YearMenu.pack()
        self.button.pack()
        self.backButton.pack()

    def loadGraph(self,ShowingMonth, ShowingYear,controller):
        selectedMonth = ShowingMonth.get()
        selectedYear = ShowingYear.get()

        selectedMonthN = MonthsReverse[selectedMonth]

        DateOfOldExpenses = "MonthlyExpense" + selectedMonthN + selectedYear + ".txt"
        OldUsedExpensesFile = "UsedExpenses" + selectedMonthN + selectedYear + ".txt"
        self.OldMonthlyItemsFile = "Items" + selectedMonthN + selectedYear + ".csv"
        OldTotalDailyExpenditureFile = "DailyExpenditure" + selectedMonthN + selectedYear + ".csv"
        OldCurrency = "Currency" + selectedMonthN + selectedYear + ".txt"

        try:
            MI2file = ReadMonthlyItems(self.OldMonthlyItemsFile)
            oldExpense = ReadMonthlyExpense(DateOfOldExpenses)
            oldUsedExpense = ReadUsedExpenses(OldUsedExpensesFile)
            TDE2file = ReadTDEF(OldTotalDailyExpenditureFile)

            textfile = open(OldCurrency, "r")
            currence = textfile.read()

            self.label.destroy()
            self.MonthMenu.destroy()
            self.YearMenu.destroy()
            self.button.destroy()
            self.backButton.destroy()

            self.returnButton = ttk.Button(self, text="Return", command=lambda: self.Graph2Options(controller))
            self.DSBG = ttk.Button(self, text="Show General Spendings (BG)", command=lambda: self.InitiateGraph(0, OldTotalDailyExpenditureFile,  self.OldMonthlyItemsFile))
            self.DSLG = ttk.Button(self, text="Show General Spendings (LG)", command=lambda: self.InitiateGraph(1, OldTotalDailyExpenditureFile,  self.OldMonthlyItemsFile))
            self.SIBG = ttk.Button(self, text="Show Spendings per Item", command=lambda: self.InitiateGraph(2, OldTotalDailyExpenditureFile,  self.OldMonthlyItemsFile))
            self.SpS = ttk.Button(self, text="Show Spendings per Store", command=lambda: self.InitiateGraph(3, OldTotalDailyExpenditureFile,  self.OldMonthlyItemsFile))
            self.SpSI = ttk.Button(self, text="Show Spendings per Store Item", command=lambda: self.InitiateGraph(4, OldTotalDailyExpenditureFile,  self.OldMonthlyItemsFile))
            self.returnButton.grid(column=0,row=0, columnspan=2)
            self.graphL = Label(self, text="Graph Controls")
            self.graphL.grid(column=0,row=2, columnspan=2)
            self.DSBG.grid(column=0,row=3, columnspan=2)
            self.DSLG.grid(column=0,row=4, columnspan=2)
            self.SIBG.grid(column=0,row=5, columnspan=2)
            self.SpS.grid(column=0,row=6, columnspan=2)
            self.SpSI.grid(column=0,row=7, columnspan=2)

            print("n")
            self.canvas = makeGraph(self, True, False, False, False, False, 2, 0, 17, 14, OldTotalDailyExpenditureFile,  self.OldMonthlyItemsFile, "Kappa")
            print("n")
            oldRemainingExpenses = str(float(oldExpense) - float(oldUsedExpense))
            print("n")
            saved = "You saved " + currence + oldRemainingExpenses + " this month!"
            print("n")
            spent = "You spent " + currence + str(oldUsedExpense) + " this month!"
            print("n")
            expensivestDay = maxMessage(TDE2file, True)
            print("n")
            expensivestItem = maxMessage(MI2file, False)

            self.ODlabel = Label(self, text=saved, borderwidth=2, relief="solid", bg="yellow")
            self.ODlabel2 = Label(self, text=spent, borderwidth=2, relief="solid", bg="yellow")
            self.ODlabel3 = Label(self, text=expensivestDay, borderwidth=2, relief="solid", bg="yellow")
            self.ODlabel4 = Label(self, text=expensivestItem, borderwidth=2, relief="solid", bg="yellow")
            self.ODlabel.grid(column=0, row=9, columnspan=2)
            self.ODlabel2.grid(column=0, row=11, columnspan=2)
            self.ODlabel3.grid(column=0, row=13, columnspan=2)
            self.ODlabel4.grid(column=0, row=15, columnspan=2)

        except:
            messagebox.showerror(title="Files not found!",message="You do not have any data in this time period!")

    def InitiateGraph(self, a, fileA, fileB):
        self.canvas.get_tk_widget().destroy()
        if a == 0:
            self.canvas = makeGraph(self, True, False, False, False, False, 2, 0, 17, 14, fileA,  fileB, "Kappa")
        elif a == 1:
            self.canvas = makeGraph(self, False, True, False, False, False, 2, 0, 17, 14, fileA,  fileB, "Kappa")
        elif a == 2:
            self.canvas = makeGraph(self, False, False, True, False, False, 2, 0, 17, 14, fileA,  fileB, "Kappa")
        elif a == 3:
            self.canvas = makeGraph(self, False, False, False, True, False, 2, 0, 17, 14, fileA,  fileB, "Kappa")
        else:
            self.StorePerItem(fileA)

    def Graph2Options(self, controller):
        self.returnButton.destroy()
        self.graphL.destroy()
        self.DSBG.destroy()
        self.DSLG.destroy()
        self.SIBG.destroy()
        self.SpS.destroy()
        self.SpSI.destroy()
        self.canvas.get_tk_widget().destroy()
        self.ODlabel.destroy()
        self.ODlabel2.destroy()
        self.ODlabel3.destroy()
        self.ODlabel4.destroy()
        self.Options(controller)

    def StorePerItem(self, TotalDailyExpenditureFile):
        self.canvas.get_tk_widget().destroy()

        self.SPIlabel = Label(self, text="Select Store to View")
        MIFlist = ReadMonthlyItems(self.OldMonthlyItemsFile)
        storeList = [MIFlist[2][0]]
        for x in range(2, len(MIFlist)):
            storeList.append(MIFlist[x][0])
        print(storeList)
        ShowingStore = StringVar()
        ShowingStore.set(storeList[0])
        self.StoreMenu = ttk.OptionMenu(self, ShowingStore, *storeList)
        self.SPIlabel.grid(column=7, row=6, rowspan=2)
        self.StoreMenu.grid(column=7, row=8, rowspan=2)
        self.SPIbutton = ttk.Button(self, text="ENTER", command=lambda: self.GetStoreAndProceed(ShowingStore, TotalDailyExpenditureFile))
        self.SPIbutton.grid(column=7, row=10, rowspan=2)

    def GetStoreAndProceed(self, ShowingStore, TotalDailyExpenditureFile):
        chosenStore = ShowingStore.get()
        self.SPIlabel.destroy()
        self.StoreMenu.destroy()
        self.SPIbutton.destroy()
        self.canvas = makeGraph(self, False, False, False, False, True, 2, 0, 17, 10, TotalDailyExpenditureFile, self.OldMonthlyItemsFile, chosenStore)

app = MainFrame()
app.mainloop()

raise SystemExit(0)


import sys, os
import xml.etree.ElementTree as ET

class gameBox:
    """An info container class"""
    name = ""
    filename = ""
    imgsrc = ""
    programID = 0
    PID = 0

    def __init__(self, filename):
        self.filename = filename
        self.parseXML()

    def parseXML(self):
        gamesDirectoryPresent = False
        for dirname, dirnames, filenames in os.walk('.'):
            for subdirname in dirnames:
                if subdirname == "games":
                    gamesDirectoryPresent = True

            if not gamesDirectoryPresent:
                raise Exception("No games directory")
        infoFilePresent = False
        for dirname, dirnames, filenames in os.walk("./games"):
            for filename in filenames:
                if filename == self.filename:
                    infoFilePresent = True
            if not infoFilePresent:
                raise Exception("No game info file")

        gameTree = ET.parse("./games/" + self.filename)
        gameRoot = gameTree.getroot()

        self.programID = gameRoot.find("PID").text
        self.PID = self.programID
        self.name = gameRoot.find("name").text
        self.imgsrc = gameRoot.find("imagesource").text


# -*- coding: utf-8 -*-
import statistics

from _anwfunction.analyze_antanswer import dpCompares, compare
from _anwfunction.read_antanswer import readAnw, readOpt, anwDetail

with open("anwOpt.json", 'r', encoding='utf-8-sig') as f:
    anwOpt = readOpt(f)
with open(anwOpt["ads_path"], 'r', encoding='utf-8-sig') as f:
    dMode = anwOpt["detail_mode"]
    DATA = readAnw(f)
    data_list = DATA.data_list
    DLen = len(data_list)
    if dMode == "opt":
        wil = anwOpt["basic_wil"]
        recent = anwOpt["basic_recent"]
        DATA = DATA._replace(anwDetail=anwDetail(wil=wil, recent=recent))
    elif dMode == "opt_rv":
        wil = anwOpt["basic_wil"]
        recent = int(DLen * anwOpt["basic_recentValue"])
        DATA = DATA._replace(anwDetail=anwDetail(wil=wil, recent=recent))
    elif dMode == "file":
        wil = DATA.anwDetail.wil
        recent = DATA.anwDetail.recent
        DATA = DATA._replace(anwDetail=anwDetail(wil=wil, recent=recent))
    else :
        quit()

n = DLen - 1
history = [None for i in range(recent)]

iMode = anwOpt["interface"].upper()
if iMode == "GUI":
    isCLI = False
    from _anwUi.gui_antanswer import *
    import sys
    app = QtWidgets.QApplication(sys.argv)

    ui = anwUi(DATA)
    sys.exit(app.exec_())

elif iMode == "CLI":
    from random import randint
    from time import time
    from _anwfunction.analyze_antanswer import compare, dpCompares
    isCLI = True

else :
    quit()

if not isCLI:
    quit()
while True:
    score = 0
    aHistory = []
    dpHistory = []
    qHistory = []

    st = time()

    # core
    for i in range(wil):
        r = i #appended
        #r = randint(0, n)
        #while r in history[-recent:]:
            #r = randint(0, n)

        #history.append(r)
        answer, question = data_list[r][0], data_list[r][1][randint(0, len(data_list[r][1]) - 1)]

        print(question)
        a = input()
        if a in answer:
            score += 1
            print("\n" * 30)
            print("정답! : %d" % score)

        else :
            print("\n" * 30)
            print("틀림! : %d" % score)

        aHistory.append(a)
        dpHistory.append(answer)
        qHistory.append(question)
        a = None

    print("정답률 :" + str(score / wil))
    st = time() - st
    print(str(st) + "초걸림 |")
    print(str(st / wil) + "초/한문항당")
    result = dpCompares(aHistory, dpHistory)
    print("평균 정확도:", statistics.mean(result[0]))

    # display result
    if compare(input("결산을 보시고 싶다면 'result'를 입력"), "result") > 0.9:
        print("\n")
        print("단어 일치율 // 당신이 쓴 답 // 원래 답")
        for i in range(len(result[0])):
            print(round(result[0][i], 3), "//", result[1][i], "//", result[2][i])
    # repeat
    if compare(input("반복 하려면 repeat"), "repeat") > 0.9:
        continue
    else :
        break