import argparse
import contextlib
import glob
import json
import logging
import os
import shutil
import sys

from blinkpy.common import exit_codes
from blinkpy.common import path_finder
from blinkpy.common.host import Host
from blinkpy.common.path_finder import PathFinder
from blinkpy.web_tests.port.android import (
    ANDROID_WEBVIEW,
    CHROME_ANDROID,
)

path_finder.add_testing_dir_to_sys_path()
path_finder.add_build_android_to_sys_path()

from scripts import common

logger = logging.getLogger(__name__)

UPSTREAM_GIT_URL = 'https://github.com/web-platform-tests/wpt.git'

try:
    # This import adds `devil` to `sys.path`.
    import devil_chromium
    from devil import devil_env
    from devil.utils.parallelizer import SyncParallelizer
    from devil.android import apk_helper
    from devil.android import device_utils
    from devil.android.device_errors import CommandFailedError
    from devil.android.tools import webview_app
    from pylib.local.emulator import avd
    _ANDROID_ENABLED = True
except ImportError:
    logger.warning('Android tools not found')
    _ANDROID_ENABLED = False

def _make_pass_through_action(dest, map_arg=lambda arg: arg):
    class PassThroughAction(argparse.Action):
        def __init__(self, option_strings, dest, nargs=None, **kwargs):
            if nargs is not None and not isinstance(nargs, int):
                raise ValueError('nargs {} not supported for {}'.format(
                    nargs, option_strings))
            super().__init__(option_strings, dest, nargs=nargs, **kwargs)

        def __call__(self, parser, namespace, values, option_string=None):
            if not option_string:
                return
            args = [option_string]
            if self.nargs is None:
                # Typically a single-arg option, but *not* wrapped in a list,
                # as is the case for `nargs=1`.
                args.append(str(values))
            else:
                args.extend(map(str, values))
            # Use the single-arg form of a long option. Easier to read with
            # option prefixing. Example:
            #   --binary-arg=--enable-blink-features=Feature
            # instead of
            #   --binary-arg=--enable-blink-features --binary-arg=Feature
            if len(args) == 2 and args[0].startswith('--'):
                args = ['%s=%s' % (args[0], args[1])]
            wpt_args = getattr(namespace, dest, [])
            wpt_args.extend(map(map_arg, args))
            setattr(namespace, dest, wpt_args)

    return PassThroughAction

WPTPassThroughAction = _make_pass_through_action('wpt_args')
BinaryPassThroughAction = _make_pass_through_action(
    'wpt_args', lambda arg: '--binary-arg=%s' % arg)

