from __future__ import absolute_import
import logging
import os
import posixpath
import shutil

from py_utils import exc_util

from telemetry.core import exceptions
from telemetry.internal.platform import android_platform_backend as \
  android_platform_backend_module
from telemetry.internal.backends.chrome import android_minidump_symbolizer
from telemetry.internal.backends.chrome import chrome_browser_backend
from telemetry.internal.backends.chrome import minidump_finder
from telemetry.internal.browser import user_agent
from telemetry.internal.results import artifact_logger

from devil.android import app_ui
from devil.android import device_signal
from devil.android.sdk import intent
from devil.android.sdk import version_codes

class AndroidBrowserBackend(chrome_browser_backend.ChromeBrowserBackend):
  """The backend for controlling a browser instance running on Android."""
  DEBUG_ARTIFACT_PREFIX = 'android_debug_info'

  def __init__(self, android_platform_backend, finder_options,
               browser_directory, profile_directory, backend_settings,
               build_dir=None, local_apk_path=None):
    """
    Args:
      android_platform_backend: The
          android_platform_backend.AndroidPlatformBackend instance to use.
      browser_options: The browser_options.BrowserOptions instance to use.
      browser_directory: A string containing the path to the directory on the
          device where the browser is installed.
      profile_directory: A string containing a path to the directory on the
          device to store browser profile information in.
      backend_settings: The
          android_browser_backend_settings.AndroidBrowserBackendSettings
          instance to use.
      build_dir: A string containing a path to the directory on the host that
          the browser was built in, for finding debug artifacts. Can be None if
          the browser was not locally built, or the directory otherwise cannot
          be determined.
      local_apk_path: A string containing a path to the APK for the browser. Can
          be None if the browser was not locally built.
    """
    assert isinstance(android_platform_backend,
                      android_platform_backend_module.AndroidPlatformBackend)
    super().__init__(
        android_platform_backend,
        browser_options=finder_options.browser_options,
        browser_directory=browser_directory,
        profile_directory=profile_directory,
        supports_extensions=False,
        supports_tab_control=backend_settings.supports_tab_control,
        build_dir=build_dir)
    self._backend_settings = backend_settings

    # Initialize fields so that an explosion during init doesn't break in Close.
    self._saved_sslflag = ''
    self._app_ui = None
    self._browser_package = None
    self._finder_options = finder_options
    self._local_apk_path = local_apk_path

    # Set the debug app if needed.
    self.platform_backend.SetDebugApp(self.browser_package)

  @property
  def log_file_path(self):
    return None

  @property
  def device(self):
    return self.platform_backend.device

  @property
  def supports_app_ui_interactions(self):
    return True

  def GetAppUi(self):
    if self._app_ui is None:
      self._app_ui = app_ui.AppUi(self.device, package=self.browser_package)
    return self._app_ui

  def _StopBrowser(self):
    # Note: it's important to stop and _not_ kill the browser app, since
    # stopping also clears the app state in Android's activity manager.
    self.platform_backend.StopApplication(self.browser_package)

  def Start(self, startup_args):
    assert not startup_args, (
        'Startup arguments for Android should be set during '
        'possible_browser.SetUpEnvironment')
    self._dump_finder = minidump_finder.MinidumpFinder(
        self.browser.platform.GetOSName(), self.browser.platform.GetArchName())
    user_agent_dict = user_agent.GetChromeUserAgentDictFromType(
        self.browser_options.browser_user_agent_type)
    self.device.StartActivity(
        intent.Intent(package=self.browser_package,
                      activity=self._backend_settings.activity,
                      action=None, data='about:blank', category=None,
                      extras=user_agent_dict),
        blocking=True)
    try:
      self.BindDevToolsClient()
    except:
      self.Close()
      raise

  def BindDevToolsClient(self):
    super().BindDevToolsClient()
    package = self.devtools_client.GetVersion().get('Android-Package')
    if package is None:
      logging.warning('Could not determine package name from DevTools client.')
    elif package == self.browser_package:
      logging.info('Successfully connected to %s DevTools client', package)
    else:
      raise exceptions.BrowserGoneException(
          self.browser, 'Expected connection to %s but got %s.' % (
              self.browser_package, package))

  def _FindDevToolsPortAndTarget(self):
    devtools_port = self._backend_settings.GetDevtoolsRemotePort(
        self.device, self.browser_package)
    browser_target = None  # Use default
    return devtools_port, browser_target

  def Foreground(self):
    package = self.browser_package
    activity = self._backend_settings.activity
    self.device.StartActivity(
        intent.Intent(package=package,
                      activity=activity,
                      action=None,
                      flags=[intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED]),
        blocking=False)
    # TODO(crbug.com/601052): The following waits for any UI node for the
    # package launched to appear on the screen. When the referenced bug is
    # fixed, remove this workaround and just switch blocking above to True.
    try:
      app_ui.AppUi(self.device).WaitForUiNode(package=package)
    except Exception as e:
      raise exceptions.BrowserGoneException(
          self.browser,
          'Timed out waiting for browser to come back foreground.') from e

  def Background(self):
    package = 'org.chromium.push_apps_to_background'
    activity = package + '.PushAppsToBackgroundActivity'
    self.device.StartActivity(
        intent.Intent(
            package=package,
            activity=activity,
            action=None,
            flags=[intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED]),
        blocking=True)

  def ForceJavaHeapGarbageCollection(self):
    # Send USR1 signal to force GC on Chrome processes forked from Zygote.
    # (c.f. crbug.com/724032)
    self.device.KillAll(
        self.browser_package,
        exact=False,  # Send signal to children too.
        signum=device_signal.SIGUSR1,
        as_root=True)

  @property
  def processes(self):
    try:
      zygotes = self.device.ListProcesses('zygote')
      zygote_pids = set(p.pid for p in zygotes)
      assert zygote_pids, 'No Android zygote found'
      processes = self.device.ListProcesses(self.browser_package)
      return [p for p in processes if p.ppid in zygote_pids]
    except Exception as exc:
      # Re-raise as an AppCrashException to get further diagnostic information.
      # In particular we also get the values of all local variables above.
      raise exceptions.AppCrashException(
          self.browser, 'Error getting browser PIDs: %s' % exc)

  def GetPid(self):
    browser_processes = self._GetBrowserProcesses()
    assert len(browser_processes) <= 1, (
        'Found too many browsers: %r' % browser_processes)
    if not browser_processes:
      raise exceptions.BrowserGoneException(self.browser)
    return browser_processes[0].pid

  def _GetBrowserProcesses(self):
    """Return all possible browser processes."""
    package = self.browser_package
    return [p for p in self.processes if p.name == package]

  @property
  def browser_package(self):
    if not self._browser_package:
      self._browser_package = self._backend_settings.package
      if self._backend_settings.IsWebView():
        self._browser_package = (
            self._backend_settings.
            GetEmbedderPackageName(self._finder_options))
    return self._browser_package

  @property
  def activity(self):
    return self._backend_settings.activity

  @exc_util.BestEffort
  def Close(self):
    if os.getenv('CHROME_PGO_PROFILING'):
      self.devtools_client.DumpProfilingDataOfAllProcesses(timeout=120)
    super().Close()
    self._StopBrowser()
    if self._tmp_minidump_dir:
      shutil.rmtree(self._tmp_minidump_dir, ignore_errors=True)
      self._tmp_minidump_dir = None

  def IsBrowserRunning(self):
    return len(self._GetBrowserProcesses()) > 0

  def GetStandardOutput(self):
    return self.platform_backend.GetStandardOutput()

  def PullMinidumps(self):
    self._PullMinidumpsAndAdjustMtimes()

  def CollectDebugData(self, log_level):
    """Collects various information that may be useful for debugging.

    In addition to any data collected by parents' implementation, this also
    collects the following and stores it as artifacts:
      1. UI state of the device
      2. Logcat
      3. Symbolized logcat
      4. Tombstones

    Args:
      log_level: The logging level to use from the logging module, e.g.
          logging.ERROR.

    Returns:
      A debug_data.DebugData object containing the collected data.
    """
    # Store additional debug information as artifacts.
    # We do these in a mixed order so that higher priority ones are done first.
    # This is so that if an error occurs during debug data collection (e.g.
    # adb issues), we're more likely to end up with useful debug information.
    suffix = artifact_logger.GetTimestampSuffix()
    self._StoreLogcatAsArtifact(suffix)
    retval = super().CollectDebugData(log_level)
    self._StoreUiDumpAsArtifact(suffix)
    self._StoreTombstonesAsArtifact(suffix)
    return retval

  def SymbolizeMinidump(self, minidump_path):
    dump_symbolizer = android_minidump_symbolizer.AndroidMinidumpSymbolizer(
        self._dump_finder, self.build_dir,
        symbols_dir=self._CreateExecutableUniqueDirectory('chrome_symbols_'))
    stack = dump_symbolizer.SymbolizeMinidump(minidump_path)
    if not stack:
      return (False, 'Failed to symbolize minidump.')
    self._symbolized_minidump_paths.add(minidump_path)
    return (True, stack)

  def _GetBrowserExecutablePath(self):
    return self._local_apk_path

  def _PullMinidumpsAndAdjustMtimes(self):
    """Pulls any minidumps from the device to the host.

    Skips pulling any dumps that have already been pulled. The modification time
    of any pulled dumps will be set to the modification time of the dump on the
    device, offset by any difference in clocks between the device and host.
    """
    # The offset is (device_time - host_time), so a positive value means that
    # the device clock is ahead.
    time_offset = self.platform_backend.GetDeviceHostClockOffset()
    device = self.platform_backend.device

    device_dump_path = posixpath.join(
        self.platform_backend.GetDumpLocation(self.browser_package),
        'Crashpad', 'pending')
    if not device.PathExists(device_dump_path):
      logging.warning(
          'Device minidump path %s does not exist - not attempting to pull '
          'minidumps', device_dump_path)
      return
    device_dumps = device.ListDirectory(device_dump_path)
    for dump_filename in device_dumps:
      # Skip any .lock files since they're not useful and are prone to being
      # deleted by the time we try to actually pull them.
      if dump_filename.endswith('.lock'):
        continue
      host_path = os.path.join(self._tmp_minidump_dir, dump_filename)
      if os.path.exists(host_path):
        continue
      device_path = posixpath.join(device_dump_path, dump_filename)
      # Skip any files that have a .lock file associated with them, as that
      # implies that the minidump hasn't been fully written to disk yet.
      device_lock_path = device_path + '.lock'
      if device.FileExists(device_lock_path):
        logging.debug('Not pulling file %s because a .lock file exists for it',
                      device_path)
        continue
      device.PullFile(device_path, host_path)
      # Set the local version's modification time to the device's
      # The mtime returned by device_utils.StatPath only has a resolution down
      # to the minute, so we can't use that.
      # On Android L and earlier, 'stat' is not available, so fall back to
      # device_utils.StatPath and adjust the returned value so that it's as new
      # as possible while still fitting in that 1 minute resolution.
      device_mtime = None
      if device.build_version_sdk >= version_codes.MARSHMALLOW:
        device_mtime = device.RunShellCommand(
            ['stat', '-c', '%Y', device_path], single_line=True)
        device_mtime = int(device_mtime.strip())
      else:
        stat_output = device.StatPath(device_path)
        device_mtime = stat_output['st_mtime'] + 59
      host_mtime = device_mtime - time_offset
      os.utime(host_path, (host_mtime, host_mtime))

  def _StoreUiDumpAsArtifact(self, suffix):
    try:
      ui_dump = self.platform_backend.GetSystemUi().ScreenDump()
      artifact_name = posixpath.join(
          self.DEBUG_ARTIFACT_PREFIX, 'ui_dump-%s.txt' % suffix)
      artifact_logger.CreateArtifact(artifact_name, '\n'.join(ui_dump))
    except Exception:  # pylint: disable=broad-except
      logging.exception('Failed to store UI dump')

  def _StoreLogcatAsArtifact(self, suffix):
    # Get more lines than the default since this is likely to be run after a
    # failure, so this gives us a better chance of getting useful debug info.
    logcat = self.platform_backend.GetLogCat(number_of_lines=3000)
    artifact_name = posixpath.join(
        self.DEBUG_ARTIFACT_PREFIX, 'logcat-%s.txt' % suffix)
    artifact_logger.CreateArtifact(artifact_name, logcat)

    symbolized_logcat = self.platform_backend.SymbolizeLogCat(logcat)
    if symbolized_logcat is None:
      symbolized_logcat = 'Failed to symbolize logcat. Is the script available?'
    artifact_name = posixpath.join(
        self.DEBUG_ARTIFACT_PREFIX, 'symbolized_logcat-%s.txt' % suffix)
    artifact_logger.CreateArtifact(artifact_name, symbolized_logcat)

  def _StoreTombstonesAsArtifact(self, suffix):
    tombstones = self.platform_backend.GetTombstones()
    if tombstones is None:
      tombstones = 'Failed to get tombstones. Is the script available?'
    artifact_name = posixpath.join(
        self.DEBUG_ARTIFACT_PREFIX, 'tombstones-%s.txt' % suffix)
    artifact_logger.CreateArtifact(artifact_name, tombstones)


