#!/apps/base/python3/bin/python3

# Ctrl+C Handler
import signal

# System
import sys
import os
from os.path import abspath

# Input Parsing
import argparse

# Time Handling
import time
from datetime import date
from datetime import timedelta

# Handle json import from file
import json

# Fix backspace on input
import readline

# Unit Testing
import unittest
from apm.classes import mock

# APM Imports
import apm
from apm import Files
from apm import UI
from apm import VapMgr

from apm import Stage
from apm import Rename
from apm import Process
from apm import Review
from apm import Remove
from apm import Archive
from apm import Cleanup
from apm import Demo

from apm.classes.system import jprint

from apm import test

global max_tries
global today

max_tries = 3;
today = int(date.fromtimestamp(time.time()).strftime("%Y%m%d"))

# TODO FOR DEBUGGING DURRING DEVELOPMENT
from inspect import currentframe, getframeinfo, getouterframes
# print("\n\t*** frameinfo ***\n{}\n".format(getframeinfo(currentframe(), context=2)))
# print("\n\t*** outerframes ***\n{}\n".format(getouterframes(currentframe(), context=2)))

def main():
    if not len(sys.argv) > 1:
        sys.argv.append("-h")

    if '-v' in sys.argv:
        print(apm.__version__)
        return

    # Retrieve arguments from user
    config = parse_args()
    command = config['command'].lower()

    # Check to see if this is a test
    if command == 'test':
        test_config = test.config()

        sys.argv = [sys.argv[0]]
        jprint(config, sort_keys=True, indent=4)
        unittest.main(buffer=True)
        # unittest.main()
        return

    # Not a test
    if command == 'info' or command == 'vapinfo':
        vap = VapMgr({})
        vap.vap_info()
        return

    if command == 'auto':
        temp = validate_config(config, command)
        config = temp if temp else config
    else:
        # Validate user arguments
        temp = validate_config(config, command)
        config = temp if temp else config

    if command == 'check':
        jprint(config, sort_keys=True, indent=4)
        return

    # Save the config to file
    s = time.time()
    f = Files(config)
    f.save_config()
    f.load_filenames()
    files = f.files

    # Check to see if any files are not currently being tracked
    # Or if any tracked files have been deleted
    print("Checking status of tracked files...", end="")
    sys.stdout.flush()

    json_file = '{0}/{1}/{1}.json'.format(config['stage'], config['job'])
    if os.path.exists(json_file) and config['ingest']:
        fp = open(json_file, 'r')
        files = json.loads(fp.read())
        fp.close()

        cwd = os.getcwd()
        os.chdir('{}/{}/collection'.format(config['stage'], config['job']))

        keys = files.keys()

        sites = set(os.listdir('.'))
        for site in keys:
            if site not in sites:
                files.pop(site)
                continue

            os.chdir(site)

            instruments = set(os.listdir('.'))
            ins_keys = files[site].keys()

            for ins in ins_keys:
                if ins not in instruments:
                    files[site].pop(ins)
                    continue

                os.chdir(ins)

                filelist = set(os.listdir('.'))
                for i in filelist:
                    if i not in files[site][ins] and not (os.path.isdir(i) and i == "other_files"):
                        exit("\nThe file {0}/{1}/{2} is currently untracked.\nPlease edit {3}.json to start tracking this file.\n".format(site, ins, i, config['job']))

                for i in files[site][ins]:
                    if i not in filelist:
                        files[site][ins][i]["deleted"] = True

                os.chdir('..')

            os.chdir('..')

        os.chdir(cwd)
    print("Done") # Done checking status of tracked files
    sys.stdout.flush()

    # Run the appropriate command
    if command == 'auto':
        print('Attempting to stage files for datastreams: {}'.format(config['datastream']))

        skip = False

        if not config['duplicates']:
            s = Stage(config, files)
            config, files = s.run()

            if config['exit']:
                exit()

            if config['duplicates']:
                skip = True

        if not skip and not config['vap']:
            r = Rename(config, files)
            config, files = r.run()
            if config['exit']:
                exit()
        exit()

    elif command == 'stage':
        print("*"*50,"\n", json.dumps(config, indent=2), "*"*50, "\n")
        skip = False

        if not config['duplicates']:
            s = Stage(config, files)
            config, files = s.run()

            if config['exit']:
                exit()

            if config['duplicates']:
                skip = True

        if not skip and not config['vap']:
            r = Rename(config, files)
            config, files = r.run()
            if config['exit']:
                exit()

    elif command == 'rename':
        # If rename is called explicitly, force rename even if config is set to false
        switch = True if config['rename'] == False else False

        if switch:
            config['rename'] = True

        if not config['vap']:
            r = Rename(config, files)
            config, files = r.run()
            if config['exit']:
                exit()

        if switch:
            config['rename'] = False

    elif command == 'process':
        r = Rename(config, files)
        has_coll = r.check_for_collisions()
        files = r.files

        if has_coll:
            config = r.config
            files = r.files
        else:
            p = Process(config, files)
            config, files = p.run()
            if config['exit']:
                exit()

    elif command == 'review':
        r = Rename(config, files)
        has_coll = r.check_for_collisions()
        files = r.files

        if has_coll:
            config = r.config
            files = r.files
        else:
            r = Review(config, files)
            config, files = r.run()
            if config['exit']:
                exit()

    elif command == 'remove':
        r = Rename(config, files)
        has_coll = r.check_for_collisions()
        files = r.files

        if has_coll:
            config = r.config
            files = r.files
        else:
            r = Remove(config, files)
            config, files = r.run()
            if config['exit']:
                exit()

    elif command == 'archive':
        r = Rename(config, files)
        has_coll = r.check_for_collisions()
        files = r.files

        if has_coll:
            config = r.config
            files = r.files
        else:
            a = Archive(config, files)
            config, files = a.run()
            if config['exit']:
                exit()

    elif command == 'cleanup':
        r = Rename(config, files)
        has_coll = r.check_for_collisions()
        files = r.files

        if has_coll:
            config = r.config
            files = r.files
        else:
            c = Cleanup(config, files)
            config, files = c.run()
            if config['exit']:
                exit()

    elif command == 'prep':
        r = Rename(config, files)
        has_coll = r.check_for_collisions()
        files = r.files

        if has_coll:
            config = r.config
            files = r.files
        else:
            d = Demo(config, files)
            config, files = d.run()
            if config['exit']:
                exit()

    elif command == "notification":
        # Alka's module goes here
        print('Yay notify the user shit has changed.')

    else:
        sys.argv.append("-h")
        config = parse_args()

    f.config = config
    f.files = files
    f.save_config()
    f.save_filenames()

def parse_args():
    """ Setup argument parsing and parse the arguments """
    username = os.environ.get('USER')

    # Setup parser and groups
    parser = argparse.ArgumentParser(description='ARM Processing Manager')
    ui_flags = parser.add_mutually_exclusive_group()
    stage_type = parser.add_mutually_exclusive_group()

    # Setup positional arguments
    parser.add_argument('command', help='Which of the APM stages to run: stage, rename, process, review, remove, archive, cleanup')

    # Demo options
    parser.add_argument('--demo', help='Prep for different stages of a demo, available options include: remove, archive, cleanup')

    # Date
    parser.add_argument('-b', '--begin', type=int, default=0, help='Format: YYYYMMDD - date to start processing data')
    parser.add_argument('-e', '--end', type=int, default=0, help='Format:YYYYMMDD - date to stop processing data')

    # SIF/Datastreams
    parser.add_argument('-s', '--site', help='The site the data is from')
    parser.add_argument('-i', '--instrument', help='The instrument used to collect the data')
    parser.add_argument('-f', '--facility', help='The facility where the instrument is located')
    parser.add_argument('-d', '--datastream', nargs='+', help='One or more datastream patterns. "%%" and "*" can be used as wildcards.')

    # Job
    parser.add_argument('-j', '--job', required=True, help='DQR # for job')

    # Alias
    parser.add_argument('-a', '--alias', help='An alias for the Ingest to use to connect to the database. Def: apm')

    # Flow control flags
    parser.add_argument('--stage', help='Specify a staging directory')
    parser.add_argument('--source', help='Specify a source directory')
    parser.add_argument('--no-rename', action='store_false', help='Do not strip the ARM prefix from the files')
    parser.add_argument('--no-db-up', action='store_false', help='Do not update the config database')
    parser.add_argument('--no-compare', action='store_false', help='Do not compare the ingest output for re-archiving')

    # Other
    parser.add_argument('--ingest-flags', nargs='+', help='Flags you want APM to pass to the INGEST. Ex. --ingest-flags F (Do not use "-F" APM will add the "-") (Will apply to all ingests if running for multiple datastreams)')

    # Ingest Vs Vap
    stage_type.add_argument('--ingest', action='store_true', help='Ingest vs. VAP (default)')
    stage_type.add_argument('--vap', action='store_true', help='VAP vs. Ingest')

    # UI Flags
    ui_flags.add_argument('-I', '--interactive', action='store_true', help='Prompt for various inputs')
    ui_flags.add_argument('-q', '--quiet', action='store_true', help='Suppresses prompts and exits gracefully if unable to run')
    ui_flags.add_argument('-D', '--devel', action='store_true', help='Run APM in development mode')

    # Parse the args
    arguments = parser.parse_args()

    if (arguments.ingest == False) and (arguments.vap == False):
        arguments.ingest = True

    args = {
        'command': arguments.command,
        'demo': arguments.demo,
        'begin': arguments.begin,
        'end': arguments.end,
        'site': arguments.site,
        'instrument': arguments.instrument,
        'facility': arguments.facility,
        'datastream': arguments.datastream,
        'duplicates': False,
        'job': arguments.job,
        'alias': arguments.alias,
        'stage': arguments.stage,
        'source': arguments.source,
        'rename': arguments.no_rename,
        'db_up': arguments.no_db_up,
        'compare': arguments.no_compare,
        'iflags': arguments.ingest_flags,
        'ingest': arguments.ingest,
        'vap': arguments.vap,
        'interactive': arguments.interactive,
        'quiet': arguments.quiet,
        'devel': arguments.devel,
        'username': username,
        'exit': False,
        "cleanup_status": {
                "review": {
                    "status": True,
                },
                "remove": {
                    "status": False,
                    "deletion_list": False,
                    "archive_list": False,
                    "files_bundled": False,
                },
                "archive": {
                    "status": False,
                    "files_deleted": False,
                    "move_files": False,
                    "files_released": False,
                },
                "cleanup": {
                    "status": False,
                    "files_archived": False,
                    "files_cleaned_up": False,
                },
            }
        }

    f = Files(args)

    if args['stage'] != None:
        temp = f.clean_path(args['stage'])
        args['stage'] = abspath(temp)
    if args['source'] != None:
        temp = f.clean_path(args['source'])
        args['source'] = abspath(temp)

    return args