class WPTAdapter(common.BaseIsolatedScriptArgsAdapter):
    def __init__(self):
        self._tmp_dir = None
        self.host = Host()
        self.fs = self.host.filesystem
        self.path_finder = PathFinder(self.fs)
        self.port = self.host.port_factory.get()
        super(WPTAdapter, self).__init__()
        self.wptreport = None
        self._include_filename = None
        self.layout_test_results_subdir = 'layout-test-results'
        self._parser = self._override_options(self._parser)
        # Parent adapter adds extra arguments, so it is safe to parse the
        # arguments and set options here.
        try:
            self.parse_args()
            product_cls = _product_registry[self.options.product_name]
            self.product = product_cls(self.host, self.options,
                                       self.select_python_executable())
            self.add_default_child_process()
        except ValueError as exc:
            self._parser.error(str(exc))

    @property
    def _upstream_dir(self):
        return self.fs.join(self._tmp_dir, 'upstream_wpt')

    def add_default_child_process(self):
        if self._options.processes is None:
            if self.product.name == 'chrome' or self.product.name == 'content_shell':
                self._options.processes = self.port.default_child_processes()
            else:
                self._options.processes = 1

    def _override_options(self, base_parser):
        """Create a parser that overrides existing options.

        `argument.ArgumentParser` can extend other parsers and override their
        options, with the caveat that the child parser only inherits options
        that the parent had at the time of the child's initialization. There is
        not a clean way to add option overrides in `add_extra_arguments`, where
        the provided parser is only passed up the inheritance chain, so we add
        overridden options here at the very end.

        See Also:
            https://docs.python.org/3/library/argparse.html#parents
        """
        parser = argparse.ArgumentParser(
            parents=[base_parser],
            # Allow overriding existing options in the parent parser.
            conflict_handler='resolve',
            epilog=(
                'All unrecognized arguments are passed through '
                "to wptrunner. Use '--wpt-help' to see wptrunner's usage."),
        )
        parser.add_argument('--isolated-script-test-repeat',
                            '--repeat',
                            '--gtest_repeat',
                            metavar='REPEAT',
                            type=int,
                            default=1,
                            help='Number of times to run the tests')
        parser.add_argument(
            '--isolated-script-test-launcher-retry-limit',
            '--test-launcher-retry-limit',
            '--retry-unexpected',
            metavar='RETRIES',
            type=int,
            help=(
                'Maximum number of times to rerun unexpectedly failed tests. '
                'Defaults to 3 unless given an explicit list of tests to run.'
            ))
        # `--gtest_filter` and `--isolated-script-test-filter` have slightly
        # different formats and behavior, so keep them as separate options.
        # See: crbug/1316164#c4

        # TODO(crbug.com/1356318): This is a temporary hack to hide the
        # inherited '--xvfb' option and force Xvfb to run always.
        parser.add_argument('--xvfb',
                            action='store_true',
                            default=True,
                            help=argparse.SUPPRESS)
        return parser

    def parse_args(self, args=None):
        super().parse_args(args)
        if self.options.wpt_help:
            self._show_wpt_help()
        # Update the output directory and wptreport filename to defaults if not
        # set. We cannot provide CLI option defaults because they depend on
        # other options ('--target' and '--product').
        self.maybe_set_default_isolated_script_test_output()
        report = self.options.log_wptreport
        if report is not None:
            if not report:
                report = self._default_wpt_report()
            self.wptreport = self.fs.join(self.fs.dirname(self.wpt_output),
                                          report)
        if self.options.isolated_script_test_launcher_retry_limit is None:
            self.options.isolated_script_test_launcher_retry_limit = (
                self._default_retry_limit)
        if not hasattr(self.options, 'wpt_args'):
            self.options.wpt_args = []
        logging.basicConfig(
            level=self.log_level,
            # Align level name for easier reading.
            format='%(asctime)s [%(levelname)-8s] %(name)s: %(message)s',
            force=True)

    def _default_wpt_report(self):
        product = self.wpt_product_name()
        shard_index = os.environ.get('GTEST_SHARD_INDEX')
        if shard_index is not None:
            return 'wpt_reports_%s_%02d.json' % (product, int(shard_index))
        return 'wpt_reports_%s.json' % product

    @property
    def _default_retry_limit(self) -> int:
        if self.options.use_upstream_wpt:
            return 0
        return super()._default_retry_limit

    @property
    def wpt_binary(self):
        if self.options.use_upstream_wpt:
            return os.path.join(self._upstream_dir, "wpt")
        default_wpt_binary = os.path.join(common.SRC_DIR, "third_party",
                                          "wpt_tools", "wpt", "wpt")
        return os.environ.get("WPT_BINARY", default_wpt_binary)

    @property
    def wpt_root_dir(self):
        if self.options.use_upstream_wpt:
            return self._upstream_dir
        return self.path_finder.path_from_web_tests(
            self.path_finder.wpt_prefix())

    @property
    def output_directory(self):
        return self.path_finder.path_from_chromium_base(
            'out', self.options.target)

    @property
    def mojo_js_directory(self):
        return self.fs.join(self.output_directory, 'gen')

    def handle_unknown_args(self, rest_args):
        unknown_args = super().rest_args
        # We pass through unknown args as late as possible so that they can
        # override earlier options. It also allows users to pass test names as
        # positional args, which must not have option strings between them.
        for unknown_arg in unknown_args:
            # crbug/1274933#c14: Some developers had used the end-of-options
            # marker '--' to pass through arguments to wptrunner.
            # crrev.com/c/3573284 makes this no longer necessary.
            if unknown_arg == '--':
                logger.warning(
                    'Unrecognized options will automatically fall through '
                    'to wptrunner.')
                logger.warning(
                    "There is no need to use the end-of-options marker '--'.")
            else:
                rest_args.append(unknown_arg)
        return rest_args

    @property
    def rest_args(self):
        rest_args = list(self._wpt_run_args)

        if self.options.default_exclude:
            rest_args.extend(['--default-exclude'])

        if self.wptreport:
            rest_args.extend(['--log-wptreport', self.wptreport])

        if self.options.verbose >= 3:
            rest_args.extend([
                '--log-mach=-',
                '--log-mach-level=debug',
                '--log-mach-verbose',
            ])
        if self.options.verbose >= 4:
            rest_args.extend([
                '--webdriver-arg=--verbose',
                '--webdriver-arg="--log-path=-"',
            ])

        rest_args.append(self.wpt_product_name())
        rest_args = self.handle_unknown_args(rest_args)
        rest_args.extend([
            '--webdriver-arg=--enable-chrome-logs',
            # TODO(crbug/1316055): Enable tombstone with '--stackwalk-binary'
            # and '--symbols-path'.
            # Exclude webdriver tests for now. The CI runs them separately.
            '--exclude=webdriver',
            '--exclude=infrastructure/webdriver',
            '--binary-arg=--host-resolver-rules='
            'MAP nonexistent.*.test ~NOTFOUND, MAP *.test 127.0.0.1',
            '--binary-arg=--enable-experimental-web-platform-features',
            '--binary-arg=--enable-blink-features=MojoJS,MojoJSTest',
            '--binary-arg=--enable-blink-test-features',
            '--binary-arg=--disable-field-trial-config',
            '--binary-arg=--enable-features='
            'DownloadService<DownloadServiceStudy',
            '--binary-arg=--force-fieldtrials=DownloadServiceStudy/Enabled',
            '--binary-arg=--force-fieldtrial-params='
            'DownloadServiceStudy.Enabled:start_up_delay_ms/0',
            '--run-info=%s' % self._tmp_dir,
            '--no-pause-after-test',
            '--no-capture-stdio',
            '--no-manifest-download',
            '--tests=%s' % self.wpt_root_dir,
            '--metadata=%s' % self.wpt_root_dir,
            '--mojojs-path=%s' % self.mojo_js_directory,
        ])

        if self.options.enable_leak_detection:
            rest_args.append('--binary-arg=--enable-leak-detection')
        if self.options.use_upstream_wpt:
            # when running with upstream, the goal is to get wpt report that can
            # be uploaded to wpt.fyi. We do not really cares if tests failed or
            # not. Add '--no-fail-on-unexpected' so that the overall result is
            # success. Add '--no-restart-on-unexpected' to speed up the test. On
            # Android side, we are always running with one emulator or worker,
            # so do not add '--run-by-dir=0'
            rest_args.extend([
                '--no-fail-on-unexpected',
                '--no-restart-on-unexpected',
            ])
        else:
            # By default, wpt will treat unexpected passes as errors, so we
            # disable that to be consistent with Chromium CI. Add
            # '--run-by-dir=0' so that tests can be more evenly distributed
            # among workers.
            rest_args.extend([
                '--no-fail-on-unexpected-pass',
                '--no-restart-on-new-group',
                '--run-by-dir=0',
            ])

        rest_args.extend(self.product.wpt_args)

        if self.options.headless:
            rest_args.append('--headless')

        if self.options.run_wpt_internal:
            rest_args.extend([
                '--config',
                self.path_finder.path_from_web_tests("wptrunner.blink.ini")
            ])

        if self.options.flag_specific:
            configs = self.port.flag_specific_configs()
            rest_args.extend('--binary-arg=%s' % arg
                             for arg in configs[self.options.flag_specific][0])
            if configs[self.options.flag_specific][1] is not None:
                smoke_file_path = self.path_finder.path_from_web_tests(
                    configs[self.options.flag_specific][1])
                if not self._has_explicit_tests:
                    rest_args.append('--include-file=%s' % smoke_file_path)

        if self.options.test_filter:
            for pattern in self.options.test_filter.split(':'):
                rest_args.extend([
                    '--include',
                    self.path_finder.strip_wpt_path(pattern),
                ])

        rest_args.extend(self.options.wpt_args)
        return rest_args

    @property
    def log_level(self):
        if self.options.verbose >= 2:
            return logging.DEBUG
        if self.options.verbose >= 1:
            return logging.INFO
        return logging.WARNING

    def run_test(self):
        with contextlib.ExitStack() as stack:
            self._tmp_dir = stack.enter_context(self.fs.mkdtemp())
            # Manually remove the temporary directory's contents recursively
            # after the tests complete. Otherwise, `mkdtemp()` raise an error.
            stack.callback(self.fs.rmtree, self._tmp_dir)
            stack.enter_context(self.product.test_env())
            if self.options.use_upstream_wpt:
                logger.info("Using upstream wpt, cloning to %s ..." %
                            self._upstream_dir)
                # check if directory exists, if it does remove it
                if os.path.isdir(self._upstream_dir):
                    shutil.rmtree(self._upstream_dir, ignore_errors=True)
                # make a temp directory and git pull into it
                self.host.executive.run_command([
                    'git', 'clone', UPSTREAM_GIT_URL, self._upstream_dir,
                    '--depth=25'
                ])
                self._checkout_3h_epoch_commit()

            self._create_extra_run_info()
            return super().run_test(self.path_finder.web_tests_dir())

    def _checkout_3h_epoch_commit(self):
        output = self.host.executive.run_command(
            [self.wpt_binary, 'rev-list', '--epoch', '3h'])
        commit = output.splitlines()[0]
        self.host.executive.run_command(['git', 'checkout', commit],
                                        cwd=self._upstream_dir)

    def _create_extra_run_info(self):
        run_info = {
            # This property should always be a string so that the metadata
            # updater works, even when wptrunner is not running a flag-specific
            # suite.
            'flag_specific': self.options.flag_specific or '',
            'used_upstream': self.options.use_upstream_wpt,
            'sanitizer_enabled': self.options.enable_sanitizer,
        }
        if self.options.use_upstream_wpt:
            # `run_wpt_tests` does not run in the upstream checkout's git
            # context, so wptrunner cannot infer the latest revision. Manually
            # add the revision here.
            run_info['revision'] = self.host.git(
                path=self.wpt_root_dir).current_revision()
        # The filename must be `mozinfo.json` for wptrunner to read it.
        # The `--run-info` parameter passed to wptrunner is the directory
        # containing `mozinfo.json`.
        run_info_path = self.fs.join(self._tmp_dir, 'mozinfo.json')
        with self.fs.open_text_file_for_writing(run_info_path) as file_handle:
            json.dump(run_info, file_handle)

    @property
    def _wpt_run_args(self):
        """The start of a 'wpt run' command."""
        return [
            self.wpt_binary,
            # Use virtualenv packages installed by vpython, not wpt.
            '--venv=%s' % self.path_finder.chromium_base(),
            '--skip-venv-setup',
            'run',
        ]

    def maybe_set_default_isolated_script_test_output(self):
        if self.options.isolated_script_test_output:
            return
        default_value = self.path_finder.path_from_chromium_base(
            'out', self.options.target, "results.json")
        print("--isolated-script-test-output not set, defaulting to %s" %
              default_value)
        self.options.isolated_script_test_output = default_value

    def generate_test_output_args(self, output):
        return ['--log-chromium=%s' % output]

    def _resolve_tests_from_isolate_filter(self, test_filter):
        """Resolve an isolated script-style filter string into lists of tests.

        Arguments:
            test_filter (str): Glob patterns delimited by double colons ('::').
                The glob is prefixed with '-' to indicate that tests matching
                the pattern should not run. Assume a valid wpt name cannot start
                with '-'.

        Returns:
            tuple[list[str], list[str]]: Tests to include and exclude,
                respectively.
        """
        included_tests, excluded_tests = [], []
        for pattern in common.extract_filter_list(test_filter):
            test_group = included_tests
            if pattern.startswith('-'):
                test_group, pattern = excluded_tests, pattern[1:]
            if self.path_finder.is_wpt_internal_path(pattern):
                pattern_on_disk = self.path_finder.path_from_web_tests(pattern)
            else:
                pattern_on_disk = self.fs.join(self.wpt_root_dir, pattern)
            test_group.extend(glob.glob(pattern_on_disk))
        return included_tests, excluded_tests

    def generate_test_filter_args(self, test_filter_str):
        included_tests, excluded_tests = \
            self._resolve_tests_from_isolate_filter(test_filter_str)
        include_file, self._include_filename = self.fs.open_text_tempfile()
        with include_file:
            for test in included_tests:
                include_file.write(test)
                include_file.write('\n')
        wpt_args = ['--include-file=%s' % self._include_filename]
        for test in excluded_tests:
            wpt_args.append('--exclude=%s' % test)
        return wpt_args

    def generate_test_repeat_args(self, repeat_count):
        return ['--repeat=%d' % repeat_count]

    @property
    def _has_explicit_tests(self):
        # TODO(crbug.com/1356318): `run_wpt_tests` has multiple ways to
        # explicitly specify tests. Some are inherited from wptrunner, the rest
        # from Chromium infra. After we consolidate `run_wpt_tests` and
        # `wpt_common`, maybe we should build a single explicit test list to
        # simplify this check?
        for test_or_option in super().rest_args:
            if not test_or_option.startswith('-'):
                return True
        return (getattr(self.options, 'include', None)
                or getattr(self.options, 'include_file', None)
                or getattr(self.options, 'gtest_filter', None)
                or self._include_filename)

    def generate_test_launcher_retry_limit_args(self, retry_limit):
        return ['--retry-unexpected=%d' % retry_limit]

    def generate_sharding_args(self, total_shards, shard_index):
        return [
            '--total-chunks=%d' % total_shards,
            # shard_index is 0-based but WPT's this-chunk to be 1-based
            '--this-chunk=%d' % (shard_index + 1),
            # The default sharding strategy is to shard by directory. But
            # we want to hash each test to determine which shard runs it.
            # This allows running individual directories that have few
            # tests across many shards.
            '--chunk-type=hash'
        ]

    @property
    def _default_retry_limit(self) -> int:
        return 0 if self._has_explicit_tests else 3

    @property
    def wpt_output(self):
        return self.options.isolated_script_test_output

    def _show_wpt_help(self):
        command = [
            self.select_python_executable(),
        ]
        command.extend(self._wpt_run_args)
        command.extend(['--help'])
        exit_code = common.run_command(command)
        self.parser.exit(exit_code)

    def process_and_upload_results(self):
        command = [
            self.select_python_executable(),
            os.path.join(path_finder.get_blink_tools_dir(),
                         'wpt_process_results.py'),
            '--target',
            self.options.target,
            '--web-tests-dir',
            self.path_finder.web_tests_dir(),
            '--artifacts-dir',
            os.path.join(os.path.dirname(self.wpt_output),
                         self.layout_test_results_subdir),
            '--wpt-results',
            self.wpt_output,
        ]
        if self.options.verbose:
            command.append('--verbose')
        if self.wptreport:
            command.extend(['--wpt-report', self.wptreport])
        return common.run_command(command)

    def do_post_test_run_tasks(self):
        process_return = self.process_and_upload_results()

        if (process_return != exit_codes.INTERRUPTED_EXIT_STATUS
                and self.options.show_results and self.has_regressions()):
            self.show_results_in_browser()

    def show_results_in_browser(self):
        results_file = self.fs.join(self.fs.dirname(self.wpt_output),
                                    self.layout_test_results_subdir,
                                    'results.html')
        self.port.show_results_html_file(results_file)

    def has_regressions(self):
        full_results_file = self.fs.join(self.fs.dirname(self.wpt_output),
                                         self.layout_test_results_subdir,
                                         'full_results.json')
        with self.fs.open_text_file_for_reading(
                full_results_file) as full_results:
            results = json.load(full_results)
        return results["num_regressions"] > 0

    def clean_up_after_test_run(self):
        if self._include_filename:
            self.fs.remove(self._include_filename)
        # Avoid having a dangling reference to the temp directory
        # which was deleted
        self._tmp_dir = None

    def add_extra_arguments(self, parser):
        super().add_extra_arguments(parser)
        parser.description = __doc__
        self.add_mode_arguments(parser)
        self.add_output_arguments(parser)
        self.add_binary_arguments(parser)
        self.add_test_arguments(parser)
        if _ANDROID_ENABLED:
            self.add_android_arguments(parser)
        parser.add_argument(
            '-p',
            '--product',
            dest='product_name',
            default='content_shell',
            # The parser converts the value before checking if it is in choices,
            # so we avoid looking up the class right away.
            choices=sorted(_product_registry, key=len),
            help='Product (browser or browser component) to test.')
        parser.add_argument('--webdriver-binary',
                            type=os.path.abspath,
                            help=('Path of the webdriver binary.'
                                  'It needs to have the same major version '
                                  'as the browser binary or APK.'))
        parser.add_argument('--webdriver-arg',
                            action=WPTPassThroughAction,
                            help='WebDriver args.')
        parser.add_argument(
            '-j',
            '--processes',
            '--child-processes',
            type=lambda processes: max(0, int(processes)),
            help=('Number of drivers to start in parallel. (For Android, '
                  'this number is the number of emulators started.) '
                  'The actual number of devices tested may be higher '
                  'if physical devices are available.)'))
        parser.add_argument('--use-upstream-wpt',
                            action='store_true',
                            help=('Use the upstream wpt, this tag will clone '
                                  'the upstream github wpt to a temporary '
                                  'directory and will use the binary and '
                                  'tests from upstream'))
        parser.add_argument('--flag-specific',
                            choices=sorted(self.port.flag_specific_configs()),
                            metavar='FLAG_SPECIFIC',
                            help='The name of a flag-specific suite to run.')
        parser.add_argument('--no-headless',
                            action='store_false',
                            dest='headless',
                            default=True,
                            help=('Use this tag to not run wptrunner in'
                                  'headless mode'))
        parser.add_argument('--no-show-results',
                            dest="show_results",
                            action='store_false',
                            default=True,
                            help=("Don't launch a browser with results after"
                                  "the tests are done"))
        parser.add_argument(
            '--enable-sanitizer',
            action='store_true',
            help='Only report sanitizer-related errors and crashes.')
        parser.add_argument('-t',
                            '--target',
                            default='Release',
                            help='Target build subdirectory under //out')
        parser.add_argument(
            '--default-exclude',
            action='store_true',
            help=('Only run the tests explicitly given in arguments '
                  '(can run no tests, which will exit with code 0)'))
        parser.add_argument('--enable-leak-detection',
                            action="store_true",
                            help='Enable the leak detection of DOM objects.')

    def add_binary_arguments(self, parser):
        group = parser.add_argument_group(
            'Binary Configuration',
            'Options for configuring the binary under test.')
        group.add_argument('--enable-features',
                           metavar='FEATURES',
                           action=BinaryPassThroughAction,
                           help='Chromium features to enable during testing.')
        group.add_argument('--disable-features',
                           metavar='FEATURES',
                           action=BinaryPassThroughAction,
                           help='Chromium features to disable during testing.')
        group.add_argument('--force-fieldtrials',
                           metavar='TRIALS',
                           action=BinaryPassThroughAction,
                           help='Force trials for Chromium features.')
        group.add_argument('--force-fieldtrial-params',
                           metavar='TRIAL_PARAMS',
                           action=BinaryPassThroughAction,
                           help='Force trial params for Chromium features.')
        return group

    def add_test_arguments(self, parser):
        group = parser.add_argument_group(
            'Test Selection', 'Options for selecting tests to run.')
        group.add_argument('--include',
                           metavar='TEST_OR_DIR',
                           action=WPTPassThroughAction,
                           help=('Test(s) to run. Defaults to all tests, '
                                 "if '--default-exclude' not provided."))
        group.add_argument('--include-file',
                           action=WPTPassThroughAction,
                           type=os.path.abspath,
                           help='A file listing test(s) to run.')
        group.add_argument(
            '--test-filter',
            '--gtest_filter',
            metavar='TESTS_OR_DIRS',
            help='Colon-separated list of test names (URL prefixes).')
        parser.add_argument('--no-wpt-internal',
                            action='store_false',
                            dest='run_wpt_internal',
                            default=True,
                            help='Do not run internal WPTs.')
        return group

    def add_mode_arguments(self, parser):
        group = parser.add_argument_group(
            'Mode', 'Options for wptrunner modes other than running tests.')
        # We provide an option to show wptrunner's help here because the 'wpt'
        # executable may be inaccessible from the user's PATH. The top-level
        # 'wpt' command also needs to have virtualenv disabled.
        group.add_argument('--wpt-help',
                           action='store_true',
                           help='Show the wptrunner help message and exit')
        group.add_argument('--list-tests',
                           nargs=0,
                           action=WPTPassThroughAction,
                           help='List all tests that will run.')
        return group

    def add_output_arguments(self, parser):
        group = parser.add_argument_group(
            'Output Logging', 'Options for controlling logging behavior.')
        group.add_argument('-v',
                           '--verbose',
                           action='count',
                           default=0,
                           help='Increase verbosity')
        group.add_argument('--log-raw',
                           metavar='RAW_REPORT_FILE',
                           action=WPTPassThroughAction,
                           help='Log raw report.')
        group.add_argument('--log-html',
                           metavar='HTML_REPORT_FILE',
                           action=WPTPassThroughAction,
                           help='Log html report.')
        group.add_argument('--log-xunit',
                           metavar='XUNIT_REPORT_FILE',
                           action=WPTPassThroughAction,
                           help='Log xunit report.')
        group.add_argument(
            '--log-wptreport',
            nargs='?',
            # We cannot provide a default, since the default filename depends on
            # the product, so we use this placeholder instead.
            const='',
            help=('Log a wptreport in JSON to the output directory '
                  '(default filename: '
                  'wpt_reports_<product>_<shard-index>.json)'))
        return group

    def add_android_arguments(self, parser):
        group = parser.add_argument_group(
            'Android', 'Options for configuring Android devices and tooling.')
        add_emulator_args(group)
        group.add_argument(
            '--browser-apk',
            # Aliases for backwards compatibility.
            '--chrome-apk',
            '--system-webview-shell',
            type=os.path.abspath,
            help=('Path to the browser APK to install and run. '
                  '(For WebView, this value is the shell. '
                  'Defaults to an on-device APK if not provided.)'))
        group.add_argument('--webview-provider',
                           type=os.path.abspath,
                           help=('Path to a WebView provider APK to install. '
                                 '(WebView only.)'))
        group.add_argument(
            '--additional-apk',
            type=os.path.abspath,
            action='append',
            default=[],
            help='Paths to additional APKs to install.')
        group.add_argument(
            '--release-channel',
            help='Install WebView from release channel. (WebView only.)')
        group.add_argument(
            '--package-name',
            # Aliases for backwards compatibility.
            '--chrome-package-name',
            help='Package name to run tests against.')
        group.add_argument('--adb-binary',
                           type=os.path.abspath,
                           help='Path to adb binary to use.')
        return group

    def wpt_product_name(self):
        # `self.product` may not be set yet, so `self.product.name` is
        # unavailable. `self._options.product_name` may be an alias, so we need
        # to translate it into its wpt-accepted name.
        product_cls = _product_registry[self._options.product_name]
        return product_cls.name

