from logging import Logger
from typing import Dict, List, Set, Union

from faker import Faker
from selenium.common.exceptions import TimeoutException, WebDriverException
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.remote.webdriver import WebDriver
from selenium.webdriver.support import expected_conditions as ec
from selenium.webdriver.support.wait import WebDriverWait

from pylenium import webdriver_factory
from pylenium.a11y import PyleniumAxe
from pylenium.cdp import CDP
from pylenium.config import PyleniumConfig
from pylenium.element import Element, Elements
from pylenium.log import logger as log
from pylenium.performance import Performance
from pylenium.switch_to import SwitchTo
from pylenium.wait import PyleniumWait

class PyleniumShould:
    """A collection of conditions (aka expectations) for the Pylenium Driver including the browser, window, and more.

    Examples:
    ```
        py.should().have_title("QA at the Point")
    ```
    """

    def __init__(self, py: "Pylenium", timeout: int, ignored_exceptions: list = None):
        self._py = py
        self._wait: PyleniumWait = self._py.wait(timeout=timeout, use_py=True, ignored_exceptions=ignored_exceptions)

    def have_title(self, title: str) -> "Pylenium":
        """An expectation that the current title matches the given title.

        Args:
            title: The title to match.

        Returns:
            The current instance of Pylenium.

        Raises:
            `AssertionError` if the condition is not met within the timeout.
        """
        log.command("Pylenium.should().have_title(): `%s`", title)
        try:
            value = self._wait.until(ec.title_is(title))
        except TimeoutException:
            value = False
        if value:
            return self._py
        raise AssertionError(f"Expected Title: `{title}` - Actual Title: `{self._py.title()}`")

    def contain_title(self, string: str) -> "Pylenium":
        """An expectation that the current title contains the given string.

        Args:
            string: The case-sensitive string for the title to contain.

        Returns:
            The current instance of Pylenium.

        Raises:
            `AssertionError` if the condition is not met within the timeout.
        """
        log.command("Pylenium.should().contain_title(): `%s`", string)
        try:
            value = self._wait.until(ec.title_contains(string))
        except TimeoutException:
            value = False
        if value:
            return self._py
        raise AssertionError(f"Expected `{string}` to be in `{self._py.title()}`")

    def have_url(self, url: str) -> "Pylenium":
        """An expectation that the current URL matches the given url.

        Args:
            url: The url to match.

        Returns:
            The current instance of Pylenium.

        Raises:
            `AssertionError` if the condition is not met within the timeout.
        """
        log.command("Pylenium.should().have_url(): `%s`", url)
        try:
            value = self._wait.until(ec.url_to_be(url))
        except TimeoutException:
            value = False
        if value:
            return self._py
        raise AssertionError(f"Expected URL: `{url}` - Actual URL: `{self._py.url()}`")

    def contain_url(self, string: str) -> "Pylenium":
        """An expectation that the current URL contains the given string.

        Args:
            string: The case-sensitive string for the url to contain.

        Returns:
            The current instance of Pylenium.

        Raises:
            `AssertionError` if the condition is not met within the timeout.
        """
        log.command("Pylenium.should().contain_url(): `%s`", string)
        try:
            value = self._wait.until(ec.url_contains(string))
        except TimeoutException:
            value = False
        if value:
            return self._py
        raise AssertionError(f"Expected `{string}` to be in `{self._py.url()}`")

    def not_find(self, css: str) -> bool:
        """An expectation that there are no elements with the given CSS in the DOM.

        Args:
            css: The CSS selector.

        Returns:
            True if no elements are found.

        Raises:
            `AssertionError` if the condition is not met within the timeout.
        """
        log.command("Pylenium.should().not_find() elements with CSS: `%s`", css)
        try:
            self._wait.until_not(lambda x: x.find_element(By.CSS_SELECTOR, css))
            return True
        except TimeoutException:
            raise AssertionError(f"Found element with css: `{css}`")

    def not_findx(self, xpath: str) -> bool:
        """An expectation that there are no elements with the given XPATH in the DOM.

        Args:
            xpath: The XPATH selector.

        Returns:
            True if no elements are found.

        Raises:
            `AssertionError` if the condition is not met within the timeout.
        """
        log.command("Pylenium.should().not_findx() elements with XPATH: `%s`", xpath)
        try:
            self._wait.until_not(lambda x: x.find_element(By.XPATH, xpath))
            return True
        except TimeoutException:
            raise AssertionError(f"Found element with xpath: `{xpath}`")

    def not_contain(self, text: str) -> bool:
        """An expectation that there are no elements with the given text in the DOM.

        Args:
            text: The text to contain.

        Returns:
            True if no elements are found.

        Raises:
            `AssertionError` if the condition is not met within the timeout.
        """
        log.command("Pylenium.should().not_contain() any elements with the text: `%s`", text)
        try:
            self._wait.until_not(lambda x: x.find_element(By.XPATH, f'//*[contains(text(), "{text}")]'))
            return True
        except TimeoutException:
            raise AssertionError(f"Found element containing text: `{text}`")