def validate_config(config, command):
    f = Files(config)
    if command == "auto":
        temp = f.db_load_config()
    else:
        temp = f.load_config()
    config = temp if temp else config

    config['begin'], config['end'] = check_dates(config)
    config['site'], config['instrument'], config['facility'], config['datastream'] = check_sif_datastream(config)
    config['source'] = check_source(config)
    config['stage'] = check_stage(config)
    config['job'] = check_job(config)

    return config

def check_dates(config):
    begin = int(config['begin'])
    end = int(config['end'])
    interactive = config['interactive']
    quiet = config['quiet']

    if interactive:
        # Prompt for dates
        begin = get_date(begin, 'begin')
        end = get_date(end, 'end')

    # Validate dates
    if (begin == 0) or (end == 0):
        if quiet:
            exit('Valid dates are required')
        else:
            if (begin == 0) and (end != 0):
                print('A begin date is required.')
                begin = get_date(begin, 'begin')
            elif (begin != 0) and (end == 0):
                print('An end date is required.')
                end = get_date(end, 'end')
            else:
                begin = get_date(begin, 'begin')
                end = get_date(end, 'end')
                # if not vap:
                # exit('No dates specified. Please run again and provide begin and/or end dates.')

    if (begin > end) and (end != 0):
        exit('Start date must be the same day or before the end date')
    if begin > today:
        exit('No data for the selected date range.')
    if end > today:
        end = today

    return begin, end

def get_date(date, text, level=1):
    date = date if date > 0 else None
    error = False
    error_msg = "A valid date is required. Please try again{}"
    try:
        print('\nPlease enter a {} date (format: YYYYMMDD)'.format(text))
        temp = input('{}: '.format('00000000' if date == None else str(date)))

        if temp == '':
            if not date:
                error = True
        else:
            try:
                date = int(temp)
            except TypeError:
                return None

        if error:
            if level < max_tries:
                print(error_msg.format(": "))
                date = get_date(date, text, level=level+1)
            else:
                exit(error_msg.format("."))

    except ValueError:
        error_msg = 'A numberic value is required for dates. Please try again{}'
        if level < max_tries:
            print(error_msg.format(": "))
            date = get_date(date, text, level=level+1)
        else:
            exit(error_msg.format("."))

    return date

def check_sif_datastream(config):
    site = config['site']
    instrument = config['instrument']
    facility = config['facility']
    datastream = config['datastream']
    interactive = config['interactive']
    quiet = config['quiet']
    vap = config['vap']

    if datastream == None or not (type(datastream) == list and len(datastream) > 0) or vap == True:
        datastream = None
        if quiet and (instrument == None or (vap == False and (site == None or facility == None or len(site) != 3))):
            exit("There was an error retrieving your site, instrument, facility or datastream information. Please try again.")
        if interactive:
            site, instrument, facility = ask_sif(s=site, i=instrument, f=facility, vap=vap)
        if site == None:
            site = ask_sif(s=site, vap=vap)[0]
        elif (len(site) != 3) and not (vap and site == ''):
            print("ERROR: Site abbreviations are 3 characters in length. Please try again.")
            site = ask_sif(s=site, vap=vap)[0]
        if instrument == None:
            instrument = ask_sif(i=instrument)[1]
        if facility == None:
            facility = ask_sif(f=facility, vap=vap)[2]
    else:
        site = None
        instrument = None
        facility = None

    return site, instrument, facility, datastream

def ask_sif(s=False, i=False, f=False, level=1, vap=False):
    """ Ask for site instrument and facility data """
    if s != False:
        temp = input("\nPlease enter the three character site abbreviation you would like to use: ")
        if temp == '' and not vap:
            if s == None or s == True:
                if level < max_tries:
                    s = ask_sif(s=s, level=level+1)[0]
                else:
                    exit("ERROR: Site abbreviations are 3 characters in length. Please try again.")
            else: # Use default value
                temp = s

        if len(temp) == 3 or (temp == '' and vap):
            s = temp
        else:
            print("ERROR: Site abbreviations are 3 characters in length. Please try again.")
            s = ask_sif(s=s, level=level+1)[0]

    if i != False:
        temp = input("\nPlease enter the name of the instrument/process you would like to use: ")
        if temp == '':
            if i == None or i == True:
                if level < max_tries:
                    i = ask_sif(i=i, level=level+1)[1]
                else:
                    exit("ERROR: An instrument/process must be specified. Please try again.")
        else:
            i = temp

    if f != False:
        temp = input("\nPlease enter the facility you would like to use: ")
        if temp == '' and not vap:
            if f == None or f == True:
                if level < max_tries:
                    f = ask_sif(f=f, level=level+1)[2]
                else:
                    exit("ERROR: A facility must be specified. Please try again.")
        elif temp == '' and vap:
            f = temp
        else:
            f = temp

    return s, i, f

def check_source(config, level=1):
    source = config['source']
    interactive = config['interactive']
    quiet = config['quiet']

    if source == None:
        source = '/data/archive'

    if interactive:
        message = "Specify a source directory: ({})".format(source)
        error = 'A source directory must be specified. Please try again.'
        default = source
        source = ask_for_dir(message, error=error, default=default, required=True)

    if not os.path.exists(source):
        if level < max_tries and not quiet:
            message = '{} does not exists.\nPlease specify and existing source directory: '.format(source)
            error = 'A source directory must be specified. Please try again.'
            config['source'] = ask_for_dir(message, error=error, required=True)
            source = check_source(config, level+1)
        else:
            exit('{} does not exists. Please try again and specify and existing source directory.'.format(source))

    if len(os.listdir(source)) == 0:
        if level < max_tries and not quiet:
            message = '{} is not readable or contains no data.\nPlease specify a valid source directory: '.format(source)
            error = 'A source directory must be specified. Please try again.'
            config['source'] = ask_for_dir(message, error=error, required=True)
            source = check_source(config, level+1)
        else:
            exit('{} is not readable or contains no data. Please try again and specify a valid source directory.'.format(source))

    return source

def check_stage(config, level=1):
    f = Files(config)
    stage = config['stage']
    interactive = config['interactive']
    quiet = config['quiet']

    if stage == None:
        reproc_home = os.environ.get('REPROC_HOME')
        home = os.environ.get('HOME')
        if reproc_home != None:
            stage = reproc_home
        elif home.split('/')[1] == 'data':
            stage = '{}/reprocessing/data'.format(home)
        else:
            stage = '/data/home/{}/reprocessing/data'.format(config['username'])
    if interactive:
        message = "Specify a stage directory: ({})".format(stage)
        error = 'A stage directory must be specified. Please try again.'
        default = stage
        stage = ask_for_dir(message, error=error, default=default, required=True)

    if not os.path.exists(stage):
        try:
            os.makedirs(stage)
        except:
            if level < max_tries and not quiet:
                print('Unable to create {}'.format(stage))
                message = 'Please specify a new location: '
                error = 'A stage directory must be specified. Please try again.'
                config['stage'] = ask_for_dir(message, error=error, required=True)
                stage = check_stage(config, level+1)
            else:
                exit('Unable to create {}. Please try again.'.format(stage))

    if not f.is_dir_writable(stage):
        if level < max_tries and not quiet:
            print('{} is not writable.'.format(stage))
            message = 'Please specify a new location: '
            error = 'A stage directory must be specified. Please try again.'
            config['stage'] = ask_for_dir(message, error=error, required=True)
            stage = check_stage(config, level+1)
        else:
            exit('{} is not writable. Please try again.'.format(stage))

    return stage

def ask_for_dir(message, default=None, error=None, required=False, level=1):
    """ Ask user for a directory location """
    f = Files({})
    folder = input(message)
    if folder == '':
        if default != None:
            folder = default
        elif required:
            if level < max_tries:
                folder = ask_for_dir(message, default=default, error=error, required=required, level=level+1)
            else:
                exit(error)
        else:
            return

    folder = abspath(f.clean_path(folder))
    return folder

def check_job(config):
    job = config['job']
    interactive = config['interactive']

    if interactive:
        message = 'Please specify a job name: '
        if job:
            message = '{0}({1})'.format(message, job)
        temp = input(message)
        if temp != '':
            job = temp

    if not job:
        import uuid
        uid = str(uuid.uuid1())
        uid = uid.split('-')
        job = '{0}{1}'.format(uid[0], uid[3])

    config['job'] = job
    f = Files(config)
    f.setup_job_dir()

    return job