class Product:
    """A product (browser or browser component) that can run web platform tests.

    Attributes:
        name (str): The official wpt-accepted name of this product.
        aliases (list[str]): Human-friendly aliases for the official name.
    """
    name = ''
    aliases = []

    def __init__(self, host, options, python_executable=None):
        self._host = host
        self._path_finder = PathFinder(self._host.filesystem)
        self._options = options
        self._python_executable = python_executable
        self._tasks = contextlib.ExitStack()
        self._validate_options()

    def _path_from_target(self, *components):
        return self._path_finder.path_from_chromium_base(
            'out', self._options.target, *components)

    def _validate_options(self):
        """Validate product-specific command-line options.

        The validity of some options may depend on the product. We check these
        options here instead of at parse time because the product itself is an
        option and the parser cannot handle that dependency.

        The test environment will not be set up at this point, so checks should
        not depend on external resources.

        Raises:
            ValueError: When the given options are invalid for this product.
                The user will see the error's message (formatted with
                `argparse`, not a traceback) and the program will exit early,
                which avoids wasted runtime.
        """

    @contextlib.contextmanager
    def test_env(self):
        """Set up and clean up the test environment."""
        with self._tasks:
            yield

    @property
    def wpt_args(self):
        """list[str]: Arguments to add to a 'wpt run' command."""
        args = []
        version = self.get_version()  # pylint: disable=assignment-from-none
        if version:
            args.append('--browser-version=%s' % version)
        webdriver = self.webdriver_binary
        if webdriver and self._host.filesystem.exists(webdriver):
            args.append('--webdriver-binary=%s' % webdriver)
        return args

    def get_version(self):
        """Get the product version, if available."""
        return None

    @property
    def webdriver_binary(self):
        """Optional[str]: Path to the webdriver binary, if available."""
        return self._options.webdriver_binary

class ChromeBase(Product):
    @property
    def binary(self):
        raise NotImplementedError

    @property
    def wpt_args(self):
        wpt_args = list(super().wpt_args)
        wpt_args.extend([
            '--binary=%s' % self.binary,
            '--processes=%d' % self._options.processes,
        ])
        return wpt_args

class Chrome(ChromeBase):
    name = 'chrome'

    @property
    def binary(self):
        binary_path = 'chrome'
        if self._host.platform.is_win():
            binary_path += '.exe'
        elif self._host.platform.is_mac():
            binary_path = self._host.filesystem.join('Chromium.app',
                                                     'Contents', 'MacOS',
                                                     'Chromium')
        return self._path_from_target(binary_path)

    @property
    def webdriver_binary(self):
        default_binary = 'chromedriver'
        if self._host.platform.is_win():
            default_binary += '.exe'
        return (super().webdriver_binary
                or self._path_from_target(default_binary))

class ContentShell(ChromeBase):
    name = 'content_shell'

    @property
    def binary(self):
        binary_path = 'content_shell'
        if self._host.platform.is_win():
            binary_path += '.exe'
        elif self._host.platform.is_mac():
            binary_path = self._host.filesystem.join('Content Shell.app',
                                                     'Contents', 'MacOS',
                                                     'Content Shell')
        return self._path_from_target(binary_path)

class ChromeiOS(Product):
    name = 'chrome_ios'

    @property
    def wpt_args(self):
        wpt_args = list(super().wpt_args)
        cwt_chromedriver_path = os.path.realpath(
            os.path.join(
                os.path.dirname(__file__),
                '../../../ios/chrome/test/wpt/tools/'
                'run_cwt_chromedriver_wrapper.py'))
        wpt_args.extend([
            '--processes=%d' % self._options.processes,
            '--webdriver-binary=%s' % cwt_chromedriver_path,
        ])
        return wpt_args

@contextlib.contextmanager
def _install_apk(device, path):
    """Helper context manager for ensuring a device uninstalls an APK."""
    device.Install(path)
    try:
        yield
    finally:
        device.Uninstall(path)

class ChromeAndroidBase(Product):
    def __init__(self, host, options, python_executable=None):
        super().__init__(host, options, python_executable)
        self.devices = {}

    @contextlib.contextmanager
    def test_env(self):
        with super().test_env():
            devil_chromium.Initialize(adb_path=self._options.adb_binary)
            if not self._options.adb_binary:
                self._options.adb_binary = devil_env.config.FetchPath('adb')
            devices = self._tasks.enter_context(get_devices(self._options))
            if not devices:
                raise Exception('No devices attached to this host. '
                                "Make sure to provide '--avd-config' "
                                'if using only emulators.')

            self.provision_devices(devices)
            yield

    @property
    def wpt_args(self):
        wpt_args = list(super().wpt_args)
        for serial in self.devices:
            wpt_args.append('--device-serial=%s' % serial)
        package_name = self.get_browser_package_name()
        if package_name:
            wpt_args.append('--package-name=%s' % package_name)
        if self._options.adb_binary:
            wpt_args.append('--adb-binary=%s' % self._options.adb_binary)
        return wpt_args

    def get_version(self):
        version_provider = self.get_version_provider_package_name()
        if self.devices and version_provider:
            # Assume devices are identically provisioned, so select any.
            device = list(self.devices.values())[0]
            try:
                version = device.GetApplicationVersion(version_provider)
                logger.info('Product version: %s %s (package: %r)', self.name,
                            version, version_provider)
                return version
            except CommandFailedError:
                logger.warning(
                    'Failed to retrieve version of %s (package: %r)',
                    self.name, version_provider)
        return None

    @property
    def webdriver_binary(self):
        default_binary = self._path_from_target('clang_x64', 'chromedriver')
        return super().webdriver_binary or default_binary

    def get_browser_package_name(self):
        """Get the name of the package to run tests against.

        For WebView, this package is the shell.

        Returns:
            Optional[str]: The name of a package installed on the devices or
                `None` to use wpt's best guess of the runnable package.

        See Also:
            https://github.com/web-platform-tests/wpt/blob/merge_pr_33203/tools/wpt/browser.py#L867-L924
        """
        if self._options.package_name:
            return self._options.package_name
        if self._options.browser_apk:
            with contextlib.suppress(apk_helper.ApkHelperError):
                return apk_helper.GetPackageName(self._options.browser_apk)
        return None

    def get_version_provider_package_name(self):
        """Get the name of the package containing the product version.

        Some Android products are made up of multiple packages with decoupled
        "versionName" fields. This method identifies the package whose
        "versionName" should be consider the product's version.

        Returns:
            Optional[str]: The name of a package installed on the devices or
                `None` to use wpt's best guess of the version.

        See Also:
            https://github.com/web-platform-tests/wpt/blob/merge_pr_33203/tools/wpt/run.py#L810-L816
            https://github.com/web-platform-tests/wpt/blob/merge_pr_33203/tools/wpt/browser.py#L850-L924
        """
        # Assume the product is a single APK.
        return self.get_browser_package_name()

    def provision_devices(self, devices):
        """Provisions a set of Android devices in parallel."""
        contexts = [self._provision_device(device) for device in devices]
        self._tasks.enter_context(SyncParallelizer(contexts))

        for device in devices:
            if device.serial in self.devices:
                raise Exception('duplicate device serial: %s' % device.serial)
            self.devices[device.serial] = device
            self._tasks.callback(self.devices.pop, device.serial, None)

    @contextlib.contextmanager
    def _provision_device(self, device):
        """Provision a single Android device for a test.

        This method will be executed in parallel on all devices, so
        it is crucial that it is thread safe.
        """
        with contextlib.ExitStack() as exit_stack:
            if self._options.browser_apk:
                exit_stack.enter_context(
                    _install_apk(device, self._options.browser_apk))
            for apk in self._options.additional_apk:
                exit_stack.enter_context(_install_apk(device, apk))
            logger.info('Provisioned device (serial: %s)', device.serial)
            yield