"""
Plotting routines for stereo pair footprints
"""
from typing import List
import json
from urllib import request
from pandas import DataFrame
from geopandas import GeoDataFrame
from shapely import wkt
from matplotlib import pyplot

def get_geometry_from_ODE(product_id: str):
    geom_resp = request.urlopen(
        url=f'http://oderest.rsl.wustl.edu/live2/?query=product&result=x&output=json&pdsid={product_id}'
    )
    geom_resp = json.loads(geom_resp.read())
    return geom_resp['ODEResults']['Products']['Product']['Footprint_geometry']

def get_footprints(product_ids: List, plot=True, save_path=None):
    """
    Plot footprints of images, given PDS (Planetary Data System) product IDs
    :param product_ids: Product id, for example M106761561LE
    :return: GeoDataFrame containing the plot footprints
    """
    df = DataFrame({'index': product_ids, 'footprint': product_ids})
    df['footprint'] = df['footprint'].apply(get_geometry_from_ODE).apply(wkt.loads)
    gdf = GeoDataFrame(df, geometry='footprint')
    gdf.geometry = gdf.geometry.boundary
    footprint_plot = gdf.plot(
        column='index',
        legend=True,
        legend_kwds={'loc': 'center left', 'bbox_to_anchor': (1, 0.5)}
    )
    pyplot.xlabel('Longitude, degrees E')
    pyplot.ylabel('Latitude, degrees N')
    return gdf