class Pylenium:
    """The Pylenium API."""

    def __init__(self, config: PyleniumConfig):
        self.config = config
        log.setLevel(self.config.logging.pylog_level)
        self.fake = Faker()
        self.Keys = Keys
        self._webdriver = None
        self._wait = None

    def init_webdriver(self):
        """Initialize WebDriver using the Pylenium Config."""
        self._webdriver = webdriver_factory.build_from_config(self.config)
        caps = self._webdriver.capabilities
        try:
            log.debug(
                "Capabilities: browserName: %s, browserVersion: %s, platformName: %s, session_id: %s",
                caps["browserName"],
                caps["browserVersion"],
                caps["platformName"],
                self._webdriver.session_id,
            )
        except Exception:
            log.warning("webdriver.capabilities did not have a key that Pylenium was expecting. " "Is your driver executable the right version?")

        # Default instance of PyleniumWait
        self._wait = PyleniumWait(self, self._webdriver, self.config.driver.wait_time, ignored_exceptions=None)

        # Initial Browser Setup
        if self.config.driver.page_load_wait_time:
            self.set_page_load_timeout(self.config.driver.page_load_wait_time)

        if self.config.viewport.maximize:
            self.maximize_window()
        else:
            self.viewport(self.config.viewport.width, self.config.viewport.height, self.config.viewport.orientation)
        return self._webdriver

    @property
    def webdriver(self) -> WebDriver:
        """The current instance of Selenium's `WebDriver` API."""
        return self.init_webdriver() if self._webdriver is None else self._webdriver

    @property
    def log(self) -> Logger:
        """A simple and convenient logger to use.

        This is Pylenium's logger which can be useful, but it's recommended that you use your own.
        However, sometimes you need something simple and this logger may be enough.

        Levels:
            - CRITICAL = 50
            - ERROR    = 40
            - WARNING  = 30
            - USER     = 25 * (for YOU!)
            - INFO     = 20
            - COMMAND  = 15 * (default, used by Pylenium)
            - DEBUG    = 10

        Examples:
        ```
            # You can use the familiar debug, info, warning, error, and critical levels
            py.log.info("INFO MESSAGE")

            # Or use the custom levels (see Levels above)
            py.log.this("Hello, %s", "world")    # a message at the USER level
            py.log.command("Don't use this one") # a message at the COMMAND level
        ```

        Returns:
            Pylenium's instance of a Logger class
        """
        return log

    # region Sub APIs

    def should(self, timeout: int = 0, ignored_exceptions: list = None) -> PyleniumShould:
        """PyleniumdShould API: A collection of expectations for the current Pylenium Driver.

        Examples:
        ```
            py.should().contain_title("QA at the Point")
            py.should().have_url("https://qap.dev")
        ```
        """
        if timeout:
            wait_time = timeout
        else:
            wait_time = self.config.driver.wait_time
        return PyleniumShould(self, wait_time, ignored_exceptions)

    @property
    def axe(self) -> PyleniumAxe:
        """PyleniumAxe API: Accessibility (a11y) Auditing and Reporting.

        Examples:
        ```
            # Write automated tests against the report aXe returns!
            def test_zero_violations(py: Pylenium):
                py.visit("https://fake.website.com")
                report = py.axe.run()
                violation_count = len(report.violations)
                assert violation_count == 0, f"{violation_count} violation(s) found!"
        ```
        """
        return PyleniumAxe(self.webdriver)

    @property
    def performance(self) -> Performance:
        """Performance API: Pylenium's custom way of capturing web performnace metrics.

        Examples:
        ```
            # Store the entire WebPerformance object and log it
            perf = py.performance.get()
            log.info(perf.dict())

            # Get a single data point from WebPerformance
            tti = py.performance.get().time_to_interactive()
        ```
        """
        return Performance(self.webdriver)

    @property
    def cdp(self) -> CDP:
        """Chrome DevTools Protocol API.

        Examples:
        ```
            metrics = py.cdp.get_performance_metrics()
            >>> metrics

            {'metrics': [
                {'name': 'Timestamp', 'value': 425608.80694},
                {'name': 'AudioHandlers', 'value': 0},
                {'name': 'ThreadTime', 'value': 0.002074},
                ...
                ]
            }
        ```
        """
        return CDP(self.webdriver)

    # endregion

    # region METHODS

    def title(self) -> str:
        """Get the current page's title."""
        log.command("py.title() - Get the current page title")
        return self.webdriver.title

    def url(self) -> str:
        """Get the current page's URL."""
        log.command("py.url() - Get the current page URL")
        return self.webdriver.current_url

    # endregion

    # region NAVIGATION

    def visit(self, url: str) -> "Pylenium":
        """Navigate to the given URL.

        Returns:
            The current instance of Pylenium
        """
        log.command("py.visit() - Visit URL: `%s`", url)
        self.webdriver.get(url)
        return self

    def go(self, direction: str, number: int = 1) -> "Pylenium":
        """Navigate forward or back.

        Args:
            direction: `"forward"` or `"back"`
            number: default is 1, will go back or forward one page in history.

        Examples:
        ```
            py.go("back", 2) # will go back 2 pages in history.
            py.go("forward") # will go forward 1 page in history.
        ```

        Returns:
            The current instance of Pylenium
        """
        log.command("py.go() - Go %s %s in browser history", direction, number)
        if direction == "back":
            self.webdriver.execute_script("window.history.go(arguments[0])", number * -1)
        elif direction == "forward":
            self.webdriver.execute_script("window.history.go(arguments[0])", number)
        else:
            raise ValueError(f"direction was invalid. Must be `forward` or `back` but was {direction}")
        return self

    def reload(self) -> "Pylenium":
        """Reload (aka refresh) the current window.

        Returns:
            The current instance of Pylenium
        """
        log.command("py.reload() - Reload (refresh) the current page")
        self.webdriver.refresh()
        return self

    # endregion

    # region FIND ELEMENTS

    def contains(self, text: str, timeout: int = None) -> Element:
        """Get the DOM element containing the given text.

        * If `timeout=None` (default), use the default wait_time.
        * If `timeout > 0`, override the default wait_time.
        * If `timeout=0`, poll the DOM immediately without any waiting.

        Args:
            text: The text for the element to contain.
            timeout: The number of seconds to wait for this to succeed. Overrides the default wait_time.

        Returns:
            The first element that is found, even if multiple elements match the query.
        """
        log.command("py.contains() - Get the element containing the text: `%s`", text)
        locator = (By.XPATH, f'//*[contains(text(), "{text}")]')

        if timeout == 0:
            element = self.webdriver.find_element(*locator)
        else:
            element = self.wait(timeout).until(lambda x: x.find_element(*locator), f"Could not find element with the text `{text}`")
        return Element(self, element, locator)

    def get(self, css: str, timeout: int = None) -> Element:
        """Get the DOM element that matches the CSS selector.

        * If `timeout=None` (default), use the default wait_time.
        * If `timeout > 0`, override the default wait_time.
        * If `timeout=0`, poll the DOM immediately without any waiting.

        Args:
            css: The selector to use.
            timeout: The number of seconds to wait for this to succeed. Overrides the default wait_time.

        Returns:
            The first element that is found, even if multiple elements match the query.
        """
        log.command("py.get() - Find the element with CSS: `%s`", css)
        by = By.CSS_SELECTOR

        if timeout == 0:
            element = self.webdriver.find_element(by, css)
        else:
            element = self.wait(timeout).until(lambda x: x.find_element(by, css), f"Could not find element with the CSS `{css}`")
        return Element(self, element, locator=(by, css))

    def find(self, css: str, timeout: int = None) -> Elements:
        """Finds all DOM elements that match the CSS selector.

        * If `timeout=None (default)`, use the default wait_time.
        * If `timeout > 0`, override the default wait_time.
        * If `timeout=0`, poll the DOM immediately without any waiting.

        Args:
            css: The selector to use.
            timeout: The number of seconds to wait for this to succeed. Overrides the default wait_time.

        Returns:
            A list of the found elements.
        """
        by = By.CSS_SELECTOR
        log.command("py.find() - Find elements with CSS: `%s`", css)

        try:
            if timeout == 0:
                elements = self.webdriver.find_elements(by, css)
            else:
                elements = self.wait(timeout).until(lambda x: x.find_elements(by, css), f"Could not find any elements with the CSS `{css}`")
        except TimeoutException:
            elements = []
        return Elements(self, elements, locator=(by, css))

    def getx(self, xpath: str, timeout: int = None) -> Element:
        """Finds the DOM element that match the XPATH selector.

        * If `timeout=None` (default), use the default wait_time.
        * If `timeout > 0`, override the default wait_time.
        * If `timeout=0`, poll the DOM immediately without any waiting.

        Args:
            xpath: The selector to use.
            timeout: The number of seconds to wait for this to succeed. Overrides the default wait_time.

        Returns:
            The first element that is found, even if multiple elements match the query.
        """
        by = By.XPATH
        log.command("py.getx() - Find the element with xpath: `%s`", xpath)

        if timeout == 0:
            element = self.webdriver.find_element(by, xpath)
        else:
            element = self.wait(timeout).until(lambda x: x.find_element(by, xpath), f"Could not find an element with xpath: `{xpath}`")
        return Element(self, element, locator=(by, xpath))

    def findx(self, xpath: str, timeout: int = None) -> Elements:
        """Finds the DOM elements that match the XPATH selector.

        * If `timeout=None` (default), use the default wait_time.
        * If `timeout > 0`, override the default wait_time.
        * If `timeout=0`, poll the DOM immediately without any waiting.

        Args:
            xpath: The selector to use.
            timeout: The number of seconds to wait for this to succeed. Overrides the default wait_time.

        Returns:
            A list of the found elements.
        """
        by = By.XPATH
        log.command("py.findx() - Find elements with xpath: `%s`", xpath)

        try:
            if timeout == 0:
                elements = self.webdriver.find_elements(by, xpath)
            else:
                elements = self.wait(timeout).until(lambda x: x.find_elements(by, xpath), f"Could not find an element with xpath: `{xpath}`")
        except TimeoutException:
            elements = []
        return Elements(self, elements, locator=(by, xpath))

    # endregion

    # region UTILITIES

    def wait(self, timeout: int = None, use_py: bool = False, ignored_exceptions: List = None) -> Union[WebDriverWait, PyleniumWait]:
        """The Wait object with the given timeout in seconds.

        If `timeout=None` or `timeout=0`,
        return the default instance of wait, else return a new instance of WebDriverWait or PyleniumWait.

        Args:
            timeout: The number of seconds to wait for the condition.
            use_py: True for a PyleniumWait.
            ignored_exceptions: List of exceptions for the condition to ignore.

        Default Ignored Exceptions:
            * `NoSuchElementException`

        Examples:
        ```
            # Use the default wait_time in pylenium.json

            py.wait().until(lambda x: x.find_element(By.ID, "foo").get_attribute("style") == "display: block;")

            # Use a different timeout to control how long to wait for

            py.wait(5).until(lambda x: x.find_element(By.ID, "foo").is_displayed())
            py.wait(15, [NoSuchElementException, WebDriverException]).until(lambda x: x.find_element(By.ID, "foo"))
        ```
        """
        if timeout:  # if not None and greater than 0
            return self._wait.build(timeout, use_py, ignored_exceptions)
        return self._wait.build(self.config.driver.wait_time, use_py, ignored_exceptions)

    # endregion

    # region COOKIES

    def delete_cookie(self, name: str) -> None:
        """Deletes the cookie with the given name.

        Examples:
        ```
            py.delete_cookie("cookie_name")
        ```
        """
        log.command("py.delete_cookie() - Delete cookie named: `%s`", name)
        self.webdriver.delete_cookie(name)

    def delete_all_cookies(self) -> None:
        """Delete all cookies in the current session."""
        log.command("py.delete_all_cookies() - Delete all cookies")
        self.webdriver.delete_all_cookies()

    def get_cookie(self, name) -> Dict:
        """Get the cookie with the given name.

        Returns:
            The cookie if found, else None.

        Examples:
        ```
            cookie = py.get_cookie("cookie_name")
            assert cookie["name"] == name
        ```
        """
        log.command("py.get_cookie() - Get cookie with name: `%s`", name)
        return self.webdriver.get_cookie(name)

    def get_all_cookies(self) -> Set[Dict]:
        """Get all cookies.

        Returns:
            A set of cookies

        Examples:
        ```
            cookies = py.get_all_cookies()
            assert len(cookies) > 0
        ```
        """
        log.command("py.get_cookies() - Get all cookies")
        return self.webdriver.get_cookies()

    def set_cookie(self, cookie: Dict):
        """Adds a cookie to your current session.

        Args:
            cookie: A dictionary object, with required keys: "name" and "value";

                * optional keys: "path", "domain", "secure", "expiry"

        Examples:
        ```
            py.set_cookie({'name' : 'foo', 'value' : 'bar'})`
            py.set_cookie({'name' : 'foo', 'value' : 'bar', 'path' : '/'})
            py.set_cookie({'name' : 'foo', 'value' : 'bar', 'path' : '/', 'secure':True})
        ```
        """
        log.command("py.set_cookie() - Set a cookie with name=`%s` and value=`%s`", cookie["name"], cookie["value"])
        self.webdriver.add_cookie(cookie)

    # endregion

    # region BROWSER

    def execute_script(self, javascript: str, *args):
        """Executes javascript in the current window or frame.

        Args:
            javascript: The script string to execute.
            args: Any arguments to be used in the script.

        Returns:
            The value returned by the script.

        Examples:
        ```
            title = py.execute_script("return document.title;")
            webelement = py.execute_script("return document.getElementById(arguments[0]);", element_id)
        ```
        """
        log.command("py.execute_script() - Execute javascript into the Browser")
        return self.webdriver.execute_script(javascript, *args)

    def execute_async_script(self, javascript: str, *args):
        """Executes javascript asynchronously in the current window or frame.

        Args:
            javascript: The script string to execute.
            args: Any arguments to be used in the script.

        Returns:
            The value returned by the script.

        Examples:
        ```
            py.execute_async_script("return document.title;")
        ```
        """
        log.command("py.execute_async_script() - Execute javascript asynchronously into the Browser")
        return self.webdriver.execute_async_script(javascript, *args)

    def quit(self):
        """Quits the driver.

        Closes any and every window/tab associated with the current session.
        """
        log.command("py.quit() - Quit Pylenium and close all windows from the browser session")
        self.webdriver.quit()

    def screenshot(self, filename: str) -> str:
        """Take a screenshot of the current Window.

        Args:
            filename: the filepath including the filename and extension (like `.png`)

        Examples:
        ```
            py.screenshot("screenshots/home_page.png")
        ```
        """
        log.command("py.screenshot() - Save screenshot to: `%s`}", filename)
        self.webdriver.save_screenshot(filename)
        return filename

    def scroll_to(self, x, y) -> "Pylenium":
        """Scroll to a location on the page.

        Args:
            x: The number of pixels to scroll horizontally.
            y: The number of pixels to scroll vertically.

        Examples:
        ```
            # Scroll down 500 px
            py.scroll_to(0, 500)
        ```
        """
        log.command("py.scroll_to() - Scroll to (%s, %s)", x, y)
        js = "window.scrollTo(arguments[0], arguments[1]);"
        self.webdriver.execute_script(js, x, y)
        return self

    @property
    def switch_to(self) -> SwitchTo:
        """Switch between contexts like Windows, Tabs, and iFrames.

        Examples:
        ```
            py.switch_to.new_tab()
            py.switch_to.frame("iframe-id")
        ```
        """
        return SwitchTo(self)

    def maximize_window(self) -> "Pylenium":
        """Maximizes the current Window."""
        try:
            self.webdriver.maximize_window()
        except WebDriverException as e:
            log.error(f"Unable to maximize window: {e.msg}")
        return self

    def set_page_load_timeout(self, timeout: int) -> "Pylenium":
        """Set the amount of time to wait for a page load to complete before throwing an error.

        Args:
            timeout: The time to wait for.
        """
        self.webdriver.set_page_load_timeout(timeout)
        return self

    def viewport(self, width: int, height: int, orientation: str = "portrait") -> "Pylenium":
        """Control the size and orientation of the current context's browser window.

        Args:
            width: The width in pixels
            height: The height in pixels
            orientation: default is 'portrait'. Pass 'landscape' to reverse the width/height.

        Examples:
        ```
            py.viewport(1280, 800) # macbook-13 size
            py.viewport(1440, 900) # macbook-15 size
            py.viewport(375, 667)  # iPhone X size
        ```
        """
        log.command("py.viewport() - Set viewport to width=%s, height=%s, orientation=%s", width, height, orientation)
        if orientation == "portrait":
            self.webdriver.set_window_size(width, height)
        elif orientation == "landscape":
            self.webdriver.set_window_size(height, width)
        else:
            raise ValueError("Orientation must be `portrait` or `landscape`.")
        return self

    @property
    def window_handles(self) -> List[str]:
        """Returns the handles of all Windows in the current session.

        * This property is mainly used to switch between windows or tabs

        Examples:
        ```
            # assert that there are two windows - the main website and a new tab
            windows = py.window_handles
            assert len(windows) == 2

            # then switch to the new tab
            py.switch_to.window(name_or_handle=windows[1])
        ```
        """
        return self.webdriver.window_handles

    @property
    def window_size(self) -> Dict:
        """Gets the width and height of the current Window.

        Examples:
        ```
            size = py.window_size
            print(size["width"])
            print(size["height"])
        ```
        """
        return self.webdriver.get_window_size()

    # endregion


from tkinter import *
import tkinter as tk
from tkinter import messagebox as mb
import time