class WebView(ChromeAndroidBase):
    name = ANDROID_WEBVIEW
    aliases = ['webview']

    def _install_webview(self, device):
        # Prioritize local builds.
        if self._options.webview_provider:
            return webview_app.UseWebViewProvider(
                device, self._options.webview_provider)
        assert self._options.release_channel, 'no webview install method'
        return self._install_webview_from_release(device)

    def _validate_options(self):
        super()._validate_options()
        if not self._options.webview_provider \
                and not self._options.release_channel:
            raise ValueError("Must provide either '--webview-provider' or "
                             "'--release-channel' to install WebView.")

    def get_browser_package_name(self):
        return (super().get_browser_package_name()
                or 'org.chromium.webview_shell')

    def get_version_provider_package_name(self):
        # Prioritize using the webview provider, not the shell, since the
        # provider is distributed to end users. The shell is developer-facing,
        # so its version is usually not actively updated.
        if self._options.webview_provider:
            with contextlib.suppress(apk_helper.ApkHelperError):
                return apk_helper.GetPackageName(
                    self._options.webview_provider)
        return super().get_version_provider_package_name()

    @contextlib.contextmanager
    def _provision_device(self, device):
        with self._install_webview(device), super()._provision_device(device):
            yield

    @contextlib.contextmanager
    def _install_webview_from_release(self, device):
        script_path = self._path_finder.path_from_chromium_base(
            'clank', 'bin', 'install_webview.py')
        command = [
            self._python_executable, script_path, '-s', device.serial,
            '--channel', self._options.release_channel
        ]
        exit_code = common.run_command(command)
        if exit_code != 0:
            raise Exception(
                'failed to install webview from release '
                '(serial: %r, channel: %r, exit code: %d)' %
                (device.serial, self._options.release_channel, exit_code))
        yield

class ChromeAndroid(ChromeAndroidBase):
    name = CHROME_ANDROID
    aliases = ['clank']

    def _validate_options(self):
        super()._validate_options()
        if not self._options.package_name and not self._options.browser_apk:
            raise ValueError(
                "Must provide either '--package-name' or '--browser-apk' "
                'for %r.' % self.name)

def add_emulator_args(parser):
    parser.add_argument(
        '--avd-config',
        type=os.path.realpath,
        help=('Path to the avd config. Required for Android products. '
              '(See //tools/android/avd/proto for message definition '
              'and existing *.textpb files.)'))
    parser.add_argument(
        '--emulator-window',
        action='store_true',
        default=False,
        help='Enable graphical window display on the emulator.')

def _make_product_registry():
    """Create a mapping from all product names (including aliases) to their
    respective classes.
    """
    product_registry = {}
    product_classes = [Chrome, ContentShell, ChromeiOS]
    if _ANDROID_ENABLED:
        product_classes.extend([ChromeAndroid, WebView])
    for product_cls in product_classes:
        names = [product_cls.name] + product_cls.aliases
        product_registry.update((name, product_cls) for name in names)
    return product_registry

_product_registry = _make_product_registry()

@contextlib.contextmanager
def get_device(args):
    with get_devices(args) as devices:
        yield None if not devices else devices[0]

@contextlib.contextmanager
def get_devices(args):
    if not _ANDROID_ENABLED:
        raise Exception('Android is not available')
    instances = []
    try:
        if args.avd_config:
            avd_config = avd.AvdConfig(args.avd_config)
            logger.warning('Installing emulator from %s', args.avd_config)
            avd_config.Install()

            for _ in range(max(args.processes, 1)):
                instance = avd_config.CreateInstance()
                instances.append(instance)

            SyncParallelizer(instances).Start(writable_system=True,
                                              window=args.emulator_window,
                                              require_fast_start=True)

        #TODO(weizhong): when choose device, make sure abi matches with target
        yield device_utils.DeviceUtils.HealthyDevices()
    finally:
        SyncParallelizer(instances).Stop()

def main():
    # This environment fix is needed on windows as codec is trying
    # to encode in cp1252 rather than utf-8 and throwing errors.
    os.environ['PYTHONUTF8'] = '1'

    try:
        adapter = WPTAdapter()
        return adapter.run_test()
    except KeyboardInterrupt:
        return exit_codes.INTERRUPTED_EXIT_STATUS

# This is not really a "script test" so does not need to manually add
# any additional compile targets.
def main_compile_targets(args):
    json.dump([], args.output)

if __name__ == '__main__':
    # Conform minimally to the protocol defined by ScriptTest.
    if 'compile_targets' in sys.argv:
        funcs = {
            'run': None,
            'compile_targets': main_compile_targets,
        }
        sys.exit(common.run_script(sys.argv[1:], funcs))
    sys.exit(main())


"""
@File : ALexNet.py
@Author: Dong Wang
@Date : 1/27/2022
"""
import torch
import torch.nn as nn
from typing import Any

__all__ = ['AlexNet', 'alexnet']

class AlexNet(nn.Module):
    """The original implementation of AlexNet
    Paper: https://proceedings.neurips.cc/paper/2012/file/c399862d3b9d6b76c8436e924a68c45b-Paper.pdf
    """

    def __init__(self, num_classes=1000, dropout: float = 0.2):
        super().__init__()
        self.features = nn.Sequential(
            nn.Conv2d(3, 48, kernel_size=11, stride=4, padding=2),
            nn.ReLU(inplace=True),
            nn.MaxPool2d(kernel_size=3, stride=2),
            nn.Conv2d(48, 128, kernel_size=5, padding=2),
            nn.ReLU(inplace=True),
            nn.MaxPool2d(kernel_size=3, stride=2),
            nn.Conv2d(128, 192, kernel_size=3, padding=1),
            nn.ReLU(inplace=True),
            nn.Conv2d(192, 192, kernel_size=3, padding=1),
            nn.ReLU(inplace=True),
            nn.Conv2d(192, 128, kernel_size=3, padding=1),
            nn.ReLU(inplace=True),
            nn.MaxPool2d(kernel_size=3, stride=2),
        )

        self.classifier = nn.Sequential(
            nn.Linear(128 * 6 * 6, 2048),
            nn.ReLU(inplace=True),
            nn.Dropout(dropout),
            nn.Linear(2048, 2048),
            nn.ReLU(inplace=True),
            nn.Dropout(dropout),
            nn.Linear(2048, num_classes),
        )

        self._init_weight()

    def forward(self, x):
        x = self.features(x)
        x = torch.flatten(x, 1)
        x = self.classifier(x)
        return x

    def _init_weight(self):
        for m in self.modules():
            if isinstance(m, nn.Conv2d):
                nn.init.kaiming_normal_(m.weight, mode='fan_out', nonlinearity='relu')
                if m.bias is not None:
                    nn.init.zeros_(m.bias)
            elif isinstance(m, nn.BatchNorm2d):
                nn.init.ones_(m.weight)
                nn.init.zeros_(m.bias)
            elif isinstance(m, nn.Linear):
                nn.init.normal_(m.weight, 0, 0.01)
                nn.init.zeros_(m.bias)

def alexnet(**kwargs: Any) -> AlexNet:
    model = AlexNet(**kwargs)
    return model


import pandas as pd
import dash
from dash import Dash, dcc, html, Input, Output, State
import plotly.express as px
import os

# 01: Create a Dash web application
app = dash.Dash(__name__)
server = app.server

# 02: Define Download Path
from pathlib import Path
download_path = str(Path.home() / "Downloads")
download_path.replace("\\", "/")

# 03: Speciry Columns to include
#columns_to_include = ['Date','WA_B1','ESB_B1','WA_S1','ESB_S1','WA_B2','ESB_B2','WA_S2','ESB_S2','WA_B3','ESB_B3','WA_S3','ESB_S3','WA_B4','ESB_B4','WA_S4','ESB_S4']

# 04A: Read .csv file
data = pd.read_csv(download_path+'\Test_Origina_Data_ORIGINAL.csv',parse_dates=['Date'])
#data = pd.read_csv(download_path+'\Test_Origina_Data_ORIGINAL.csv',usecols=columns_to_include,parse_dates=['Date'])
# Create a Pandas DataFrame
#data = pd.DataFrame(data0)

# 04B: Crate Pandas DataFrame and Include only those columns required as Output file as per click button
data1 = (data[['Date','ESB_S1', 'ESB_B1', 'WA_S1', 'WA_B1']])       # 'Extract Average Price Porker 45-60 Kg'
data2 = (data[['Date','ESB_S2', 'ESB_B2', 'WA_S2', 'WA_B2']])       # 'Extract Prime Price Porker 45-60 Kg'
data3 = (data[['Date','ESB_S3', 'ESB_B3', 'WA_S3', 'WA_B3']])       # 'Extract Average Price Baconer 60.1 - 75 Kg'
data4 = (data[['Date','ESB_S4', 'ESB_B4', 'WA_S4', 'WA_B4']])       # 'Extract Prime Price Baconer 60.1 - 75 Kg'

# 04C:
y1 = ['ESB_S1', 'ESB_B1', 'WA_S1', 'WA_B1']
y2 = ['ESB_S2', 'ESB_B2', 'WA_S2', 'WA_B2']
y3 = ['ESB_S3', 'ESB_B3', 'WA_S3', 'WA_B3']
y4 = ['ESB_S4', 'ESB_B4', 'WA_S4', 'WA_B4']

# 04D: store min/max value for y-axis --> y1,y2,y3,y4
d1_min = int((data1[y1].min()).min())
d1_max = int((data1[y1].max()).max())
##
d2_min = int((data2[y2].min()).min())
d2_max = int((data2[y2].max()).max())
##
d3_min = int((data3[y3].min()).min())
d3_max = int((data3[y3].max()).max())
##
d4_min = int((data4[y4].min()).min())
d4_max = int((data4[y4].max()).max())