def get_pair_footprints(pair_ids: List, plot=True, save_path=None):
    """
    Plot or save overlapping areas between pairs of NAC images

    :param save_path: Path to output a vector file of the geometries, or None for no output. File based on extension.
    :param plot: Create a figure
    :param pair_ids: Pair ids, a list given like ['M106761561LExxM1101080055RE', 'M1096364254RExxM1142334242LE']
    :return: GeoDataFrame containing the pair footprints
    """
    pairs = [
        pair_id.split('xx')
        for pair_id
        in pair_ids
    ]
    df = DataFrame(pairs, index=pair_ids, columns=['prod_id_0', 'prod_id_1'])
    df = df.applymap(get_geometry_from_ODE).applymap(wkt.loads)
    df['intersection'] = df.apply(
        lambda a: a[0].intersection(a[1]),
        axis='columns')
    gdf = GeoDataFrame(df, geometry='intersection')
    gdf['pair_ids'] = gdf.index.values
    if save_path:
        if save_path.endswith('.json'):
            save_driver = 'GeoJSON'
        gdf.drop(['prod_id_0', 'prod_id_1'], axis='columns').to_file(save_path, driver=save_driver)
    if plot:
        gdf.geometry = gdf.geometry.boundary
        intersection_plot = gdf.plot(
            column='pair_ids',
            legend=True,
            legend_kwds={'loc': 'center left', 'bbox_to_anchor': (1, 0.5)}
        )
        pyplot.xlabel('Longitude, degrees E')
        pyplot.ylabel('Latitude, degrees N')
    return gdf