################################################################################
# Unittest Test Cases
################################################################################
class TestCheckDates(unittest.TestCase):
    """ Check the check_dates and get_date functions """
    def setUp(self):
        today = date.fromtimestamp(time.time())
        self.config = test.config()
        self.begin = self.config['begin']
        self.end = self.config['end']
        self.today = int(today.strftime("%Y%m%d"))
        self.earlier = int((today - timedelta(days=3)).strftime("%Y%m%d"))
        self.later = int((today + timedelta(days=3)).strftime("%Y%m%d"))
        self.evenLater = int((today + timedelta(days=6)).strftime("%Y%m%d"))
        self.message = "Please enter a date (YYYYMMDD): "

    ################################################
    # Test Get Date
    ################################################
    def test_1(self):
        """ No input -> FAIL """
        with mock.patch('__builtin__.input', side_effect=['', '', '']):
            with self.assertRaises(SystemExit):
                get_date(0, self.message)

    def test_2(self):
        """ Non-numeric input -> FAIL """
        with mock.patch('__builtin__.input', side_effect=['asdf', 'asdf', 'asdf']):
            with self.assertRaises(SystemExit):
                get_date(0, self.message)

    def test_3(self):
        """ 'None' -> 'non-numberic' -> 'valid date' -> PASS """
        with mock.patch('__builtin__.input', side_effect=['', 'asdf', self.today]):
            result = get_date(0, self.message)
            expected = self.today

            print("Result:   {}\nExpected: {}".format(result, expected))
            assert result == expected

    def test_4(self):
        """ Valid date -> PASS """
        with mock.patch('__builtin__.input', side_effect=[self.today]):
            result = get_date(0, self.message)
            expected = self.today

            print("Result:   {}\nExpected: {}".format(result, expected))
            assert result == expected

    def test_5(self):
        """ 'None' -> PASS """
        with mock.patch('__builtin__.input', side_effect=['']):
            result = get_date(self.today, self.message)
            expected = self.today

            print("Result:   {}\nExpected: {}".format(result, expected))
            assert result == expected

    ################################################
    # Test Check Dates
    ################################################
    # Interactive
    def test_6(self):
        """ Ask for dates even with dates given """
        self.config['interactive'] = True
        with mock.patch('__builtin__.input', side_effect=['', '']):
            result = check_dates(self.config)
            expected = (self.begin, self.end)

            print("Result:   {}\nExpected: {}".format(result, expected))
            assert result == expected

    def test_7(self):
        """ Supply new dates even though dates already provided """
        self.config['interactive'] = True
        with mock.patch('__builtin__.input', side_effect=[self.earlier, self.today]):
            result = check_dates(self.config)
            expected = (self.earlier, self.today)

            print("Result:   {}\nExpected: {}".format(result, expected))
            assert result == expected

    def test_8(self):
        """ No supplied dates, No passed dates -> FAIL """
        self.config['interactive'] = True
        self.config['begin'] = 0
        self.config['end'] = 0
        with mock.patch('__builtin__.input', side_effect=['', '', '']):
            with self.assertRaises(SystemExit):
                check_dates(self.config)

    def test_9(self):
        """ Supply begin date, test end date -> FAIL """
        self.config['interactive'] = True
        self.config['begin'] = 0
        self.config['end'] = 0
        with mock.patch('__builtin__.input', side_effect=[self.today, '', '', '']):
            with self.assertRaises(SystemExit):
                check_dates(self.config)

    # Quiet
    def test_10(self):
        """ No begin date -> FAIL """
        self.config['quiet'] = True
        self.config['begin'] = 0
        with self.assertRaises(SystemExit):
            check_dates(self.config)

    def test_11(self):
        """ No end date -> FAIL """
        self.config['quiet'] = True
        self.config['end'] = 0
        with self.assertRaises(SystemExit):
            check_dates(self.config)

    def test_12(self):
        """ No dates -> FAIL """
        self.config['quiet'] = True
        self.config['begin'] = 0
        self.config['end'] = 0
        with self.assertRaises(SystemExit):
            check_dates(self.config)

    # Check for dates
    def test_13(self):
        """ No begin date -> PASS """
        self.config['begin']
        with mock.patch('__builtin__.input', side_effect=[self.begin]):
            result = check_dates(self.config)
            expected = (self.begin, self.end)

            print("Result:   {}\nExpected: {}".format(result, expected))
            assert result == expected

    def test_14(self):
        """ No end date -> PASS """
        self.config['end'] = 0
        with mock.patch('__builtin__.input', side_effect=[self.end]):
            result = check_dates(self.config)
            expected = (self.begin, self.end)

            print("Result:   {}\nExpected: {}".format(result, expected))
            assert result == expected

    def test_15(self):
        """ No dates -> PASS """
        self.config['begin'] = 0
        self.config['end'] = 0
        with mock.patch('__builtin__.input', side_effect=[self.begin, self.end]):
            result = check_dates(self.config)
            expected = (self.begin, self.end)

            print("Result:   {}\nExpected: {}".format(result, expected))
            assert result == expected

    # Validate dates
    def test_16(self):
        """ Begin > End -> Fail """
        self.config['begin'] = self.end
        self.config['end'] = self.begin
        with self.assertRaises(SystemExit):
            check_dates(self.config)

    def test_17(self):
        """ Begin > Today -> FAIL """
        self.config['begin'] = self.later
        self.config['end'] = self.evenLater
        with self.assertRaises(SystemExit):
            check_dates(self.config)

    def test_18(self):
        """ End > Today -> PASS """
        self.config['begin'] = self.today
        self.config['end'] = self.later
        result = check_dates(self.config)
        expected = (self.today, self.today)

        print("Result:   {}\nExpected: {}".format(result, expected))
        assert result == expected

############################################################
# Test SIF Datastream
############################################################
class TestSIFD(unittest.TestCase):
    """ Test SIF and Datastream validation """
    def setUp(self):
        self.config = test.config()

    ################################################
    # Test Ask SIF
    ################################################
    def test_1(self):
        """ Site -> No Input -> FAIL """
        with mock.patch('__builtin__.input', side_effect=['', '', '']):
            with self.assertRaises(SystemExit):
                ask_sif(s=True)

    def test_2(self):
        """ Site -> Too Short Input -> Too Long Input -> No Input -> FAIL """
        with mock.patch('__builtin__.input', side_effect=['as', 'asdf', '']):
            with self.assertRaises(SystemExit):
                ask_sif(s=True)

    def test_3(self):
        """ Site -> Good Input -> PASS """
        with mock.patch('__builtin__.input', side_effect=[self.config['site']]):
            result = ask_sif(s=None)
            expected = (self.config['site'], False, False)

            print("Result:   {}\nExpected: {}".format(result, expected))
            assert result == expected

    def test_4(self):
        """ Instrument -> No Input -> FAIL """
        with mock.patch('__builtin__.input', side_effect=['', '', '']):
            with self.assertRaises(SystemExit):
                ask_sif(i=True)

    def test_5(self):
        """ Instrument -> Good Input -> PASS """
        with mock.patch('__builtin__.input', side_effect=[self.config['instrument']]):
            result = ask_sif(i=None)
            expected = (False, self.config['instrument'], False)

            print("Result:   {}\nExpected: {}".format(result, expected))
            assert result == expected

    def test_6(self):
        """ Facility -> No Input -> FAIL """
        with mock.patch('__builtin__.input', side_effect=['', '', '']):
            with self.assertRaises(SystemExit):
                ask_sif(f=True)

    def test_7(self):
        """ Facility -> Good Input -> PASS """
        with mock.patch('__builtin__.input', side_effect=[self.config['facility']]):
            result = ask_sif(f=None)
            expected = (False, False, self.config['facility'])

            print("Result:   {}\nExpected: {}".format(result, expected))
            assert result == expected

    def test_8(self):
        """ All -> Good Input -> PASS """
        with mock.patch('__builtin__.input', side_effect=[self.config['site'], self.config['instrument'], self.config['facility']]):
            result = ask_sif(s=None, i=None, f=None)
            expected = (self.config['site'], self.config['instrument'], self.config['facility'])

            print("Result:   {}\nExpected: {}".format(result, expected))
            assert result == expected

    def test_9(self):
        """ VAP -> Site -> No Input -> PASS """
        with mock.patch('__builtin__.input', side_effect=['']):
            result = ask_sif(s=None, vap=True)[0]
            expected = ''

            print("Result:   {}\nExpected: {}".format(result, expected))
            assert result == expected

    def test_10(self):
        """ VAP -> Facility -> No Input -> PASS """
        with mock.patch('__builtin__.input', side_effect=['']):
            result = ask_sif(f=None, vap=True)[2]
            expected = ''

            print("Result:   {}\nExpected: {}".format(result, expected))
            assert result == expected

    ################################################
    # Test Check SIF Datastream
    ################################################
    def test_11(self):
        """ Datastream and SIF -> Return Datastream only -> PASS """
        self.config['datastream'] = ['sgpmfrsrC1.00', 'nsairtC1.00']
        result = check_sif_datastream(self.config)
        expected = (None, None, None, self.config['datastream'])

        print("Result:   {}\nExpected: {}".format(result, expected))
        assert result == expected

    def test_12(self):
        """ Datastream -> Return Datastream -> PASS """
        self.config['datastream'] = ['sgpmfrsrC1.00', 'nsairtC1.00']
        self.config['site'] = None
        self.config['instrument'] = None
        self.config['facility'] = None

        result = check_sif_datastream(self.config)
        expected = (None, None, None, self.config['datastream'])

        print("Result:   {}\nExpected: {}".format(result, expected))
        assert result == expected

    def test_13(self):
        """ SIF -> PASS """
        result = check_sif_datastream(self.config)
        expected = (self.config['site'], self.config['instrument'], self.config['facility'], None)

        print("Result:   {}\nExpected: {}".format(result, expected))
        assert result == expected

    def test_14(self):
        """ SIF -> Interactive -> Ask for all 3 -> PASS """
        self.config['interactive'] = True
        with mock.patch('__builtin__.input', side_effect=[self.config['site'], self.config['instrument'], self.config['facility']]):
            result = check_sif_datastream(self.config)
            expected = (self.config['site'], self.config['instrument'], self.config['facility'], None)

            print("Result:   {}\nExpected: {}".format(result, expected))
            assert result == expected

    def test_15(self):
        """ SIF -> Quiet -> PASS """
        self.config['quiet'] = False
        result = check_sif_datastream(self.config)
        expected = (self.config['site'], self.config['instrument'], self.config['facility'], None)

        print("Result:   {}\nExpected: {}".format(result, expected))
        assert result == expected

    def test_16(self):
        """ SIF -> Bad Site -> Quiet -> FAIL """
        self.config['quiet'] = True
        self.config['site'] = 'asdf'
        with self.assertRaises(SystemExit):
            check_sif_datastream(self.config)

    def test_17(self):
        """ SIF -> No Site -> Quiet -> FAIL """
        self.config['quiet'] = True
        self.config['site'] = None
        with self.assertRaises(SystemExit):
            check_sif_datastream(self.config)

    def test_18(self):
        """ SIF -> No Instrument -> Quiet -> FAIL """
        self.config['quiet'] = True
        self.config['instrument'] = None
        with self.assertRaises(SystemExit):
            check_sif_datastream(self.config)

    def test_19(self):
        """ SIF -> No Facility -> Quiet -> FAIL """
        self.config['quiet'] = True
        self.config['facility'] = None
        with self.assertRaises(SystemExit):
            check_sif_datastream(self.config)

    def test_20(self):
        """ No SIF -> Ask for Data -> PASS """
        result = check_sif_datastream(self.config)
        expected = (self.config['site'], self.config['instrument'], self.config['facility'], None)

        print("Result:   {}\nExpected: {}".format(result, expected))
        assert result == expected