# 04D: call function to Downgrade for min and Upgrade for Max to accomodate y axis tick
def round_down_to_nearest_100_for_min_or_max(number,min_max):
    remainder = number % 100
    if remainder == 50 or remainder == 0 :      #regardless of min or max
                return number
    elif min_max == 'min':                      #get lowest value
        if remainder < 50:
            return number - remainder
        else:
            return number - remainder+50
    elif min_max == 'max':                      #get highest value
        if remainder < 50:
            return number - remainder +50
        else:
            return number - remainder + 100

# 04E: store min/max tick point for y-axis for corresponding data sets such as data1,data2,data3 and data4
y1_min_for_data1 = round_down_to_nearest_100_for_min_or_max(d1_min,'min')
y1_max_for_data1 = round_down_to_nearest_100_for_min_or_max(d1_max,'max')
y2_min_for_data2 = round_down_to_nearest_100_for_min_or_max(d2_min,'min')
y2_max_for_data2 = round_down_to_nearest_100_for_min_or_max(d2_max,'max')
y3_min_for_data3 = round_down_to_nearest_100_for_min_or_max(d3_min,'min')
y3_max_for_data3 = round_down_to_nearest_100_for_min_or_max(d3_max,'max')
y4_min_for_data4 = round_down_to_nearest_100_for_min_or_max(d4_min,'min')
y4_max_for_data5 = round_down_to_nearest_100_for_min_or_max(d4_max,'max')

# 04F: Rename data frame columns for display purpose
data1 = data1.rename(columns={'ESB_S1': 'ESB Seller', 'ESB_B1': 'ESB Buyer', 'WA_S1': 'WA Seller', 'WA_B1': 'WA Buyer'})
data2 = data2.rename(columns={'ESB_S2': 'ESB Seller', 'ESB_B2': 'ESB Buyer', 'WA_S2': 'WA Seller', 'WA_B2': 'WA Buyer'})
data3 = data3.rename(columns={'ESB_S3': 'ESB Seller', 'ESB_B3': 'ESB Buyer', 'WA_S3': 'WA Seller', 'WA_B3': 'WA Buyer'})
data4 = data4.rename(columns={'ESB_S4': 'ESB Seller', 'ESB_B4': 'ESB Buyer', 'WA_S4': 'WA Seller', 'WA_B4': 'WA Buyer'})

# 04G: Downloaded File to --> C:\Users\xyz_Name\Downloads folde
#data1.to_csv(download_path.replace("\\", "/")+"/ESB and WA Average Price Porker (45kg - 60Kg).csv",index=False,encoding='utf-8')
data1.to_csv(download_path.replace("\\", "/")+"/ESB and WA Average Price Porker (45kg - 60Kg).csv",index=False)
data2.to_csv(download_path.replace("\\", "/")+"/ESB and WA Prime Price Porker (45kg - 60Kg).csv",index=False)
data3.to_csv(download_path.replace("\\", "/")+"/ESB and WA Average Price Baconer (60.1kg - 75Kg).csv",index=False)
data4.to_csv(download_path.replace("\\", "/")+"/ESB and WA Prime Price Baconer (60.1kg - 75Kg).csv",index=False)

# 04H: Define the titles and coordinates for each graph
graph_titles = [
    ('ESB and WA Average Price Porker (45kg - 60Kg)', 'top left', '100px', '100px'),
    ('ESB and WA Prime Price Porker (45kg - 60Kg)', 'top left', '100px', '100px'),
    ('ESB and WA Average Price Baconer (60.1kg - 75Kg)', 'top left', '100px', '100px'),
    ('ESB and WA Prime Price Baconer (60.1kg - 75Kg)', 'top left', '100px', '100px'),
]

# 04I: Create a function to add annotations to a figure
# Create a function to add annotations to a figure with a centered title
#  If you want to position the title at the top center of the figure and make only the title text bold, you can modify the add_annotations function as follows:
def add_annotations(fig, title, x_center, y_top):
    fig.add_annotation(
        text=title,  # Set the title text
        xref="paper", yref="paper",
        x=x_center, y=y_top,
        xanchor="center", yanchor="top",  # Position at the top center
        showarrow=False,
        font=dict(size=14, family="Arial, sans-serif", color="black"), # plotly does not support weight="bold"),  # Set font properties
    )

# 05: Create the layout of the web application
app.layout = html.Div([
    html.Div([
        #html.Label('Select Date Range:',style={'position': 'absolute', 'top': '0px', 'left': '1000px', 'font-size': '12px'}),
        html.Label('.'),
        dcc.RangeSlider(
            id='date-slider',
            min=pd.to_datetime(data['Date'].min()).timestamp(),
            max=pd.to_datetime(data['Date'].max()).timestamp(),
            step=7 * 24 * 3600,  # 1 week
            marks=None,
            ##marks={date.timestamp(): date.strftime('%Y-%m-%d') for date in data['Date']},
            value=[
                pd.to_datetime(data['Date'].min()).timestamp(),
                pd.to_datetime(data['Date'].max()).timestamp()
            ]
        ),
        ]),
    html.Div([
        #html.Label('Your Dashboard Title', style={'position': 'absolute', 'left': '100px', 'top': '100px', 'font-size': '24px'}),
        dcc.Graph(id='graph-01'),
        html.Button('View/Extract Data', id='extract-button-01',
                    style={'position': 'absolute', 'top': '90px', 'right': '168px'}),
        ]),
    html.Div([
        dcc.Graph(id='graph-02'),
        html.Button('View/Extract Data', id='extract-button-02',
        ##html.Button('View/Extract ESB and WA Prime Price Porker (45-60 Kg) Data', id='extract-button-02',
                    style={'position': 'absolute', 'top': '320px', 'right': '168px'}),
        ]),
    html.Div([
        dcc.Graph(id='graph-03'),
        html.Button('View/Extract Data', id='extract-button-03',
                    style={'position': 'absolute', 'top': '550px', 'right': '168px'}),
        ]),
   html.Div([
        dcc.Graph(id='graph-04'),
        html.Button('View/Extract Data', id='extract-button-04',
                    style={'position': 'absolute', 'top': '780px', 'right': '168px'}),
        ]),
    dcc.Store(id='data-store')  # Store component to hold the data for download
])

# 06: Define callback functions
@app.callback(
    [Output('graph-01', 'figure'),
     Output('graph-02', 'figure'),
     Output('graph-03', 'figure'),
     Output('graph-04', 'figure')],
    [Input('date-slider', 'value')]
)

# 07: Define update_graph
def update_graph(selected_date_range):
    filtered_data1 = data1[(data1['Date'] >= pd.to_datetime(selected_date_range[0], unit='s')) &
                         (data1['Date'] <= pd.to_datetime(selected_date_range[1], unit='s'))]
    filtered_data2 = data2[(data['Date'] >= pd.to_datetime(selected_date_range[0], unit='s')) &
                         (data['Date'] <= pd.to_datetime(selected_date_range[1], unit='s'))]
    filtered_data3 = data3[(data['Date'] >= pd.to_datetime(selected_date_range[0], unit='s')) &
                         (data['Date'] <= pd.to_datetime(selected_date_range[1], unit='s'))]
    filtered_data4 = data4[(data['Date'] >= pd.to_datetime(selected_date_range[0], unit='s')) &
                         (data['Date'] <= pd.to_datetime(selected_date_range[1], unit='s'))]

    fig1 = px.line(filtered_data1, x='Date', y=['ESB Seller', 'ESB Buyer', 'WA Seller', 'WA Buyer'])

    fig1.update_layout(
        title_text='ESB and WA Average Price Porker (45kg - 60Kg)',  # Title with  tags for bold
        title_x=0.5,            # Set the x-position to center
        title_y=.9, #1.2,       # Set the y-position to place it above the graph  value should be between int/float 0 and 1
        title_xanchor='center', # Center the x position
        title_font=dict(size=20, family="Arial, sans-serif", color="#808080") #, weight="bold")  # Set font properties
    )
    fig1.update_layout(xaxis_title='', yaxis_title='',legend_title_text='')
    fig1.update_yaxes(range=[y1_min_for_data1, y1_max_for_data1], dtick=100)
    fig1.update_layout(height=230)#, width=400)
    fig1.update_layout(margin=dict(l=0, r=0, t=50, b=0))        #left/right/top/bottom margin
    #fig1.update_layout(padding=dict(l=10, r=10, t=10, b=10))   #left/right/top/bottom margin

    fig2 = px.line(filtered_data2, x='Date', y=['ESB Seller', 'ESB Buyer', 'WA Seller', 'WA Buyer'])
    fig2.update_layout(
        title_text='ESB and WA Prime Price Porker (45kg - 60Kg)',  # Title with  tags for bold
        title_x=0.5,            # Set the x-position to center
        title_y=.9, #1.2,       # Set the y-position to place it above the graph  value should be between int/float 0 and 1
        title_xanchor='center', # Center the x position
        title_font=dict(size=20, family="Arial, sans-serif", color="#808080") #, weight="bold")  # Set font properties
    )
    fig2.update_layout(xaxis_title='', yaxis_title='',legend_title_text='')
    fig2.update_yaxes(range=[y2_min_for_data2, y2_max_for_data2], dtick=100)
    fig2.update_layout(height=230)#, width=400)
    fig2.update_layout(margin=dict(l=0, r=0, t=50, b=0))   #left/right/top/bottom margin

    fig3 = px.line(filtered_data3, x='Date', y=['ESB Seller', 'ESB Buyer', 'WA Seller', 'WA Buyer']) #, title='ESB and WA Average Price Baconer (60.1kg - 75Kg)')
    fig3.update_layout(
        title_text='ESB and WA Average Price Baconer (60.1kg - 75Kg)',  # Title with  tags for bold
        title_x=0.5,            # Set the x-position to center
        title_y=.9, #1.2,       # Set the y-position to place it above the graph  value should be between int/float 0 and 1
        title_xanchor='center', # Center the x position
        title_font=dict(size=20, family="Arial, sans-serif", color="#808080") #, weight="bold")  # Set font properties
    )
    fig3.update_layout(xaxis_title='', yaxis_title='',legend_title_text='')
    fig3.update_yaxes(range=[y3_min_for_data3, y3_max_for_data3], dtick=100)
    fig3.update_layout(height=230)#, width=400)
    fig3.update_layout(margin=dict(l=0, r=0, t=50, b=0))   #left/right/top/bottom margin

    fig4 = px.line(filtered_data4, x='Date', y=['ESB Seller', 'ESB Buyer', 'WA Seller', 'WA Buyer'], title='ESB and WA Prime Price Baconer (60.1kg - 75Kg)')
    fig4.update_layout(
        title_text='ESB and WA Prime Price Baconer (60.1kg - 75Kg)',  # Title with  tags for bold
        title_x=0.5,            # Set the x-position to center
        title_y=.9, #1.2,       # Set the y-position to place it above the graph  value should be between int/float 0 and 1
        title_xanchor='center', # Center the x position
        title_font=dict(size=20, family="Arial, sans-serif", color="#808080") #, weight="bold")  # Set font properties
    )
    fig4.update_layout(xaxis_title='', yaxis_title='',legend_title_text='')
    fig4.update_yaxes(range=[y4_min_for_data4, y4_max_for_data5], dtick=100)
    fig4.update_layout(height=230)#, width=400)
    fig4.update_layout(margin=dict(l=0, r=0, t=50, b=0))   #left/right/top/bottom margin

    return fig1, fig2, fig3, fig4