# -*- coding:utf-8 -*-

import sys
from pathlib import Path
from multiprocessing import current_process
sys.path.append(str(Path(__file__).resolve().parents[3]))
from libs.ansible_hepler.runner import Runner
from utils.logging import get_logger
error_logger = get_logger('log_error')
info_logger = get_logger('log_info')

def NginxAnsibleCmd(**kwargs):

    """
    远程执行sync, reload nginx
    :param kwargs:
    :return:
    """
    # import socket
    # 获取程序本地运行IP，获取生成配置文件使用
    # try:
    #     processIp = socket.gethostbyname(socket.gethostname())
    #     print(processIp)
    # except Exception as e:
    #     error_logger.error(str(e))
    #     return {'status': 500, 'msg': "获取系统IP错误!! 详情:" + str(e)}
    try:
        current_process()._config = {'semprefix': '/mp'}
        print(current_process()._config)
        res = [{'username': 'root', 'hostname': kwargs['ansibleIp']}]
        tqm = Runner(res)
        # 判断操作类型, sync or reload
        if kwargs['type'] == 'sync':
            # {'ansibleIp': '10.0.0.80', 'type': 'sync', 'srcFile': '/tmp/luffy.ob1api.com.conf', 'destPath': '/etc/nginx/conf.d/', 'syncCmd': ''}
            import subprocess
            val = subprocess.check_call('scp -P 22 {0} root@{1}:{2}'.format(kwargs['srcFile'], kwargs['ansibleIp'], kwargs['destPath']), shell=True)
            if val is not 0:
                return
            command = "bash {0}".format(kwargs['syncCmd'])
            # command = "scp -P 22 {0} root@{1}:{2} && bash {3}".format(kwargs['srcFile'], "10.0.0.1", kwargs['destPath'], kwargs['syncCmd'])
            print(command)
        elif kwargs['type'] == "add_dump":
            command = "bash {0} {1}".format(kwargs['addCmd'], kwargs['domain'])
            print(command)
            # 远程到 ansible 主机 dump 文件 ; 操作ansible主机上的脚本
        elif kwargs['type'] == "reload":
            command = kwargs['reloadCmd']
            print(command)
        elif kwargs['type'] == 'rmConf':
            command = "bash {0} {1}".format(kwargs['rmCmd'], kwargs['rmConf'])
        elif kwargs['type'] == 'justSync':
            command = "bash {0}".format(kwargs['syncCmd'])
        else:
            return {'status': 500, 'msg': "type非法参数!!"}
        ret = tqm.run(module_args=command)
        # print(ret)
        return {"status": 20000, "data": ret}
    except Exception as e:
        error_logger.info(str(e))
        return {'status': 500, 'msg': str(e)}