############################################################
# Test Source/Stage Dir
############################################################
class TestSourceStage(unittest.TestCase):
    """ Test source and stage directory validation """
    def setUp(self):
        self.stage = None
        self.source = None
        self.config = test.config()
        self.message = "Please enter a directory: "
        self.error = "A directory is required. Please try again."
        self.default = {
            'stage': '/data/home/{0}/.test_apm'.format(self.config['username']),
            'source': '/data/archive'
        }

        self.alt = {
            'stage': {
                'not_exists': '/data/stage',
                'not_writable': '/data/archive',
                'new': '/data/home/{0}/.test_apm/stage'.format(self.config['username'])
            },
            'source': {
                'not_exists': '/data/home/{0}/.test/source'.format(self.config['username']),
                'empty': '/data/home/{0}/.test_apm/empty_source'.format(self.config['username'])
            }
        }

        dirs = [self.default['stage'], self.alt['source']['empty']]
        for i in dirs:
            if not os.path.exists(i):
                os.makedirs(i)

    def tearDown(self):
        f = Files({})
        dirs = [self.stage, self.source, self.alt['source']['empty'], self.default['stage']]
        for i in dirs:
            if not i:
                continue

            if not os.path.exists(i):
                continue

            if not f.is_dir_empty(i):
                f.empty_dir(i)
            os.rmdir(i)

    ################################################
    # Test Ask for Dir
    ################################################
    def test_1(self):
        """ Default -> No input -> PASS """
        self.stage = self.default['stage']
        with mock.patch('__builtin__.input', side_effect=['']):
            result = ask_for_dir(self.message, default=self.default['stage'])
            expected = self.default['stage']

            print("Result:   {}\nExpected: {}".format(result, expected))
            assert result == expected

    def test_2(self):
        """ Required -> No input -> FAIL """
        with mock.patch('__builtin__.input', side_effect=['', '', '']):
            with self.assertRaises(SystemExit):
                ask_for_dir(self.message, required=True, error=self.error)

    def test_3(self):
        """ No input -> PASS """
        with mock.patch('__builtin__.input', side_effect=['', '', '']):
            result = ask_for_dir(self.message)
            expected = None

            print("Result:   {}\nExpected: {}".format(result, expected))
            assert result == expected

    ################################################
    # Test Check Source
    ################################################
    def test_4(self):
        """ None -> Exists -> Not empty -> PASS """
        result = check_source(self.config)
        expected = self.default['source']

        print("Result:   {}\nExpected: {}".format(result, expected))
        assert result == expected

    def test_5(self):
        """ Dir -> Not exists -> Not empty -> PASS """
        self.config['source'] = self.alt['source']['not_exists']
        with mock.patch('__builtin__.input', side_effect=[self.default['source']]):
            result = check_source(self.config)
            expected = self.default['source']

            print("Result:   {}\nExpected: {}".format(result, expected))
            assert result == expected

    def test_6(self):
        """ Dir -> Not exists -> No input -> FAIL """
        self.config['source'] = self.alt['source']['not_exists']
        with mock.patch('__builtin__.input', side_effect=['', '', '']):
            with self.assertRaises(SystemExit):
                check_source(self.config)

    def test_7(self):
        """ Dir -> Exists -> Empty -> FAIL """
        self.config['source'] = self.alt['source']['empty']
        with mock.patch('__builtin__.input', side_effect=['', '', '']):
            with self.assertRaises(SystemExit):
                check_source(self.config)

    def test_8(self):
        """ None -> Interactive -> No input -> PASS """
        self.config['interactive'] = True
        with mock.patch('__builtin__.input', side_effect=['']):
            result = check_source(self.config)
            expected = self.default['source']

            print("Result:   {}\nExpected: {}".format(result, expected))
            assert result == expected

    def test_9(self):
        """ Dir -> Interactive -> Dir -> PASS """
        self.config['interactive'] = True
        self.config['source'] = self.alt['source']['not_exists']
        with mock.patch('__builtin__.input', side_effect=[self.default['source']]):
            result = check_source(self.config)
            expected = self.default['source']

            print("Result:   {}\nExpected: {}".format(result, expected))
            assert result == expected

    def test_10(self):
        """ Dir -> Quiet -> Not Exists -> FAIL """
        self.config['quiet'] = True
        self.config['source'] = self.alt['source']['not_exists']
        with self.assertRaises(SystemExit):
            check_source(self.config)

    def test_11(self):
        """ Dir -> Quiet -> Empty -> FAIL """
        self.config['quiet'] = True
        self.config['source'] = self.alt['source']['empty']
        with self.assertRaises(SystemExit):
            check_source(self.config)

    ################################################
    # Test Check Stage
    ################################################
    def test_12(self):
        """ None -> Exists -> Writable -> PASS """
        result = check_stage(self.config)
        expected = '/data/home/{0}/apm'.format(self.config['username'])

        print("Result:   {}\nExpected: {}".format(result, expected))
        assert result == expected

    def test_13(self):
        """ Dir -> Not exists -> Writable -> PASS """
        self.stage = self.alt['stage']['new']
        self.config['stage'] = self.stage
        result = check_stage(self.config)
        expected = self.stage

        print("Result:   {}\nExpected: {}".format(result, expected))
        assert result == expected

    def test_14(self):
        """ Dir -> Not exists -> No input -> FAIL """
        self.config['stage'] = self.alt['stage']['not_exists']
        with mock.patch('__builtin__.input', side_effect=['', '', '']):
            with self.assertRaises(SystemExit):
                check_stage(self.config)

    def test_15(self):
        """ Dir -> Exists -> Not Writable -> FAIL """
        self.config['stage'] = self.alt['stage']['not_writable']
        with mock.patch('__builtin__.input', side_effect=['', '', '']):
            with self.assertRaises(SystemExit):
                check_stage(self.config)

    def test_16(self):
        """ None -> Interactive -> No input -> PASS """
        self.config['interactive'] = True
        with mock.patch('__builtin__.input', side_effect=['']):
            result = check_stage(self.config)
            expected = '/data/home/{0}/apm'.format(self.config['username'])

            print("Result:   {}\nExpected: {}".format(result, expected))
            assert result == expected

    def test_17(self):
        """ None -> Interactive -> Dir -> PASS """
        self.stage = self.alt['stage']['new']
        self.config['interactive'] = True
        with mock.patch('__builtin__.input', side_effect=[self.stage]):
            result = check_stage(self.config)
            expected = self.stage

            print("Result:   {}\nExpected: {}".format(result, expected))
            assert result == expected

    def test_18(self):
        """ Dir -> Quiet -> Not exists -> FAIL """
        self.config['quiet'] = True
        self.config['stage'] = self.alt['stage']['not_exists']
        with self.assertRaises(SystemExit):
            check_stage(self.config)

    def test_19(self):
        """ Dir -> Quiet -> Not writable -> FAIL """
        self.config['quiet'] = True
        self.config['stage'] = self.alt['stage']['not_writable']
        with self.assertRaises(SystemExit):
            check_stage(self.config)

############################################################
# Test Job
############################################################
class TestJob(unittest.TestCase):
    """ Test Job input and directory validation """
    def setUp(self):
        self.config = test.config()
        self.config['stage'] = '/data/home/{0}/.test_apm'.format(self.config['username'])
        self.stage = self.config['stage']

        if not os.path.exists(self.config['stage']):
            os.makedirs(self.config['stage'])

    def tearDown(self):
        if not self.stage:
            return

        if not os.path.exists(self.stage):
            return

        f = Files({})
        if not f.is_dir_empty(self.stage):
            f.empty_dir(self.stage)

        os.rmdir(self.stage)

    def get_job(self, files):
        jobs = set(os.listdir(self.stage))
        if len(jobs) == len(files) + 1:
            for i in jobs:
                if i not in files:
                    return i
        else:
            return

    def test_1(self):
        """ No Job -> Return Random Job -> PASS """
        self.config['job'] = None
        files = set(os.listdir(self.stage))
        result = check_job(self.config)
        expected = self.get_job(files) if self.get_job(files) != None else os.listdir(self.stage)[0]

        print("Result:   {}\nExpected: {}".format(result, expected))
        assert result == expected

    def test_2(self):
        """ No Job -> Interactive -> Job Name -> PASS """
        self.config['job'] = None
        self.config['interactive'] = True
        with mock.patch('__builtin__.input', side_effect=['test']):
            result = check_job(self.config)
            expected = 'test'

            print("Result:   {}\nExpected: {}".format(result, expected))
            assert result == expected

    def test_3(self):
        """ No Job -> Interactive -> No Job -> PASS """
        files = set(os.listdir(self.stage))
        self.config['job'] = None
        self.config['interactive'] = True
        with mock.patch('__builtin__.input', side_effect=['']):
            result = check_job(self.config)
            expected = self.get_job(files) if self.get_job(files) != None else os.listdir(self.stage)[0]

            print("Result:   {}\nExpected: {}".format(result, expected))
            assert result == expected

    def test_4(self):
        """ Job -> PASS """
        result = check_job(self.config)
        expected = 'test'

        print("Result:   {}\nExpected: {}".format(result, expected))
        assert result == expected

################################################################################

def signal_handler(signum, frame):
    """ Catch keyboard interrupts gracefully """
    signal.signal(signal.SIGINT, original_sigint)

    ui = UI()
    try:
        terminate = ui.yn_choice("\nAre you sure you want to terminate?", 'y')
        if terminate:
            sys.exit(0)
    except KeyboardInterrupt:
        # If user Presses Ctrl-C while in this section
        print("\nTerminating")
        exit()
    except RuntimeError:
        # If user presses Ctrl-C while already asking for input
        print('\nTerminating')
        exit()
    except Exception as e:
        # Other errors
        raise e

    signal.signal(signal.SIGINT, signal_handler)

if __name__ == '__main__':
    original_sigint = signal.getsignal(signal.SIGINT)
    signal.signal(signal.SIGINT, signal_handler)
    main()


from __future__ import print_function
from __future__ import division

import os
import h5py
import threading

import numpy
import vigra

import nifty
import nifty.hdf5
import nifty.graph.rag

blockLabelsIlpFile = "/home/tbeier/block_labels.ilp"

rawFile = "/media/tbeier/4cf81285-be72-45f5-8c63-fb8e9ff4476c/datasets/hhess_2nm/raw_2k_2nm.h5"
pmapFile = "/home/tbeier/prediction_semantic_binary_full.h5"
oversegFile = "/media/tbeier/4cf81285-be72-45f5-8c63-fb8e9ff4476c/supervoxels/2nm/aggloseg_0.3_50.h5"
oversegFile = "/home/tbeier/Desktop/aggloseg_0.3_50.h5"
workDir = "/media/tbeier/4cf81285-be72-45f5-8c63-fb8e9ff4476c/opt2k"

def getLabels(blockLabelsIlpFile):

    allLabels = dict()
    blockLabelsIlpFileH5 = h5py.File(blockLabelsIlpFile,'r')
    edgeLabels = blockLabelsIlpFileH5['Training and Multicut']['EdgeLabelsDict']

    for key in edgeLabels.keys():
        el = edgeLabels[key]
        labels = el['labels'][:]
        spIds = el['sp_ids'][:,:]

        for l, spId in zip(labels, spIds):
            if l == 1 or l == 2:
                uv = long(spId[0]),long(spId[1])
                if uv in allLabels:
                    allLabels[uv] = max(l, allLabels[uv])
                else:
                    allLabels[uv] = l
    blockLabelsIlpFileH5.close()

    l  = numpy.array(allLabels.values())
    uv = numpy.array(allLabels.keys())
    return l-1, uv