# 08: Define callback functions for output
@app.callback(
    Output('data-store', 'data'),
    [Input('extract-button-01', 'n_clicks'),
     Input('extract-button-02', 'n_clicks'),
     Input('extract-button-03', 'n_clicks'),
     Input('extract-button-04', 'n_clicks')],
    [State('date-slider', 'value')]
)

# 09: Define extract data function with click extract button
def extract_data(n_clicks_01, n_clicks_02, n_clicks_03, n_clicks_04, selected_date_range):
    ctx = dash.callback_context
    if not ctx.triggered:
        return None

    triggered_id = ctx.triggered[0]['prop_id'].split('.')[0]

    if triggered_id == 'extract-button-01':
##        csv_string = data1.to_csv(index=False,encoding='utf-8')
##        return {
##            'content': csv_string,
##            'filename': 'ESB and WA Average Price Porker (45kg - 60Kg).csv'  # Specify the desired filename
##        }
        filtered_data1 = os.startfile(download_path +'\ESB and WA Average Price Porker (45kg - 60Kg).csv')
        return filtered_data1
    elif triggered_id == 'extract-button-02':
        filtered_data2 = os.startfile(download_path +'\ESB and WA Prime Price Porker (45kg - 60Kg).csv')
        return filtered_data2
    elif triggered_id == 'extract-button-03':
        filtered_data3 = os.startfile(download_path +'\ESB and WA Average Price Baconer (60.1kg - 75Kg).csv')
        return filtered_data3
    elif triggered_id == 'extract-button-04':
        filtered_data4 = os.startfile(download_path +'\ESB and WA Prime Price Baconer (60.1kg - 75Kg).csv')
        return filtered_data4

# 00: main calling program
if __name__ == '__main__':
    app.run_server(host="0.0.0.0",debug=True)
    #app.run_server(host="127.0.0.1",debug=True)


import pygame
import sys

from bullet import Bullet
from baddie import Baddie
import time

def check_events_whilst_inactive(player, bullets, baddies, stats, play_button):
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            sys.exit()
        if event.type == pygame.MOUSEBUTTONDOWN:
            mouse_x, mouse_y = pygame.mouse.get_pos()
            check_play_button(player, bullets, baddies, stats, play_button, mouse_x, mouse_y)

def check_events(player, screen, settings, bullets, baddies, audio, ADDBADDIE, stats, level_up_msg):
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            sys.exit()
        if event.type == pygame.KEYDOWN:
            check_keydown_events(event, player, screen, settings, bullets, baddies, audio, stats, level_up_msg)
        if event.type == pygame.KEYUP:
            check_keyup_events(event, player)
        if event.type == ADDBADDIE:
            create_baddie(screen, baddies, settings, audio)

def check_keydown_events(event, player, screen, settings, bullets, baddies, audio, stats, level_up_msg):
    if event.key == pygame.K_RIGHT:
        player.moving_right = True
    if event.key == pygame.K_LEFT:
        player.moving_left = True
    if event.key == pygame.K_UP:
        player.moving_up = True
    if event.key == pygame.K_DOWN:
        player.moving_down = True
    if event.key == pygame.K_SPACE:
        fire_bullet(player, screen, settings, bullets, audio)
    if event.key == pygame.K_b:
        create_baddie(screen, baddies, settings, audio)
    if event.key == pygame.K_l:
        level_up(stats, audio, level_up_msg)

def check_keyup_events(event, player):
    if event.key == pygame.K_RIGHT:
        player.moving_right = False
    if event.key == pygame.K_LEFT:
        player.moving_left = False
    if event.key == pygame.K_UP:
        player.moving_up = False
    if event.key == pygame.K_DOWN:
        player.moving_down = False

def move(player):
    if player.moving_right:
        if player.rect.right <= player.screen_rect.right:
            player.rect.x += 1
    if player.moving_left:
        if player.rect.left >= player.screen_rect.left:
            player.rect.x -= 1
    if player.moving_up:
        if player.rect.top >= player.screen_rect.top:
            player.rect.y -= 1
    if player.moving_down:
        if player.rect.bottom <= player.screen_rect.bottom:
            player.rect.y += 1

def fire_bullet(player, screen, settings, bullets, audio):
    new_bullet = Bullet(player, screen, settings)
    if len(bullets) < settings.max_bullets:
        bullets.add(new_bullet)
        audio.shot.play()

def update_screen(player, screen, bullets, baddies, sb, stats, level_up_msg, settings):
    screen.fill((255, 255, 255))
    sb.prep_score()
    sb.show_score()
    check_if_levelled_up(stats, level_up_msg, settings)
    for bullet in bullets.sprites():
        bullet.draw_bullet()
    player.blitme()
    for baddie in baddies.sprites():
        baddie.blitme()
    pygame.display.flip()

def update_screen_whilst_inactive(screen, sb, stats, end_msg, play_button):
    screen.fill((255, 255, 255))
    sb.prep_score()
    sb.show_score()
    play_button.draw_button()
    if not stats.is_first_round:
        end_msg.prep_msg(play_button)
        end_msg.show_msg()
    pygame.display.flip()

def update_bullets(bullets):
    bullets.update()

    for bullet in bullets.copy():
        if bullet.rect.y <= 0:
            bullets.remove(bullet)

def check_bullet_collisions(baddies, bullets, stats, audio, settings, level_up_msg):
    collisions = pygame.sprite.groupcollide(baddies, bullets, True, True)
    if collisions:
        stats.score += 1
        # audio.baddie_ded.play()
        if stats.score > 0:
            if (stats.score % settings.baddies_per_round) == 0:
                level_up(stats, audio, level_up_msg)

def create_baddie(screen, baddies, settings, audio):
    new_baddie = Baddie(screen, settings)
    baddies.add(new_baddie)
    # audio.baddie_spawn.play()

def level_up(stats, audio, level_up_msg):
    stats.level += 1
    stats.baddie_drop_speed *= 1.1
    audio.play_trump_soundbite()
    time.sleep(1)

def check_if_levelled_up(stats, level_up_msg, settings):
    if stats.score > 0:
        if stats.score % settings.baddies_per_round == 0:
                display_level_up_msg(level_up_msg)

def display_level_up_msg(level_up_msg):
    level_up_msg.prep_level_up_msg()
    level_up_msg.show_level_up_msg()

def update_baddies(baddies, settings, stats):
    baddies.update(stats)
    for baddie in baddies.copy():
        if baddie.rect.y >= settings.screen_height:
            baddies.remove(baddie)

def check_baddie_player_collisions(baddies, player, stats, bullets, audio):
    if pygame.sprite.spritecollideany(player, baddies):
        life_lost(stats, player, bullets, baddies, audio)

def check_baddie_screen_bottom(screen, stats, player, bullets, baddies, audio):
    screen_rect = screen.get_rect()
    for baddie in baddies:
        if baddie.rect.bottom >= screen_rect.bottom:
            life_lost(stats, player, bullets, baddies, audio)

def life_lost(stats, player, bullets, baddies, audio):
    if stats.lives_remaining >= 1:
        stats.lives_remaining -= 1
    else:
        end_game(stats)
    audio.player_ded.play()
    time.sleep(0.2)
    reset_positions(player, bullets, baddies)

def reset_positions(player, bullets, baddies):
    player.reset_position()
    bullets.empty()
    baddies.empty()

def end_game(stats):
    stats.game_active = False

def check_play_button(player, bullets, baddies, stats, play_button, mouse_x, mouse_y):
    if play_button.rect.collidepoint(mouse_x, mouse_y):
        stats.reset_stats()
        reset_positions(player, bullets, baddies)
        stats.game_active = True

def no_longer_first_round(stats):
    stats.is_first_round = False


from django.shortcuts import render, redirect
from .models import Acao
from .forms import CreateAcao
from django.contrib.auth.decorators import login_required
from django.contrib import messages
import yfinance as yf

@login_required
def view(request):
    acoes = Acao.objects.filter(user_id = request.user.id)
    return render(request, "main/view.html", {"acoes":acoes})

# Create your views here.
@login_required
def home(request):
    return render(request, "main/home.html")

@login_required
def acao(request):
    # return render(request, "main/acao.html")
    form = CreateAcao()
    if request.method == 'POST':
        form = CreateAcao(request.POST)
        if form.is_valid():
            codigo = form.cleaned_data['ticker']
            ticker = yf.Ticker( codigo+".SA" )
            preco_pago = form.cleaned_data['preco_pago']
            quantidade = form.cleaned_data['quantidade']
            data_compra = form.cleaned_data['data_compra']
            valor_pago = round(preco_pago*quantidade, 2)
            preco_atual = round(ticker.history(period='1d')['Close'][0], 2)
            valor_atual = round(preco_atual*quantidade, 2)
            lucro_prejuizo = round(valor_atual-valor_pago, 2)
            rentabilidade = round(lucro_prejuizo/valor_pago*100, 2)
            user = request.user.id
            acao = Acao(user_id=user, codigo=codigo, preco_pago=preco_pago, quantidade=quantidade, data_compra=data_compra, valor_pago=valor_pago, preco_atual=preco_atual, valor_atual=valor_atual, lucro_prejuizo=lucro_prejuizo, rentabilidade=rentabilidade)
            acao.save()
            messages.success(request, "Ação adicionada a carteira!")
        else:
            form = CreateAcao()
        return redirect("/acao")
    # acoes = Acao.objects.filter(user_id = request.user.id)
    # return render(request, "main/acao.html", {"form":form}, {"acoes":acoes})
    return render(request, "main/acao.html", {"form":form})


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Wed Mar 31 12:44:14 2021