# -*- coding: utf-8 -*-
"""
Script to calculate a "disaggregated CRPS", i.e. output a value for each
single observation/grid ("ensemble member" instead of only the actual CRPS value)

Used in combination with PEST/OSTRICH, to make the individual observations
visible to PEST/OSTRICH (and, hence, give correct estimates of parameter
confidence intervals etc. if the used optimization algorithm supports that)
I.e. after PEST/OSTRICH squares and sums all residuals, the resulting value is
equal to the CRPS squared

Input: _grids.txt file, which is output from LSobs_to_grid_all_norms.py
Output: _crpsDis.txt file, which displays all crps values (weighted and
    unweighted), aggregated as well as disaggregated per grid

Uses code (adapted) from the properscoring python package:
https://github.com/TheClimateCorporation/properscoring
(LICENSE see below)

Usage:
    CRPS_disagg.py -i <LS_grids_file.txt>

@author: Raphael Schneider, GEUS Hydro, rs@geus.dk, May 2022
Reference: Schneider, R., Henriksen, H. J., and Stisen, S.: A robust objective
function for calibration of groundwater models in light of deficiencies of
model structure and observations, 613, 128339,
https://doi.org/10.1016/j.jhydrol.2022.128339, 2022.
"""

import sys
import getopt
import pandas as pd
import numpy as np