def h5Max(dset, blockShape=None):

    shape = dset.shape
    ndim = len(shape)
    assert ndim == 3
    if blockShape is None:
        blockShape = [100]*len(shape)

    blocking = nifty.tools.blocking(roiBegin=(0,0,0), roiEnd=shape, blockShape=list(blockShape))

    lock = threading.Lock()
    done = [0]
    gMax = [0]

    with nifty.tools.progressBar(size=blocking.numberOfBlocks) as bar:

        def f(blockIndex):

            block = blocking.getBlock(blockIndex)
            b,e = block.begin, block.end

            d = dset[b[0]:e[0], b[1]:e[1], b[2]:e[2]]

            with lock:
                done[0] += 1
                bar.update(done[0])

                gMax[0] = max(d.max(),gMax[0])

        nifty.tools.parallelForEach(range(blocking.numberOfBlocks), f=f, nWorkers=1)

    return gMax[0]

def computeRag(oversegFile, dset):

    oversegFileH5 = h5py.File(oversegFile)
    oversegDset = oversegFileH5[dset]
    try:
        maxLabel = oversegDset.attrs['maxLabel']
        #print("load max label")
    except:
        print("compute max label")
        maxLabel = h5Max(oversegDset)
        oversegDset.attrs['maxLabel'] = maxLabel

    oversegFileH5.close()

    print("max label",maxLabel)

    nLabels = maxLabel + 1

    cs = nifty.hdf5.CacheSettings()
    cs.hashTabelSize = 977
    cs.nBytes = 990000000
    cs.rddc = 0.5

    h5File = nifty.hdf5.openFile(oversegFile, cs)
    labelsArray = nifty.hdf5.Hdf5ArrayUInt32(h5File, dset)

    ragFile = os.path.join(workDir,'rag.h5')
    if not os.path.isfile(ragFile):

        with vigra.Timer("rag"):
            gridRag = nifty.graph.rag.gridRagHdf5(labelsArray, nLabels, blockShape=[150,150,150], numberOfThreads=20)

        print("serialize")
        serialization = gridRag.serialize()

        print("save serialization")
        ragH5File = h5py.File(ragFile)
        ragH5File['data'] = serialization
        ragH5File.close()
    else:
        ragH5File = h5py.File(ragFile,'r')
        serialization = ragH5File['data']
        gridRag = nifty.graph.rag.gridRagHdf5(labelsArray, nLabels, serialization=serialization)
        ragH5File.close()

    return gridRag

uv,l = getLabels(blockLabelsIlpFile)
oversegFileH5 = h5py.File(oversegFile)

# rag
rag = computeRag(oversegFile,'data')

h5File = nifty.hdf5.openFile(rawFile,)
rawArray = nifty.hdf5.Hdf5ArrayUInt8(h5File, 'data')

# accumulate features
rawFeat = nifty.graph.rag.accumulateStandartFeatures(rag=rag, data=rawArray, minVal=0.0, maxVal=255.0, blockShape=[75,75,75], numberOfThreads=-1)


from common_tools.Pattern_tools_py3.builders.common_directors_py3 import construct_all_handlers
import datetime
import msgpack

class Construct_RPC_File_Library(object):

   def __init__( self, qs, site_data ):
       search_list = [ "FILE_SERVER_CLIENT", "FILE_SERVER_CLIENT"]
       self.ds_handlers = construct_all_handlers(site_data,qs,search_list)
       self.rpc_client = self.ds_handlers["FILE_SERVER_RPC_SERVER"]

   def check_file(self,file_name,return_value):
       if return_value[0] == False:
          raise ValueError("file does not exist  "+file_name)
       return return_value[1]

   def load_file(self,path,file_name):

       parameters = {}
       parameters["path"] = path
       parameters["file_name"] = file_name
       return_value = self.rpc_client.send_rpc_message( method="load",parameters=parameters,timeout=3 )
       return self.check_file(file_name,return_value)

   def save_file(self,path,file_name,data):

       parameters = {}
       parameters["path"] = path
       parameters["file_name"] = file_name
       parameters["data"] = data
       return_value = self.rpc_client.send_rpc_message( method="save",parameters=parameters,timeout=3 )
       return self.check_file(file_name,return_value)

   def file_exists(self,path,file_name):

       parameters = {}
       parameters["path"] = path
       parameters["file_name"] = file_name
       return_value = self.rpc_client.send_rpc_message( method="file_exists",parameters=parameters,timeout=3 )
       return return_value[0]

   def file_directory(self,path):

       parameters = {}
       parameters["path"] = path
       return_value = self.rpc_client.send_rpc_message( method="file_directory",parameters=parameters,timeout=3 )
       return self.check_file(path,return_value)

   def delete_file(self, path,file_name):

       parameters = {}
       parameters["path"] = path
       parameters["file_name"] = file_name
       return_value = self.rpc_client.send_rpc_message( method="delete_file",parameters=parameters,timeout=3 )
       return return_value[0]

   def mkdir(self,path):

       parameters = {}
       parameters["path"] = path
       return_value = self.rpc_client.send_rpc_message( method="make_dir",parameters=parameters,timeout=3 )
       return return_value[0]

if __name__ == "__main__":

    import datetime
    import time
    import string
    import urllib.request
    import math
    import redis

    import json

    import os
    import copy

    #
    #
    # Read Boot File
    # expand json file
    #
    file_handle = open("/data/redis_server.json",'r')
    data = file_handle.read()
    file_handle.close()
    redis_site = json.loads(data)

    #
    # Setup handle
    # open data stores instance

    qs = Query_Support( redis_site )

    file_client = Construct_RPC_Library(qs,redis_site)
    print(file_client.file_directory(""))
    print(file_client.mkdir("test_path"))
    print(file_client.file_directory(""))
    print(file_client.save_file("test_path","test_file.test","hi\nthere\nbrown\ncow"))
    print(file_client.load_file("test_path","test_file.test"))
    print(file_client.file_directory("test_path"))
    print(file_client.file_exists("test_path","test_file.test"))
    print(file_client.delete_file("test_path","test_file.test"))
    print(file_client.file_directory("test_path"))


import pygame as pg
from board import board
from tkinter import *
from tkinter import messagebox
from draw_window import *
from draw_flag import draw_flag

