import setuptools
import os

#from pathlib import Path
#this_directory = Path(__file__).parent
#long_description = (this_directory / "README.md").read_text()

with open("README.md", "r", encoding="utf-8") as fh:
    long_description = fh.read()

setuptools.setup(
    name="jpaccident",
    version="0.0.1",
    author="Tsuyoshi Hine",
    author_email="tsuyoshi.h0920@gmail.com",
    description='Package for visualization of aggregate data on fatalities in traffic accidents',
    long_description=long_description,
    long_description_content_type="text/plain",

    #long_description=open(path.join(here, 'README.md'), encoding='utf-8').read().replace("\r", "")
    #long_description_content_type="text/markdown",

    url="https://github.com/s2022027/jpaccident",
    project_urls={
        "Bug Tracker": "https://github.com/s2022027/jpaccident",
    },
    classifiers=[
        "Programming Language :: Python :: 3",
        "License :: OSI Approved :: MIT License",
        "Operating System :: OS Independent",
    ],
    package_dir={"": "src"},
    py_modules=['jpaccident'],
    packages=setuptools.find_packages(where="src"),
    python_requires=">=3.7",
    entry_points = {
        'console_scripts': [
            'jpaccident = jpaccident:main'
        ]
    },
)


class Solution:
    def largestRectangleArea(self, heights: List[int]) -> int:
        #complete copy from neetcode youtube solution (https://www.youtube.com/watch?v=zx5Sw9130L0&t=1029s)
        maxArea = 0
        stack = []

        for i, h in enumerate(heights):
            start=i
            while stack and stack[-1][1] > h:
                index, height = stack.pop()
                maxArea = max(maxArea, height * (i-index))
                start = index
            stack.append((start, h))
        for i, h in stack:
            maxArea = max(maxArea, h * (len(heights)-i))
        return maxArea

    #status: correct (#complete copy from neetcode youtube solution (https://www.youtube.com/watch?v=zx5Sw9130L0&t=1029s))
    #analysis: Time O(n), Space O(n)
    #ref: 12/17/2022P2:track1-cpGrind75;3QuestionPerDay(perQuestion-x3pomo),perDay-x10pomo;35hoursperWeek;15hrPerTopic+15hrPerTopic+5hrBuffer;Day53/54,1.largestRectangleInHistogramTImed25Mins-x1pomo(6:00-6:30),2.implement-x2pomo(6:30-7:30),3.study:tree-x1pomo(7:30-8:00),4.maximumDepthOfABinaryTreeTimed25Mins-x1pomo(8:00-8:30),5.implement-x2pomo(8:30-9:30),6.invert/flipBinaryTreeTimed25Mins-x1pomo(9:30-10:00),7.implement-x2pomo(10:00-11:00),8.lowestCommonAncestorOfABinaryTreeTimed25Mins-x1pomo(10:00-10:30),9.absorber-x1pomo(10:30-11:00)=x12pomo(6:00-12:00)


from collections import Counter
n = int(input())
a = Counter(list(map(int, input().split())))

a = [i for i in a.items() if i[0]!=i[1]]
ans = 0

for i, j in a:
    if i>j:
        ans += j
    else:
        ans += j-i

print(ans)


import sys

from django.core.management.base import BaseCommand, CommandError

from imagefield.fields import IMAGEFIELDS

def iterator(queryset):
    # Relatively low chunk_size to avoid slowness when having to load
    # width and height for images when instantiating models.
    try:
        return queryset.iterator(chunk_size=100)
    except TypeError:  # Older versions of Django
        return queryset.iterator()