#%% CRPS Functions
"""
The following functions are taken and adapted from the properscoring python package:
https://github.com/TheClimateCorporation/properscoring
(for ease of use, to avoid having to install the package)

----
LICENSE (properscoring)
----
Copyright 2015 The Climate Corporation
Licensed under the Apache License, Version 2.0 (the "License"); you may not
use this file except in compliance with the License. You may obtain a copy
of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
License for the specific language governing permissions and limitations
under the License.
"""
def _move_axis_to_end(array, axis):
    """
    Function taken directly from properscoring package (_utils.py):
    https://github.com/TheClimateCorporation/properscoring
    """
    array = np.asarray(array)
    return np.rollaxis(array, axis, start=array.ndim)

def _argsort_indices(a, axis=-1):
    """
    Function taken directly from properscoring package (_utils.py):
    https://github.com/TheClimateCorporation/properscoring

    Like argsort, but returns an index suitable for sorting the
    the original array even if that array is multidimensional
    """
    a = np.asarray(a)
    ind = list(np.ix_(*[np.arange(d) for d in a.shape]))
    ind[axis] = a.argsort(axis)
    return tuple(ind)

def crps_disaggregated(observations, forecasts, weights=None, gridID=None, issorted=False, axis=-1):
    """
    Code taken and adapted from properscoring package:
    https://github.com/TheClimateCorporation/properscoring
    crps_ensemble() in _crps.py and _crps_ensemble_gufunc() in _gufuncs.py

    PLEASE NOTE: This function works only for a single "observation", e.g. the
    expected value of 0 for ME on wells!

    Parameters:
    ----
    observations : float
        The optimal value, typically 0 (ME = 0).
        (In the standard formulation of CRPS, this would be the observed/true
        value, and could be a vector. Here, single "observation" is expected)
    forecasts: array of len N
        The actual forecasted/simulated values (i.e. actual MEs)
    weights : array_like, optional (default=None)
        If provided, the CRPS is calculated exactly with the assigned
        probability weights to each forecast. Weights should be positive, but
        do not need to be normalized. By default, each forecast is weighted
        equally.
    gridID : array of len N
        (optional) Identifier of each well/grid as additional input; returns it
        in same order as the individual CRPS values
    issorted : bool, optional (default=False)
        Optimization flag to indicate that the elements of `ensemble` are
        already sorted along `axis`.
    axis : int, optional (default=-1)
        Axis in forecasts and weights which corresponds to different ensemble
        members, along which to calculate CRPS.

    Returns
    -------
    integral : float
        CRPS
    increment : np.ndarray
        CRPS, individual contribution of each forecast
    gridID : np.ndarray
        gridID, sorted as increments
    """
    observations = np.asarray(observations)
    forecasts = np.asarray(forecasts)
    if gridID is None:  # gridID added by Raphael Schneider
        gridID = np.arange(len(forecasts))
    else:
        gridID = np.asarray(gridID)
    if axis != -1:
        forecasts = _move_axis_to_end(forecasts, axis)
        gridID = _move_axis_to_end(gridID, axis) #added by Raphael Schneider
    if weights is not None:
        weights = _move_axis_to_end(weights, axis)
        if weights.shape != forecasts.shape:
            raise ValueError('forecasts and weights must have the same shape')
    if observations.shape not in [forecasts.shape, forecasts.shape[:-1]]:
        raise ValueError('observations and forecasts must have matching '
                         'shapes or matching shapes except along `axis=%s`'
                         % axis)
    if observations.shape == forecasts.shape:
        if weights is not None:
            raise ValueError('cannot supply weights unless you also supply '
                             'an ensemble forecast')
        return abs(observations - forecasts)
    if not issorted:
        if weights is None: #adapted by Raphael Schneider
            #forecasts = np.sort(forecasts, axis=-1)
            idx = _argsort_indices(forecasts, axis=-1)
            forecasts = forecasts[idx]
            gridID = gridID[idx]
        else:
            idx = _argsort_indices(forecasts, axis=-1)
            forecasts = forecasts[idx]
            gridID = gridID[idx] #added by Raphael Schneider
            weights = weights[idx]
    if weights is None:
        weights = np.ones_like(forecasts)
    # BEWARE! forecasts are assumed sorted in NumPy's sort order
    # we index the 0th element to get the scalar value from this 0d array:
    # http://numba.pydata.org/numba-doc/0.18.2/user/vectorize.html#the-guvectorize-decorator
    #obs = observations[0] # works only if function definded in "numba" style as in _gufuncs
    obs = observations
    if np.isnan(obs):
        #result = np.nan
        #return
        return np.nan #adapted by Raphael Schneider
    total_weight = 0.0
    for n, weight in enumerate(weights):
        if np.isnan(forecasts[n]):
            # NumPy sorts NaN to the end
            break
        if not weight >= 0: #adapted by Raphael Schneider
            # this catches NaN weights
            return np.nan
        total_weight += weight
    obs_cdf = 0
    forecast_cdf = 0
    prev_forecast = 0
    integral = 0
    increment = np.empty_like(forecasts) #introduced by Raphael Schneider to be able to return the single values assigned to each single observation
    for n, forecast in enumerate(forecasts):
        if np.isnan(forecast):
            # NumPy sorts NaN to the end
            if n == 0:
                integral = np.nan
            # reset for the sake of the conditional below
            forecast = prev_forecast
            break

        if obs_cdf == 0 and obs < forecast:
            increment[n] = (obs - prev_forecast) * forecast_cdf ** 2 + (forecast - obs) * (forecast_cdf - 1) ** 2
            integral += increment[n]
            obs_cdf = 1
        else:
            increment[n] = ((forecast - prev_forecast) * (forecast_cdf - obs_cdf) ** 2)
            integral += increment[n]

        forecast_cdf += weights[n] / total_weight
        prev_forecast = forecast
    if obs_cdf == 0:
        # forecast can be undefined here if the loop body is never executed
        # (because forecasts have size 0), but don't worry about that because
        # we want to raise an error in that case, anyways
        integral += obs - forecast
    return integral, increment, gridID