def main():
    pg.init()
    pg.mixer.init()

    res = (400, 480)
    mute = False

    surface = pg.image.load('flag_icon.png')
    mine_surface = pg.image.load('mine_icon.jpg')
    pg.display.set_icon(surface)

    level_surface = pg.image.load('level_icon.jpg')
    mute_surface = pg.image.load('mute2_icon.png')
    sound_surface = pg.image.load('sound2_icon.png')
    settings_surface = pg.image.load('settings_icon.png')

    color = (255, 255, 102)
    color_light = (170, 170, 170)
    color_light2 = (220, 220, 220)
    color_dark = (100, 100, 100)
    main_text_color = (153, 204, 255)

    screen = pg.display.set_mode(res)

    pg.display.set_caption('Majnsviper')

    width = screen.get_width()
    height = screen.get_height()

    size = 500

    smallfont = pg.font.SysFont('Corbel', 35)
    #smallfont = pg.font.SysFont('times-bold', 35)

    main_text = smallfont.render('Majnsviper', True, main_text_color)

    text1 = smallfont.render('Quit', True, color)
    text2 = smallfont.render('Start', True, color)

    selection1 = smallfont.render('Small', True, color)
    selection2 = smallfont.render('Normal', True, color)
    selection3 = smallfont.render('Large', True, color)

    text3 = smallfont.render('Mode', True, color)

    is_mode_selected = False

    brojac_1 = 1

    if mute == False:
        pg.mixer.music.load('Wii_music.mp3')
        pg.mixer.music.play(-1)

    while True:
        game = board()
        game.mute = mute
        screen = pg.display.set_mode(res)
        blockSize = 60

        for ev in pg.event.get():

            if ev.type == pg.QUIT:
                pg.quit()

            if ev.type == pg.MOUSEBUTTONUP:
                if width / 2 - 70 <= mouse[0] <= width / 2 + 70 and height / 2 - 80 <= mouse[1] <= height / 2 - 40:
                    pg.mixer.music.stop()
                    game.set_blocksize_and_size(blockSize, size)
                    while main_f(brojac_1, game) == 0:
                        pass

                    pg.display.set_icon(surface)
                    pg.display.set_caption('Majnsviper')

                    mute = game.mute
                    blockSize = game.blocksize
                    pg.mixer.music.load('Wii_music.mp3')
                    game.set_number_of_mines()
                    game.set_blocksize_and_size(blockSize, size)
                    if mute == False:
                        pg.mixer.music.play(-1)

                elif width / 2 - 70 <= mouse[0] <= width / 2 + 70 and height / 2 - 20 <= mouse[1] <= height / 2 + 20:
                    pg.quit()
                    return

                elif is_mode_selected and width / 2 - 70 <= mouse[0] <= width / 2 + 70 and height / 2 + 80 <= mouse[
                    1] <= height / 2 + 120:
                    pg.mixer.music.stop()
                    blockSize = 60
                    game.set_blocksize_and_size(blockSize, size)
                    while main_f(brojac_1, game) == 0:
                        pass

                    pg.display.set_icon(surface)
                    pg.display.set_caption('Majnsviper')

                    mute = game.mute
                    blockSize = game.blocksize
                    game.set_number_of_mines()
                    game.set_blocksize_and_size(blockSize, size)
                    pg.mixer.music.load('Wii_music.mp3')
                    if mute == False:
                        pg.mixer.music.play(-1)

                elif is_mode_selected and width / 2 - 70 <= mouse[0] <= width / 2 + 70 and height / 2 + 120 <= mouse[
                    1] <= height / 2 + 160:
                    pg.mixer.music.stop()
                    blockSize = 30
                    game.set_blocksize_and_size(blockSize, size)
                    while main_f(brojac_1, game) == 0:
                        pass

                    pg.display.set_icon(surface)
                    pg.display.set_caption('Majnsviper')

                    mute = game.mute
                    blockSize = game.blocksize
                    game.set_number_of_mines()
                    game.set_blocksize_and_size(blockSize, size)
                    pg.mixer.music.load('Wii_music.mp3')
                    if mute == False:
                        pg.mixer.music.play(-1)

                elif is_mode_selected and width / 2 - 70 <= mouse[0] <= width / 2 + 70 and height / 2 + 160 <= mouse[
                    1] <= height / 2 + 200:
                    pg.mixer.music.stop()
                    blockSize = 20
                    game.set_blocksize_and_size(blockSize, size)
                    while main_f(brojac_1, game) == 0:
                        pass

                    pg.display.set_icon(surface)
                    pg.display.set_caption('Majnsviper')

                    mute = game.mute
                    blockSize = game.blocksize
                    game.set_number_of_mines()
                    game.set_blocksize_and_size(blockSize, size)
                    pg.mixer.music.load('Wii_music.mp3')
                    if mute == False:
                        pg.mixer.music.play(-1)

                elif width - 60 <= mouse[0] <= width - 20 and height - 60 <= mouse[1] <= height - 20:
                    mute = not mute
                    if mute == True:
                        pg.mixer.music.stop()
                    else:
                        pg.mixer.music.play(-1)

                elif width - 105 <= mouse[0] <= width - 65 and height - 60 <= mouse[1] <= height - 20:
                    settings_window = pg.display.set_mode((width, height))
                    pg.display.set_caption('Settings')
                    pg.display.set_icon(settings_surface)

                    run = True

                    while run:
                        settings_window.fill(color)
                        pg.display.update()
                        for event in pg.event.get():
                            if event.type == pg.QUIT:
                                run = False

                    pg.display.set_icon(surface)
                    pg.display.set_caption('Majnsviper')

        # fills the screen with a color
        screen.fill(color)

        # stores the (x,y) coordinates into
        # the variable as a tuple
        mouse = pg.mouse.get_pos()

        # if mouse is hovered on a button it
        # changes to lighter shade

        pg.draw.rect(screen, color_dark, [width / 2 - 70, height / 2 - 20, 140, 40])
        pg.draw.rect(screen, color_dark, [width / 2 - 70, height / 2 - 80, 140, 40])
        pg.draw.rect(screen, color_dark, [width / 2 - 70, height / 2 + 40, 140, 40])

        flag_img = pg.transform.scale(surface, (40, 40))
        mine_img = pg.transform.scale(mine_surface, (40, 40))
        level_icon = pg.transform.scale(level_surface, (40, 40))
        sound_icon = pg.transform.scale(sound_surface, (40, 40))
        mute_icon = pg.transform.scale(mute_surface, (40, 40))
        settings_icon = pg.transform.scale(settings_surface, (40, 40))

        screen.blit(flag_img, (width / 2 - 130, height / 2 - 80))
        screen.blit(mine_img, (width / 2 - 130, height / 2 - 20))
        screen.blit(level_icon, (width / 2 - 130, height / 2 + 40))
        if mute == False:
            screen.blit(mute_icon, (width - 60, height - 60))
        else:
            screen.blit(sound_icon, (width - 60, height - 60))
        screen.blit(settings_icon, (width - 105, height - 60))

        if width / 2 - 70 <= mouse[0] <= width / 2 + 70 and height / 2 - 20 <= mouse[1] <= height / 2 + 20:
            is_mode_selected = False
            pg.draw.rect(screen, color_light, [width / 2 - 70, height / 2 - 20, 140, 40])

        elif width / 2 - 70 <= mouse[0] <= width / 2 + 70 and height / 2 - 80 <= mouse[1] <= height / 2 - 40:
            is_mode_selected = False
            pg.draw.rect(screen, color_light, [width / 2 - 70, height / 2 - 80, 140, 40])

        elif width / 2 - 70 <= mouse[0] <= width / 2 + 70 and height / 2 + 40 <= mouse[1] <= height / 2 + 80:
            is_mode_selected = True
            pg.draw.rect(screen, color_light, [width / 2 - 70, height / 2 + 40, 140, 40])
            pg.draw.rect(screen, color_dark, [width / 2 - 70, height / 2 + 80, 140, 40])
            pg.draw.rect(screen, color_dark, [width / 2 - 70, height / 2 + 120, 140, 40])
            pg.draw.rect(screen, color_dark, [width / 2 - 70, height / 2 + 160, 140, 40])

        elif is_mode_selected and width / 2 - 70 <= mouse[0] <= width / 2 + 70 and height / 2 + 80 <= mouse[
            1] <= height / 2 + 120:
            pg.draw.rect(screen, color_light, [width / 2 - 70, height / 2 + 80, 140, 40])
            pg.draw.rect(screen, color_dark, [width / 2 - 70, height / 2 + 120, 140, 40])
            pg.draw.rect(screen, color_dark, [width / 2 - 70, height / 2 + 160, 140, 40])

        elif is_mode_selected and width / 2 - 70 <= mouse[0] <= width / 2 + 70 and height / 2 + 120 <= mouse[
            1] <= height / 2 + 160:
            pg.draw.rect(screen, color_dark, [width / 2 - 70, height / 2 + 80, 140, 40])
            pg.draw.rect(screen, color_light, [width / 2 - 70, height / 2 + 120, 140, 40])
            pg.draw.rect(screen, color_dark, [width / 2 - 70, height / 2 + 160, 140, 40])

        elif is_mode_selected and width / 2 - 70 <= mouse[0] <= width / 2 + 70 and height / 2 + 160 <= mouse[
            1] <= height / 2 + 200:
            pg.draw.rect(screen, color_dark, [width / 2 - 70, height / 2 + 80, 140, 40])
            pg.draw.rect(screen, color_dark, [width / 2 - 70, height / 2 + 120, 140, 40])
            pg.draw.rect(screen, color_light, [width / 2 - 70, height / 2 + 160, 140, 40])

        else:
            is_mode_selected = False

        if is_mode_selected:
            pg.draw.rect(screen, color_light2, [width / 2 - 70, height / 2 + 40, 140, 40])
            screen.blit(selection1, (width / 2 - 40, height / 2 + 80))
            screen.blit(selection2, (width / 2 - 50, height / 2 + 120))
            screen.blit(selection3, (width / 2 - 40, height / 2 + 160))

        # superimposing the text onto our button
        screen.blit(text1, (width / 2 - 30, height / 2 - 18))
        screen.blit(text2, (width / 2 - 30, height / 2 - 78))
        screen.blit(text3, (width / 2 - 40, height / 2 + 43))
        pg.draw.rect(screen, color_dark, [width / 2 - 77, height / 2 - 180, 160, 40])
        screen.blit(main_text, (width / 2 - 75, height / 2 - 180))

        # updates the frames of the game
        pg.display.update()

def main_f(brojac_1, game):
    gray1 = (155, 155, 155)
    gray2 = (202, 207, 202)
    gray2 = (224,224,224)
    black = (16,16,16)
    brown = (102, 102, 51)
    green = (0, 102, 0)
    tirkizna = (0, 155, 155)
    white = (255, 255, 255)
    red = (255, 25, 0)
    blue = (0, 102, 255)
    light_green = (153, 255, 204)
    yellow = (255, 255, 102)
    test_color = (0, 50, 70)

    background_color = test_color

    font = pg.font.Font('freesansbold.ttf', 32)
    caption = "MyMineSweeper"
    icon = 'mine_icon.jpg'
    icon_surface = pg.image.load(icon)

    # flag_count_img = pg.image.load('flag_icon.png')

    mute_icon_image = pg.image.load('mute2_icon.png')
    mute_icon_image = pg.transform.scale(mute_icon_image, (40, 40))

    sound_icon_image = pg.image.load('sound2_icon.png')
    sound_icon_image = pg.transform.scale(sound_icon_image, (40, 40))

    if game.mute == False:
        sound_or_mute_image = mute_icon_image
    else:
        sound_or_mute_image = sound_icon_image

    width = game.size
    height = game.size

    window = pg.display.set_mode((width, height + 40))
    pg.display.set_caption(caption)
    pg.display.set_icon(icon_surface)

    run = True

    counter_of_moves_played = 0

    while run:
        if game.num_of_free_squares == 0:
            if game.blocksize == 60:
                if game.mute == False:
                    pg.mixer.music.load('Win_music_1.mp3')
                    pg.mixer.music.play(1)
                Tk().wm_withdraw()  # to hide the main window
                messagebox.showinfo('Yeah', 'You won!')
                # animation for win
            elif game.blocksize == 30:
                if game.mute == False:
                    pg.mixer.music.load('Win_music_2.mp3')
                    pg.mixer.music.play(1)
                Tk().wm_withdraw()  # to hide the main window
                messagebox.showinfo('Yeah', 'You won!')
                # animation for win
            elif game.blocksize == 15:
                if game.mute == False:
                    pg.mixer.music.load('Win_music_3.mp3')
                    pg.mixer.music.play(1)
                Tk().wm_withdraw()  # to hide the main window
                messagebox.showinfo('Yeah', 'You won!')
                # animation for win
            game.__init__()
            return 0

        for event in pg.event.get():
            if event.type == pg.MOUSEBUTTONUP:
                if (event.button == 1):
                    pos = pg.mouse.get_pos()
                    if 450 <= pos[0] <= 490 and height - 5 <= pos[1] <= height + 35:
                        if game.mute == True:
                            game.mute = False
                            sound_or_mute_image = mute_icon_image
                        else:
                            game.mute = True
                            sound_or_mute_image = sound_icon_image

                    if game.num_of_moves_played == 0:
                        mem_x = -1
                        mem_y = -1
                        x = 0
                        y = 0
                        for i in range(10, game.size - 10, game.blocksize):
                            y = y % game.num_of_rows

                            for j in range(10, game.size - 10, game.blocksize):
                                x = x % game.num_of_rows

                                rect = pg.Rect(i, j, game.blocksize, game.blocksize)
                                pg.draw.rect(window, (50, 100, 200), rect, 2)

                                if 0 < pos[0] - i < game.blocksize and 0 < pos[1] - j < game.blocksize:
                                    mem_x = x
                                    mem_y = y
                                x = x + 1

                            y = y + 1

                        if mem_x != -1 and mem_y != -1:
                            game.generate_new_array_with_free_starting_square(mem_x, mem_y)
                            game.num_of_moves_played = 1

                    info = draw_window(pos, True, brojac_1, window, game, background_color)
                    if info == 1:
                        game.__init__()
                        return 0
                elif event.button == 3 and game.num_of_flags_remaining >= 0 and game.num_of_moves_played != 0:
                    pos = pg.mouse.get_pos()
                    draw_flag(window, pos, game)
                brojac_1 = 0
            elif event.type == pg.QUIT:
                return 1
            else:
                info = draw_window(0, False, brojac_1, window, game, background_color)
                if info == 1:
                    game.__init__()
                    return 0
                brojac_1 = 0

        flag_count_text_1 = font.render(": ", True, white, black)
        flag_count_text = font.render(str(game.num_of_flags_remaining), True, white, black)
        flag_count_text_1 = pg.transform.scale(flag_count_text_1, (28, 21))
        flag_count_text = pg.transform.scale(flag_count_text, (38, 30))
        window.fill((0, 0, 0), (42, height, 110, 40))
        window.blit(flag_count_text_1, (43, height + 4))
        window.blit(flag_count_text, (60, height + 1))
        window.blit(sound_or_mute_image, (450, height - 5))

    pg.quit()