class Command(BaseCommand):
    def add_arguments(self, parser):
        parser.add_argument(
            "--all", action="store_true", dest="all", help="Process all fields."
        )
        parser.add_argument(
            "--force",
            action="store_true",
            dest="force",
            help="Force processing of images even if they exist already.",
        )
        parser.add_argument(
            "--housekeep",
            choices=["blank-on-failure"],
            default="",
            help="Run house-keeping tasks.",
        )
        parser.add_argument(
            "field",
            nargs="*",
            type=str,
            help="Fields to process:\n{}".format(
                ", ".join(sorted(f.field_label for f in IMAGEFIELDS))
            ),
        )

        # TODO --clear for removing previously generated images.

    def handle(self, **options):
        self._fields = self._compile_imagefield_labels(options)

        for field in sorted(IMAGEFIELDS, key=lambda f: f.field_label):
            if field.field_label in self._fields:
                self._process_field(field, options)

    def _compile_imagefield_labels(self, options):
        if options["all"]:
            return type("c", (), {"__contains__": lambda *a: True})()
        elif options["field"]:
            unknown = set(options["field"]).difference(
                f.field_label for f in IMAGEFIELDS
            )
            if unknown:
                raise CommandError(
                    "Unknown imagefields: {}".format(", ".join(sorted(unknown)))
                )
            return options["field"]
        else:
            self.print_help(sys.argv[0], sys.argv[1])
            sys.exit(1)

    def _process_field(self, field, options):
        queryset = field.model._default_manager.exclude(**{field.name: ""}).order_by(
            "-pk"
        )
        count = queryset.count()
        self.stdout.write(
            "{} - {} objects - {}".format(
                field.field_label,
                count,
                ", ".join(sorted(field.formats.keys())) or "<no formats!>",
            )
        )
        self.stdout.write("\r|{}| {}/{}".format(" " * 50, 0, count), ending="")

        if field._fallback:
            self._process_instance(
                field.model(),
                field,
                housekeep=None,
                force=options.get("force"),
            )

        for index, instance in enumerate(iterator(queryset)):
            self._process_instance(
                instance,
                field,
                housekeep=options.get("housekeep"),
                force=options.get("force"),
            )
            progress = "*" * (50 * index // count)
            self.stdout.write(
                f"\r|{progress.ljust(50)}| {index + 1}/{count}", ending=""
            )

            # Save instance once for good measure; fills in width/height
            # if not done already
            instance._skip_generate_files = True
            instance.save()

        self.stdout.write("\r|{}| {}/{}".format("*" * 50, count, count))

    def _process_instance(self, instance, field, housekeep, **kwargs):
        fieldfile = getattr(instance, field.name)
        for key in field.formats:
            try:
                fieldfile.process(key, **kwargs)
            except Exception as exc:
                self.stderr.write(
                    "Error while processing {} ({}, #{}):\n{}\n".format(
                        fieldfile.name, field.field_label, instance.pk, exc
                    )
                )
                if housekeep == "blank-on-failure":
                    field.save_form_data(instance, "")


from __future__ import annotations

import asyncio
import httpx
from io import BytesIO
import jwt
import logging
import os
import pathlib
import requests
import sys
import tempfile
import time

from contextlib import nullcontext
from typing import Any, ContextManager, Optional, overload, Sequence, Tuple, List
from requests.compat import json  # type: ignore
from packaging.version import Version, parse

import firecrest.FirecrestException as fe
import firecrest.types as t
from firecrest.AsyncExternalStorage import AsyncExternalUpload, AsyncExternalDownload

if sys.version_info >= (3, 8):
    from typing import Literal
else:
    from typing_extensions import Literal

logger = logging.getLogger(__name__)

# This function is temporarily here
def handle_response(response):
    print("\nResponse status code:")
    print(response.status_code)
    print("\nResponse headers:")
    print(json.dumps(dict(response.headers), indent=4))
    print("\nResponse json:")
    try:
        print(json.dumps(response.json(), indent=4))
    except json.JSONDecodeError:
        print("-")

class ComputeTask:
    """Helper object for blocking methods that require multiple requests"""

    def __init__(
        self,
        client: AsyncFirecrest,
        task_id: str,
        previous_responses: Optional[List[requests.Response]] = None,
    ) -> None:
        self._responses = [] if previous_responses is None else previous_responses
        self._client = client
        self._task_id = task_id

    async def poll_task(self, final_status):
        logger.info(f"Polling task {self._task_id} until status is {final_status}")
        resp = await self._client._task_safe(self._task_id, self._responses)
        while resp["status"] < final_status:
            # The rate limit is handled by the async client so no need to
            # add a `sleep` here
            resp = await self._client._task_safe(self._task_id, self._responses)

        logger.info(f'Status of {self._task_id} is {resp["status"]}')
        return resp["data"]

class AsyncFirecrest:
    """
    This is the basic class you instantiate to access the FirecREST API v1.
    Necessary parameters are the firecrest URL and an authorization object.

    :param firecrest_url: FirecREST's URL
    :param authorization: the authorization object. This object is responsible of handling the credentials and the only requirement for it is that it has a method get_access_token() that returns a valid access token.
    :param verify: either a boolean, in which case it controls whether requests will verify the server’s TLS certificate, or a string, in which case it must be a path to a CA bundle to use
    :param sa_role: this corresponds to the `F7T_AUTH_ROLE` configuration parameter of the site. If you don't know how FirecREST is setup it's better to leave the default.
    """

    TOO_MANY_REQUESTS_CODE = 429

    def _retry_requests(func):
        async def wrapper(*args, **kwargs):
            client = args[0]
            num_retries = 0
            resp = await func(*args, **kwargs)
            while True:
                if resp.status_code != client.TOO_MANY_REQUESTS_CODE:
                    break
                elif (
                    client.num_retries_rate_limit is not None
                    and num_retries >= client.num_retries_rate_limit
                ):
                    logger.debug(
                        f"Rate limit is reached and the request has "
                        f"been retried already {num_retries} times"
                    )
                    break
                else:
                    reset = resp.headers.get(
                        "Retry-After",
                        default=resp.headers.get("RateLimit-Reset", default=10),
                    )
                    reset = int(reset)
                    microservice = kwargs["endpoint"].split("/")[1]
                    client = args[0]
                    logger.info(
                        f"Rate limit in `{microservice}` is reached, next "
                        f"request will be possible in {reset} sec"
                    )
                    retry_time = time.time() + reset
                    if retry_time > client._next_request_ts[microservice]:
                        client._next_request_ts[microservice] = retry_time

                    resp = await func(*args, **kwargs)
                    num_retries += 1

            return resp

        return wrapper

    def __init__(
        self,
        firecrest_url: str,
        authorization: Any,
        verify: Optional[str | bool] = None,
        sa_role: str = "firecrest-sa",
    ) -> None:
        self._firecrest_url = firecrest_url
        self._authorization = authorization
        # This should be used only for blocking operations that require multiple requests,
        # not for external upload/download
        self._current_method_requests: List[requests.Response] = []
        self._verify = verify  # TODO: not supported in httpx
        self._sa_role = sa_role
        #: This attribute will be passed to all the requests that will be made.
        #: How many seconds to wait for the server to send data before giving up.
        #: After that time a `requests.exceptions.Timeout` error will be raised.
        #:
        #: It can be a float or a tuple. More details here: https://www.python-httpx.org/advanced/#fine-tuning-the-configuration.
        self.timeout: Any = None
        # type is Any because of some incompatibility between https and requests library

        #: Number of retries in case the rate limit is reached. When it is set to `None`, the
        #: client will keep trying until it gets a different status code than 429.
        self.num_retries_rate_limit: Optional[int] = None
        self._api_version: Version = parse("1.13.1")
        self._session = httpx.AsyncClient()

        #: Seconds between requests in each microservice
        self.time_between_calls: dict[str, float] = {  # TODO more detailed docs
            "compute": 5,
            "reservations": 5,
            "status": 5,
            "storage": 5,
            "tasks": 5,
            "utilities": 5,
        }
        self._next_request_ts: dict[str, float] = {
            "compute": 0,
            "reservations": 0,
            "status": 0,
            "storage": 0,
            "tasks": 0,
            "utilities": 0,
        }
        self._locks = {
            "compute": asyncio.Lock(),
            "reservations": asyncio.Lock(),
            "status": asyncio.Lock(),
            "storage": asyncio.Lock(),
            "tasks": asyncio.Lock(),
            "utilities": asyncio.Lock(),
        }

        # The following objects are used to "merge" requests in the same endpoints,
        # for example requests to tasks or polling for jobs
        self._polling_ids: dict[str, set] = {"compute": set(), "tasks": set()}
        self._polling_results: dict[str, List] = {"compute": [], "tasks": []}
        self._polling_events: dict[str, Optional[asyncio.Event]] = {
            "compute": None,
            "tasks": None,
        }

    def set_api_version(self, api_version: str) -> None:
        """Set the version of the api of firecrest. By default it will be assumed that you are
        using version 1.13.1 or compatible. The version is parsed by the `packaging` library.
        """
        self._api_version = parse(api_version)

    @_retry_requests  # type: ignore
    async def _get_request(
        self, endpoint, additional_headers=None, params=None
    ) -> httpx.Response:
        microservice = endpoint.split("/")[1]
        url = f"{self._firecrest_url}{endpoint}"

        async def _merged_get(event):
            await self._stall_request(microservice)
            async with self._locks[microservice]:
                results = self._polling_results[microservice]
                ids = self._polling_ids[microservice].copy()
                self._polling_events[microservice] = None
                self._polling_ids[microservice] = set()
                comma_sep_par = "tasks" if microservice == "tasks" else "jobs"
                if ids == {"*"}:
                    if comma_sep_par in params:
                        del params[comma_sep_par]
                else:
                    params[comma_sep_par] = ",".join(ids)

                headers = {
                    "Authorization": f"Bearer {self._authorization.get_access_token()}"
                }
                if additional_headers:
                    headers.update(additional_headers)

                logger.info(f"Making GET request to {endpoint}")
                resp = await self._session.get(
                    url=url, headers=headers, params=params, timeout=self.timeout
                )

                self._next_request_ts[microservice] = (
                    time.time() + self.time_between_calls[microservice]
                )

                results.append(resp)
                event.set()

            return

        if microservice == "tasks" or endpoint in ("/compute/jobs", "/compute/acct"):
            async with self._locks[microservice]:
                if self._polling_ids[microservice] != {"*"}:
                    comma_sep_par = "tasks" if microservice == "tasks" else "jobs"
                    if comma_sep_par not in params:
                        self._polling_ids[microservice] = {"*"}
                    else:
                        task_ids = params[comma_sep_par].split(",")
                        self._polling_ids[microservice].update(task_ids)

                if self._polling_events[microservice] is None:
                    self._polling_events[microservice] = asyncio.Event()
                    my_event = self._polling_events[microservice]
                    self._polling_results[microservice] = []
                    my_result = self._polling_results[microservice]
                    waiter = True
                    task = asyncio.create_task(_merged_get(my_event))
                else:
                    waiter = False
                    my_event = self._polling_events[microservice]
                    my_result = self._polling_results[microservice]

            if waiter:
                await task

            await my_event.wait()  # type: ignore
            resp = my_result[0]
            return resp

        # Otherwise just do what you usually do
        async with self._locks[microservice]:
            await self._stall_request(microservice)
            headers = {
                "Authorization": f"Bearer {self._authorization.get_access_token()}"
            }
            if additional_headers:
                headers.update(additional_headers)

            logger.info(f"Making GET request to {endpoint}")
            resp = await self._session.get(
                url=url, headers=headers, params=params, timeout=self.timeout
            )
            self._next_request_ts[microservice] = (
                time.time() + self.time_between_calls[microservice]
            )

        return resp

    @_retry_requests  # type: ignore
    async def _post_request(
        self, endpoint, additional_headers=None, data=None, files=None
    ) -> httpx.Response:
        microservice = endpoint.split("/")[1]
        url = f"{self._firecrest_url}{endpoint}"
        async with self._locks[microservice]:
            await self._stall_request(microservice)
            headers = {
                "Authorization": f"Bearer {self._authorization.get_access_token()}"
            }
            if additional_headers:
                headers.update(additional_headers)

            logger.info(f"Making POST request to {endpoint}")
            resp = await self._session.post(
                url=url, headers=headers, data=data, files=files, timeout=self.timeout
            )
            self._next_request_ts[microservice] = (
                time.time() + self.time_between_calls[microservice]
            )

        return resp

    @_retry_requests  # type: ignore
    async def _put_request(
        self, endpoint, additional_headers=None, data=None
    ) -> httpx.Response:
        microservice = endpoint.split("/")[1]
        url = f"{self._firecrest_url}{endpoint}"
        async with self._locks[microservice]:
            await self._stall_request(microservice)
            headers = {
                "Authorization": f"Bearer {self._authorization.get_access_token()}"
            }
            if additional_headers:
                headers.update(additional_headers)

            logger.info(f"Making PUT request to {endpoint}")
            resp = await self._session.put(
                url=url, headers=headers, data=data, timeout=self.timeout
            )
            self._next_request_ts[microservice] = (
                time.time() + self.time_between_calls[microservice]
            )

        return resp

    @_retry_requests  # type: ignore
    async def _delete_request(
        self, endpoint, additional_headers=None, data=None
    ) -> requests.Response:
        microservice = endpoint.split("/")[1]
        url = f"{self._firecrest_url}{endpoint}"
        async with self._locks[microservice]:
            await self._stall_request(microservice)
            headers = {
                "Authorization": f"Bearer {self._authorization.get_access_token()}"
            }
            if additional_headers:
                headers.update(additional_headers)

            logger.info(f"Making DELETE request to {endpoint}")
            # TODO: httpx doesn't support data in delete so we will have to
            # keep using the `requests` package for this
            resp = requests.delete(
                url=url, headers=headers, data=data, timeout=self.timeout
            )
            self._next_request_ts[microservice] = (
                time.time() + self.time_between_calls[microservice]
            )

        return resp

    async def _stall_request(self, microservice: str) -> None:
        if self._next_request_ts[microservice] is not None:
            while time.time() <= self._next_request_ts[microservice]:
                logger.debug(
                    f"`{microservice}` microservice has received too many requests. "
                    f"Going to sleep for "
                    f"~{self._next_request_ts[microservice] - time.time()} sec"
                )
                await asyncio.sleep(self._next_request_ts[microservice] - time.time())

    @overload
    def _json_response(
        self,
        responses: List[requests.Response],
        expected_status_code: int,
        allow_none_result: Literal[False] = ...,
    ) -> dict:
        ...

    @overload
    def _json_response(
        self,
        responses: List[requests.Response],
        expected_status_code: int,
        allow_none_result: Literal[True],
    ) -> Optional[dict]:
        ...

    def _json_response(
        self,
        responses: List[requests.Response],
        expected_status_code: int,
        allow_none_result: bool = False,
    ):
        # Will examine only the last response
        response = responses[-1]
        status_code = response.status_code
        # handle_response(response)
        exc: fe.FirecrestException
        for h in fe.ERROR_HEADERS:
            if h in response.headers:
                logger.critical(f"Header '{h}' is included in the response")
                exc = fe.HeaderException(responses)
                logger.critical(exc)
                raise exc

        if status_code == 401:
            logger.critical(f"Status of the response is 401")
            exc = fe.UnauthorizedException(responses)
            logger.critical(exc)
            raise exc
        elif status_code == 404:
            logger.critical(f"Status of the response is 404")
            exc = fe.NotFound(responses)
            logger.critical(exc)
            raise exc
        elif status_code >= 400:
            logger.critical(f"Status of the response is {status_code}")
            exc = fe.FirecrestException(responses)
            logger.critical(exc)
            raise exc
        elif status_code != expected_status_code:
            logger.critical(
                f"Unexpected status of last request {status_code}, it should have been {expected_status_code}"
            )
            exc = fe.UnexpectedStatusException(responses, expected_status_code)
            logger.critical(exc)
            raise exc

        try:
            ret = response.json()
        except json.decoder.JSONDecodeError:
            if allow_none_result:
                ret = None
            else:
                exc = fe.NoJSONException(responses)
                logger.critical(exc)
                raise exc

        return ret

    async def _tasks(
        self,
        task_ids: Optional[List[str]] = None,
        responses: Optional[List[requests.Response]] = None,
    ) -> dict[str, t.Task]:
        """Return a dictionary of FirecREST tasks and their last update.
        When `task_ids` is an empty list or contains more than one element the
        `/tasks` endpoint will be called. Otherwise `/tasks/{taskid}`.
        When the `/tasks` is called the method will not give an error for invalid IDs,
        but `/tasks/{taskid}` will raise an exception.

        :param task_ids: list of task IDs. When empty all tasks are returned.
        :param responses: list of responses that are associated with these tasks (only relevant for error)
        :calls: GET `/tasks` or `/tasks/{taskid}`
        """
        task_ids = [] if task_ids is None else task_ids
        responses = [] if responses is None else responses
        endpoint = "/tasks"
        params = {}
        if task_ids:
            params = {"tasks": ",".join([str(j) for j in task_ids])}

        resp = await self._get_request(endpoint=endpoint, params=params)
        responses.append(resp)
        taskinfo = self._json_response(responses, 200)
        if len(task_ids) == 0:
            return taskinfo["tasks"]
        else:
            return {k: v for k, v in taskinfo["tasks"].items() if k in task_ids}

    async def _task_safe(
        self, task_id: str, responses: Optional[List[requests.Response]] = None
    ) -> t.Task:
        if responses is None:
            responses = self._current_method_requests

        task = (await self._tasks([task_id], responses))[task_id]
        status = int(task["status"])
        exc: fe.FirecrestException
        if status == 115:
            logger.critical("Task has error status code 115")
            exc = fe.StorageUploadException(responses)
            logger.critical(exc)
            raise exc

        if status == 118:
            logger.critical("Task has error status code 118")
            exc = fe.StorageDownloadException(responses)
            logger.critical(exc)
            raise exc

        if status >= 400:
            logger.critical(f"Task has error status code {status}")
            exc = fe.FirecrestException(responses)
            logger.critical(exc)
            raise exc

        return task

    async def _invalidate(
        self, task_id: str, responses: Optional[List[requests.Response]] = None
    ):
        responses = [] if responses is None else responses
        resp = await self._post_request(
            endpoint="/storage/xfer-external/invalidate",
            additional_headers={"X-Task-Id": task_id},
        )
        responses.append(resp)
        return self._json_response(responses, 201, allow_none_result=True)

    async def _poll_tasks(self, task_id: str, final_status, sleep_time):
        logger.info(f"Polling task {task_id} until status is {final_status}")
        resp = await self._task_safe(task_id)
        while resp["status"] < final_status:
            t = next(sleep_time)
            logger.info(
                f'Status of {task_id} is {resp["status"]}, sleeping for {t} sec'
            )
            await asyncio.sleep(t)
            resp = await self._task_safe(task_id)

        logger.info(f'Status of {task_id} is {resp["status"]}')
        return resp["data"]

    # Status
    async def all_services(self) -> List[t.Service]:
        """Returns a list containing all available micro services with a name, description, and status.

        :calls: GET `/status/services`
        """
        resp = await self._get_request(endpoint="/status/services")
        return self._json_response([resp], 200)["out"]

    async def service(self, service_name: str) -> t.Service:
        """Returns information about a micro service.
        Returns the name, description, and status.

        :param service_name: the service name
        :calls: GET `/status/services/{service_name}`
        """
        resp = await self._get_request(endpoint=f"/status/services/{service_name}")
        return self._json_response([resp], 200)  # type: ignore

    async def all_systems(self) -> List[t.System]:
        """Returns a list containing all available systems and response status.

        :calls: GET `/status/systems`
        """
        resp = await self._get_request(endpoint="/status/systems")
        return self._json_response([resp], 200)["out"]

    async def system(self, system_name: str) -> t.System:
        """Returns information about a system.
        Returns the name, description, and status.

        :param system_name: the system name
        :calls: GET `/status/systems/{system_name}`
        """
        resp = await self._get_request(endpoint=f"/status/systems/{system_name}")
        return self._json_response([resp], 200)["out"]

    async def parameters(self) -> t.Parameters:
        """Returns configuration parameters of the FirecREST deployment that is associated with the client.

        :calls: GET `/status/parameters`
        """
        resp = await self._get_request(endpoint="/status/parameters")
        return self._json_response([resp], 200)["out"]

    # Utilities
    async def list_files(
        self, machine: str, target_path: str, show_hidden: bool = False
    ) -> List[t.LsFile]:
        """Returns a list of files in a directory.

        :param machine: the machine name where the filesystem belongs to
        :param target_path: the absolute target path
        :param show_hidden: show hidden files
        :calls: GET `/utilities/ls`
        """
        params: dict[str, Any] = {"targetPath": f"{target_path}"}
        if show_hidden is True:
            params["showhidden"] = show_hidden

        resp = await self._get_request(
            endpoint="/utilities/ls",
            additional_headers={"X-Machine-Name": machine},
            params=params,
        )
        return self._json_response([resp], 200)["output"]

    async def mkdir(
        self, machine: str, target_path: str, p: Optional[bool] = None
    ) -> None:
        """Creates a new directory.

        :param machine: the machine name where the filesystem belongs to
        :param target_path: the absolute target path
        :param p: no error if existing, make parent directories as needed
        :calls: POST `/utilities/mkdir`
        """
        data: dict[str, Any] = {"targetPath": target_path}
        if p:
            data["p"] = p

        resp = await self._post_request(
            endpoint="/utilities/mkdir",
            additional_headers={"X-Machine-Name": machine},
            data=data,
        )
        self._json_response([resp], 201)

    async def mv(self, machine: str, source_path: str, target_path: str) -> None:
        """Rename/move a file, directory, or symlink at the `source_path` to the `target_path` on `machine`'s filesystem.

        :param machine: the machine name where the filesystem belongs to
        :param source_path: the absolute source path
        :param target_path: the absolute target path
        :calls: PUT `/utilities/rename`
        """
        resp = await self._put_request(
            endpoint="/utilities/rename",
            additional_headers={"X-Machine-Name": machine},
            data={"targetPath": target_path, "sourcePath": source_path},
        )
        self._json_response([resp], 200)

    async def chmod(self, machine: str, target_path: str, mode: str) -> None:
        """Changes the file mod bits of a given file according to the specified mode.

        :param machine: the machine name where the filesystem belongs to
        :param target_path: the absolute target path
        :param mode: same as numeric mode of linux chmod tool
        :calls: PUT `/utilities/chmod`
        """
        resp = await self._put_request(
            endpoint="/utilities/chmod",
            additional_headers={"X-Machine-Name": machine},
            data={"targetPath": target_path, "mode": mode},
        )
        self._json_response([resp], 200)

    async def chown(
        self,
        machine: str,
        target_path: str,
        owner: Optional[str] = None,
        group: Optional[str] = None,
    ) -> None:
        """Changes the user and/or group ownership of a given file.
        If only owner or group information is passed, only that information will be updated.

        :param machine: the machine name where the filesystem belongs to
        :param target_path: the absolute target path
        :param owner: owner ID for target
        :param group: group ID for target
        :calls: PUT `/utilities/chown`
        """
        if owner is None and group is None:
            return

        data = {"targetPath": target_path}
        if owner:
            data["owner"] = owner

        if group:
            data["group"] = group

        resp = await self._put_request(
            endpoint="/utilities/chown",
            additional_headers={"X-Machine-Name": machine},
            data=data,
        )
        self._json_response([resp], 200)

    async def copy(self, machine: str, source_path: str, target_path: str) -> None:
        """Copies file from `source_path` to `target_path`.

        :param machine: the machine name where the filesystem belongs to
        :param source_path: the absolute source path
        :param target_path: the absolute target path
        :calls: POST `/utilities/copy`
        """
        resp = await self._post_request(
            endpoint="/utilities/copy",
            additional_headers={"X-Machine-Name": machine},
            data={"targetPath": target_path, "sourcePath": source_path},
        )
        self._json_response([resp], 201)

    async def file_type(self, machine: str, target_path: str) -> str:
        """Uses the `file` linux application to determine the type of a file.

        :param machine: the machine name where the filesystem belongs to
        :param target_path: the absolute target path
        :calls: GET `/utilities/file`
        """
        resp = await self._get_request(
            endpoint="/utilities/file",
            additional_headers={"X-Machine-Name": machine},
            params={"targetPath": target_path},
        )
        return self._json_response([resp], 200)["output"]

    async def stat(
        self, machine: str, target_path: str, dereference: bool = False
    ) -> t.StatFile:
        """Uses the stat linux application to determine the status of a file on the machine's filesystem.
        The result follows: https://docs.python.org/3/library/os.html#os.stat_result.

        :param machine: the machine name where the filesystem belongs to
        :param target_path: the absolute target path
        :param dereference: follow link (default False)
        :calls: GET `/utilities/stat`
        """
        params: dict[str, Any] = {"targetPath": target_path}
        if dereference:
            params["dereference"] = dereference

        resp = await self._get_request(
            endpoint="/utilities/stat",
            additional_headers={"X-Machine-Name": machine},
            params=params,
        )
        return self._json_response([resp], 200)["output"]

    async def symlink(self, machine: str, target_path: str, link_path: str) -> None:
        """Creates a symbolic link.

        :param machine: the machine name where the filesystem belongs to
        :param target_path: the absolute path that the symlink will point to
        :param link_path: the absolute path to the new symlink
        :calls: POST `/utilities/symlink`
        """
        resp = await self._post_request(
            endpoint="/utilities/symlink",
            additional_headers={"X-Machine-Name": machine},
            data={"targetPath": target_path, "linkPath": link_path},
        )
        self._json_response([resp], 201)

    async def simple_download(
        self, machine: str, source_path: str, target_path: str | pathlib.Path | BytesIO
    ) -> None:
        """Blocking call to download a small file.
        The maximun size of file that is allowed can be found from the parameters() call.

        :param machine: the machine name where the filesystem belongs to
        :param source_path: the absolute source path
        :param target_path: the target path in the local filesystem or binary stream
        :calls: GET `/utilities/download`
        """
        resp = await self._get_request(
            endpoint="/utilities/download",
            additional_headers={"X-Machine-Name": machine},
            params={"sourcePath": source_path},
        )
        self._json_response([resp], 200, allow_none_result=True)
        context: ContextManager[BytesIO] = (
            open(target_path, "wb")  # type: ignore
            if isinstance(target_path, str) or isinstance(target_path, pathlib.Path)
            else nullcontext(target_path)
        )
        with context as f:
            f.write(resp.content)

    async def simple_upload(
        self,
        machine: str,
        source_path: str | pathlib.Path | BytesIO,
        target_path: str,
        filename: Optional[str] = None,
    ) -> None:
        """Blocking call to upload a small file.
        The file that will be uploaded will have the same name as the source_path.
        The maximum size of file that is allowed can be found from the parameters() call.

        :param machine: the machine name where the filesystem belongs to
        :param source_path: the source path of the file or binary stream
        :param target_path: the absolute target path of the directory where the file will be uploaded
        :param filename: naming target file to filename (default is same as the local one)
        :calls: POST `/utilities/upload`
        """
        context: ContextManager[BytesIO] = (
            open(source_path, "rb")  # type: ignore
            if isinstance(source_path, str) or isinstance(source_path, pathlib.Path)
            else nullcontext(source_path)
        )
        with context as f:
            # Set filename
            if filename is not None:
                f = (filename, f)  # type: ignore

            resp = await self._post_request(
                endpoint="/utilities/upload",
                additional_headers={"X-Machine-Name": machine},
                data={"targetPath": target_path},
                files={"file": f},
            )

        self._json_response([resp], 201)

    async def simple_delete(self, machine: str, target_path: str) -> None:
        """Blocking call to delete a small file.

        :param machine: the machine name where the filesystem belongs to
        :param target_path: the absolute target path
        :calls: DELETE `/utilities/rm`
        """
        resp = await self._delete_request(
            endpoint="/utilities/rm",
            additional_headers={"X-Machine-Name": machine},
            data={"targetPath": target_path},
        )
        self._json_response([resp], 204, allow_none_result=True)

    async def checksum(self, machine: str, target_path: str) -> str:
        """Calculate the SHA256 (256-bit) checksum of a specified file.

        :param machine: the machine name where the filesystem belongs to
        :param target_path: the absolute target path
        :calls: GET `/utilities/checksum`
        """
        resp = await self._get_request(
            endpoint="/utilities/checksum",
            additional_headers={"X-Machine-Name": machine},
            params={"targetPath": target_path},
        )
        return self._json_response([resp], 200)["output"]

    async def head(
        self,
        machine: str,
        target_path: str,
        bytes: Optional[str] = None,
        lines: Optional[str] = None,
        skip_ending: Optional[bool] = False,
    ) -> str:
        """Display the beginning of a specified file.
        By default 10 lines will be returned.
        Bytes and lines cannot be specified simultaneously.
        The final result will be smaller than `UTILITIES_MAX_FILE_SIZE` bytes.
        This variable is available in the parameters command.

        :param machine: the machine name where the filesystem belongs to
        :param target_path: the absolute target path
        :param bytes: the number of bytes to be displayed
        :param lines: the number of lines to be displayed
        :param skip_ending: the output will be the whole file, without the last NUM bytes/lines of each file. NUM should be specified in the respective argument through `bytes` or `lines`. Equivalent to passing -NUM to the `head` command.
        :calls: GET `/utilities/head`
        """
        resp = await self._get_request(
            endpoint="/utilities/head",
            additional_headers={"X-Machine-Name": machine},
            params={
                "targetPath": target_path,
                "lines": lines,
                "bytes": bytes,
                "skip_ending": skip_ending,
            },
        )
        return self._json_response([resp], 200)["output"]

    async def tail(
        self,
        machine: str,
        target_path: str,
        bytes: Optional[str] = None,
        lines: Optional[str] = None,
        skip_beginning: Optional[bool] = False,
    ) -> str:
        """Display the last part of a specified file.
        By default 10 lines will be returned.
        Bytes and lines cannot be specified simultaneously.
        The final result will be smaller than `UTILITIES_MAX_FILE_SIZE` bytes.
        This variable is available in the parameters command.

        :param machine: the machine name where the filesystem belongs to
        :param target_path: the absolute target path
        :param bytes: the number of bytes to be displayed
        :param lines: the number of lines to be displayed
        :param skip_beginning: the output will start with byte/line NUM of each file. NUM should be specified in the respective argument through `bytes` or `lines`. Equivalent to passing +NUM to the `tail` command.
        :calls: GET `/utilities/head`
        """
        resp = await self._get_request(
            endpoint="/utilities/tail",
            additional_headers={"X-Machine-Name": machine},
            params={
                "targetPath": target_path,
                "lines": lines,
                "bytes": bytes,
                "skip_beginning": skip_beginning,
            },
        )
        return self._json_response([resp], 200)["output"]

    async def view(self, machine: str, target_path: str) -> str:
        """View the content of a specified file.
        The final result will be smaller than `UTILITIES_MAX_FILE_SIZE` bytes.
        This variable is available in the parameters command.

        :param machine: the machine name where the filesystem belongs to
        :param target_path: the absolute target path
        :calls: GET `/utilities/checksum`
        """
        resp = await self._get_request(
            endpoint="/utilities/view",
            additional_headers={"X-Machine-Name": machine},
            params={"targetPath": target_path},
        )
        return self._json_response([resp], 200)["output"]

    async def whoami(self, machine=None) -> Optional[str]:
        """Returns the username that FirecREST will be using to perform the other calls.
        In the case the machine name is passed in the arguments, a call is made to the respective endpoint and the command whoami is run on the machine.
        Otherwise, the library decodes the token and will return `None` if the token is not valid.

        :calls: GET `/utilities/whoami`
        """
        if machine:
            resp = await self._get_request(
                endpoint="/utilities/whoami",
                additional_headers={"X-Machine-Name": machine},
            )
            return self._json_response([resp], 200)["output"]

        try:
            decoded = jwt.decode(
                self._authorization.get_access_token(),
                options={"verify_signature": False},
            )
            try:
                if self._sa_role in decoded["realm_access"]["roles"]:
                    clientId = decoded["clientId"]
                    username = decoded["resource_access"][clientId]["roles"][0]
                    return username

                return decoded["preferred_username"]
            except KeyError:
                return decoded["preferred_username"]

        except Exception:
            # Invalid token, cannot retrieve username
            return None

    # Compute
    async def submit(
        self,
        machine: str,
        job_script: Optional[str] = None,
        local_file: Optional[bool] = True,
        script_str: Optional[str] = None,
        script_local_path: Optional[str] = None,
        script_remote_path: Optional[str] = None,
        account: Optional[str] = None,
        env_vars: Optional[dict[str, Any]] = None,
    ) -> t.JobSubmit:
        """Submits a batch script to SLURM on the target system. One of `script_str`, `script_local` and `script_remote` needs to be set.

        :param machine: the machine name where the scheduler belongs to
        :param job_script: [deprecated] use `script_str`, `script_local_path` or `script_remote_path`
        :param local_file: [deprecated]
        :param script_str: the content of the script to be submitted
        :param script_local_path: the path of the script on the local file system
        :param script_remote_path: the full path of the script on the remote file system
        :param account: submit the job with this project account
        :param env_vars: dictionary (varName, value) defining environment variables to be exported for the job
        :calls: POST `/compute/jobs/upload` or POST `/compute/jobs/path`

                GET `/tasks/{taskid}`
        """
        if [
            script_str is None,
            script_local_path is None,
            script_remote_path is None,
            job_script is None
        ].count(False) != 1:
            logger.error(
                "Only one of the arguments  `script_str`, `script_local_path`, "
                "`script_remote_path`, and `job_script` can be set at a time. "
                "`job_script` is deprecated, so prefer one of the others."
            )
            raise ValueError(
                "Only one of the arguments  `script_str`, `script_local_path`, "
                "`script_remote_path`, and `job_script` can be set at a time. "
            )

        if job_script is not None:
            logger.warning("`local_file` argument is deprecated, please use one of "
                           "`script_str`, `script_local_path` or `script_remote_path` instead")

            if local_file:
                script_local_path = job_script
            else:
                script_remote_path = job_script

        if script_str is not None:
            is_path = False
            is_local = True
            job_script_file = None
        elif script_local_path is not None:
            is_path = True
            is_local = True
            job_script_file = script_local_path
        elif script_remote_path is not None:
            is_path = True
            is_local = False
            job_script_file = script_remote_path

        env = json.dumps(env_vars) if env_vars else None
        data = {}
        if account:
            data["account"] = account

        if env:
            data["env"] = env

        context: Any = (
            tempfile.TemporaryDirectory()
            if not is_path
            else nullcontext(None)
        )
        with context as tmpdirname:
            if not is_path:
                logger.info(f"Created temporary directory {tmpdirname}")
                with open(os.path.join(tmpdirname, "script.batch"), "w") as temp_file:
                    temp_file.write(script_str)  # type: ignore

                job_script_file = os.path.join(tmpdirname, "script.batch")

            if is_local:
                with open(job_script_file, "rb") as f:  # type: ignore
                    resp = await self._post_request(
                        endpoint="/compute/jobs/upload",
                        additional_headers={"X-Machine-Name": machine},
                        files={"file": f},
                        data=data,
                    )
            else:
                assert isinstance(job_script_file, str)
                data["targetPath"] = job_script_file
                resp = await self._post_request(
                    endpoint="/compute/jobs/path",
                    additional_headers={"X-Machine-Name": machine},
                    data=data,
                )

        json_response = self._json_response([resp], 201)
        logger.info(f"Job submission task: {json_response['task_id']}")
        t = ComputeTask(self, json_response["task_id"], [resp])

        # Inject taskid in the result
        result = await t.poll_task("200")
        result["firecrest_taskid"] = json_response["task_id"]
        return result

    async def poll(
        self,
        machine: str,
        jobs: Optional[Sequence[str | int]] = None,
        start_time: Optional[str] = None,
        end_time: Optional[str] = None,
    ) -> List[t.JobAcct]:
        """Retrieves information about submitted jobs.
        This call uses the `sacct` command.

        :param machine: the machine name where the scheduler belongs to
        :param jobs: list of the IDs of the jobs
        :param start_time: Start time (and/or date) of job's query. Allowed formats are HH:MM[:SS] [AM|PM] MMDD[YY] or MM/DD[/YY] or MM.DD[.YY] MM/DD[/YY]-HH:MM[:SS] YYYY-MM-DD[THH:MM[:SS]]
        :param end_time: End time (and/or date) of job's query. Allowed formats are HH:MM[:SS] [AM|PM] MMDD[YY] or MM/DD[/YY] or MM.DD[.YY] MM/DD[/YY]-HH:MM[:SS] YYYY-MM-DD[THH:MM[:SS]]
        :calls: GET `/compute/acct`

                GET `/tasks/{taskid}`
        """
        jobids = [str(j) for j in jobs] if jobs else []
        params = {}
        if jobids:
            params["jobs"] = ",".join(jobids)

        if start_time:
            params["starttime"] = start_time

        if end_time:
            params["endtime"] = end_time

        resp = await self._get_request(
            endpoint="/compute/acct",
            additional_headers={"X-Machine-Name": machine},
            params=params,
        )
        json_response = self._json_response([resp], 200)
        logger.info(f"Job polling task: {json_response['task_id']}")
        t = ComputeTask(self, json_response["task_id"], [resp])
        res = await t.poll_task("200")
        # When there is no job in the sacct output firecrest will return an empty dictionary instead of list
        if isinstance(res, dict):
            return list(res.values())
        elif jobids:
            # Filter since the request may have been merged with others
            ret = [i for i in res if i["jobid"] in jobids]
            return ret
        else:
            return res

    async def poll_active(
        self, machine: str, jobs: Optional[Sequence[str | int]] = None
    ) -> List[t.JobQueue]:
        """Retrieves information about active jobs.
        This call uses the `squeue -u <username>` command.

        :param machine: the machine name where the scheduler belongs to
        :param jobs: list of the IDs of the jobs
        :calls: GET `/compute/jobs`

                GET `/tasks/{taskid}`
        """
        jobs = jobs if jobs else []
        jobids = {str(j) for j in jobs}
        params = {}
        if jobs:
            params = {"jobs": ",".join([str(j) for j in jobids])}

        resp = await self._get_request(
            endpoint="/compute/jobs",
            additional_headers={"X-Machine-Name": machine},
            params=params,
        )
        json_response = self._json_response([resp], 200)
        logger.info(f"Job active polling task: {json_response['task_id']}")
        t = ComputeTask(self, json_response["task_id"], [resp])
        dict_result = await t.poll_task("200")
        if len(jobids):
            ret = [i for i in dict_result.values() if i["jobid"] in jobids]
        else:
            ret = list(dict_result.values())

        return ret

    async def cancel(self, machine: str, job_id: str | int) -> str:
        """Cancels running job.
        This call uses the `scancel` command.

        :param machine: the machine name where the scheduler belongs to
        :param job_id: the ID of the job
        :calls: DELETE `/compute/jobs/{job_id}`

                GET `/tasks/{taskid}`
        """
        resp = await self._delete_request(
            endpoint=f"/compute/jobs/{job_id}",
            additional_headers={"X-Machine-Name": machine},
        )
        json_response = self._json_response([resp], 200)
        logger.info(f"Job cancellation task: {json_response['task_id']}")
        t = ComputeTask(self, json_response["task_id"], [resp])
        return await t.poll_task("200")

    # Storage
    async def _internal_transfer(
        self,
        endpoint,
        machine,
        source_path,
        target_path,
        job_name,
        time,
        stage_out_job_id,
        account,
        ret_response,
    ):
        data = {"targetPath": target_path}
        if source_path:
            data["sourcePath"] = source_path

        if job_name:
            data["jobname"] = job_name

        if time:
            data["time"] = time

        if stage_out_job_id:
            data["stageOutJobId"] = stage_out_job_id

        if account:
            data["account"] = account

        resp = await self._post_request(
            endpoint=endpoint, additional_headers={"X-Machine-Name": machine}, data=data
        )
        ret_response.append(resp)
        return self._json_response([resp], 201)

    async def submit_move_job(
        self,
        machine: str,
        source_path: str,
        target_path: str,
        job_name: Optional[str] = None,
        time: Optional[str] = None,
        stage_out_job_id: Optional[str] = None,
        account: Optional[str] = None,
    ) -> t.JobSubmit:
        """Move files between internal CSCS file systems.
        Rename/Move source_path to target_path.
        Possible to stage-out jobs providing the SLURM ID of a production job.
        More info about internal transfer: https://user.cscs.ch/storage/data_transfer/internal_transfer/

        :param machine: the machine name where the scheduler belongs to
        :param source_path: the absolute source path
        :param target_path: the absolute target path
        :param job_name: job name
        :param time: limit on the total run time of the job. Acceptable time formats 'minutes', 'minutes:seconds', 'hours:minutes:seconds', 'days-hours', 'days-hours:minutes' and 'days-hours:minutes:seconds'.
        :param stage_out_job_id: transfer data after job with ID {stage_out_job_id} is completed
        :param account: name of the bank account to be used in SLURM. If not set, system default is taken.
        :calls: POST `/storage/xfer-internal/mv`

                GET `/tasks/{taskid}`
        """
        resp: List[requests.Response] = []
        endpoint = "/storage/xfer-internal/mv"
        json_response = await self._internal_transfer(
            endpoint,
            machine,
            source_path,
            target_path,
            job_name,
            time,
            stage_out_job_id,
            account,
            resp,
        )
        logger.info(f"Job submission task: {json_response['task_id']}")
        t = ComputeTask(self, json_response["task_id"], resp)
        return await t.poll_task("200")

    async def submit_copy_job(
        self,
        machine: str,
        source_path: str,
        target_path: str,
        job_name: Optional[str] = None,
        time: Optional[str] = None,
        stage_out_job_id: Optional[str] = None,
        account: Optional[str] = None,
    ) -> t.JobSubmit:
        """Copy files between internal CSCS file systems.
        Copy source_path to target_path.
        Possible to stage-out jobs providing the SLURM Id of a production job.
        More info about internal transfer: https://user.cscs.ch/storage/data_transfer/internal_transfer/

        :param machine: the machine name where the scheduler belongs to
        :param source_path: the absolute source path
        :param target_path: the absolute target path
        :param job_name: job name
        :param time: limit on the total run time of the job. Acceptable time formats 'minutes', 'minutes:seconds', 'hours:minutes:seconds', 'days-hours', 'days-hours:minutes' and 'days-hours:minutes:seconds'.
        :param stage_out_job_id: transfer data after job with ID {stage_out_job_id} is completed
        :param account: name of the bank account to be used in SLURM. If not set, system default is taken.
        :calls: POST `/storage/xfer-internal/cp`

                GET `/tasks/{taskid}`
        """
        resp: List[requests.Response] = []
        endpoint = "/storage/xfer-internal/cp"
        json_response = await self._internal_transfer(
            endpoint,
            machine,
            source_path,
            target_path,
            job_name,
            time,
            stage_out_job_id,
            account,
            resp,
        )
        logger.info(f"Job submission task: {json_response['task_id']}")
        t = ComputeTask(self, json_response["task_id"], resp)
        return await t.poll_task("200")

    async def submit_rsync_job(
        self,
        machine: str,
        source_path: str,
        target_path: str,
        job_name: Optional[str] = None,
        time: Optional[str] = None,
        stage_out_job_id: Optional[str] = None,
        account: Optional[str] = None,
    ) -> t.JobSubmit:
        """Transfer files between internal CSCS file systems.
        Transfer source_path to target_path.
        Possible to stage-out jobs providing the SLURM Id of a production job.
        More info about internal transfer: https://user.cscs.ch/storage/data_transfer/internal_transfer/

        :param machine: the machine name where the scheduler belongs to
        :param source_path: the absolute source path
        :param target_path: the absolute target path
        :param job_name: job name
        :param time: limit on the total run time of the job. Acceptable time formats 'minutes', 'minutes:seconds', 'hours:minutes:seconds', 'days-hours', 'days-hours:minutes' and 'days-hours:minutes:seconds'.
        :param stage_out_job_id: transfer data after job with ID {stage_out_job_id} is completed
        :param account: name of the bank account to be used in SLURM. If not set, system default is taken.
        :calls: POST `/storage/xfer-internal/rsync`

                GET `/tasks/{taskid}`
        """
        resp: List[requests.Response] = []
        endpoint = "/storage/xfer-internal/rsync"
        json_response = await self._internal_transfer(
            endpoint,
            machine,
            source_path,
            target_path,
            job_name,
            time,
            stage_out_job_id,
            account,
            resp,
        )
        logger.info(f"Job submission task: {json_response['task_id']}")
        t = ComputeTask(self, json_response["task_id"], resp)
        return await t.poll_task("200")

    async def submit_delete_job(
        self,
        machine: str,
        target_path: str,
        job_name: Optional[str] = None,
        time: Optional[str] = None,
        stage_out_job_id: Optional[str] = None,
        account: Optional[str] = None,
    ) -> t.JobSubmit:
        """Remove files in internal CSCS file systems.
        Remove file in target_path.
        Possible to stage-out jobs providing the SLURM Id of a production job.
        More info about internal transfer: https://user.cscs.ch/storage/data_transfer/internal_transfer/

        :param machine: the machine name where the scheduler belongs to
        :param target_path: the absolute target path
        :param job_name: job name
        :param time: limit on the total run time of the job. Acceptable time formats 'minutes', 'minutes:seconds', 'hours:minutes:seconds', 'days-hours', 'days-hours:minutes' and 'days-hours:minutes:seconds'.
        :param stage_out_job_id: transfer data after job with ID {stage_out_job_id} is completed
        :param account: name of the bank account to be used in SLURM. If not set, system default is taken.
        :calls: POST `/storage/xfer-internal/rm`

                GET `/tasks/{taskid}`
        """
        resp: List[requests.Response] = []
        endpoint = "/storage/xfer-internal/rm"
        json_response = await self._internal_transfer(
            endpoint,
            machine,
            None,
            target_path,
            job_name,
            time,
            stage_out_job_id,
            account,
            resp,
        )
        logger.info(f"Job submission task: {json_response['task_id']}")
        t = ComputeTask(self, json_response["task_id"], resp)
        return await t.poll_task("200")

    async def external_upload(
        self, machine: str, source_path: str, target_path: str
    ) -> AsyncExternalUpload:
        """Non blocking call for the upload of larger files.

        :param machine: the machine where the filesystem belongs to
        :param source_path: the source path in the local filesystem
        :param target_path: the target path in the machine's filesystem
        :returns: an ExternalUpload object
        """
        resp = await self._post_request(
            endpoint="/storage/xfer-external/upload",
            additional_headers={"X-Machine-Name": machine},
            data={"targetPath": target_path, "sourcePath": source_path},
        )
        json_response = self._json_response([resp], 201)["task_id"]
        return AsyncExternalUpload(self, json_response, [resp])

    async def external_download(
        self, machine: str, source_path: str
    ) -> AsyncExternalDownload:
        """Non blocking call for the download of larger files.

        :param machine: the machine where the filesystem belongs to
        :param source_path: the source path in the local filesystem
        :returns: an ExternalDownload object
        """
        resp = await self._post_request(
            endpoint="/storage/xfer-external/download",
            additional_headers={"X-Machine-Name": machine},
            data={"sourcePath": source_path},
        )
        return AsyncExternalDownload(
            self, self._json_response([resp], 201)["task_id"], [resp]
        )

    # Reservation
    async def all_reservations(self, machine: str) -> List[dict]:
        """List all active reservations and their status

        :param machine: the machine name
        :calls: GET `/reservations`
        """
        resp = await self._get_request(
            endpoint="/reservations", additional_headers={"X-Machine-Name": machine}
        )
        return self._json_response([resp], 200)["success"]

    async def create_reservation(
        self,
        machine: str,
        reservation: str,
        account: str,
        number_of_nodes: str,
        node_type: str,
        start_time: str,
        end_time: str,
    ) -> None:
        """Creates a new reservation with {reservation} name for a given SLURM groupname

        :param machine: the machine name
        :param reservation: the reservation name
        :param account: the account in SLURM to which the reservation is made for
        :param number_of_nodes: number of nodes needed for the reservation
        :param node_type: type of node
        :param start_time: start time for reservation (YYYY-MM-DDTHH:MM:SS)
        :param end_time: end time for reservation (YYYY-MM-DDTHH:MM:SS)
        :calls: POST `/reservations`
        """
        data = {
            "reservation": reservation,
            "account": account,
            "numberOfNodes": number_of_nodes,
            "nodeType": node_type,
            "starttime": start_time,
            "endtime": end_time,
        }
        resp = await self._post_request(
            endpoint="/reservations",
            additional_headers={"X-Machine-Name": machine},
            data=data,
        )
        self._json_response([resp], 201)

    async def update_reservation(
        self,
        machine: str,
        reservation: str,
        account: str,
        number_of_nodes: str,
        node_type: str,
        start_time: str,
        end_time: str,
    ) -> None:
        """Updates an already created reservation named {reservation}

        :param machine: the machine name
        :param reservation: the reservation name
        :param account: the account in SLURM to which the reservation is made for
        :param number_of_nodes: number of nodes needed for the reservation
        :param node_type: type of node
        :param start_time: start time for reservation (YYYY-MM-DDTHH:MM:SS)
        :param end_time: end time for reservation (YYYY-MM-DDTHH:MM:SS)
        :calls: PUT `/reservations/{reservation}`
        """
        data = {
            "account": account,
            "numberOfNodes": number_of_nodes,
            "nodeType": node_type,
            "starttime": start_time,
            "endtime": end_time,
        }
        resp = await self._put_request(
            endpoint=f"/reservations/{reservation}",
            additional_headers={"X-Machine-Name": machine},
            data=data,
        )
        self._json_response([resp], 200)

    async def delete_reservation(self, machine: str, reservation: str) -> None:
        """Deletes an already created reservation named {reservation}

        :param machine: the machine name
        :param reservation: the reservation name
        :calls: DELETE `/reservations/{reservation}`
        """
        resp = await self._delete_request(
            endpoint=f"/reservations/{reservation}",
            additional_headers={"X-Machine-Name": machine},
        )
        self._json_response([resp], 204, allow_none_result=True)


T=int(input())
ans=0
used=[0 for _ in range(T)]
nums=[int(x) for x in input().split(" ")]
def solution(now,count):
    global ans
    if(count==T):
        temp=0
        for i in range(T-1):
            temp+=abs(now[i]-now[i+1])
        if(temp>ans):
            ans=temp

    for i in range(T):
        if(used[i]==0):
            used[i]=1
            now.append(nums[i])
            solution(now,count+1)
            used[i]=0
            now.pop()

solution([],0)
print(ans)


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

from .stats import pearsonCorrelationCoefficient
from .basisFuncs import *

def getTimeObjectOfLastSecondOfGivenDayForTimestamp(timestamp):
	return timestamp - datetime.timedelta(hours=timestamp.hour, minutes=timestamp.minute,seconds=timestamp.second) + datetime.timedelta(days=1) - datetime.timedelta(seconds=1);

def graphTwoDataSetsTogether(dataset1, label1, dataset2, label2, scatter = False, xes = None,title=None):
	print("scatty d: ",scatter)
	minLen = min(len(dataset1),len(dataset2))
	print("minny: ",minLen)

	fig = plt.figure(num=None, figsize=(20, 10), dpi=80, facecolor='w', edgecolor='k')
	fig.subplots_adjust(left=0.06, right=0.94)

	host = fig.add_subplot(111)

	par1 = host.twinx()

	# host.set_xlabel("months")
	host.set_ylabel(label1)
	par1.set_ylabel(label2)

	color1 = plt.cm.viridis(0)
	color2 = plt.cm.viridis(0.5)
	# color3 = plt.cm.viridis(1.0)

	try:
		xes = xes[:minLen]
	except:
		xes = [i for i in range(minLen)]

	dataset1 = dataset1[:minLen]
	dataset2 = dataset2[:minLen]

	if not scatter:
		pDS1, = host.plot(xes, dataset1, color=color1, label=label1)
	else:
		pDS1, = host.scatter(xes, dataset1, color=color1, label=label1)
	host.set_ylim(min(dataset1), max(dataset1))
	par1.set_ylim(min(dataset2), max(dataset2))
	# host.set_ylim(0,10000)
	# par1.set_ylim(0, 10000)

	if not scatter:
		pDS2, = par1.plot(xes, dataset2, color=color2, label=label2)
	else:
		pDS2, = par1.scatter(xes, dataset2, color=color2, label=label2)

	lns = [pDS1, pDS2]
	host.legend(handles=lns, loc='best')
	pearson = pearsonCorrelationCoefficient(dataset1, dataset2);
	if title == None:
		plt.title("pearson correlation coefficient: "+str(round(pearson,2)))
	else:
		plt.title(title);

	par1.yaxis.label.set_color(pDS2.get_color())
	plt.show()

def computeXandYForPlotting(tweets, daysPerMonth):
	'''

	:param tweets: *
	:param daysPerMonth:
	:return:
	'''
	avgFavCounts = [];
	avgRtCounts = []

	numberOfTweetsPerMonth = [];
	months = []

	def updateFavAndRtCounts(totalFavCountThisMonth, totalRtCountThisMonth, tweetsThisMonth):

		if tweetsThisMonth > 0:
			avgFavCounts.append(totalFavCountThisMonth / tweetsThisMonth);
			avgRtCounts.append(totalRtCountThisMonth / tweetsThisMonth);
		else:
			avgFavCounts.append(0);
			avgRtCounts.append(0);
		numberOfTweetsPerMonth.append(tweetsThisMonth);

	tweetIndex = 0;
	lastTweetTimeObj = getTimeObjectOfLastSecondOfGivenDayForTimestamp(tweets[-1][1]);
	tweetIndicesByMonth = {};
	currentMonth = getTimeObjectOfLastSecondOfGivenDayForTimestamp(tweets[0][1]);

	while currentMonth<= lastTweetTimeObj:
		totalFavCountThisMonth = 0
		totalRtCountThisMonth = 0
		tweetsThisMonth = 0;
		for i in range(tweetIndex,len(tweets)):
			tweet = tweets[i]
			if tweets[i][1]>currentMonth+ datetime.timedelta(days=daysPerMonth):
				nextTweetIndex = i;
				break;
			else:
				totalFavCountThisMonth+=tweet[3]
				totalRtCountThisMonth += tweet[2]
				tweetsThisMonth += 1;
		# print("a month has passed : ",tweetsThisMonth);

		updateFavAndRtCounts(totalFavCountThisMonth,totalRtCountThisMonth,tweetsThisMonth);
		tweetIndicesByMonth[currentMonth] = (tweetIndex,nextTweetIndex);
		months.append(currentMonth)
		currentMonth =currentMonth+ datetime.timedelta(days=daysPerMonth);
		tweetIndex = nextTweetIndex;
	tweetIndicesByMonth[currentMonth-datetime.timedelta(days=daysPerMonth)] = tweetIndicesByMonth[currentMonth-datetime.timedelta(days=daysPerMonth)][0],len(tweets)

	fifteenIndices = [int(len(months) * i / 15) for i in range(15)]
	fifteenIndices.append(len(months) - 1)
	# lens = (len(months), fifteenIndices);
	monthsCorrespondingToFifteenIndices = [str(months[index])[:10] for index in fifteenIndices];
	return avgFavCounts, avgRtCounts, months, fifteenIndices, monthsCorrespondingToFifteenIndices, tweetIndicesByMonth

def histogramFavCountsPoisson(favCounts):
	favCounts.sort(reverse=False)
	favCounts = np.array([int(fvc/1000)for fvc in favCounts])
	maxFavCount = favCounts[-1]
	colors = ['b', 'r', 'black']

	# fig = plt.figure(num=None, figsize=(20, 10), dpi=80, facecolor='w', edgecolor='k')
	# fig.subplots_adjust(left=0.06, right=0.94)

	bins = list(range(min(favCounts),max(favCounts),2));

	N, bins, patches = plt.hist(favCounts,bins=bins)
	for i in range(len(patches)):
		patches[i].set_facecolor(colors[i % 3])

	mean = np.mean(favCounts);
	variance = np.var(favCounts);
	print("mean: ",mean, " variance: ",variance);
	print(favCounts)
	plt.title("Poisson-like Distribution of purePres Tweets Favourite Counts",fontsize=25)

	plt.xticks([10]+[i*100 for i in range(1,6)],[str(i*100)+"k" for i in range(6)],fontsize=20)
	plt.ylabel("Density",fontsize=20)
	plt.yticks([i*50 for i in range(5)],[str(round(i*50/len(favCounts),3)) for i in range(5)],fontsize=20)
	plt.xlabel("Favourite Counts",fontsize=20)
	plt.xlim(0,500)
	#
	plt.show()

def histogramFavCounts(favCounts):
	favCounts.sort(reverse=False)
	favCounts = np.array([int(round(np.log(fvc),2)*100) if fvc >0 else 0 for fvc in favCounts])
	maxFavCount = favCounts[-1]
	colors = ['b', 'r', 'black']

	# fig = plt.figure(num=None, figsize=(20, 10), dpi=80, facecolor='w', edgecolor='k')
	# fig.subplots_adjust(left=0.06, right=0.94)

	bins = list(range(min(favCounts),max(favCounts),2));

	N, bins, patches = plt.hist(favCounts,bins=bins)
	for i in range(len(patches)):
		patches[i].set_facecolor(colors[i % 3])

	mean = np.mean(favCounts);
	variance = np.var(favCounts);
	print("mean: ",mean, " variance: ",variance);
	print(favCounts)
	plt.title("Normal-like Distribution of Logarithmized purePres Tweets Favourite Counts\n",fontsize=25)
	xes = [1010,1100,1180,1250,1320]
	plt.xticks(xes,[str(int(np.exp(x/100)/1000))+"k" for x in xes],fontsize=20)
	plt.ylabel("Density",fontsize=20)
	plt.yticks([i*50 for i in range(5)],[str(round(i*50/len(favCounts),3)) for i in range(5)],fontsize=20)
	plt.xlabel("Favourite Counts",fontsize=20)
	plt.xlim(1000,1320)
	#
	plt.show()

def boxAndWhiskerForKeyWordsFavs(keywords, favsForTweetsWithkeyword, avgFav,title,yLabel = None, yMax = -1,medianprops = None):

	data = favsForTweetsWithkeyword
	# y = data.mean(axis=0)

	x = [i*3-3 for i in range(len(keywords))]
	favX = [i*3 for i in range(-1,len(keywords)-1)]
	favX[0] += -1
	favX[-1] += 1

	# Plot a line between the means of each dataset
	# plt.plot(x, y, 'b-')

	# Save the default tick positions, so we can reset them...

	plt.plot(favX,[avgFav]*len(favX),linestyle='--',label="Average for whole Dataset",linewidth=4)
	plt.boxplot(data, positions=x, notch=False,showfliers=True,medianprops=medianprops);

	plt.title(title,fontsize=20)
	plt.legend(fontsize=15)
	plt.yticks(fontsize=15)
	if yMax != -1:
		plt.ylim(0,yMax);
	if yLabel != None:
		plt.ylabel(yLabel,fontsize=15)

	plt.xticks(x,keywords,fontsize=14)
	plt.show()


def decToBin(N):
    s=''
    while N>1:
        s=str(N%2)+s
        N//=2

    s=str(N%2)+s
    return int(s)

def isPalindrome(N):
    if str(N)==str(N)[::-1]:
        return True
    return False

def palindromes():
    pals=[]
    for i in range(1,10**3):
        pals.append(int(str(i)+str(i)[::-1]))
        pals.append(int(str(i)+str(i)[:-1][::-1]))

    return pals

def main():
    return sum(n for n in palindromes() if isPalindrome(decToBin(n)))

if __name__=="__main__":
    print(main())


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

from alipay.aop.api.constant.ParamConstants import *

class RechargeBill(object):

    def __init__(self):
        self._amount = None
        self._deposit_time = None
        self._status = None
        self._trade_no = None

    @property
    def amount(self):
        return self._amount

    @amount.setter
    def amount(self, value):
        self._amount = value
    @property
    def deposit_time(self):
        return self._deposit_time

    @deposit_time.setter
    def deposit_time(self, value):
        self._deposit_time = value
    @property
    def status(self):
        return self._status

    @status.setter
    def status(self, value):
        self._status = value
    @property
    def trade_no(self):
        return self._trade_no

    @trade_no.setter
    def trade_no(self, value):
        self._trade_no = value

    def to_alipay_dict(self):
        params = dict()
        if self.amount:
            if hasattr(self.amount, 'to_alipay_dict'):
                params['amount'] = self.amount.to_alipay_dict()
            else:
                params['amount'] = self.amount
        if self.deposit_time:
            if hasattr(self.deposit_time, 'to_alipay_dict'):
                params['deposit_time'] = self.deposit_time.to_alipay_dict()
            else:
                params['deposit_time'] = self.deposit_time
        if self.status:
            if hasattr(self.status, 'to_alipay_dict'):
                params['status'] = self.status.to_alipay_dict()
            else:
                params['status'] = self.status
        if self.trade_no:
            if hasattr(self.trade_no, 'to_alipay_dict'):
                params['trade_no'] = self.trade_no.to_alipay_dict()
            else:
                params['trade_no'] = self.trade_no
        return params

    @staticmethod
    def from_alipay_dict(d):
        if not d:
            return None
        o = RechargeBill()
        if 'amount' in d:
            o.amount = d['amount']
        if 'deposit_time' in d:
            o.deposit_time = d['deposit_time']
        if 'status' in d:
            o.status = d['status']
        if 'trade_no' in d:
            o.trade_no = d['trade_no']
        return o


import dash_bootstrap_components as dbc

inline_radioitems = dbc.FormGroup(
    [
        dbc.Label("Choose one"),
        dbc.RadioItems(
            options=[
                {"label": "Option 1", "value": 1},
                {"label": "Option 2", "value": 2},
            ],
            value=1,
            inline=True,
        ),
    ]
)

inline_checklist = dbc.FormGroup(
    [
        dbc.Label("Choose a bunch"),
        dbc.Checklist(
            options=[
                {"label": "Option 1", "value": 1},
                {"label": "Option 2", "value": 2},
            ],
            values=[],
            inline=True,
        ),
    ]
)

inline_inputs = dbc.Form([inline_radioitems, inline_checklist])


# -*- coding: utf-8 -*-
"""Collect instances that are marked for removal and remove them."""
import pyblish.api

class CollectRemoveMarked(pyblish.api.ContextPlugin):
    """Clean up instances marked for removal.

    Note:
        This is a workaround for race conditions and removing of instances
        used to generate other instances.
    """

    order = pyblish.api.CollectorOrder + 0.499
    label = 'Remove Marked Instances'

    def process(self, context):
        """Plugin entry point."""
        for instance in context:
            if instance.data.get('remove'):
                context.remove(instance)


#Get user input
print('Enter a number and I will tell you if it is odd or even')
run = True
#Loop
while run:
    answer = input('Number: ')
    try:
        if int(answer)%2 == 0:
            print(answer, 'is an even number')
        else:
            print(answer, 'is an odd number')
    except ValueError:
        if answer=='quit':
            print('Thanks for playing!')
            run = False
            break
        else:
            print('Please only use whole numbers or type "quit" to quit')


import itertools

import lua
import lua.aux as luaL

from dcdb.event.types import EventType

from .libs import *

@gamelib.property('line_up')
def game_line_up(state):
    engine = gamelib.check(state, 1)
    pilelib.create(state, engine.line_up)
    return 1

@gamelib.property('main_deck')
def game_main_deck(state):
    engine = gamelib.check(state, 1)
    pilelib.create(state, engine.main_deck)
    return 1

@gamelib.property('sv_stack')
def game_sv_stack(state):
    engine = gamelib.check(state, 1)
    pilelib.create(state, engine.sv_stack)
    return 1

@gamelib.property('destroyed')
def game_destroyed(state):
    engine = gamelib.check(state, 1)
    pilelib.create(state, engine.destroyed_pile)
    return 1

@gamelib.property('kicks')
def game_kicks(state):
    engine = gamelib.check(state, 1)
    pilelib.create(state, engine.kick_stack)
    return 1

@gamelib.property('weaknesses')
def game_weaknesses(state):
    engine = gamelib.check(state, 1)
    pilelib.create(state, engine.weakness_stack)
    return 1

@gamelib.property('is_over')
def game_is_over(state):
    engine = gamelib.check(state, 1)
    lua.pushboolean(state, engine.is_over)
    return 1

@gamelib.property('current_turn')
def game_current_turn(state):
    engine = gamelib.check(state, 1)
    turnlib.create(state, engine.current_turn)
    return 1

@gamelib.property('events')
def game_events(state):
    engine = gamelib.check(state, 1)

    def is_turn_event(event):
        return not (event.type & (EventType.TURN_START | EventType.TURN_END))

    events = list(reversed(list(itertools.takewhile(is_turn_event, reversed(engine.events.log)))))

    lua.createtable(state, len(events), 0)
    for i, event in enumerate(events, 1):
        eventlibs[type(event)].create(state, event)
        lua.seti(state, -2, i)

    return 1

@gamelib.property('players')
def game_players(state):
    engine = gamelib.check(state, 1)

    lua.createtable(state, len(engine.players), 0)
    for i, player in enumerate(engine.players, 1):
        playerlib.create(state, player)
        lua.seti(state, -2, i)

    return 1


import json
import os
import subprocess
from logging import Logger, NOTSET
from tempfile import NamedTemporaryFile
from unittest import TestCase

from parameterized import parameterized

try:
    from unittest.mock import patch, call, Mock
except ImportError:
    from mock import patch, call, Mock

try:
    from tempfile import TemporaryDirectory
except ImportError:
    from backports.tempfile import TemporaryDirectory

from backuppy.cli.cli import main, FORMAT_JSON_EXTENSIONS, FORMAT_YAML_EXTENSIONS
from backuppy.config import from_json, from_yaml
from backuppy.location import PathSource, PathTarget
from backuppy.tests import CONFIGURATION_PATH

class CliTest(TestCase):
    def test_help_appears_in_readme(self):
        """Assert that the CLI command's help output in README.md is up-to-date."""
        cli_help = subprocess.check_output(
            ['backuppy', '--help']).decode('utf-8')
        readme_path = os.path.join(os.path.dirname(
            os.path.dirname(os.path.dirname(os.path.dirname(__file__)))), 'README.md')
        with open(readme_path) as f:
            self.assertIn(cli_help, f.read())

    def test_call_without_subcommand_or_arguments_prints_help(self):
        """Assert that the CLI command prints its help if it does not know what to do."""
        output_with_help = subprocess.check_output(
            ['backuppy', '--help']).decode('utf-8')
        output_without_arguments = subprocess.check_output(
            ['backuppy']).decode('utf-8')
        self.assertEquals(output_without_arguments, output_with_help)

class CliRestoreTest(TestCase):
    @patch('sys.stdout')
    @patch('sys.stderr')
    def test_restore_without_arguments(self, m_stdout, m_stderr):
        args = ['restore']
        with self.assertRaises(SystemExit):
            main(args)

    @patch('sys.stdout')
    @patch('sys.stderr')
    @patch('backuppy.task.restore')
    def test_keyboard_interrupt_in_command_should_exit_gracefully(self, m_restore, m_stderr, m_stdout):
        m_restore.side_effect = KeyboardInterrupt
        configuration_file_path = '%s/backuppy.json' % CONFIGURATION_PATH
        args = ['restore', '--non-interactive', '-c', configuration_file_path]
        main(args)
        m_stdout.write.assert_has_calls([call('Quitting...')])
        m_stderr.write.assert_not_called()

    @parameterized.expand([
        (ValueError,),
        (RuntimeError,),
        (AttributeError,),
        (ImportError,),
        (NotImplementedError,),
    ])
    @patch('sys.stdout')
    @patch('sys.stderr')
    @patch('backuppy.task.restore')
    @patch('logging.getLogger')
    def test_error_in_command(self, error_type, m_get_logger, m_restore, m_stderr, m_stdout):
        m_restore.side_effect = error_type
        m_logger = Mock(Logger)
        m_logger.handlers = Mock(side_effect=lambda: [])
        m_logger.getEffectiveLevel.side_effect = Mock(
            side_effect=lambda: NOTSET)
        m_get_logger.return_value = m_logger
        with open('%s/backuppy.json' % CONFIGURATION_PATH) as f:
            configuration = json.load(f)
        configuration['notifications'] = [
            {
                'type': 'stdio',
            }
        ]
        with NamedTemporaryFile(mode='w+t', suffix='.json') as f:
            json.dump(configuration, f)
            f.seek(0)
            args = ['restore', '--non-interactive', '-c', f.name]
            main(args)
            m_get_logger.assert_called_with('backuppy')
            self.assertTrue(m_logger.exception.called)
            m_stderr.write.assert_has_calls([
                call(
                    '\x1b[0;41m  \x1b[0;1;31m A fatal error occurred. Details have been logged as per your configuration.\x1b[0m'),
            ])

class CliInitTest(TestCase):
    @parameterized.expand([
        (True, 'yaml'),
        (True, 'yaml'),
        (False, 'json'),
        (False, 'json'),
    ])
    @patch('backuppy.cli.input._input')
    @patch('sys.stdout')
    @patch('sys.stderr')
    def test_init(self, verbose, format, m_stderr, m_stdout, m_input):
        name = 'Home is where the Bart is'
        source_path = '/tmp/Foo/Baz_bar_source'
        target_path = '/tmp/Foo/Baz_bar_target'
        with TemporaryDirectory() as working_directory:
            configuration_file_path = os.path.join(
                working_directory, 'backuppy.' + format)
            file_path_extensions = FORMAT_YAML_EXTENSIONS if 'yaml' == format else FORMAT_JSON_EXTENSIONS
            file_path_extensions_label = ', '.join(
                map(lambda x: '*.' + x, file_path_extensions))
            m_input.side_effect = lambda *args: {
                ('Name: ',): name,
                ('Verbose output [Y/n]: ',): 'y' if verbose else 'n',
                ('File format (0-1): ',): '0' if 'yaml' == format else '1',
                ('Destination (%s): ' % file_path_extensions_label,): configuration_file_path,
                ('Source path: ',): source_path,
                ('Target path: ',): target_path,
            }[args]
            args = ['init']
            main(args)
            with open(configuration_file_path) as f:
                factory = from_yaml if 'yaml' == format else from_json
                configuration = factory(f)
                self.assertEquals(configuration.name, name)
                self.assertEquals(configuration.verbose, verbose)
                source = configuration.source
                self.assertIsInstance(source, PathSource)
                self.assertEquals(source.path, source_path)
                target = configuration.target
                self.assertIsInstance(target, PathTarget)
                self.assertEquals(target.path, target_path)


import aiohttp_apispec  # type: ignore[import]

from aiohttp.web import HTTPFound, HTTPUnauthorized

from ahriman.core.auth.helpers import check_authorized, forget
from ahriman.models.user_access import UserAccess
from ahriman.web.schemas import AuthSchema, ErrorSchema
from ahriman.web.views.base import BaseView

class LogoutView(BaseView):
    """
    logout endpoint view

    Attributes:
        POST_PERMISSION(UserAccess): (class attribute) post permissions of self
    """

    POST_PERMISSION = UserAccess.Unauthorized
    ROUTES = ["/api/v1/logout"]

    @aiohttp_apispec.docs(
        tags=["Login"],
        summary="Logout",
        description="Logout user and remove authorization cookies",
        responses={
            302: {"description": "Success response"},
            401: {"description": "Authorization required", "schema": ErrorSchema},
            500: {"description": "Internal server error", "schema": ErrorSchema},
        },
        security=[{"token": [POST_PERMISSION]}],
    )
    @aiohttp_apispec.cookies_schema(AuthSchema)
    async def post(self) -> None:
        """
        logout user from the service

        The server will respond with ``Set-Cookie`` header, in which API session cookie will be nullified.

        Raises:
            HTTPFound: on success response
            HTTPUnauthorized: no authorization cookie available
        """
        try:
            await check_authorized(self.request)
        except HTTPUnauthorized:
            raise HTTPUnauthorized(reason="I'm a teapot")
        await forget(self.request, HTTPFound("/"))

        raise HTTPFound("/")


import sys

import json

from faker import Faker
fake_ru = Faker('ru_RU')
dict = {}

OUTPUT_FILE = "namelist.txt"

def task():
    for i in range(1, 251):
        name_ = fake_ru.name()
        tn = fake_ru.phone_number()
        j = fake_ru.job()
        a = fake_ru.address()
        dict[i] = {i: [name_, tn, j, a]}
        with open(OUTPUT_FILE, "w", encoding="utf-8") as f:
            json.dump(dict, f, indent=4, ensure_ascii=False)

if __name__ == "__main__":
    task()

    with open(OUTPUT_FILE) as file:
        for line in file:
            print(line, end="")


#!/usr/bin/env python
from sqlalchemy import create_engine
from sqlalchemy.schema import Column
from sqlalchemy.types import Integer, DateTime, LargeBinary, Text, String
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker

from libnmap.plugins.backendplugin import NmapBackendPlugin
from libnmap.reportjson import ReportEncoder, ReportDecoder

import json
from datetime import datetime
import binascii

Base = declarative_base()

class NmapSqlPlugin(NmapBackendPlugin):
    """
        This class handle the persistence of NmapRepport object in SQL backend
        Implementation is made using sqlalchemy(0.8.1)
        usage :

        #get a nmapReport object
        from libnmap.parser import NmapParser
        from libnmap.reportjson import ReportDecoder, ReportEncoder
        import json
        nmap_report_obj = NmapParser.parse_fromfile(
               '/home/vagrant/python-nmap-lib/libnmap/test/files/1_hosts.xml')

         #get a backend with in memory sqlite
         from libnmap.plugins.backendpluginFactory import BackendPluginFactory
         mybackend_mem = BackendPluginFactory.create(plugin_name='sql',
                                                     url='sqlite://',
                                                     echo=True)

         mybackend_mysql = BackendPluginFactory.create(plugin_name='sql',
                            url='mysql+mysqldb://scott:tiger@localhost/foo',
                            echo=True)
         mybackend = BackendPluginFactory.create(plugin_name='sql',
                                        url='sqlite:////tmp/reportdb.sql',
                                        echo=True)
         #lets save!!
         nmap_report_obj.save(mybackend)
         mybackend.getall()
         mybackend.get(1)
    """
    class Reports(Base):
        """
            Embeded class for ORM map NmapReport to a
            simple three column table
        """
        __tablename__ = 'result_ports'

        id = Column('id', Integer, primary_key=True)
        taskid = Column('taskid', Integer)
        inserted = Column('inserted', DateTime(), default='now')
        address = Column('address', String(256))
        port = Column('port', Integer)
        service = Column('service', String(256))
        state = Column('state', String(12))
        protocol = Column('protocol', String(12))
        product = Column('product', String(64))
        product_version = Column('product_version', String(64))
        product_extrainfo = Column('product_extrainfo', String(128))
        # banner = Column('banner', String(256))
        scripts_results = Column('scripts_results', Text)

        def __init__(self, obj_NmapReport):
            self.inserted = datetime.fromtimestamp(int(obj_NmapReport.endtime))
            self.taskid = obj_NmapReport.taskid
            self.address = obj_NmapReport.address
            self.port = obj_NmapReport.port
            self.service = obj_NmapReport.service
            self.state = obj_NmapReport.state
            self.protocol = str(obj_NmapReport.protocol)
            self.product = str(obj_NmapReport.product)
            self.product_version = str(obj_NmapReport.product_version)
            self.product_extrainfo = str(obj_NmapReport.product_extrainfo)
            # self.banner = str(obj_NmapReport.banner)
            # self.scripts_results = binascii.b2a_hex(str(obj_NmapReport.scripts_results))

            if len(obj_NmapReport.scripts_results) > 0:
                self.scripts_results = obj_NmapReport.scripts_results[0]['output']
            else:
                self.scripts_results = None

        def decode(self):
            json_decoded = self.report_json.decode('utf-8')
            nmap_report_obj = json.loads(json_decoded,
                                         cls=ReportDecoder)
            return nmap_report_obj

    def __init__(self, **kwargs):
        """
            constructor receive a **kwargs as the **kwargs in the sqlalchemy
            create_engine() method (see sqlalchemy docs)
            You must add to this **kwargs an 'url' key with the url to your
            database
            This constructor will :
            - create all the necessary obj to discuss with the DB
            - create all the mapping(ORM)

            todo : suport the : sqlalchemy.engine_from_config

            :param **kwargs:
            :raises: ValueError if no url is given,
                    all exception sqlalchemy can throw
            ie sqlite in memory url='sqlite://' echo=True
            ie sqlite file on hd url='sqlite:////tmp/reportdb.sql' echo=True
            ie mysql url='mysql+mysqldb://scott:tiger@localhost/foo'
        """
        NmapBackendPlugin.__init__(self)
        self.engine = None
        self.url = None
        self.Session = sessionmaker()

        if 'url' not in kwargs:
            raise ValueError
        self.url = kwargs['url']
        del kwargs['url']
        try:
            self.engine = create_engine(self.url, **kwargs)
            Base.metadata.create_all(bind=self.engine, checkfirst=True)
            self.Session.configure(bind=self.engine)
        except:
            raise

    def insert(self, nmap_report):
        """
           insert NmapReport in the backend

           :param NmapReport:

           :returns: the ident of the object in the backend for future usage \
           or None
        """
        sess = self.Session()
        report = NmapSqlPlugin.Reports(nmap_report)
        sess.add(report)
        sess.commit()
        reportid = report.id
        sess.close()
        return reportid if reportid else None

    def get(self, report_id=None):
        """
            retreive a NmapReport from the backend

            :param id: str

            :returns: NmapReport
        """
        if report_id is None:
            raise ValueError
        sess = self.Session()
        our_report = (
            sess.query(NmapSqlPlugin.Reports).filter_by(id=report_id).first())
        sess.close()
        return our_report.decode() if our_report else None

    def getall(self):
        """
            :param filter: Nice to have implement a filter capability

            :returns: collection of tuple (id,NmapReport)
        """
        sess = self.Session()
        nmapreportList = []
        for report in (
                sess.query(NmapSqlPlugin.Reports).
                order_by(NmapSqlPlugin.Reports.inserted)):
            nmapreportList.append((report.id, report.decode()))
        sess.close()
        return nmapreportList

    def delete(self, report_id=None):
        """
            Remove a report from the backend

            :param id: str

            :returns: The number of rows deleted
        """
        if report_id is None:
            raise ValueError
        nb_line = 0
        sess = self.Session()
        nb_line = sess.query(NmapSqlPlugin.Reports).\
            filter_by(id=report_id).delete()
        sess.commit()
        sess.close()
        return nb_line


import os
os.environ["TF_CPP_MIN_LOG_LEVEL"] = "2"

#test

import numpy as np
import cv2
import pandas as pd
from tqdm import tqdm
import tensorflow as tf
from keras.utils import CustomObjectScope
from sklearn.metrics import f1_score, jaccard_score, precision_score, recall_score
from metrics import dice_loss, dice_coef
from data_preprocessing import load_dataset, resize_with_aspect_ratio
import imageio
from data_preprocessing import create_dir

""" Global parameters """
H = 256
W = 256

def save_results(form, image, mask, y_pred, save_image_path):
    if form == "cat":
        mask = np.expand_dims(mask, axis=-1)
        mask = np.concatenate([mask, mask, mask], axis=-1)
        y_pred = np.expand_dims(y_pred, axis=-1)
        y_pred = np.concatenate([y_pred, y_pred, y_pred], axis=-1)
        y_pred = y_pred * 255
        line = np.ones((H, 10, 3)) * 255
        cat_images = np.concatenate([image, line, mask, line, y_pred], axis=1)
        cv2.imwrite(save_image_path+ ".jpg", cat_images)
    if form == "pred":
        y_pred = y_pred *255
        y_pred = y_pred.astype(np.uint8)
        imageio.imwrite(save_image_path, y_pred, format = 'TIFF')

def evaluate_model(dataset_path, model):
    np.random.seed(42)
    tf.random.set_seed(42)
    _, _, (test_x, test_y) = load_dataset(dataset_path)
    """ Prediction and Evaluation """
    SCORE = []
    for x, y in tqdm(zip(test_x, test_y), total=len(test_y)):
        """ Extracting the name """
        name = os.path.basename(x)
        #        print("NAME:",name)

        """ Reading the image """
        image = cv2.imread(x, cv2.IMREAD_COLOR)  ## [H, w, 3]
        image = resize_with_aspect_ratio('image', image, (W, H))  ## [H, w, 3]
        x = image / 255.0  ## [H, w, 3]
        x = np.expand_dims(x, axis=0)  ## [1, H, w, 3]

        """ Reading the mask """
        mask = cv2.imread(y, cv2.IMREAD_GRAYSCALE)
        mask = resize_with_aspect_ratio('mask', mask, (W, H))

        """ Prediction """
        y_pred = model.predict(x, verbose=0)[0]
        y_pred = np.squeeze(y_pred, axis=-1)
        y_pred = y_pred >= 0.5
        y_pred = y_pred.astype(np.int32)

        """ Saving the prediction """
        save_image_path = os.path.join(r"C:\Users\wdgst\Data\ShiData\WDG\Atheroscelerotic_Lesion_UNET\results", name)
        save_results("pred", image, mask, y_pred, save_image_path)
        #        print("image", image)
        #        print("mask", mask)
        #        print("pred", y_pred)
        #        print(save_image_path)
        """ Flatten the array """
        mask = mask / 255.0
        mask = (mask > 0.5).astype(np.int32).flatten()
        y_pred = y_pred.flatten()

        """ Calculating the metrics values """
        f1_value = f1_score(mask, y_pred, labels=[0, 1], average="binary")
        jac_value = jaccard_score(mask, y_pred, labels=[0, 1], average="binary")
        recall_value = recall_score(mask, y_pred, labels=[0, 1], average="binary", zero_division=0)
        precision_value = precision_score(mask, y_pred, labels=[0, 1], average="binary", zero_division=0)
        SCORE.append([name, f1_value, jac_value, recall_value, precision_value])

    """ Metrics values """
    score = [s[1:] for s in SCORE]
    score = np.mean(score, axis=0)
    print(f"F1: {score[0]:0.5f}")
    print(f"Jaccard: {score[1]:0.5f}")
    print(f"Recall: {score[2]:0.5f}")
    print(f"Precision: {score[3]:0.5f}")

    df = pd.DataFrame(SCORE, columns=["Image", "F1", "Jaccard", "Recall", "Precision"])
    df.to_csv("files/score.csv")

if __name__ == "__main__":
    """ Directory for storing files """
    create_dir("results")

    dataset_path = r"C:\Users\wdgst\Data\ShiData\WDG"

    """ Load the model """
    with CustomObjectScope({"dice_coef": dice_coef, "dice_loss": dice_loss}):
        model = tf.keras.models.load_model(os.path.join(r"C:\Users\wdgst\Data\ShiData\WDG\Atheroscelerotic_Lesion_UNET\files", "model.h5"))

    evaluate_model(dataset_path, model)