#%% main code - called from command line
def main():
    # command line option handling
    try:
        opts, args = getopt.getopt(sys.argv[1:], 'i:')
    except getopt.GetoptError as err:
        print(str(err))
        print(r'Usage: CRPS_disagg.py -i <LS_wells or grids file>')
        sys.exit(2)
    infile = ''
    for o, a in opts:
        if o == '-i':
            infile = a
        else:
            assert False, 'unhandled option'

    # use next line to test from IDE
    # infile = 'Storaa_all_QS_2000-2008_grids.txt'

    # actual stuff
    lsin = pd.read_csv(infile, index_col=False, sep='\t') #read in ls file as DataFrame
    lsin.rename(columns = {'depth_mean':'Depth'}, inplace=True)
    # assign weights based on depth and number of observations
    lsin['weight'] = np.nan
    lsin.loc[lsin['nobs']==1, ['weight']] = 1
    lsin.loc[(lsin['nobs']>=2) & (lsin['nobs']<10), ['weight']] = 2
    lsin.loc[(lsin['nobs']>=10) & (lsin['nobs']<100), ['weight']] = 3
    lsin.loc[lsin['nobs']>=100, ['weight']] = 5
    if sum(np.isnan(lsin.weight)) > 0:
        sys.exit(r'ERROR: did not manage to assign weights to all observations')

    # initialize Dataframe to hold results (only aggregated values; rest added later)
    cols = ['unweighted','weighted','ME','nobs']
    lsout = pd.DataFrame(index=['CRPS'], columns=cols, dtype=float)
    # get CRPS from properscoring (i.e. the aggregated value)
    temp = np.asarray(lsin.ME).copy()
    lsout.loc['CRPS','unweighted'] = crps_disaggregated(0, temp)[0] #unweighted
    tempw = np.asarray(lsin.weight)
    lsout.loc['CRPS','weighted'] = crps_disaggregated(0, temp, weights=tempw)[0] #weighted
    lsout.loc['CRPS','ME'] = np.nan
    lsout.loc['CRPS','nobs'] = lsin['nobs'].sum()

    # disaggregated CRPS
    temp = None; temp = np.asarray(lsin.ME).copy()
    tempg = None; tempg = np.asarray(lsin.gridID).copy()
    crps_d = crps_disaggregated(0, temp, tempg) #unweighted
    tempw = None; tempw = np.asarray(lsin.weight)
    crpsw_d = crps_disaggregated(0, temp, tempg, tempw) #weighted
    """
    NOTE: The single CRPS values do not come in the same order as input
    i.e. the gridID is carried through the CRPS function and returned in
    respective order (3rd element of return tuple)
    Here, the observations still will be written out in original order, which
    also is fixed between runs, to allow handling with PEST/OSTRICH
    """
    markerdf = pd.DataFrame(index=['start of disaggregated CRPS'], columns=cols, data='') #add to provide marker for PEST/OSTRICH
    tempdf = pd.DataFrame(index=lsin.gridID, columns=cols, dtype=float)
    for i, row in tempdf.iterrows():
        """
        OBIVOUS IMPLEMENTATION - DOES NOT WORK: pass to PEST as sqrt(CRPS_i), results as CRPS^2 as final phi, does not work as the individual contributions to CRPS from each observation differ wildly from run to run
           tempdf.loc[i, 'unweighted'] = np.sqrt(crps_d[1][crps_d[2] == row.name]) #pass to PEST as sqrt(), as PEST itself squares and sums - results in CRPS^2 as final Phi
           tempdf.loc[i, 'weighted'] = np.sqrt(crpsw_d[1][crpsw_d[2] == row.name]) #pass to PEST as sqrt(), as PEST itself squares and sums - results in CRPS^2 as final Phi
        """
        #ALTERNATIVE: pass to PEST as ME scaled with CRPS/RSSE, results in CRPS^2 as final Phi
        tempdf.loc[i, 'unweighted'] = float(lsin[lsin['gridID'] == row.name]['ME']) * (crps_d[0] / np.sqrt(np.sum(lsin['ME']**2)))
        tempdf.loc[i, 'weighted'] = float(lsin[lsin['gridID'] == row.name]['ME']) * (crpsw_d[0] / np.sqrt(np.sum(lsin['ME']**2)))
        tempdf.loc[i, 'ME'] = float(lsin[lsin['gridID'] == row.name]['ME'])
        tempdf.loc[i, 'nobs'] = int(lsin[lsin['gridID'] == row.name]['nobs'])

    lsout = lsout.append(markerdf)
    # remove all boreholes with negative z (indication of boreholes below bottom of model or above topography)
    tempdf.drop(tempdf[np.asarray(list(zip(*tempdf.index.str.split('z')))[1], dtype=int) < 0].index, inplace=True)
    lsout = lsout.append(tempdf)

    # write back to .txt file that can be read by PEST
    lsout.to_csv(infile.split('.txt')[0]+'_crpsDis.txt', sep='\t', na_rep='NaN')