def setup():
    def change_colour(coord):
        btn = root.alphabet_list[coord[1]][coord[0]]
        if btn[1] == "":
            btn[0].config(bg = red)
            btn[1] = "R"
        elif btn[1] == "R":
            btn[0].config(bg = lblbg)
            btn[1] = ""

    root.title("ALPHA")
    root.iconbitmap("A.ico")
    root.attributes("-fullscreen", True)
    root.fullscreen = True
    root.bind("<F11>", toggle_fullscreen)
    root.bind("<Escape>", quit)
    root.width = root.winfo_screenwidth()
    root.height = root.winfo_screenheight()
    root.configure(bg = rootbg)
    root.bind("<BackSpace>", backspace)
    root.bind("<Delete>", backspace)
    root.bind("<Return>", submit)

    root.title_label = Label(root, text = "ALPHA", font = "Calibri 60 bold underline", bg = rootbg)
    root.title_label.place(x = root.width//2, y = 0, anchor = N)

    test_frame = Frame(root, bg = rootbg)
    test_frame.place(x = root.width//2, y = root.height*5//6, anchor = S)

    root.row_count = 0
    root.column_count = 0
    root.test_list = []
    for y in range(6): #len of word
        temp_l = []
        for x in range(5): #num of trials
            label = Label(test_frame, text = "", font = "Calirbi 32 bold", width = 1,
                          bg = lblbg, height = 1, relief = "ridge", bd = 1)
            label.grid(row = y, column = x, padx = 2, pady = 4, ipadx = 20, ipady = 12)
            temp_l.append([label, ""])
        root.test_list.append(temp_l)
    submit_btn = Button(test_frame, text = "Submit!", font = "Calirbi 20 bold", command = confirm_pressed)
    submit_btn.grid(row = 6, column = 0, columnspan = 5, pady = 20, ipadx = 10, ipady = 10)

    alphabet_frame = Frame(root, bg = rootbg)
    alphabet_frame.place(x = root.width//2, y = root.height, anchor = S)

    root.alphabet_list = []
    for y in range(2):
        temp_l = []
        for x in range(13):
            letter = chr(y * 13 + x + 65)
            root.bind(chr(y * 13 + x + 97), key_pressed)
            root.bind(chr(y * 13 + x + 65), key_pressed)
            lbl = Button(alphabet_frame, text = letter, width = 2, height = 1, font = "Calibri 20 bold",
                         bg = lblbg, bd = 1, command = lambda coord = [x, y]: change_colour(coord))
            lbl.grid(row = y, column = x, padx = 4, pady = 8, ipadx = 3, ipady = 3)
            temp_l.append([lbl, ""])
        root.alphabet_list.append(temp_l)

def reset(option):
    root.row_count = 0
    root.column_count = 0
    for y in root.test_list:
        for x in y:
            x[0].config(text = "", bg = lblbg)
            x[1] = ""
            animation()
    for y in root.alphabet_list:
        for x in y:
            x[0].config(bg = lblbg)
            x[1] = ""
            animation()
    if option == "GG":
        for i in range(len(root.test_list[5])):
            lbl = root.test_list[5][i]
            lbl[0].config(text = root.chosen_word[i].upper(), bg = green)
            animation()

def choose_word():
    def check_word(cw_window, ent):
        root.chosen_word = ent.get().lower()
        valid = False
        with open("5LetterWords.txt", 'r') as file:
            five_letter_words = file.readlines()
        if len(root.chosen_word) == 5 and root.chosen_word + "\n" in five_letter_words:
            valid = True
            cw_window.destroy()
            root.deiconify()
        else:
            for fg in ("Red", "Black"):
                ent.config(fg = fg)
                time.sleep(0.2)
                cw_window.update()

    def caps(event):
        text = cw_window.ent.get().upper()
        cw_window.ent.delete(0, END)
        cw_window.ent.insert(0, text)

    cw_window = Toplevel()
    cw_window.iconbitmap("A.ico")
    x = 360
    y = 80
    cw_window.geometry("{}x{}+{}+{}".format(x, y,
                                           root.width // 2 - x // 2,
                                           root.height // 2 - y // 2 - 50))
    cw_window.focus()
    cw_window.resizable(False, False)
    cw_window.grab_set()
    cw_window.attributes("-topmost", True)
    #root.iconify()
    cw_window.ent = tk.Entry(cw_window, font = "Calibri 26", width = 10, justify = "center")
    cw_window.ent.grid(row = 0, column = 0, padx = 10, pady = 10)
    cw_window.ent.bind("<KeyRelease>", caps)
    cw_window.ent.icursor(0)
    btn = Button(cw_window, text = "OK!", bg = rootbg, width = 6, font = "Calibri 16",
                 command = lambda: check_word(cw_window, cw_window.ent))
    btn.grid(row = 0, column = 1, padx = 10, pady = 10)

def highlight(option, i):
    letter = root.test_list[root.row_count][i][0].cget("text")
    n = ord(letter) - 65
    x = n % 13
    y = n // 13
    if option == "G": colour = green
    elif option == "A": colour = amber
    else: colour = grey
    root.test_list[root.row_count][i][0].config(bg = colour)
    root.alphabet_list[y][x][0].config(bg = colour)
    root.alphabet_list[y][x][1] = option

def animation():
    time.sleep(0.02)
    root.update()

def comparison(guess):
    if guess == root.chosen_word:
        for i in range(5):
            highlight("G", i)
            animation()
        return True
    else:
        for i in range(5):
            chosen_occ = 0
            for j in range(5):
                if root.chosen_word[i] == root.chosen_word[j]:
                    chosen_occ += 1
            if chosen_occ != 0:
                guess_occ = 0
                for j in range(5):
                    if guess[j] == root.chosen_word[j] and guess[j] == root.chosen_word[i]:
                        guess_occ += 1
                        root.test_list[root.row_count][j][1] = "G"
                for j in range(5):
                    if guess_occ == chosen_occ:
                        break
                    elif root.test_list[root.row_count][j][1] != "G":
                        if root.chosen_word[i] == guess[j]:
                            root.test_list[root.row_count][j][1] = "A"
                            guess_occ += 1
        for i in range(5):
            letter = root.test_list[root.row_count][i]
            if letter[1] == "":
                root.test_list[root.row_count][i][1] = "B"
        for i in range(5):
            option = root.test_list[root.row_count][i][1]
            highlight(option, i)
            animation()
        return False

def submit(event):
    confirm_pressed()

def confirm_pressed():
    if root.column_count == 5:
        guess = ""
        for i in range(5):
            guess += root.test_list[root.row_count][i][0].cget("text").lower()
        with open("5letterWords.txt", 'r') as file:
            five_letter_dict = file.readlines()
        if guess + "\n" in five_letter_dict:
            correct = comparison(guess)
            if correct:
                #update_score()
                ans = mb.askyesno("Restart", "Do you want to restart?")
                if ans:
                    reset("")
                    choose_word()
                else: root.destroy()
            else:
                root.row_count += 1
                if root.row_count < 6:
                    root.column_count = 0
                else:
                    reset("GG")
                    ans = mb.askyesno("Game over", "Do you want to restart?")
                    if ans:
                        for x in root.test_list[5]:
                            x[0].config(text = "", bg = lblbg)
                            animation()
                        choose_word()
                    else: root.destroy()
        else:
            error()
    else:
        error()

def key_pressed(event):
    if root.column_count < 5:
        letter = event.char.upper()
        root.test_list[root.row_count][root.column_count][0].config(text = letter)
        root.column_count += 1

def backspace(event):
    if root.column_count != 0 :
        root.column_count -= 1
        root.test_list[root.row_count][root.column_count][0].config(text = "")

def error():
    for fg in ("Red", "Black"):
        for lbl in root.test_list[root.row_count]:
            lbl[0].config(fg = fg)
        time.sleep(0.2)
        root.update()

def toggle_fullscreen(event):
    if root.fullscreen:
        root.attributes("-fullscreen", False)
        root.geometry("{}x{}".format(root.width, root.height))
        root.fullscreen = False
    else:
        root.attributes("-fullscreen", True)
        root.fullscreen = True

root = tk.Tk()
rootbg = "#FFFFFF"
lblbg = "#DDDDDD"
green = "#3EC525"
amber = "#F7CE0A"
grey = "#666666"
red = "#CC3232"

setup()
choose_word()

root.mainloop()


n,k = map(int,input().split())
def solve(n,k):
    fibo = [0,1]
    sum = 1
    for i in range(0,2*k):
        fibo.append(fibo[1]+fibo[0])
        fibo.pop(0)
        if i % 2 == 0:
            sum+=fibo[1]
    return sum*n%(10**9+7)
print(solve(n,k))


# Загружаем задачи, комментарии и сопутствующую информацию из АПИ ПФ

import json
import os
import requests
import xmltodict
from lxml import etree, objectify
import csv
from datetime import datetime, timedelta

from lib import format_phone, l
from hide_data import USR_Tocken, PSR_Tocken, PF_ACCOUNT, DEPARTMENTS, OFFICETOWNS, DOMAIN
from api2backup import reload_all, api_load_from_list

URL = "https://apiru.planfix.ru/xml"
BACKUP_DIRECTORY = 'current'
PF_DATA = '../data'
DOCFLOW = '../../docflow/data'
PF_HEADER = {"Accept": 'application/xml', "Content-Type": 'application/xml'}
RELOAD_ALL_FROM_API = False
HAS_TASK_LIMIT = True
TASKS_FROM = 83000
TASKS_TO = 83020

def create_record(id, model, sources):
    """  Создаем запись БД flectra  """
    record = objectify.Element('record', id=id, model=model)
    fields = []
    i = -1
    for source in sources:
        if (str(source).endswith('_id') or str(source).endswith('_uid')) and sources[source]:
            i += 1
            fields.append(objectify.SubElement(record, 'field', name=source, ref=sources[source]))
        elif str(source).endswith('_ids') and str(type(sources[source])).find('list') > -1:
            i += 1
            parts = []
            for part in sources[source]:
                if part != '':
                    parts.append(part)
            if len(parts):
                fields.append(objectify.SubElement(
                    record,
                    'field',
                    name=source,
                    eval="[(6, 0, [ ref('" + "'), ref('".join(parts) + "')])]"))
            else:
                continue
        elif source and sources[source]:
            i += 1
            fields.append(objectify.SubElement(record, 'field', name=source))
            fields[i]._setText(str(sources[source]))
        else:
            pass
            # print(id, source, sources[source])
    return record

def dict_key(key, test_dict, process_index=False):
    """  Проверяет наличие test_dict[key]. Если есть - возвращает key, если нет - ''
    process_index=True - если есть, то возвращает строку, состояющую только из цифр
    """
    if test_dict.get(key ,None):
        if process_index:
            return processes4flectra[key]['name'].split('(')[1].split(')')[0]
        else:
            return key
    else:
        return ''

def check_parent_id(tid, tdict):
    if tid:
        if tdict.get(int(tdict[int(tid)]['parent']['id']), None):
            if int(tid) > int(tdict[int(tid)]['parent']['id']):
                return int(tdict[int(tid)]['parent']['id'])
            else:
                return None
        else:
            return None
    else:
        return None

def printProgressBar (iteration, total, prefix = '', suffix = '', decimals = 1, length = 100, fill = '█'):
    """
    Call in a loop to create terminal progress bar
    @params:
        iteration   - Required  : current iteration (Int)
        total       - Required  : total iterations (Int)
        prefix      - Optional  : prefix string (Str)
        suffix      - Optional  : suffix string (Str)
        decimals    - Optional  : positive number of decimals in percent complete (Int)
        length      - Optional  : character length of bar (Int)
        fill        - Optional  : bar fill character (Str)
    """
    if total == 0:
        total = 1
    percent = ("{0:." + str(decimals) + "f}").format(100 * (iteration / float(total)))
    filledLength = int(length * iteration // total)
    bar = fill * filledLength + '-' * (length - filledLength)
    print('\r%s |%s| %s%% %s' % (prefix, bar, percent, suffix), end = '\r')
    # Print New Line on Complete
    if iteration == total:
        print()

def chk_users(id):
    if int(id) in users_ids:
        return str(id)
    else:
        return '5309784'

if __name__ == "__main__":
    # Перезагружаем всё из АПИ в файлы
    if RELOAD_ALL_FROM_API:
        request_count = 0
        limit_overflow = False
        reload_all()

    # backup2hr_pf():
    # Загружаем список групп и пустой список членов для каждой группы
    with open(os.path.join(BACKUP_DIRECTORY, 'usergroups_full.json'), 'r') as read_file:
        groups = json.load(read_file)
    groups_id2names = {}
    groups_id2members = {}
    for group in groups:
        groups_id2names[group] = groups[group]['name']
        groups_id2members[group] = []

    # Загружаем данные юзеров из файла
    with open(os.path.join(BACKUP_DIRECTORY, 'users_full.json'), 'r') as read_file:
        users_loaded = json.load(read_file)
    users_list = list(users_loaded.values())
    employees2mails = {}
    users2mails = {}
    users2groups = {}
    # Переводим в формат users_db[email], заполняем БД слияния контактов и юзеров:
    #                                                   employees2mails[e-mail] и users2mails[e-mail]
    for user in users_list:
        if user.get('email', None) or user.get('name', '') == 'робот ПланФикса':
            if user.get('name', '') == 'робот ПланФикса':
                user_email = 'robot_pf@odoo.' + DOMAIN
            else:
                if user['email'].find('_old@'+ DOMAIN) > -1:
                    user_email = user['email'].replace('_old@' + DOMAIN, '@odoo.' + DOMAIN)
                elif user['email'].find('@'+ DOMAIN) > -1:
                    user_email = user['email'].replace('@' + DOMAIN, '@odoo.' + DOMAIN)
                else:
                    user_email = user['email'].replace('_old@','@').split('@')[0] + '@odoo.' + DOMAIN
            if user['midName']:
                employees2mails[user_email] = {'name': str(user['lastName']) + ' ' + str(user['name']) + ' '
                                                    + str(user['midName'])}
                users2mails[user_email] = {'name': str(user['lastName']) + ' ' + str(user['name']) + ' '
                                                    + str(user['midName'])}
                users2groups[user_email] = set()
            else:
                employees2mails[user_email] = {'name': str(user['lastName']) + ' ' + str(user['name'])}
                users2mails[user_email] = {'name': str(user['lastName']) + ' ' + str(user['name'])}
                users2groups[user_email] = set()
            users2mails[user_email]['login'] = user_email
            employees2mails[user_email]['work_email'] = user_email
            if user.get('phones', None):
                if user['phones']:
                    if str(type(user['phones']['phone'])).find('list') > -1:
                        employees2mails[user_email]['mobile_phone'] = \
                            str(format_phone(user['phones']['phone'][0]['number']))
                    else:
                        employees2mails[user_email]['mobile_phone'] = str(format_phone(user['phones']['phone']['number']))
            if user.get('sex', None):
                employees2mails[user_email]['gender'] = str(user['sex']).lower()
            if user.get('id', None):
                users2mails[user_email]['id_pf'] = str(user['id'])
            if user.get('general', None):
                users2mails[user_email]['general_user_pf'] = str(user['general'])
            if user.get('active', None):
                users2mails[user_email]['active'] = str(user['status'] == 'ACTIVE')
            if user.get('userGroups', None):
                if str(type(user['userGroups']['userGroup'])).find('list') > -1:
                    for group in user['userGroups']['userGroup']:
                        groups_id2members[group['id']] += [user_email]
                        users2groups[user_email].add(group['id'])
                else:
                    groups_id2members[user['userGroups']['userGroup']['id']] += [user_email]
        else:
            print(str(user['id']), str(user['lastName']), str(user['name']), str(user['midName']), ' - нет e-mail')

    # Загружаем данные сотрудников из файла
    with open(os.path.join(BACKUP_DIRECTORY, 'contacts_' + PF_ACCOUNT + '.json'), 'r') as read_file:
        contacts_loaded = json.load(read_file)
    contacts_list = list(contacts_loaded.values())
    contacts_db = {}
    # Переводим в формат users_db[email], заполняем БД сляния контактов и юзеров employees2mails[e-mail]
    for contact in contacts_list:
        mail = ''
        for field in contact['customData']['customValue']:
            if field['field']['name'] == 'Корпоративная почта':
                if field['text'].find('_old@' + DOMAIN) > -1:
                    mail = field['text'].replace('_old@' + DOMAIN, '@odoo.' + DOMAIN)
                elif field['text'].find('@' + DOMAIN) > -1:
                    mail = field['text'].replace('@' +DOMAIN, '@odoo.' + DOMAIN)
                else:
                    mail = field['text'].replace('_old@','@').split('@')[0] + '@odoo.' + DOMAIN
        if mail:
            contacts_db[mail] = contact
            if not employees2mails.get(mail, None):
                employees2mails[mail] = {}
                users2mails[mail] = {'login': mail}
                employees2mails[mail]['work_email'] = mail
            users2mails[mail]['general_contact_pf'] = contact['general']
            users2mails[mail]['userid_pf'] = contact['userid']
            for field in contact['customData']['customValue']:
                if field['field']['name'] == 'ФИО':
                    if employees2mails[mail].get('name', None):
                        if len(str(field['text']).strip().split(' ')) > \
                              len(str(employees2mails[mail]['name']).strip().split(' ')):
                            employees2mails[mail]['name'] = field['text']
                            users2mails[mail]['name'] = field['text']
                    else:
                        employees2mails[mail]['name'] = field['text']
                        users2mails[mail]['name'] = field['text']
                #if field['field']['name'] == 'Город':
                #if field['field']['name'] == 'д/р сотрудника':
                if field['field']['name'] == 'Статус':
                    employees2mails[mail]['status'] = field['text']
                    users2mails[mail]['active'] = str(field['text'] == 'Активный')
                    employees2mails[mail]['active'] = str(field['text'] == 'Активный')
                if field['field']['name'] == 'Подразделение (отдел)' and field['text']:
                    if field['text'] == 'ПродБлок':
                        field['text'] = 'Продуктовый блок'
                    employees2mails[mail]['department_id'] = 'department_' +  str(DEPARTMENTS.index(str(field['text'])))
        else:
            print(str(contact['id']), str(contact['general']), ' - нет e-mail')

    i = 1
    for mail in users2mails:
        if users2mails[mail].get('id_pf', None):
            users2mails[mail]['id'] = 'user_' + users2mails[mail]['id_pf']
        elif users2mails[mail].get('userid_pf', None):
            users2mails[mail]['id'] = 'user_' + users2mails[mail]['userid_pf']
        else:
            users2mails[mail]['id'] = 'user_' + str(i)
            i += 1
        if users2groups.get(mail, None):
            if len(users2groups[mail]):
                users2mails[mail]['groups_id:id'] = ''
                for group in users2groups[mail]:
                    users2mails[mail]['groups_id:id'] += 'docflow.' + group + ','
                users2mails[mail]['groups_id:id'] = users2mails[mail]['groups_id:id'].strip(',')

    i = 1
    employees4flectra = {}
    employees4backup = {}
    for mail in employees2mails:
        if users2mails.get(mail, None):
            if users2mails[mail].get('id_pf', None):
                employees4flectra['empl_' + users2mails[mail]['id_pf']] = employees2mails[mail]
                employees4flectra['empl_' + users2mails[mail]['id_pf']]['id_pf'] = users2mails[mail]['id_pf']
                employees4backup[users2mails[mail]['id_pf']] = employees2mails[mail]
                employees4backup[users2mails[mail]['id_pf']]['id_pf'] = users2mails[mail]['id_pf']
            elif users2mails[mail].get('userid_pf', None):
                employees4flectra['empl_' + users2mails[mail]['userid_pf']] = employees2mails[mail]
                employees4flectra['empl_' + users2mails[mail]['userid_pf']]['id_pf'] = users2mails[mail]['userid_pf']
                employees4backup[users2mails[mail]['userid_pf']] = employees2mails[mail]
                employees4backup[users2mails[mail]['userid_pf']]['id_pf'] = users2mails[mail]['userid_pf']
            else:
                employees4flectra['empl_' + str(i)] = employees2mails[mail]
                employees4flectra['empl_' + str(i)]['id_pf'] = str(i)
                employees4backup[str(i)] = employees2mails[mail]
                employees4backup[str(i)]['id_pf'] = str(i)
                i += 1
    with open(os.path.join(BACKUP_DIRECTORY, 'emloyees_' + PF_ACCOUNT + '.json'), 'w') as write_file:
            json.dump(employees4backup, write_file, ensure_ascii=False)

    # Заголовок xml
    flectra_root = objectify.Element('flectra')
    flectra_data = objectify.SubElement(flectra_root, 'data')

    # Базовые департаменты, ГД, ЗГД
    record = create_record('department_gd', 'hr.department', {'name': 'Генеральный директор'})
    flectra_data.append(record)
    record = create_record('department_zgd', 'hr.department', {
        'name': 'Заместитель генерального директора',
        'parent_id': 'department_gd',
    })
    flectra_data.append(record)
    for i, department in enumerate(DEPARTMENTS):  # DEPARTMENTS - list с именами департаментов
        record = create_record('department_' + str(i), 'hr.department', {
            'name': department,
            'parent_id': 'department_zgd',
        })
        flectra_data.append(record)

    # Справочник офисов-городов
    for i, officetown in enumerate(OFFICETOWNS):   # OFFICETOWNS - list с именами офисегородов
        record = create_record('officetown_' + str(i), 'hr_pf.officetown', {'name': officetown})
        flectra_data.append(record)

    # Группы доступа, сначала корневая группа "Планфикс" в .xml
    record = create_record('category_pf', 'ir.module.category', {'name': 'ПланФикс'})
    flectra_data.append(record)
    groups4csv = {}
    for groups_id2name in groups_id2names:
        groups4csv[groups_id2name] = {'id': str(groups_id2name), 'name': groups_id2names[groups_id2name]}
        record = create_record(
            'docflow.' + str(groups_id2name),
            'res.groups',
            {
                'category_id': 'category_pf',
                'id_from_pf': str(groups_id2name)}
        )
        flectra_data.append(record)
    # Полученные группы в .csv
    with open(os.path.join(DOCFLOW, 'res.groups.csv'), 'w', newline='') as csvfile:
        writer = csv.DictWriter(csvfile, fieldnames=['id', 'name'])
        writer.writeheader()
        for group in groups4csv:
            writer.writerow(groups4csv[group])

    # Юзеры в .csv в два файла-этапа загрузки
    users4csv = {}
    for mail in users2mails:
        users4csv[mail] = {}
        for field in users2mails[mail].keys():
            if field in ['id', 'name', 'login', 'active']:
                users4csv[mail][field] = users2mails[mail][field]
    with open(os.path.join(DOCFLOW, 'res.users.csv'), 'w', newline='') as csvfile:
        writer = csv.DictWriter(csvfile, fieldnames=['id', 'name', 'login', 'active'])
        writer.writeheader()
        for mail in users2mails:
            writer.writerow(users4csv[mail])
    users4csv = {}
    for mail in users2mails:
        users4csv[mail] = {}
        for field in users2mails[mail].keys():
            if field in ['id', 'id_pf', 'general_user_pf', 'general_contact_pf', 'userid_pf', 'groups_id:id']:
                if field == 'id':
                    users4csv[mail][field] = 'docflow.' + users2mails[mail][field]
                else:
                    users4csv[mail][field] = users2mails[mail][field]
    with open(os.path.join(PF_DATA, 'res.users.csv'), 'w', newline='') as csvfile:
        writer = csv.DictWriter(csvfile, fieldnames=['id', 'id_pf', 'general_user_pf', 'general_contact_pf',
                                                     'userid_pf', 'groups_id:id'])
        writer.writeheader()
        for mail in users2mails:
            writer.writerow(users4csv[mail])

    # Контакты сотрудников в res.partners.csv + указываем что это не customer не supplier
    partners4csv = {}
    for mail in users2mails:
        partners4csv[mail] = {}
        for field in users2mails[mail].keys():
            if field in ['id', 'is_company', 'customer', 'supplier']:
                if field == 'id':
                    partners4csv[mail][field] = 'docflow.' + users2mails[mail][field] + '_res_partner'
                    partners4csv[mail]['email'] = mail
                    partners4csv[mail]['is_company'] = False
                    partners4csv[mail]['customer'] = False
                    partners4csv[mail]['supplier'] = False
    with open(os.path.join(PF_DATA, 'res.partner.csv'), 'w', newline='') as csvfile:
        writer = csv.DictWriter(csvfile, fieldnames=['id', 'email', 'is_company', 'customer', 'supplier'])
        writer.writeheader()
        for mail in users2mails:
            writer.writerow(partners4csv[mail])

    # Сотрудники в .xml
    for emloyee in employees4flectra:
        record = create_record(emloyee, 'hr.employee', employees4flectra[emloyee])
        flectra_data.append(record)

    # удаляем все lxml аннотации.
    objectify.deannotate(flectra_root)
    etree.cleanup_namespaces(flectra_root)

    # конвертируем все в привычную нам xml структуру.
    obj_xml = etree.tostring(flectra_root,
                             pretty_print=True,
                             xml_declaration=True,
                             encoding='UTF-8'
                             )

    try:
        with open(os.path.join(PF_DATA, 'hr_pf_data.xml'), "wb") as xml_writer:
            xml_writer.write(obj_xml)
    except IOError:
        pass

    #  backup2project()

    # Загружаем бэкап задач из выгрузки всех задач (task.getMulti скорректированной task.get) через АПИ ПФ
    tasks_full = {}
    with open(os.path.join(BACKUP_DIRECTORY, 'tasks_full.json'), 'r') as read_file:
        tasks_full_str = json.load(read_file)
    for task in tasks_full_str:
        tasks_full[int(task)] = tasks_full_str[task]
    print('Из сохраненных полных (task.getMulti):', len(tasks_full))

    # id загруженных в модуле hr_pf юзеров и сотрудников
    with open(os.path.join(BACKUP_DIRECTORY, 'users_full.json'), 'r') as read_file:
        users_loaded = json.load(read_file)
    users_ids = tuple([int(x) for x in users_loaded.keys()])

    # Процессы (project.project)
    with open(os.path.join(BACKUP_DIRECTORY, 'processes_full.json'), 'r') as read_file:
        processes = json.load(read_file)
    processes4flectra = {}
    for process in processes:
        processes4flectra['pr_' + str(process)] = {
            'name': processes[process]['name'],
            'id_pf': process,
        }

    # Статусы
    with open(os.path.join(BACKUP_DIRECTORY, 'statuses_flectra.json'), 'r') as read_file:
        statuses4flectra = json.load(read_file)

    # Шаблоны задач
    with open(os.path.join(BACKUP_DIRECTORY, 'tasktemplates_full.json'), 'r') as read_file:
        tasktemplates = json.load(read_file)
    tasktemplates4flectra = {}
    for tasktemplate in tasktemplates:
        tasktemplates4flectra['tt_' + str(tasktemplate)] = {
            'name': tasktemplates[tasktemplate]['title'],
            'id_pf': tasktemplates[tasktemplate]['id'],
            'id_pf_general': tasktemplates[tasktemplate]['general']
        }

    # Загружаем данные по задачам из файла, полученного из АПИ
    tasks_from_json = []
    if HAS_TASK_LIMIT:
        tasks_from_json_ids = tuple(sorted(list(tasks_full.keys()))[TASKS_FROM:TASKS_TO])
    else:
        tasks_from_json_ids = tuple(sorted(list(tasks_full.keys())))
    for task in tasks_from_json_ids:
        tasks_from_json.append(tasks_full[task])

    # Названия полей и id загружаем в процессе
    template_fields = {}
    fields_fills = {}
    stages = {}
    tasks = {}
    tasks_from_json_dict = {}
    for task in tasks_from_json:
        if task['type'] == 'task':
            tasks_from_json_dict[int(task['id'])] = task
            tasks['task_' + task['id']] = {
                'id_pf': task['id'],
                'id_pf_general': task['general'],
                'name': task['title'],
                'description': task['description'],
                'project_id': dict_key('pr_' + task['statusSet'], processes4flectra),
                'stage_id': dict_key(
                    'st_' + task['status'] + dict_key('pr_' + task['statusSet'], processes4flectra, process_index=True),
                    statuses4flectra),
                'create_date': str(task['beginDateTime']).replace('-', '.') + ':00',
                'user_id': 'docflow.user_' + chk_users(task['owner']['id']),
                'employee_id':  'pf_data.empl_' + chk_users(task['owner']['id']),
                'tasktemplate_id': dict_key('tt_' + task['template']['id'], tasktemplates4flectra),
            }
            if task.get('importance', None):
                if task['importance'] == 'HIGH':
                    tasks['task_' + task['id']]['priority'] = '0'
                else:
                    tasks['task_' + task['id']]['priority'] = '2'
            else:
                tasks['task_' + task['id']]['priority'] = '2'
            if task.get('startTime', None):
                if len(str(task['startTime'])) < 16:
                    tasks['task_' + task['id']]['date_start'] = \
                        str(task['startTime']).strip(' ')[:10].replace('-', '.') + ' 09:00:00'
                else:
                    tasks['task_' + task['id']]['date_start'] = str(task['startTime']).replace('-', '.') + ':00'
            if task.get('endTime', None):
                if len(str(task['endTime'])) < 16:
                    tasks['task_' + task['id']]['date_deadline'] = \
                        str(task['endTime']).strip(' ')[:10].replace('-', '.') + ' 09:00:00'
                else:
                    tasks['task_' + task['id']]['date_deadline'] = str(task['endTime']).replace('-', '.') + ':00'
            if task.get('duration', None) and task.get('durationUnit', None):
                if task['durationUnit'] == 0:
                    tasks['task_' + task['id']]['planned_hours'] = str(int(task['duration'])*60)
                elif task['durationUnit'] == 1:
                    tasks['task_' + task['id']]['planned_hours'] = task['duration']
                elif task['durationUnit'] == 2:
                    tasks['task_' + task['id']]['planned_hours'] = str(int(task['duration'])/24)

    for task in tasks_from_json:
        if task['type'] == 'task':
            down_recursion = True
            project_ids = [int(task['id'])]
            current_id = task['id']
            while down_recursion:
                parent_id = check_parent_id(current_id, tasks_from_json_dict)
                if parent_id:
                    tasks['task_' + task['id']]['parent_id'] = 'task_' + str(parent_id)
                    project_ids.append(parent_id)
                    current_id = parent_id
                else:
                    down_recursion = False
            project_ids.sort()
            for task_id in project_ids:
                tasks['task_' + str(task_id)]['project_id'] = tasks['task_' + str(project_ids[0])]['project_id']
            if task.get('customData', None):
                if task['customData'].get('customValue', None):
                    if str(type(task['customData']['customValue'])).find('list') > -1:
                        for field in task['customData']['customValue']:
                            if not template_fields.get('tpl_field_' + field['field']['id']):
                                template_fields['tpl_field_' + field['field']['id']] = {
                                    'name': field['field']['name'],
                                    'id_pf': field['field']['id']
                                }
                    else:
                        field = task['customData']['customValue']
                        if not template_fields.get('tpl_field_' + field['field']['id']):
                            template_fields['tpl_field_' + field['field']['id']] = {
                                'name': field['field']['name'],
                                'id_pf': field['field']['id']
                            }
    for task in tasks_from_json:
        if task['type'] == 'task':
            if task.get('customData', None):
                if task['customData'].get('customValue', None):
                    if str(type(task['customData']['customValue'])).find('list') > -1:
                        for field in task['customData']['customValue']:
                            fields_fills[task['id'] + '_' + field['field']['id']] = {
                                'task_id': dict_key('task_' +  task['id'], tasks),
                                'template_field_name_id': dict_key('tpl_field_' + field['field']['id'], template_fields),
                                'text': field.get('text',''),
                                'value': field.get('value',''),
                            }
                    else:
                        field = task['customData']['customValue']
                        fields_fills[task['id'] + '_' + field['field']['id']] = {
                            'task_id': 'task_' + task['id'],
                            'template_field_name_id': 'tpl_field_' + field['field']['id'],
                            'text': field.get('text', ''),
                            'value': field.get('value', ''),
                        }

    # Заголовок xml
    flectra_root = objectify.Element('flectra')
    flectra_data = objectify.SubElement(flectra_root, 'data')

    # Процессы (project.project)
    for process in processes4flectra:
        record = create_record(process, 'project.project', processes4flectra[process])
        flectra_data.append(record)

    # Статусы (project.task.type)
    for status in statuses4flectra:
        record = create_record(status, 'project.task.type', statuses4flectra[status])
        flectra_data.append(record)

    # Шаблоны полей (docflow.field.template)
    for template_field in template_fields:
        record = create_record(str(template_field), 'docflow.field.template', template_fields[template_field])
        flectra_data.append(record)

    # Шаблоны задач (docflow.tasktemplate)
    for tasktemplate in tasktemplates4flectra:
        record = create_record(tasktemplate, 'docflow.tasktemplate', tasktemplates4flectra[tasktemplate])
        flectra_data.append(record)

    # Задачи (project.task)
    for i in range(min(tasks_full.keys())-10,max(tasks_full.keys())+10):
        if tasks.get('task_' + str(i), None):
            record = create_record('task_' + str(i), 'project.task', tasks['task_' + str(i)])
            flectra_data.append(record)

    # Поля (docflow.field)
    for field_fill in fields_fills:
        record = create_record(field_fill, 'docflow.field', fields_fills[field_fill])
        flectra_data.append(record)

    # удаляем все lxml аннотации.
    objectify.deannotate(flectra_root)
    etree.cleanup_namespaces(flectra_root)

    # конвертируем все в привычную нам xml структуру.
    obj_xml = etree.tostring(flectra_root,
                             pretty_print=True,
                             xml_declaration=True,
                             encoding='UTF-8'
                             )

    try:
        with open(os.path.join(PF_DATA, 'docflow_data.xml'), "wb") as xml_writer:
            xml_writer.write(obj_xml)
    except IOError:
        pass

    # files() && actions()
    # Загружаем список всех файлов
    files = {}
    with open(os.path.join(BACKUP_DIRECTORY, 'files_full.json'), 'r') as read_file:
        files_loaded = json.load(read_file)
    for file in files_loaded:
        if files.get(int(file),False):  # Если есть такой же файл меньшей версии - замещаем
            if files[int(file)]['version'] < files_loaded[file]['version']:
                files[int(file)] = files_loaded[file]
        else:
            files[int(file)] = files_loaded[file]

    files4flectra = {}
    location_count = 0   #
    for file in files:
        if HAS_TASK_LIMIT:
            if int(files[file].get('task', {}).get('id', 0)) not in tasks_from_json_ids:
                continue
        if files[file]['sourceType'] in ['FILESYSTEM', 'DOCSTEMPLATE']:
            if files[file].get('full_path', False):
                files4flectra['att_' + str(file)] = {
                    'name': files[file]['name'],
                    'datas_fname': files[file]['name'],
                    'file_id_pf': file,
                    'file_path_pf': files[file]['full_path'],
                    'type': 'binary'
                }
            else:
                continue
        elif files[file]['sourceType'] == 'INTERNET':
            if files[file].get('downloadLink'):
                files4flectra['att_' + str(file)] = {
                    'name': files[file]['name'],
                    'file_id_pf': file,
                    'url': files[file]['downloadLink'],
                    'type': 'url'
                }
            else:
                continue
        else:
            continue
        if files[file]['description']:
            files4flectra['att_' + str(file)]['description'] = files[file]['description']
        if files[file].get('project', False):
            if files[file]['project'].get('id', False):
                if int(files[file]['project']['id']):
                    files4flectra['att_' + str(file)]['project_id_external'] = 'pr_' + files[file]['project']['id']
        if files[file].get('task', False):
            if files[file]['task'].get('id', False):
                if int(files[file]['task']['id']):
                    files4flectra['att_' + str(file)]['task_id_external'] = 'task_' + files[file]['task']['id']
        #if files[file].get('user', False):
        #    if files[file]['user'].get('id', False):
        #        files4flectra['att_' + str(file)]['create_uid'] = 'docflow.user_' + chk_users(files[file]['user']['id'])
        files4flectra['att_' + str(file)]['file_version_pf'] = files[file]['version']

    # Загружаем бэкап комментариев
    actions4flectra = {}
    actions = {}
    with open(os.path.join(BACKUP_DIRECTORY, 'actions_full.json'), 'r') as read_file:
        actions_str = json.load(read_file)
    for action in actions_str:
        actions[int(action)] = actions_str[action]
    print('Из сохраненных комментариев:', len(actions))

    # Формируем комментарии для вывода в Flectra
    for action in actions:
        if int(actions[action]['task']['id']) in tasks_from_json_ids:
            actions4flectra['msg_' + str(actions[action]['id'])] = {
                'date': actions[action]['dateTime'] + ':00',
                'author_id': 'docflow.user_' + chk_users(actions[action]['owner']['id']) + '_res_partner',
                'res_id': 'task_' + str(actions[action]['task']['id']),
                'body': actions[action]['description'],
                'message_type': 'comment',
                'model': 'project.task'
            }
            if len(actions[action].get('files', {}).get('file',[])):
                if str(type(actions[action]['files']['file'])).replace("'","") == '<class dict>':
                    actions4flectra['msg_' + str(actions[action]['id'])]['attachment_ids'] = \
                        [dict_key('att_' + actions[action]['files']['file']['id'], files4flectra)]
                else:
                    actions4flectra['msg_' + str(actions[action]['id'])]['attachment_ids'] = [
                        dict_key('att_' + actions[action]['files']['file'][x]['id'], files4flectra)
                        for x in range(len(actions[action]['files']['file']))
                    ]

    # Заголовок xml
    flectra_root = objectify.Element('flectra')
    flectra_data = objectify.SubElement(flectra_root, 'data')

    # Комментарии
    for action in actions4flectra:
        record = create_record(action, 'mail.message', actions4flectra[action])
        flectra_data.append(record)

    off = """
    # Файлы (ir.attachment)
    for file in files4flectra:
        record = create_record(file, 'ir.attachment', files4flectra[file])
        flectra_data.append(record)
    """

    # удаляем все lxml аннотации.
    objectify.deannotate(flectra_root)
    etree.cleanup_namespaces(flectra_root)

    # конвертируем все в привычную нам xml структуру.
    obj_xml = etree.tostring(flectra_root,
                             pretty_print=True,
                             xml_declaration=True,
                             encoding='UTF-8'
                             )

    try:
        with open(os.path.join(PF_DATA, 'actions_data.xml'), "wb") as xml_writer:
            xml_writer.write(obj_xml)
    except IOError:
        pass

    with open(os.path.join(PF_DATA, 'ir.attachment.csv'), 'w', newline='') as csvfile:
        writer = csv.DictWriter(csvfile, fieldnames=[ 'id', 'name', 'file_id_pf', 'file_path_pf', 'type', 'url',
                                                      'description', 'project_id_external', 'task_id_external',
                                                      'datas_fname', 'file_version_pf'])
        writer.writeheader()
        for file in files4flectra:
            files4flectra[file].update({'id': file})
            writer.writerow(files4flectra[file])


from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
import numpy as np
from keras.preprocessing.image import ImageDataGenerator
import sys

data_shape = (75, 75, 3)
num_train = 50000
num_test = 10000
num_classes = 10
train_batch_size = 32
test_batch_size = 32
epochs = 200
weight_decay = 1e-4
keep_prob = 0.7
# lr = 0.045
# epochs reduce learning_rate by 0.94
reduce_lr_epoch = [i for i in range(2, epochs, 2)]

train_gen = ImageDataGenerator()
test_gen = ImageDataGenerator()

config = {
    'is_SENet': False,
    'reduction': 16
}
# import Inceptionv3 as net
# testnet = net.Inceptionv3(data_shape, num_classes, weight_decay, keep_prob, 'channels_last', config)
# import Inceptionv4 as net
# testnet = net.Inceptionv4(data_shape, num_classes, weight_decay, keep_prob, 'channels_last', config)
import InceptionResnetv2 as net
testnet = net.InceptionResnetv2(data_shape, num_classes, weight_decay, keep_prob, 'channels_last', config)

for epoch in range(epochs):
    print('-'*20, 'epoch', epoch, '-'*20)
    train_acc = []
    train_loss = []
    test_acc = []

    # reduce learning rate
    if epoch in reduce_lr_epoch:
        lr = lr * 0.94
        print('reduce learning lr =', lr, 'now')

    # train one epoch
    for iter in range(num_train//train_batch_size):
        # get and preprocess image
        images,labels = train_gen.next()
        images = images / 127.5 - 1
        # train_one_batch also can accept your own session
        loss, acc = testnet.train_one_batch(images,labels,lr)
        train_acc.append(acc)
        train_loss.append(loss)
        sys.stdout.write("\r>> train "+str(iter+1)+'/'+str(num_train//train_batch_size)+' loss '+str(loss)+' acc '+str(acc))
    mean_train_loss = np.mean(train_loss)
    mean_train_acc = np.mean(train_acc)
    sys.stdout.write("\n")
    print('>> epoch', epoch, 'train mean loss', mean_train_acc, 'train mean acc', mean_train_acc)

    # validate one epoch
    for iter in range(num_test//test_batch_size):
        # get and preprocess image
        images,labels = test_gen.next()
        images = images / 127.5 - 1
        # validate_one_batch also can accept your own session
        logit, acc = testnet.validate_one_batch(images,labels)
        test_acc.append(acc)
        sys.stdout.write("\r>> test "+str(iter+1)+'/'+str(num_test//test_batch_size)+' acc '+str(acc))
    mean_val_acc = np.mean(test_acc)
    sys.stdout.write("\n")
    print('>> epoch', epoch, ' test mean acc', mean_val_acc)

    # logit = testnet.test(images)
    # testnet.save_weight(self, mode, path, sess=None)
    # testnet.load_weight(self, mode, path, sess=None)


from sklearn.model_selection import GridSearchCV
from sklearn.linear_model import LogisticRegression

def grid_search_params(classifier, X, y):
    grid_params = {
        'penalty': ['l1', 'l2'],
        'C': [0.1, 0.01, 1.0, 10.0],
        'solver': ['liblinear']
    }

    grid_search = GridSearchCV(
        estimator=classifier,
        param_grid=grid_params,
        scoring='accuracy',
        cv=3, n_jobs=-1)

    print('Begin grid search validation to find best parameters')
    grid_search.fit(
        X, y.values)
    print('Grid search validation done')

    best_params = grid_search.best_params_
    print('Best parameters: {0}'.format(best_params))

    return best_params


import tarfile
import os
f = open("iiithws_ann.txt","r")
lines = f.readlines()
folder_name = []
for line in lines:
    val = "Images_90K_Normalized/"+line.split()[0]
    folder_name.append(val)
    print(line ," Appending ", val)

#os.mkdir("../../../extra/data/hrishikesh/iiit-hws")

print("list created")
dictio = {}
tar = tarfile.open("../../../extra/data/hrishikesh/iiit-hws.tar.gz")
subdir_and_files = []
for i,tarinfo in enumerate(tar.getmembers()):
    print(i)
    dictio[tarinfo.name] = tarinfo

for i,name in enumerate(folder_name):
    print(i)
    subdir_and_files.append(dictio[name])

tar.extractall(path = "../../../extra/data/hrishikesh/iiit-hws",members=subdir_and_files)


#상하좌우
x,y = 1, 1 #시작 위치가 (1,1)
n = int(input())
plans = input().split()

#L, R, U, D에 따른 이동 방향
dx = [0,0,-1,1]
dy = [-1,1,0,0]
move = ['L','R','U','D']

for i in plans:
    for j in range(len(move)):
        if i == move[j]:
            nx = x +dx[j]
            ny = y +dy[j]
    if nx < 1 or ny < 1 or nx > n or ny > n:
        continue
    x, y = nx, ny
print(x,y)


import pygame
import random
import math

# Initialization of pygame and its font module
pygame.init()
pygame.font.init()

FPS = 60
WIN_HEIGHT = 800
WIN_WIDTH = 500
SCORE_FONT = pygame.font.SysFont("TrebuchetMSBold", 40)
show_hitbox = False

pygame.display.set_caption("Asteroids")
screen = pygame.display.set_mode((WIN_WIDTH, WIN_HEIGHT))

"""
Load images from imgs folder
"""
# Set Icon
icon = pygame.image.load("imgs/icon.png").convert_alpha()
pygame.display.set_icon(icon)

bg_img = pygame.image.load("imgs/bg.jpg").convert_alpha()

hole_imgs = [pygame.transform.scale(pygame.image.load("imgs/hole1.png"), (70, 20)).convert_alpha(),
             pygame.transform.scale(pygame.image.load("imgs/hole2.png"), (128, 20)).convert_alpha()]
asteroid_img = pygame.transform.scale(pygame.image.load("imgs/asteroid.png"), (30, 50)).convert_alpha()
asteroid_img = asteroid_img.convert_alpha()
player_img = pygame.image.load("imgs/player.png").convert_alpha()

# Set icon
pygame.display.set_icon(icon)

class Player:
    def __init__(self):
        self.x = 210
        self.y = 100
        self.speed = 0
        self.state = "ready"    # ready = ready to drop asteroid, unready = asteroid is already dropped
        self.img = player_img

    def move_left(self):
        self.speed = -5

    def move_right(self):
        self.speed = 5

    def stop_move(self):
        self.speed = 0

    def draw(self):
        screen.blit(self.img, (self.x, self.y))

class Hole:
    def __init__(self, x, y, imgnum):
        self.x = x
        self.y = y
        self.imgs = hole_imgs[imgnum]           # Image which is chosen by random.randint() for varying hole sizes
        self.h = self.imgs.get_height()
        self.w = self.imgs.get_width()
        self.hitbox = (self.x, self.y, self.w, self.h)          # Four int values that declare the rectangular hitbox

    def draw(self):
        self.hitbox = (self.x, self.y, self.w, self.h)
        screen.blit(self.imgs, (self.x, self.y))

    def collide(self, asteroid):
        # If the asteroid is between the start and the end of the black hole horizontally
        if asteroid.x <= self.x + self.w and asteroid.x + 32 >= self.x:
            # If the asteroid is between the start and the end of the black hole vertically
            if asteroid.y + 32 > self.y and asteroid.y - 32 < self.y + self.h:
                return True

            return False

class Asteroid:
    def __init__(self):
        self.x = 0
        self.y = 200
        self.speed = 10
        self.img = asteroid_img
        self.h = asteroid_img.get_height()
        self.w = asteroid_img.get_width()
        self.hitbox = (self.x + 16, self.y - 5, self.w, self.h)    # Four int values that declare the rectangular hitbox

def drop_asteroid(x, y, ast):
    screen.blit(asteroid_img, (x + 16, y - 5))
    ast.hitbox = (ast.x + 16, ast.y - 5, ast.w, ast.h)
    # Show asteroid hitbox
    if show_hitbox:
        pygame.draw.rect(screen, (255, 0, 0), ast.hitbox, 2)

def main():
    player = Player()
    ast = Asteroid()
    hole_num = 7
    combo = 0
    holes = [Hole(random.randint(1, 500 - 130), random.randint(300, 760), random.choice([0, 1])) for x in
             range(hole_num)]
    score = 0
    clock = pygame.time.Clock()

    # Game loop
    running = True
    while running:
        clock.tick(FPS)
        screen.fill((0, 0, 0))

        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                running = False
                quit()

            # Checking for keystrokes for player movement
            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_a:
                    player.move_left()
                if event.key == pygame.K_d:
                    player.move_right()
                if event.key == pygame.K_SPACE:
                    if player.state == "ready":
                        ast.x = player.x
                        player.state = "unready"
                        drop_asteroid(ast.x, ast.y, ast)

            if event.type == pygame.KEYUP:
                if event.key == pygame.K_a or event.key == pygame.K_d:
                    player.stop_move()

        # Draws the background
        screen.blit(bg_img, (0, 0))

        # Draws the score
        score_text = SCORE_FONT.render(f"Score: {score}", True, (255, 255, 255))
        screen.blit(score_text, (10, 10))

        # Draws the black holes
        for hole in holes:
            hole.draw()

        # Draws the player
        player.draw()

        # Player Boundaries
        player.x += player.speed
        if player.x <= 0:
            player.x = 0
        if player.x >= 436:
            player.x = 436

        # Check for collision
        for x, hole in enumerate(holes):
            if hole.collide(ast):
                # More points from hitting multiple holes with one asteroid
                score += 1 + combo
                combo += 1
                holes.pop(x)

        # Resets the asteroid once it reaches the edge of the screen
        if ast.y >= 800:
            ast.y = 200
            combo = 0
            player.state = "ready"

        # Asteroid movement
        if player.state is "unready":
            player.state = "unready"
            drop_asteroid(ast.x, ast.y, ast)
            ast.y += ast.speed

        # Show black hole hitbox
        if show_hitbox:
            for hole in holes:
                pygame.draw.rect(screen, (255, 0, 0), hole.hitbox, 2)

        # Restarts the game if all of the black holes are gone
        if len(holes) == 0:
            main()

        pygame.display.update()

main()


# -*- coding: UTF-8 -*-

import torch
import torch.nn as nn
import torch.nn.functional as F
import numpy as np
import os

class PolicyGradient(nn.Module):
    def __init__(self,
                 obs_dim = 6,
                 num_actions = 2,
                 neurons_per_dim=32,
                 ):
        '''
        :param obs_dim: 观察值维度
        :param num_actions:动作数量
        :param neurons_per_dim:每个维度的神经元数量
        '''
        super(PolicyGradient,self).__init__()
        self.num_actions = num_actions
        self._hidden_neurons = obs_dim * neurons_per_dim
        self.layer1 = nn.Linear(obs_dim,self._hidden_neurons)
        nn.init.normal(self.layer1.weight,mean=0, std=1./self._hidden_neurons)
        self.layer2 = nn.Linear(self._hidden_neurons,num_actions)
        nn.init.normal(self.layer2.weight,mean=0, std=1./num_actions)
        self.saved_log_probs = []
        self.rewards = []

    def forward(self,x):
        out = self.layer1(x)
        out = F.relu(out)
        logp = self.layer2(out)
        p = F.sigmoid(logp)
        return p


from omzetten import ParseToHTML, GetYamlData, JSONToDict
from website import UpdateListBrowser, Initialize
from jinja2 import Environment, FileSystemLoader
import json
import os

def toevoegen(markdownFile):
    try:
        inhoud = ParseToHTML(markdownFile)
    except:
        print(f"Bestand {markdownFile} niet gevonden.")
    else:
        page_name = markdownFile.replace(".md", "")
        page_list = JSONToDict("pages/pages.JSON")

        if page_name not in page_list:
            page_list[page_name] = ""

        page_data = GetYamlData(markdownFile)
        page_data.update({"fileName": page_name, "content": inhoud, "author": page_data.get("author", "anonymous")})
        page_list[page_name] = page_data

        with open(f"pages/{page_name}.html", mode="w", encoding="utf-8") as f:
            f.write(Environment(loader=FileSystemLoader("templates/")).get_template("page_temp.html").render(
                page_data
            ))

        with open(f"pages/{markdownFile.replace('md', 'JSON')}", "w") as f:
            json.dump(page_data, f, default=str)

        with open("pages/pages.JSON", "w") as f:
            json.dump(page_list, f, default=str)

        print(f"Pagina {page_name} van {markdownFile} is toegevoegd")

        UpdateListBrowser("pages", "pages", "pages_template.html")
        Initialize()

def delete(fileName):
    try:
        os.remove(f"pages/{fileName}.html")
        os.remove(f"pages/{fileName}.JSON")
    except:
        print(f"Bestand {fileName} niet gevonden.")
    else:
        page_list = JSONToDict("pages/pages.JSON")
        del page_list[fileName]

        with open("pages/pages.JSON", "w") as f:
            json.dump(page_list, f, default=str)

        print(f"Page {fileName} was deleted successfully")

        UpdateListBrowser("pages", "pages", "pages_template.html")
        Initialize()


import os
import time
import random
import tensorflow as tf
from sklearn.model_selection import train_test_split
import pickle

from config import Config
from utils import adjust_data_range

def load_imgpaths(dir_path, split=False):

	files = os.listdir(dir_path)
	paths = [dir_path + file for file in files]
	random.shuffle(paths)
	if split:
		train_paths, eval_paths = \
		train_test_split(paths, test_size=0.05, random_state=Config.random_seed)
		return train_paths, eval_paths
	return paths

def load_and_preprocess(img_path):

    # img_str = tf.io.read_file(img_path)
    # img = tf.image.decode_and_crop_jpeg(img_str, Config.data_crop, channels=3)
	img_str = tf.io.read_file(img_path)
	img = tf.image.decode_jpeg(img_str, channels=3)
	img = tf.image.convert_image_dtype(img, tf.float32)
	img = tf.image.resize(img, Config.data_size)
	img = tf.image.convert_image_dtype(img, tf.uint8)
	# img = tf.image.convert_image_dtype(img, tf.uint8, saturate=True)
	# print(img)
	return img

def write_tfrecord(img_paths, tfrec_path):

	path_ds = tf.data.Dataset.from_tensor_slices(img_paths)
	image_ds = path_ds.map(load_and_preprocess, num_parallel_calls = Config.parallel_threads)
	proto_ds = image_ds.map(tf.io.serialize_tensor)
	tfrec = tf.data.experimental.TFRecordWriter(tfrec_path)
	tfrec_op = tfrec.write(proto_ds)

def prepare_tfrecords():

	print("Preparing tfrecords")
	paths = load_imgpaths(Config.data_dir_path)
	if len(paths)==2:
		write_tfrecord(paths[0], Config.tfrecord_dir+'train.tfrecord')
		write_tfrecord(paths[1], Config.tfrecord_dir+'test.tfrecord')
	else:
		write_tfrecord(paths, Config.tfrecord_dir+'train.tfrecord')

def parse_fn(tfrecord):

	result_img = tf.io.parse_tensor(tfrecord, out_type=tf.uint8)
	result_img = tf.reshape(result_img, Config.img_shape)
	result_img = tf.cast(result_img, tf.float32)
	result_img = adjust_data_range(result_img, drange_in=[0,255], drange_out=[-1, 1])
	return result_img

def parse_fn_img(tfrecord):

	result_img = tf.io.parse_tensor(tfrecord, out_type=tf.uint8)
	result_img = tf.reshape(result_img, Config.img_shape)
	result_img = tf.cast(result_img, tf.float32)
	result_img = adjust_data_range(result_img, drange_in=[0,255], drange_out=[-1, 1])
	return result_img

def parse_fn_cap(tfrecord):

	result_img = tf.io.parse_tensor(tfrecord, out_type=tf.float32)
	result_img = tf.reshape(result_img, Config.embed_shape)
	result_img = tf.cast(result_img, tf.float32)
	result_img = adjust_data_range(result_img, drange_in=[0,255], drange_out=[-1, 1])
	return result_img

def prepare_dataset(tfrecord_file):
	dataset = tf.data.TFRecordDataset(tfrecord_file)
	dataset = dataset.map(map_func=parse_fn, num_parallel_calls= Config.parallel_threads)
	dataset = dataset.shuffle(buffer_size=Config.total_training_imgs, reshuffle_each_iteration=True)
	return dataset

def process_dataset(dataset):
	dataset = dataset.batch(batch_size=Config.global_batchsize, drop_remainder=True)
	dataset = dataset.prefetch(buffer_size = tf.data.experimental.AUTOTUNE)
	return dataset

def prepare_dataset_img(tfrecord_file):

	dataset = tf.data.TFRecordDataset(tfrecord_file)
	dataset = dataset.map(map_func=parse_fn_img, num_parallel_calls= Config.parallel_threads)
	dataset = dataset.shuffle(buffer_size=Config.total_training_imgs, reshuffle_each_iteration=True)
	# dataset = dataset.batch(batch_size=Config.global_batchsize, drop_remainder=True)
	# dataset = dataset.prefetch(buffer_size = tf.data.experimental.AUTOTUNE)
	return dataset

def prepare_dataset_cap(tfrecord_file):

	dataset = tf.data.TFRecordDataset(tfrecord_file)
	dataset = dataset.map(map_func=parse_fn_cap, num_parallel_calls= Config.parallel_threads)
	dataset = dataset.shuffle(buffer_size=Config.total_training_imgs, reshuffle_each_iteration=True)
	# dataset = dataset.batch(batch_size=Config.global_batchsize, drop_remainder=True)
	# dataset = dataset.prefetch(buffer_size = tf.data.experimental.AUTOTUNE)
	return dataset

def load_obj(name):
    with open(name, 'rb') as f:
        return pickle.load(f)

def load_t2i():
	def map_path(img):
		return Config.data_dir_path + img

	t2i_path = Config.text2ImgDataSplit_path
	t2i = load_obj(t2i_path)
	# Append taget image paths
	t2i['ImagePath'] = t2i['ImagePath'].apply(map_path)
	return t2i['Captions'].tolist(), t2i['ImagePath'].tolist()

def cap_preprocess_map(cap):
	return tf.constant(cap)

def cap_img_preprocess_map(cap, img_name):
	img = load_and_preprocess(img_name)
	return cap, img

def map2dataset_t2i(caps, imgs):
	# Captions and Images
	ds_cap_img = tf.data.Dataset.from_tensor_slices((caps, imgs))
	ds_cap_img = ds_cap_img.map(cap_img_preprocess_map, num_parallel_calls = Config.parallel_threads)

	# Captions
	ds_cap = tf.data.Dataset.from_tensor_slices(caps)
	# ds_cap = ds_cap.map(cap_preprocess_map, num_parallel_calls = Config.parallel_threads)

	# Images
	ds_img = tf.data.Dataset.from_tensor_slices(imgs)
	ds_img = ds_img.map(load_and_preprocess, num_parallel_calls = Config.parallel_threads)

	return ds_cap_img, ds_cap, ds_img

# # The following functions can be used to convert a value to a type compatible with tf.Example.
# def _bytes_feature(value):
#     """Returns a bytes_list from a string / byte."""
#     if isinstance(value, type(tf.constant(0))):
#         value = value.numpy() # BytesList won't unpack a string from an EagerTensor.
#     return tf.train.Feature(bytes_list=tf.train.BytesList(value=[value]))

# def _float_feature(value):
#     """Returns a float_list from a float / double."""
#     return tf.train.Feature(float_list=tf.train.FloatList(value=[value]))

# def _int64_feature(value):
#     """Returns an int64_list from a bool / enum / int / uint."""
#     return tf.train.Feature(int64_list=tf.train.Int64List(value=[value]))

# def serialize_example(feature0, feature1):
#     """
#     Creates a tf.Example message ready to be written to a file.
#     """
#     # Create a dictionary mapping the feature name to the tf.Example-compatible data type.
#     feature = {
#         'feature0': _int64_feature(feature0),
#         'feature1': _int64_feature(feature1),
#     }

#     # Create a Features message using tf.train.Example.
#     example_proto = tf.train.Example(features=tf.train.Features(feature=feature))

#     return example_proto.SerializeToString()

# def tf_serialize_example(f0,f1):
#     tf_string = tf.py_function(
#         serialize_example,
#         (f0,f1),  # pass these args to the above function.
#         tf.string)      # the return type is `tf.string`.
#     return tf.reshape(tf_string, ()) # The result is a scalar

def ser(cap, img):
	return tf.io.serialize_tensor(cap), tf.io.serialize_tensor(img)

# @tf.function
def to_tfrecord(ds, tfrec_path):
	proto_ds = ds.map(tf.io.serialize_tensor)
	tfrec = tf.data.experimental.TFRecordWriter(tfrec_path)
	tfrec_op = tfrec.write(proto_ds)

def cap_img_to_tfrecord(ds, tfrec_path):
	proto_ds = ds.map(ser)
	tfrec = tf.data.experimental.TFRecordWriter(tfrec_path)
	tfrec_op = tfrec.write(proto_ds)

# @tf.function
def make_tfrecords():
	cap, img_path = load_t2i()
	ds_cap_img, ds_cap, ds_img = map2dataset_t2i(cap, img_path)

	print("Preparing Image tfrecord")
	to_tfrecord(ds_img, Config.tfrecord_dir+'img_train.tfrecord')

	# Bugs here
	# Refer to
	# https://nthu-datalab.github.io/ml/labs/12-1_CNN/12-1_CNN.html
	# https://www.tensorflow.org/tutorials/load_data/tfrecord

	# print("Preparing Caption&Image tfrecord")
	# cap_img_to_tfrecord(ds_cap_img, Config.tfrecord_dir+'cap_img_train.tfrecord')
	print("Preparing Caption tfrecord")
	to_tfrecord(ds_cap, Config.tfrecord_dir+'cap_train.tfrecord')

def main():
	# tf.enable_eager_execution()
	start_time = time.time()
	prepare_tfrecords()
	make_tfrecords()
	end_time = time.time()
	print(end_time-start_time)

if __name__ == '__main__':
	main()
	# load_t2i()


import csv
import re

def match_name_soil():
    # Открываем исходный CSV-файл для чтения и новый CSV-файл для записи
    with open('data/herbs_info.csv', 'r', newline='') as input_file, open('data/name_soils.csv', 'w', newline='') as output_file:
        reader = csv.reader(input_file)
        writer = csv.writer(output_file)

        # Записываем заголовки в новый файл
        writer.writerow(next(reader))

        # Обрабатываем каждую строку исходного файла
        for row in reader:
            # Разбиваем строку по всем знакам препинания и создаем список
            row_list = re.split(r'[^\w\s]', row[2])

            # Записываем новую строку в новый файл с разбитым списком
            writer.writerow([row[0], ', '.join(row_list)])

    print("Запись в файл завершена.")

def match_region_soil():
    # Открываем исходный CSV-файл для чтения и новый CSV-файл для записи
    with open('data/region_soils.csv', 'r', newline='') as input_file, open('data/output_region_soils.csv', 'w',
                                                                          newline='') as output_file:
        reader = csv.reader(input_file)
        writer = csv.writer(output_file)

# Press the green button in the gutter to run the script.
if __name__ == '__main__':
    # match_name_soil()
    match_region_soil()


import random
from constansts import *
from rules import Rules

class BaseGame(object):

    def __init__(self, game_types, bet_size):
        self.game_types = game_types
        self.bet_size = bet_size
        self.players = []
        self.table_cards = []
        self.selected_game = None

    def addPlayers(self, playerA, playerB, playerC):
        self.players.append(playerA)
        self.players.append(playerB)
        self.players.append(playerC)

    def play(self):
        self.initNewGame()
        self._dealCards()
        self._selectContract()
        self._playTricks()
        card_points, trick_count = self._countPoints()
        player_points = self._countGamePoints(card_points, trick_count)
        for i in xrange(3):
            self.players[i].updatePoints(player_points[i])

    def initNewGame(self):
        for p in self.players:
            p.newGame()

    def _countGamePoints(self, card_points, trick_counts):
        player_points = [0, 0, 0]
        roles = [x.contract for x in self.players]
        small_points = 0
        if (Contract.TABLE in roles) or (Contract.BIG in roles):
            great_seat = roles.index(self.selected_game)
            small_tricks = sum(trick_counts) - trick_counts[great_seat]
            point_gain = 0
            if (Contract.BIG in roles):
                point_gain = 4
            great_points, small_points = Rules.countPartnerGamePoints(card_points[great_seat],
                                                point_gain,
                                                small_tricks)
            player_points[great_seat] = great_points

        if Contract.SMALL in roles:
            great_seat = roles.index(self.selected_game)
            if trick_counts[great_seat] != 0:
                player_points[great_seat] = -7 * 2
                small_points = 7
            else:
                player_points[great_seat] = 6 * 2
                small_points = -6

        if self.selected_game == Contract.DESK:
            max_tricks = [x if x == max(trick_counts) else 0 for x in trick_counts]
            max_trick_points = map(lambda x,y:x*y,max_tricks, card_points)
            looser = max_trick_points.index(max(max_trick_points))
            player_points[looser] = -2 * 2
            small_points = 2

        player_points = [x if x!=0 else small_points for x in player_points]
        return player_points

    def _countPoints(self):
        card_points = []
        trick_count = []
        for p in self.players:
            trickCount, points = Rules.countCardPoints(p.tricks)
            card_points.append(points)
            trick_count.append(trickCount)
        return card_points, trick_count

    def _playTricks(self):
        first = 0
        for _ in xrange(len(self.players[0].cards)):
            second = Rules.nextPlayer(first)
            last = Rules.nextPlayer(second)
            first_index = self._playTrick([self.players[first].uuid,
                                                   self.players[second].uuid,
                                                   self.players[last].uuid])
            first = next(self.players.index(x)
                     for x in self.players
                     if x.uuid == first_index)

            # In small game: small player loose game on first winning trick
            if self.players[first].contract == Contract.SMALL:
                break

    def _selectContract(self, contracts = None):
        # Ask for game type and deside what to do with table cards

        for p in self.players:
            contract = p.selectContract(self.game_types)
            if contract not in self.game_types:
                raise IndentationError

            if contract == Contract.TABLE:  #Start normal Solo game with table cards
                self.selected_game = Contract.TABLE
                #Add table cards to solo player
                table = self.table_cards
                p.addCards(table)

                #Ask solo player to place 2 cards
                self._playTrick([p.uuid])
                self._playTrick([p.uuid])
                break

            if contract == Contract.BIG:  #Start Solo game without table cards
                self.selected_game = Contract.BIG
                break

            if contract == Contract.SMALL: #Start Null game when solo palyer cannot winn a trick
                self.selected_game = Contract.SMALL
                break

        if  self.selected_game == None:  #Start to table game the looser is with moust tricks
            if Contract.DESK in self.game_types:
                self.selected_game = Contract.DESK
            else:
                self.selected_game = Contract.PARTNER

    def _playTrick(self,player_list):
        trick =[]
        first_card = None
        for pIndex in player_list:
            p = next(x for x in self.players if x.uuid == pIndex)
            card = p.selectCard(first_card)
            trick.append(card)
            if first_card == None:
                first_card = card
        winner = player_list[Rules.bestCard(trick)]

        #Add trick cards to player
        for p in self.players:
            if p.uuid == winner:
                p.addTrick(trick)

        return winner

    def _dealCards(self, card_deck = None):
        #mix up the deck
        if card_deck == None:
            card_deck = list(Cards.ALL)
            random.shuffle(card_deck)

        for p in self.players:
            cards = card_deck[:8]
            del card_deck[:8]
            p.addCards(cards)

        self.table_cards = card_deck       #remaining cards to the table

    def sendToPlayers(self, except_player, message, data):
        for p in self.players:
            if p.uuid != except_player:
                p.sendToClient(message, data)


#!/usr/bin/env python
# -*- coding: utf-8 -*-
# ---------------------
#  TempusLab
#  Authors: Cory Paik
#  Updated: Apr. 2020
# ---------------------

# General
import sys
from setuptools import setup, find_packages

if sys.version_info.major != 3:
    print('This Python is only compatible with Python 3, but you are running '
          'Python {}. The installation will likely fail.'.format(sys.version_info.major))

extras = {
    'test': [
        'pytest',
        'pytest-cov',
        'pytest-flake8',
        'mypy',
    ],
}

extras['all'] = [iv for ov in extras.values() for iv in ov]

setup(
    name='pytorch-lightning-pbt',
    packages=['pytorch_lightning_pbt', ],
    install_requires=[
        'numpy',
        'pandas',
        'parse',
        'torch==1.5.0',
        'torchvision==0.6.0',
        'pytorch_lightning==0.7.5'
    ],
    extras_require=extras,
    version='0.0.1',
    author='Cory Paik, Rocket Romero',
    author_email='corypaik@gmail.com, rocketromero4444@gmail.com',
    url='https://github.com/corypaik/pytorch-lightning-pbt',
    description='PyTorch Lightning PBT',
    long_description=open('README.md').read(),
)