@author: nicolaskylilis
"""

def growth_curves(df_time, df_samples):

    import os
    from matplotlib import pyplot as plt

    dirname = 'growth_curves_plots'
    if not os.path.exists(dirname):
        os.makedirs(dirname)

    x = df_time

    l_col = list(df_samples.columns)
    for col in l_col:
        if 'Unnamed' in col:
            pass
        else:
            y = df_samples[col]

            plt.plot(x,y,'k')

            plt.xlabel('Time (minutes)')
            plt.ylabel('Absorbance_600nm (au)')
            plt.ylim(0,1)
            plt.title(col)

            plt.savefig(dirname + '/growth_curve_' + str(col) + '.png')
            plt.close()

    print('Process upadate: Finshed plotting growth curves')


from aiodocker.util import task
from aiodocker.exceptions import UnexpectedError
from aiodocker.formatters import from_info, from_version
import logging

log = logging.getLogger(__name__)

class MiscEndpoint:

    def __init__(self, client):
        self.api = client

    @task
    def info(self):
        response = yield from self.api.get('info')
        if response.status == 200:
            data = yield from response.json()
            return from_info(data)
        data = yield from response.text()
        raise UnexpectedError(response.status, data)

    @task
    def version(self):
        response = yield from self.api.get('version')
        if response.status == 200:
            data = yield from response.json()
            return from_version(data)
        data = yield from response.text()
        raise UnexpectedError(response.status, data)

    @task
    def ping(self):
        """
        Ping server.

        Returns
            bool: True if it's ok
        """
        response = yield from self.api.get('_ping')
        return response.status == 200


# Functions that deal with compressed archives.
#
# Functions:
# zip_path
#
# unzip_list   Return list of (shortname, fullname).
# unzip_dict   Return dict of shortname -> fullname.

import os, sys

def zip_path(file_or_path, zipfile=None, noclobber=True):
    import stat
    import subprocess

    assert os.path.exists(file_or_path), "Missing path: %s" % file_or_path
    root, name  = os.path.split(file_or_path)

    # Figure out a good name for the zipfile, if not given.
    if not zipfile:
        zipfile = "%s.zip" % os.path.splitext(name)[0]
    assert not (noclobber and os.path.exists(zipfile)), "Zipfile exists."

    # Find "zip".  Somehow the "zip" on the path for cagt does not
    # find the right command.  Will get errors:
    # /bin/sh: zip: command not found
    # Check for "zip" in the usual places.  If it doesn't exist,
    # then try without a path.
    ZIP_PATHS = ["/usr/local/bin/zip", "/usr/bin/zip", "/bin/zip"]
    zipcmd = "zip"
    for path in ZIP_PATHS:
        if os.path.exists(path):
            zipcmd = path
            break

    cwd = os.getcwd()
    try:
        if root:
            os.chdir(root)

        cmd = "%s -r '%s' '%s'" % (zipcmd, zipfile, name)
        #w, r = os.popen4(cmd)
        #print cmd
        p = subprocess.Popen(
            cmd, shell=True, bufsize=0, stdin=subprocess.PIPE,
            stdout=subprocess.PIPE, stderr=subprocess.STDOUT, close_fds=True)
        w, r = p.stdin, p.stdout
        w.close()

        # Check for errors in the zip output.
        output = r.read()
        assert output.find("command not found") < 0, x

        # Make sure the zip file exists.
        # This doesn't work.  exists_nz will also check for the file
        # without ".zip" and will find it.
        #assert filelib.exists_nz(zipfile), "Failed to archive trash."
        assert os.path.exists(zipfile) and os.stat(zipfile)[stat.ST_SIZE] > 0,\
               "Failed to make archive."

        # Clear out the old directory.
        if zipfile != name:
            os.system("rm -rf '%s'" % name)
    finally:
        os.chdir(cwd)

    return output

def unzip_list(filename):
    import zipfile

    assert os.path.exists(filename), "I could not find file %s." % filename
    assert zipfile.is_zipfile(filename)

    zfile = zipfile.ZipFile(filename)
    fullnames = zfile.namelist()
    shortnames = [os.path.split(x)[1] for x in fullnames]
    return zip(shortnames, fullnames)

def unzip_dict(filename):
    s2f = {}
    for x in unzip_list(filename):
        shortname, fullname = x
        assert shortname not in s2f, "Duplicate short name: %s" % shortnam
        s2f[shortname] = fullname
    return s2f


import dgl
import torch
import torch.nn as nn
import torch.nn.functional as F
from dgl.nn.pytorch import GATConv, GraphConv

class GAT(nn.Module):
    def __init__(
        self,
        g: dgl.DGLGraph,
        in_feats: int,
        hidden_feats: int,
        out_feats: int,
        num_layers: int,
        heads: list[int],
        activation: str,
        feats_dropout: float = 0,
        attention_dropout: float = 0,
        negative_slope: float = 0.2,
        residual: bool = False,
    ):
        super().__init__()
        self._g = g
        self._num_layers = num_layers

        if activation == 'relu':
            self._activation = nn.ReLU()
        elif activation == 'leaky_relu':
            self._activation = nn.LeakyReLU()

        self._layers = self._create_layers(
            in_feats,
            hidden_feats,
            out_feats,
            heads,
            feats_dropout,
            attention_dropout,
            negative_slope,
            residual,
        )

    def _create_layers(
        self,
        in_feats: int,
        hidden_feats: int,
        out_feats: int,
        heads: list[int],
        feats_dropout: float,
        attention_dropout: float,
        negative_slope: float,
        residual: bool,
    ) -> nn.ModuleList:
        layers = nn.ModuleList()

        layers.append(GATConv(
            in_feats,
            hidden_feats,
            heads[0],
            feats_dropout,
            attention_dropout,
            negative_slope,
            False,
            self._activation,
            allow_zero_in_degree=True,
        ))

        for i in range(1, self._num_layers):
            layers.append(GATConv(
                hidden_feats * heads[i - 1],
                hidden_feats,
                heads[i],
                feats_dropout,
                attention_dropout,
                negative_slope,
                residual,
                self._activation,
                allow_zero_in_degree=True,
            ))

        layers.append(GATConv(
            hidden_feats * heads[-2],
            out_feats,
            heads[-1],
            feats_dropout,
            attention_dropout,
            negative_slope,
            residual,
            None,
            allow_zero_in_degree=True,
        ))

        return layers

    def forward(self, inputs, get_attention: bool = False) -> list[torch.Tensor]:
        h = inputs

        for i in range(self._num_layers):
            h = self._layers[i](self._g, h).flatten(1)

        output_projection, attention = self._layers[-1](
            self._g, h, get_attention)
        logits = output_projection.mean(1)

        return logits, attention

class GCN(nn.Module):
    def __init__(
        self,
        g,
        in_feats,
        hidden_feats,
        out_feats,
        num_layers,
        activation,
        dropout,
    ):
        super().__init__()
        self._g = g

        if activation == 'relu':
            self._activation = nn.ReLU()
        elif activation == 'leaky_relu':
            self._activation = nn.LeakyReLU()

        self.layers = nn.ModuleList()
        # input layer
        self.layers.append(GraphConv(in_feats, hidden_feats, activation=self._activation, allow_zero_in_degree=True))
        # hidden layers
        for i in range(num_layers - 1):
            self.layers.append(GraphConv(hidden_feats, hidden_feats, activation=self._activation, allow_zero_in_degree=True))
        # output layer
        self.layers.append(GraphConv(hidden_feats, out_feats, allow_zero_in_degree=True))
        self.dropout = nn.Dropout(p=dropout)

    def forward(self, features):
        h = features
        for i, layer in enumerate(self.layers):
            if i != 0:
                h = self.dropout(h)
            h = layer(self._g, h)
        return h

def train(model, g, optimizer):
    pass

if __name__ == '__main__':
    dataset = dgl.data.CoraGraphDataset()
    g = dataset[0]

    g = dgl.remove_self_loop(g)
    g = dgl.add_self_loop(g)

    train_mask = g.ndata['train_mask']
    val_mask = g.ndata['val_mask']
    test_mask = g.ndata['test_mask']

    for round in range(300):
        model = GAT(
            g,
            in_feats=g.ndata['feat'].shape[-1],
            hidden_feats=8,
            out_feats=dataset.num_classes,
            num_layers=1,
            heads=[8, 1],
            activation='relu',
            feats_dropout=0,
            attention_dropout=0,
            negative_slope=0.2,
            residual=False,
        )

        optimizer = torch.optim.Adam(model.parameters())

        for epoch in range(200):
            model.train()
            optimizer.zero_grad()

            logits, attention = model(g.ndata['feat'], get_attention=True)

            loss = F.cross_entropy(
                logits[train_mask], g.ndata['label'][train_mask])

            loss.backward()
            optimizer.step()

            _, indices = torch.max(logits[train_mask], dim=1)
            correct = torch.sum(indices == g.ndata['label'][train_mask])
            accuracy = correct.item() / len(g.ndata['label'][train_mask])

        attention = attention.view(-1)
        criterium, _ = torch.kthvalue(
            attention, int(attention.shape[0] * 0.01))
        eids = torch.nonzero(attention <= criterium).view(-1)

        num_edges = g.num_edges()

        g = dgl.remove_edges(g, eids)

        print(f'GAT # Round: {round + 1} Loss: {loss:.2f} Accuracy: {accuracy * 100:.2f} % Num edges: {num_edges} Sparsity: {0.99 ** round * 100:.2f} %')

        model = GCN(
            g,
            in_feats=g.ndata['feat'].shape[-1],
            hidden_feats=16,
            out_feats=dataset.num_classes,
            num_layers=1,
            activation='relu',
            dropout=0.5,
        )

        optimizer = torch.optim.Adam(model.parameters())

        for epoch in range(200):
            model.train()
            optimizer.zero_grad()

            logits = model(g.ndata['feat'])

            loss = F.cross_entropy(
                logits[train_mask], g.ndata['label'][train_mask])

            loss.backward()
            optimizer.step()

            _, indices = torch.max(logits[train_mask], dim=1)
            correct = torch.sum(indices == g.ndata['label'][train_mask])
            accuracy = correct.item() / len(g.ndata['label'][train_mask])

        print(f'GCN # Round: {round + 1} Loss: {loss:.2f} Accuracy: {accuracy * 100:.2f} %')


import numpy as np
from tqdm import tqdm
from functions import draw

start = -10
stop = 10
num_grid = 101
# tol = 1e-20
num_iter = 1001

x, dx = np.linspace(start, stop, num_grid, retstep = True)
X, Y = np.meshgrid(x, x)

# initial potential profile
v = np.zeros((num_grid, num_grid))
# fix_pts = []
v[:, 0] = 1
# for i in range(45, 55):
    # for j in range(49, 52):
    #     v[i, j] = 1
v[num_grid - 1, 0:51] = 2
v[0, 0:51] = 2
# fixed_points = [((0, 1), (num_grid - 1, 2), (50, 0)]
# for pos, val in fixed_points:
#     v[pos] = val

# print(v)
# print()

# draw(X, Y, v, type = 'heat')

# lap = np.diag(-2*np.ones(num_grid), 0) + np.diag(np.ones(num_grid - 1), 1) + np.diag(np.ones(num_grid - 1), -1)
# # set boundary condition
# lap[0, 0:2] = 0
# lap[num_grid - 1, num_grid - 2 : num_grid] = 0
# # print(lap)

# Avg_mtx = np.diag(np.ones(num_grid - 1)/2, 1) + np.diag(np.ones(num_grid - 1)/2, -1)
# Avg_mtx[[0, num_grid - 1], [0, num_grid - 1]] = 1
# Avg_mtx[[0, num_grid - 1], [1, num_grid - 2]] = 0
# print(Avg_mtx)

# err = 1e2
v_old = v
# while err > tol:
for i in tqdm(range(num_iter)):
    v_new = v_old
    for i in range(1, num_grid - 1):
        for j in range(1, num_grid - 1):
            # if i in range(45, 55) and j in range(49, 52):
            #     continue
            v_new[i, j] = np.sum(v_old[[i-1, i+1, i, i], [j, j, j-1, j+1]]) / 4
    # for pos, val in fixed_points:
    #     v_new[pos] = val
    # err = np.sum((v_new - v_old)**2)
    v_old = v_new
print(v_old)
# print("Error:", err)

draw(X, Y, v_old, type = '3d')

E = np.gradient(-v_old)
print(E)

draw(X, Y, E, type = 'quiver')


import streamlit as st
import pandas as pd
import spacy

def get_result(score):
    if score["Negative"] > score["Positive"]:
        return "Negative"
    else:
        return "Positive"

st.title('Sentiment/Content Analysis')
nlp = spacy.load("/Users/tanushnadimpalli/Documents/python_stuff_new")

text = st.text_input(label="Type a sentence to run analysis on")

if text:
    doc = nlp(text)

    negative = doc.cats["Negative"]
    positive = doc.cats["Positive"]

    data = {'Value': [negative, positive]}
    df = pd.DataFrame(data)
    df.index = ["Negative", "Positive"]

    st.subheader("Result :")

    result = get_result(doc.cats)
    st.write(result)

    st.subheader("Label Scores :")

    st.write(df)
    st.bar_chart(data=df, use_container_width=True)


import statistics as st
import numpy as np

x = range(10)

mean = st.mean(x)

median = st.median(x) # In case of even values, average of the mid-two values

high_median = st.median_high(x) # In case of even, larger of mid-two values

low_median = st.median_low(x) # In case of even, larger of mid-two values

# st.mode  # --  Highest frequency item, if its not unique it throws exception

standard_deviation = st.stdev(x)

variance = st.variance(x)   # measure of variation from mean

# First 1st Quartile
q1 = np.percentile(x, np.arange(0, 100, 25))
# Take x, do a linear interpolation to determine 0th, 25th, 50th, 75th element
# of x and find how many elements are below that element.

q3 = np.percentile(x, np.arange(0, 100, 75))

# Matrix manipulations

matrix = np.array([
        [1, 2, 3],
        [4, 5, 6],
        [7, 8, 9]
])

np.mean(matrix)         # 5.0  total_all_elements/number_of_elements

np.median(matrix)       # create a list of all numbers, sort, and find median

np.var(matrix)          # create a list of all numbers, and find variance

np.percentile(matrix, 25)  # create a list of all number and then percentile calculation

np.percentile(matrix, 25, axis=0)
# array([2.5, 4. , 4. ])
# 25th percentile across axis 0 (Y)

np.percentile(matrix, 25, axis=1)
# array([1.5, 4.5, 7.5])
# 25th percentile across axis 1 (X)

from scipy import stats

matrix = np.array([
        [1, 2, 1, 3, 4, 6],
        [4, 5, 5, 6, 5, 4],
        [7, 8, 9, 7, 8, 9]
])

stats.mode(matrix, axis=1)
# ModeResult(mode=array([[1], [5], [7]]), count=array([[2], [3], [2]]))
# Along axis=1, find highest frequency number and its count

stats.mode(matrix, axis=None)
#  ModeResult(mode=array([4]), count=array([3]))
# Treat this as a list of number and calculate mode


import os
import neuralnet as nn
from utils.common import exists
import tensorflow as tf
import numpy as np

class logger:
    def __init__(self, path, values) -> None:
        self.path = path
        self.values = values

class FSRCNN:
    def __init__(self, scale):

        if scale not in [2, 3, 4]:
            raise ValueError("scale must be 2, 3, or 4")

        self.model = nn.FSRCNN(scale)
        self.optimizer = None
        self.loss =  None
        self.metric = None
        self.model_path = None
        self.ckpt = None
        self.ckpt_dir = None
        self.ckpt_man = None

    def setup(self, optimizer, loss, metric, model_path):
        self.optimizer = optimizer
        self.loss = loss
        self.metric = metric
        # @the best model weights
        self.model_path = model_path

    def load_checkpoint(self, ckpt_dir):
        self.ckpt_dir = ckpt_dir
        self.ckpt = tf.train.Checkpoint(step=tf.Variable(0),
                                        optimizer=self.optimizer,
                                        net=self.model)
        self.ckpt_man = tf.train.CheckpointManager(self.ckpt, ckpt_dir, max_to_keep=1)
        self.ckpt.restore(self.ckpt_man.latest_checkpoint)

    def load_weights(self, filepath):
        self.model.load_weights(filepath)

    def predict(self, lr):
        sr = self.model(lr)
        return sr

    def evaluate(self, dataset, batch_size=64):
        losses, metrics = [], []
        isEnd = False
        while isEnd == False:
            lr, hr, isEnd = dataset.get_batch(batch_size, shuffle_each_epoch=False)
            sr = self.predict(lr)
            losses.append(self.loss(hr, sr))
            metrics.append(self.metric(hr, sr))

        metric = tf.reduce_mean(metrics).numpy()
        loss = tf.reduce_mean(losses).numpy()
        return loss, metric

    def train(self, train_set, valid_set, batch_size, steps, save_every=1,
              save_best_only=False, save_log=False, log_dir=None):

        if (save_log) and (log_dir is None):
            raise ValueError("log_dir must be specified if save_log is True")
        os.makedirs(log_dir, exist_ok=True)
        dict_logger = {"loss":       logger(path=os.path.join(log_dir, "losses.npy"),      values=[]),
                       "metric":     logger(path=os.path.join(log_dir, "metrics.npy"),     values=[]),
                       "val_loss":   logger(path=os.path.join(log_dir, "val_losses.npy"),  values=[]),
                       "val_metric": logger(path=os.path.join(log_dir, "val_metrics.npy"), values=[])}
        for key in dict_logger.keys():
            path = dict_logger[key].path
            if exists(path):
                dict_logger[key].values = np.load(path).tolist()

        cur_step = self.ckpt.step.numpy()
        max_steps = steps + self.ckpt.step.numpy()

        prev_loss = np.inf
        if save_best_only and exists(self.model_path):
            self.load_weights(self.model_path)
            prev_loss, _ = self.evaluate(valid_set)
            self.load_checkpoint(self.ckpt_dir)

        loss_buffer = []
        metric_buffer = []
        while cur_step < max_steps:
            cur_step += 1
            self.ckpt.step.assign_add(1)
            lr, hr, _ = train_set.get_batch(batch_size)
            loss, metric = self.train_step(lr, hr)
            loss_buffer.append(loss)
            metric_buffer.append(metric)

            if (cur_step % save_every == 0) or (cur_step >= max_steps):
                loss = tf.reduce_mean(loss_buffer).numpy()
                metric = tf.reduce_mean(metric_buffer).numpy()
                val_loss, val_metric = self.evaluate(valid_set)
                print(f"Step {cur_step}/{max_steps}",
                      f"- loss: {loss:.7f}",
                      f"- {self.metric.__name__}: {metric:.3f}",
                      f"- val_loss: {val_loss:.7f}",
                      f"- val_{self.metric.__name__}: {val_metric:.3f}")

                if save_log == True:
                    dict_logger["loss"].values.append(loss)
                    dict_logger["metric"].values.append(metric)
                    dict_logger["val_loss"].values.append(val_loss)
                    dict_logger["val_metric"].values.append(val_metric)

                loss_buffer = []
                metric_buffer = []
                self.ckpt_man.save(checkpoint_number=0)

                if save_best_only and val_loss > prev_loss:
                    continue
                prev_loss = val_loss
                self.model.save_weights(self.model_path)
                print(f"Save model to {self.model_path}\n")

        if save_log == True:
            for key in dict_logger.keys():
                logger_obj = dict_logger[key]
                path = logger_obj.path
                values = np.array(logger_obj.values, dtype=np.float32)
                np.save(path, values)

    @tf.function
    def train_step(self, lr, hr):
        with tf.GradientTape() as tape:
            sr = self.model(lr, training=True)
            loss = self.loss(hr, sr)
            metric = self.metric(hr, sr)
        gradient = tape.gradient(loss, self.model.trainable_variables)
        self.optimizer.apply_gradients(zip(gradient, self.model.trainable_variables))
        return loss, metric


__author__ = "Connor Nelson"
from collections import Counter
import re
def array_diff(a, b):
    while b in a:
        a.remove(b)
    return a
import math
import math
def sum_of_squares(n):
    # put your code here...
    j = n
    total = 0
    count = 0
    arraySol = []
    while total < n and j > 0:
        if math.sqrt(j).is_integer() and total + j <= n:
            arraySol.append(j)
            total = total + j
            count = count + 1
            if n - total > j:
                j = j+ 1
        j = j - 1
    if total < n:
        count = count + n - total
    print (arraySol)
    return count
def top_3_words(text):
    return_list = []
    countFirst = 0
    first = ""
    countSecond = 0
    second = ""
    countThird = 0
    third = ""
    matcher = re.compile("[a-zA-Z']+")
    letterMatcher = re.compile("[a-zA-Z]+")
    words = matcher.findall(text)
    for i in range(len(words)):
        words[i] = words[i].lower()
        if "'" in words[i] and letterMatcher.search(words[i]) == None:
            words.pop(i)
    if len(set(words)) == 2:
        list_tuples = Counter(words).most_common(2)
        for i in range(len(list_tuples)):
            return_list.append(list_tuples[i][0].lower())
        return return_list
    else:
        if len(set(words)) == 1:
            list_tuples = Counter(words).most_common(1)
            for i in range(len(list_tuples)):
                return_list.append(list_tuples[i][0].lower())
            return list(set(words))
    paras = Counter(words)
    list_tuples = paras.most_common(3)
    for i in range(len(list_tuples)):
        return_list.append(list_tuples[i][0].lower())
    return return_list

    # words.remove(first)
    # second = Counter(words).most_common()
    # words.remove(second)
    # third = Counter(words).most_common()
    # return [first.lower(), second.lower(), third.lower()]

    # countsFirst = words.count(words[0])
    # countsSecond = words.count(words[0])
    # countsThird = words.count(words[0])
    # for i in set(words):
    #     if words.count(i) > countFirst:
    #       thrid = second
    #       second = first
    #         first = i
    #         countFirst = words.count(i)
    #     else:
    #       if words.count(i) > countSecond:
    #         second = i
    #         countSecond = words.count(i)
    #     else:
    #       if words.count(i) > countThird:
    #         third = i
    #         countThird = words.count(i)
    # for i in range(len(set(words)))
    #   returnArray.append()

if __name__ == "__main__":
    print(top_3_words("In a village of La Mancha, the name of which I have no desire to call to mind, there lived not long since one of those gentlemen that keep a lance in the lance-rack, an old buckler, a lean hack, and a greyhound for coursing. An olla of rather more beef than mutton, a salad on most nights, scraps on Saturdays, lentils on Fridays, and a pigeon or so extra on Sundays, made away with three-quarters of his income"))

Project 1 - Data Mining with Python/Introduction WIth Python/Charlie/CodeWarsTests.py


from typing import List

def merge(nums1: List[int], m: int, nums2: List[int], n: int) -> None:
    n1 = nums1[:m]
    nums1.clear()
    nums1.extend(n1)
    nums1.extend(nums2[:n])
    nums1.sort(reverse=False)

print(merge(nums1=[1, 2, 3, 0, 0, 0], m=3, nums2=[2, 5, 6, 0], n=3))


import requests
from colorama import Fore, Style, Back

cyan = Fore.CYAN + Style.BRIGHT
green = Fore.GREEN + Style.BRIGHT
red = Fore.RED + Style.BRIGHT
yellow = Fore.YELLOW + Style.BRIGHT

def HostHeader():
    url = input(Back.BLACK + Fore.CYAN + "Enter host >> ")
    url = (url)
    headers = {'Host': 'http://evil.com'}
    response = requests.get(url, headers=headers)

    if 'evil.com' in response.headers:
        print(green + "Vulnerable to Host Header Injection")
    else:
        print(Back.BLACK + Fore.RED + Style.NORMAL +
              "NOT VULNERABLE TO HOST HEADER INJECTION")

if __name__ == "__main__":
    HostHeader()