#start of program
main()


from flask import request, jsonify
from flask import current_app as app
from app import redis
from functools import wraps
from app.Messages import Messages
import jwt

def require_login_token(func):
    @wraps(func)
    def check_token(*args, **kwargs):
        data = request.get_json()
        token = None
        if data:
            token = data.get("token")
        if request.cookies:
            token = request.cookies.get("token")
        if token == None:
            return_data = dict(Messages.error_invalid_token)
            resp = jsonify(return_data)
            resp.status_code = 200
            return resp

        # Token has been 'logged out'
        if redis.get(token) != None:
            return_data = dict(Messages.error_invalid_token)
            resp = jsonify(return_data)
            resp.status_code = 200
            return resp

        try:
            data = jwt.decode(token, app.jwt_key)
        except (ValueError, jwt.exceptions.DecodeError, jwt.ExpiredSignatureError):
            return_data = dict(Messages.error_invalid_token)
            resp = jsonify(return_data)
            resp.status_code = 200
            return resp

        kwargs["username"] = data["username"]
        kwargs["token"] = token
        kwargs["token_expire"] = data["exp"]
        return func(*args, **kwargs)

    return check_token


from langchain.llms import OpenAI

from info_gpt import constants

openai_model = OpenAI(temperature=constants.TEMPERATURE)

def debug_logs(text: str):
    """Endpoint to debug logs.

    Parameters
    ----------
    text : str
        The logs to debug.

    Returns
    -------
    str
        The debugged logs.
    """
    text = (
        "Debug the following logs which were generated during building a Docker image: \n"
        + text
    )
    return openai_model(text)


"""import numpy as np
import cv2"""

"""cap = cv2.VideoCapture(0)

while (True):

    # ret is we are returning the variable simple.
    ret, img = cap.read()
    # img = recognize(img, clf, faceCascade)
    cv2.imshow("welcome to face recognization", img)

    if cv2.waitKey(0) & 0xFF == ord('q'):
        break

cv2.destroyWindows('img')
cv2.waitKey(1)
cap.release()"""

"""def return_camera_indices():
    index = -2
    arr = []
    i = 10
    while i > 0:
        cap = cv2.VideoCapture(index)
        if cap.read()[0]:
            arr.append(index)
            cap.release()
        index += 1
        i -= 1
    return arr

print(return_camera_indices())"""

"""import numpy as np
import cv2
cap = cv2.VideoCapture(0)

while (True):
    # Capture frame-by-frame
    ret, frame = cap.read()

    # Display the resulting frame
    cv2.imshow('frame', frame)

    if cv2.waitKey(0) & 0xFF == ord('q'):
        break

cv2.destroyWindows('frame')

# When everything done, release the capture
cap.release()"""

import numpy as np
import cv2

cap = cv2.VideoCapture(0)

while(True):
    # Capture frame-by-frame
    ret, frame = cap.read()

    # Display the resulting frame
    cv2.imshow('frame',frame)

    if cv2.waitKey(1) & 0xFF == ord('q'):
        break

cv2.destroyWindow('frame')

# When everything done, release the capture
cap.release()


from pathlib import Path
from prefect.task_runners import SequentialTaskRunner
from prefect_dbt_flow import dbt_flow
from prefect_dbt_flow.dbt import DbtProfile, DbtProject

my_dbt_flow = dbt_flow(
    project=DbtProject(
        name="sample_project",
        project_dir=Path() / "prefect_demo",
        profiles_dir=Path.home() / ".dbt",
    ),
    profile=DbtProfile(
        target="prod",
    ),
    flow_kwargs={
        "task_runner": SequentialTaskRunner(),
    },
)

if __name__ == "__main__":
    my_dbt_flow()


from collections import deque
from copy import deepcopy

def get_data():
    with open('data.in', 'r') as f:
        L = [l.strip() for l in f]
    deck1 = deque(L[1:L.index('')])
    deck2 = deque(L[L.index('')+2:])
    #top card = at leftmost index
    return deck1, deck2

def calculate_score(d):
    score = [(n+1)*int(i) for n, i in enumerate(reversed(d))]
    return sum(score)

def play_p1(deck1, deck2, r):
    #print(f'round {r}')
    #print(f'player 1s deck {deck1}')
    #print(f'player 2s deck {deck2}')
    if len(deck2) == 0:
        #if deck 1 has won
        score = calculate_score(deck1)
        return score
    elif len(deck1) == 0:
        #deck 2 has won
        score = calculate_score(deck2)
        return score
    else:
        #if no one has won, conitnue playing
        d1c = deck1.popleft()
        d2c = deck2.popleft()
        #print(f'player 1 plays {d1c}')
        #print(f'player 2 plays {d2c}')
        if int(d1c) > int(d2c):
            #player 1 wins round
            #players card appended before opponents
            deck1.append(d1c)
            deck1.append(d2c)
            #print('player 1 wins')
        else:
            #player 2 wins round
            #print('player 2 wins')
            deck2.append(d2c)
            deck2.append(d1c)
        return play_p1(deck1, deck2, r+1)

#print(calculate_score(deck1))
def part_1():
    deck1, deck2 = get_data()
    return play_p1(deck1, deck2, 1)

#True if d1 wins
def play_p2(deck1, deck2):
    seen = set()
    while deck1 and deck2:
        D = (tuple(deck1), tuple(deck2))
        if D in seen:
            return calculate_score(deck1), True
        seen.add(D)
        #if no one has won, conitnue playing
        d1c = deck1.popleft()
        d2c = deck2.popleft()
        if len(deck1) >= int(d1c) and len(deck2) >= int(d2c):
            newd1 = deque(deck1[i] for i in range(int(d1c)))
            newd2 = deque(deck2[i] for i in range(int(d2c)))
            p1_wins = play_p2(deepcopy(newd1), deepcopy(newd2))[1]
        else:
            p1_wins = int(d1c) > int(d2c)
        if p1_wins:
            deck1.append(d1c)
            deck1.append(d2c)
        else:
            deck2.append(d2c)
            deck2.append(d1c)
    if deck1:
        return calculate_score(deck1), True
    else:
        return calculate_score(deck2), False

def part_2():
    deck1, deck2 = get_data()
    winner_score = play_p2(deck1, deck2)[0]
    return winner_score

print(part_1())
print(part_2())


from google.cloud import vision

# import os
# credential_path = "C:\Users\augus\OneDrive\Desktop\python\vision-ai-test\nifty-stage-313710-6d11794f516f.json"
# os.environ['GOOGLE_APPLICATION_CREDENTIALS'] = credential_path

# print("user >", end="")
# input()
# print("AI >ん？")
# print("user >", end="")
# input()
# print("AI >ええで。画像URLくれ：", end="")
img_path = input()
# print("user >", end="")
# input()
# print("AI >可愛い。")
# print("user >", end="")
# input()
# print("AI >へいへい。")
# print("")

# print("解析したい画像のナンバーを00-99で入力してください：", end="")
# input = input()

# img_path = "./images/img_" + input + ".jpg"
# print(img_path)

with open(img_path, 'rb') as image_file:
	content = image_file.read()

# visionAPIが扱えるデータ形式
image = vision.Image(content=content)
# print(image)

# インスタンスを生成
annotator_client = vision.ImageAnnotatorClient()

response_data = annotator_client.label_detection(image=image)

labels = response_data.label_annotations

print('--------RESULT--------')
for label in labels:
	print(label.description, ':', round(label.score * 100, 2), '%')
print('----------------------')


from django.core.mail import send_mail
from django.template.loader import render_to_string

from rest_framework.reverse import reverse

from alfheimproject.settings import CONFIG, SECRETS

def send_account_verification(sender, instance, **kwargs):
    html_msg = render_to_string('email_verification.html', {
        'server_name': CONFIG['server']['conf']['server_name'],
        'username': instance.user.username,
        'email': instance.user.email,
        'staff_email': SECRETS['smtp']['email_host_user'],
        'creation_date': instance.created_date,
        'verify_url': '{domain}{url}?token={token}'.format(
            domain=CONFIG['server']['conf']['server_domain'],
            url=reverse('activate_master_account'),
            token=instance.token
        )
    })
    send_mail(
        subject=CONFIG['emails']['default_subject'].format(server_name=CONFIG['server']['conf']['server_name']),
        message=CONFIG['emails']['default_message'].format(server_name=CONFIG['server']['conf']['server_name'],
                                                           user=instance.user.username),
        from_email=SECRETS['smtp']['email_host_user'],
        recipient_list=[instance.user.email],
        html_message=html_msg
    )


from .base import GnuRecipe
from .graphviz import GraphVizRecipe

class DotRecipe(GnuRecipe):
    def __init__(self, *args, **kwargs):
        super(DotRecipe, self).__init__(*args, **kwargs)
        self.name = 'dot'
        graphviz = GraphVizRecipe(settings=self)
        self.sha256 = graphviz.sha256
        self.version = graphviz.version
        self.url = graphviz.url
        self.depends = ['fontconfig', # for fonts
                        'libgd'  # for png
                       ]
        self.documentation = 'graphviz without all the dependencies for' \
                             ' building documentation'
        self.configure_args += ['--without-x',
                                '--disable-swig',
                                '--without-glut',
                                '--with-libgd=no']
    def post_install(self):
        super(DotRecipe, self).post_install()
        self.run_exe(['dot', '-c'], self.directory, self.environment)


#!/usr/bin/python3

import re

from argparse import ArgumentParser
from shutil import copy2 as copy_file
from os import remove as delete_file

LAYER_REGEX = re.compile(r"G[0|1] .*Z([\d,\.]+).* ; move to next layer \((.+)\)")  # noqa: E501

def get_filename_from_commannd_line():
    parser = ArgumentParser(description="Adds a layer count to a G-Code file.")
    parser.add_argument("filename", type=str, help="path to the gcode file")

    args = parser.parse_args()
    return args.filename