if __name__ == "__main__":
    main()


K = int(input())
N = int(input())
time = 0

for i in range(N):
    T, Z = map(str, input().split())
    time += int(T)

    if time < 210:
        if Z == 'T':
            K += 1
            if K == 9:
                K = 1
    else:
        break

print(K)

9517_아이 러브 크로아티아.py


from urllib.request import urlopen as uReq
from bs4 import BeautifulSoup as soup

my_url = "https://www.daraz.com.bd/#hp-just-for-you"

uClint = uReq(my_url)
page_html = uClint.read()
uClint.close()

page_soup = soup(page_html, "html.parser")

containers = page_soup.findAll("div", {"class": "hp-card-mod-header"})

file_name = "product.csv"
f = open(file_name, "w")
headers = "brand, shipping"
f.write(headers)

for container in containers:
    brand_container = container.findAll("div", {"class": "title"})
    brand = brand_container[0].text
    #title_container = container.findAll("a", {"class": "item-title"})
    #product_name = title_container.text.strip()
    shipping_container = container.findAll("a", {"class": "ProfileCardMini-avatar profile-picture js-tooltip"})
    shipping = shipping_container[0].text.strip()

    print("brand: " + brand)
    print("shipping:" + shipping)

f.write(brand , shipping + "\n")

f.close()