def count_layers(path):
    layers_found = 0
    with open(path, 'r') as f:
        for line in f:
            if LAYER_REGEX.match(line):
                layers_found += 1
    return layers_found

if __name__ == '__main__':
    filename = get_filename_from_commannd_line()
    backupname = f"{filename}.bak"

    # Count the layers
    layercount = count_layers(filename)

    # Create a backup
    copy_file(filename, backupname)

    # Process file
    with open(backupname, "r") as src:
        with open(filename, "w") as dest:
            for line in src:
                match = LAYER_REGEX.match(line)
                if match:
                    z_height = float(match.group(1))
                    z_layer = int(match.group(2)) + 1
                    percent = round(z_layer / layercount * 100, 1)
                    dest.write(f"M117 {z_layer}/{layercount} ({percent}%) {z_height}mm\n")  # noqa: E501
                dest.write(line)

    # Delete backup
    delete_file(backupname)


#!/usr/bin/env python3

""" Functions for backing up, restoring and snapshotting models """

import logging
import os
from datetime import datetime
from shutil import copyfile, copytree, rmtree

from lib import Serializer
from lib.utils import get_folder

logger = logging.getLogger(__name__)  # pylint: disable=invalid-name

class Backup():
    """ Holds information about model location and functions for backing up
        Restoring and Snapshotting models """
    def __init__(self, model_dir, model_name):
        logger.debug("Initializing %s: (model_dir: '%s', model_name: '%s')",
                     self.__class__.__name__, model_dir, model_name)
        self.model_dir = str(model_dir)
        self.model_name = model_name
        logger.debug("Initialized %s", self.__class__.__name__)

    def check_valid(self, filename, for_restore=False):
        """ Check if the passed in filename is valid for a backup operation """
        fullpath = os.path.join(self.model_dir, filename)
        if not filename.startswith(self.model_name):
            # Any filename that does not start with the model name are invalid
            # for all operations
            retval = False
        elif for_restore and filename.endswith(".bk"):
            # Only filenames ending in .bk are valid for restoring
            retval = True
        elif not for_restore and ((os.path.isfile(fullpath) and not filename.endswith(".bk")) or
                                  (os.path.isdir(fullpath) and
                                   filename == "{}_logs".format(self.model_name))):
            # Only filenames that do not end with .bk or folders that are the logs folder
            # are valid for backup
            retval = True
        else:
            retval = False
        logger.debug("'%s' valid for backup operation: %s", filename, retval)
        return retval

    @staticmethod
    def backup_model(fullpath):
        """ Backup Model File
            Fullpath should be the path to an h5.py file or a state.json file """
        backupfile = fullpath + ".bk"
        logger.verbose("Backing up: '%s' to '%s'", fullpath, backupfile)
        if os.path.exists(backupfile):
            os.remove(backupfile)
        if os.path.exists(fullpath):
            os.rename(fullpath, backupfile)

    def snapshot_models(self, iterations):
        """ Take a snapshot of the model at current state and back up """
        logger.info("Saving snapshot")
        snapshot_dir = "{}_snapshot_{}_iters".format(self.model_dir, iterations)

        if os.path.isdir(snapshot_dir):
            logger.debug("Removing previously existing snapshot folder: '%s'", snapshot_dir)
            rmtree(snapshot_dir)

        dst = str(get_folder(snapshot_dir))
        for filename in os.listdir(self.model_dir):
            if not self.check_valid(filename, for_restore=False):
                logger.debug("Not snapshotting file: '%s'", filename)
                continue
            srcfile = os.path.join(self.model_dir, filename)
            dstfile = os.path.join(dst, filename)
            copyfunc = copytree if os.path.isdir(srcfile) else copyfile
            logger.debug("Saving snapshot: '%s' > '%s'", srcfile, dstfile)
            copyfunc(srcfile, dstfile)
        logger.info("Saved snapshot")

    def restore(self):
        """ Restores a model from backup.
            This will place all existing models/logs into a folder named:
                - "<model_name>_archived_<timestamp>"
            Copy all .bk files to replace original files
            Remove logs from after the restore session_id from the logs folder """
        archive_dir = self.move_archived()
        self.restore_files()
        self.restore_logs(archive_dir)

    def move_archived(self):
        """ Move archived files to archived folder and return archived folder name """
        logger.info("Archiving existing model files...")
        now = datetime.now().strftime("%Y%m%d_%H%M%S")
        archive_dir = os.path.join(self.model_dir, "{}_archived_{}".format(self.model_name, now))
        os.mkdir(archive_dir)
        for filename in os.listdir(self.model_dir):
            if not self.check_valid(filename, for_restore=False):
                logger.debug("Not moving file to archived: '%s'", filename)
                continue
            logger.verbose("Moving '%s' to archived model folder: '%s'", filename, archive_dir)
            src = os.path.join(self.model_dir, filename)
            dst = os.path.join(archive_dir, filename)
            os.rename(src, dst)
        logger.verbose("Archived existing model files")
        return archive_dir

    def restore_files(self):
        """ Restore files from .bk """
        logger.info("Restoring models from backup...")
        for filename in os.listdir(self.model_dir):
            if not self.check_valid(filename, for_restore=True):
                logger.debug("Not restoring file: '%s'", filename)
                continue
            dstfile = os.path.splitext(filename)[0]
            logger.verbose("Restoring '%s' to '%s'", filename, dstfile)
            src = os.path.join(self.model_dir, filename)
            dst = os.path.join(self.model_dir, dstfile)
            copyfile(src, dst)
        logger.verbose("Restored models from backup")

    def restore_logs(self, archive_dir):
        """ Restore the log files since before archive """
        logger.info("Restoring Logs...")
        session_names = self.get_session_names()
        log_dirs = self.get_log_dirs(archive_dir, session_names)
        for log_dir in log_dirs:
            src = os.path.join(archive_dir, log_dir)
            dst = os.path.join(self.model_dir, log_dir)
            logger.verbose("Restoring logfile: %s", dst)
            copytree(src, dst)
        logger.verbose("Restored Logs")

    def get_session_names(self):
        """ Get the existing session names from state file """
        serializer = Serializer.get_serializer("json")
        state_file = os.path.join(self.model_dir,
                                  "{}_state.{}".format(self.model_name, serializer.ext))
        with open(state_file, "rb") as inp:
            state = serializer.unmarshal(inp.read().decode("utf-8"))
            session_names = ["session_{}".format(key)
                             for key in state["sessions"].keys()]
        logger.debug("Session to restore: %s", session_names)
        return session_names

    def get_log_dirs(self, archive_dir, session_names):
        """ Get the session logdir paths in the archive folder """
        archive_logs = os.path.join(archive_dir, "{}_logs".format(self.model_name))
        paths = [os.path.join(dirpath.replace(archive_dir, "")[1:], folder)
                 for dirpath, dirnames, _ in os.walk(archive_logs)
                 for folder in dirnames
                 if folder in session_names]
        logger.debug("log folders to restore: %s", paths)
        return paths


from fastapi import APIRouter,HTTPException
from pydantic import BaseModel

#El prefix funciona para definir el prefijo de la ruta que se está trabajando y refactorizar el código. El tag sirve para clasificar la ruta en la documentación SwaggerUI#
routes = APIRouter(prefix= "/users",
                   tags=["users"])

class User(BaseModel): #Esto es un BaseModel#
    name: str
    surname:str
    nickname:str

users_list = [
    User(name ="Cesar",surname="Villanueva",nickname="Lordchief"),
    User(name ="Tanjiro",surname="Kamado",nickname="FireDancer"),
    User(name ="Nezuko",surname="Kamado",nickname="GoodDemon"),
    User(name ="Zenitsu",surname="Agatsuma",nickname="BoostRain"),
    User(name ="Inosuke",surname="Hashibira",nickname="LordAnimal")
]

@routes.get("/")
async def usersForAPI():
    return users_list

@routes.get("/{name}") #Pasar parámetro por medio de una ruta #
async def user(name: str):
    usuario = filter(lambda user: user.name == name, users_list)
    try:
        return list(usuario)[0]
    except:
        return {"MessageError": "No se encontró el usuario"}

@routes.post("/", status_code=200)
async def user(user: User):

    if type(users_validate(user.name)) == User :
        raise HTTPException(409,{"Message": "El usuario ya existe"}) # Usamos el raise que para que suba la excepción al status code de la ruta#
    else:
        users_list.append(user)
        return {"Message": "El usuario se creó correctamente"}

@routes.put("/update")
async def changeInfoUser(user: User):
    found = False
    for index, saved_user in enumerate(users_list):
        if saved_user.name == user.name:
            users_list[index] = user
            found = True
            return {"Message": "Usuario actualizado con éxito"}
    if not found:
        return {"Message": "No se ha actualizado el usuario"}

@routes.delete("/delete/{name}")
async def deleteUser(name: str):
    found = False
    for index, user_saved in enumerate(users_list):
        if user_saved.name == name:
            del users_list[index]
            found = True
            return {"Message": "Usuario eliminado con éxito"}
    if not found:
         return {"Message": "No se ha eliminado el usuario"}

def users_validate(name: str):
    usuario = filter(lambda user: user.name == name, users_list)
    try:
        return list(usuario)[0]
    except:
        return {"MessageError": "No se encontró el usuario"}


from flask import *
from flask_login import login_user, logout_user, login_required, login_fresh, current_user
from .models import *
from .tableutil import *
from .main import getLang, getStrings
from werkzeug.security import check_password_hash
import os
import httpx
import subprocess

ota = Blueprint('ota', __name__)

@ota.route("/update", methods=["GET"])
@login_required
def update():
    if current_user.role == "admin":
        lang = getLang().lower()
        strings = getStrings(lang)
        r = httpx.get("https://api.github.com/repos/MeetPlan/MeetPlan/releases")
        print(r.json()[0])
        return render_template("ota.html", release=r.json()[0], strings=strings, role=current_user.role, name=current_user.first_name)
    else:
        abort(403)

@ota.route("/update", methods=["POST"])
@login_required
def updatePost():
    if current_user.role == "admin":
        lang = getLang().lower()
        strings = getStrings(lang)
        process = subprocess.Popen(["git", "pull"], stdout=subprocess.PIPE)
        output = process.communicate()[0].decode()
        print(output)
        if ('up to date' in output):
            print("UP TO DATE")
            flash(strings["UP_TO_DATE"])
            return redirect(url_for("ota.update"))
        elif ("Updating" in output):
            flash(strings["UPDATE_SUCCESSFUL"])
            return redirect(url_for("ota.update"))
        #return render_template("ota.html", release=r.json()[0], strings=strings)
    else:
        abort(403)