#!/usr/bin/env python

class Soundex(object):

   def GetSoundex(self, value):
        value = value.upper()
        soundex = []
        for char in value:
            self.AddCharacter(soundex,char)

        soundex = self.RemovePlaceholders(soundex)
        self.FixLength(soundex)

        return ''.join(soundex)

   def AddCharacter(self, soundex, ch):
        if (len(soundex) == 0):
            soundex.append(ch)
        else:
            code = self.GetSoundexDigit(ch);
            if (code != soundex[len(soundex) - 1]):
               soundex.append(code);

   def GetSoundexDigit(self, ch):
       if ("BFPV".find(ch)>=0):
            return "1";
       if ("CGJKQSXZ".find(ch)>=0):
            return "2";
       if ("DT".find(ch)>=0):
            return "3";
       if (ch == 'L'):
            return "4";
       if ("MN".find(ch)>=0):
            return "5";
       if (ch == 'R'):
            return "6";
       return ".";

   def RemovePlaceholders(self, soundex):
       cleanlist = []
       for item in soundex:
            if(item!="."):
                cleanlist.append(item)
       return cleanlist

   def FixLength(self, soundex):
       length = len(soundex)
       if(length < 4):
            for x in range(0, 4-length):
                soundex.append("0")

   def Compare(self, value1, value2):
       matches = 0
       s1 = self.GetSoundex(value1)
       s2 = self.GetSoundex(value2)

       #print s1
       #print s2
       for i in range(0,4):
           if(s1[i] == s2[i]):
                matches=matches+1
       return matches

#
# main()
#

#s = Soundex()
#print s.Compare("house md", "dr House")
"""

        public int Compare(string value1, string value2)
        {
            int matches = 0;
            string soundex1 = GetSoundex(value1);
            string soundex2 = GetSoundex(value2);

            for (int i = 0; i < 4; i++)
                if (soundex1[i] == soundex2[i]) matches++;

            return matches;
        }
    }
"""


import numpy as np
import pandas as pd
from scipy import stats
from collections import defaultdict
from functools import cmp_to_key
from scipy.optimize import nnls

class Graph:
    def __init__(self,vertices):
        self.V= vertices
        self.graph = defaultdict(list)

    def addEdge(self,u,v):
        self.graph[u].append(v)

    def printPathsFunc(self, u, d, visited, path, current_path_list):
        visited[u]= True
        path.append(u)

        if u == d:
            path_copy = path[:]
            current_path_list.append(path_copy)
        else:
            for i in self.graph[u]:
                if visited[i]==False:
                    self.printPathsFunc(i, d, visited, path, current_path_list)

        path.pop()
        visited[u]= False
        return current_path_list

    def printPaths(self, s, d):
        total_results = []
        for start in s:
            for dest in d:
                path = []
                visited =[False]*(self.V)
                current_path_list = []
                current_path_results = self.printPathsFunc(start, dest, visited, path, current_path_list)
                if len(current_path_results) != 0:
                    total_results.extend(current_path_results)
        return total_results

def graph_process(config_path):
    cluster_unify = []
    partial_order = []
    start_candidates = []
    end_candidates = []

    f = open(config_path, 'r')
    while True:
        line = f.readline()
        if '=' in line:
            unify = list(map(int, line.split('=')))
            cluster_unify.append(unify)
        elif '<' in line:
            order = list(map(int, line.split('<')))
            partial_order.append(order)
            start_candidates.append(order[0])
            end_candidates.append(order[1])

        if not line: break
    f.close()

    start = []
    end = []
    for element in start_candidates:
        if element in end_candidates:
            continue
        start.append(element)

    for element in end_candidates:
        if element in start_candidates:
            continue
        end.append(element)

    start = list(set(start))
    end = list(set(end))
    return start, end, partial_order, cluster_unify

def generate_graph(partial_order_list, vertex_num):
    cluster_graph = Graph(vertex_num)
    for pair in partial_order_list:
        cluster_graph.addEdge(pair[0], pair[1])
    return cluster_graph

def save_graph_config(ordered_list, name):
    f = open("./graph_config/{}".format(name), 'w')

    for i in range(len(ordered_list) - 1):
        f.write('{}<{}\n'.format(ordered_list[i+1][0], ordered_list[i][0]))

    for orders in ordered_list:
        if len(orders) >= 2:
            f.write(str(orders[0]))
            for element in orders[1:]:
                f.write('={}'.format(element))
            f.write('\n')
    f.close()

def graph_inference_census(df, hist, cluster_num, file_path, col_name = 'TOTPOP_CY'):
    def numeric_compare(x, y):
        pop_list1 = result_list[x]
        pop_list2 = result_list[y]
        tTestResult = stats.ttest_ind(pop_list1, pop_list2)
        if (tTestResult.pvalue < 0.05) and (np.mean(pop_list1) < np.mean(pop_list2)):
            return 1
        elif (tTestResult.pvalue < 0.05) and (np.mean(pop_list1) >= np.mean(pop_list2)):
            return -1
        else:
            return 0

    result_list = []

    for i in range(cluster_num - 1):
        result_list.append([])

    for _ in range(100):
        msk = np.random.rand(len(df)) < 0.5
        df_train = df[msk][["Directory", col_name]]
        df_test = df[~msk][["Directory", col_name]]
        selected_hist_train = hist.loc[df_train['Directory'] - 1]
        selected_hist_test = hist.loc[df_test['Directory'] - 1]

        train_X = selected_hist_train.values
        train_y = df_train["TOTPOP_CY"].values
        test_X = selected_hist_test.values
        test_y = df_test["TOTPOP_CY"].values
        result = nnls(train_X, train_y, 100)[0]
        for i in range(len(result)):
            result_list[i].append(result[i])

    sorted_list = sorted(range(cluster_num - 1), key=cmp_to_key(numeric_compare))
    ordered_list = []
    ordered_list.append([sorted_list[0]])
    curr = 0
    for i in range(len(sorted_list) - 1):
        if numeric_compare(sorted_list[i], sorted_list[i+1]) == 0:
            ordered_list[curr].append(sorted_list[i+1])
        else:
            curr += 1
            ordered_list.append([sorted_list[i+1]])

    ordered_list.append([cluster_num - 1])
    save_graph_config(ordered_list, file_path)
    return './graph_config/{}'.format(file_path)

def graph_inference_nightlight(grid_df, nightlight_df, cluster_num, file_path):
    def numeric_compare(x, y):
        pop_list1 = df_merge_group.get_group(x)['light_sum'].tolist()
        pop_list2 = df_merge_group.get_group(y)['light_sum'].tolist()
        tTestResult = stats.ttest_ind(pop_list1, pop_list2)
        if (tTestResult.pvalue < 0.01) and (np.mean(pop_list1) < np.mean(pop_list2)):
            return 1
        elif (tTestResult.pvalue < 0.01) and (np.mean(pop_list1) >= np.mean(pop_list2)):
            return -1
        else:
            return 0

    df_merge = pd.merge(nightlight_df, grid_df, how='left', on='y_x')
    df_merge = df_merge.dropna()
    df_merge_group = df_merge.groupby('cluster_id')

    sorted_list = sorted(range(cluster_num - 1), key=cmp_to_key(numeric_compare))
    ordered_list = []
    ordered_list.append([sorted_list[0]])
    curr = 0
    for i in range(len(sorted_list) - 1):
        if numeric_compare(sorted_list[i], sorted_list[i+1]) == 0:
            ordered_list[curr].append(sorted_list[i+1])
        else:
            curr += 1
            ordered_list.append([sorted_list[i+1]])

    ordered_list.append([cluster_num - 1])
    save_graph_config(ordered_list, file_path)
    return './graph_config/{}'.format(file_path)


from smbus2 import SMBus
from smbus2 import SMBusWrapper
from Leg import Leg, ILeg
from ServoControll import initialize, move_angle
import time

#chip_addr = 0x40
class IPL():
    def __init__(self, start_addr, stop_addr):

  #      self.angle = 0
        self.start_addr = start_addr[0]
        self.stop_addr = stop_addr[0]
        self.point_zero =  280
        #self.min = -44
        #self.max = 44
 #       self.min = -88
 #       self.max = 88
 #       self.mode = 1
 #       self.modes = []
 #       self.last = 0

        initialize(self.point_zero, self.start_addr, self.stop_addr)

        self.lleg = ILeg('left', start_addr[1], stop_addr[1])
        self.rleg = ILeg('right', start_addr[2], stop_addr[2])

class PairOfLegs():

    def __init__(self, start_addr, stop_addr):

        self.angle = 0
        self.start_addr = start_addr[0]
        self.stop_addr = stop_addr[0]
        self.point_zero = 280
        self.min = -64
        self.max = 64
        self.mode = 1
        self.modes = []
        self.last = 0

        self.lleg = Leg('left', start_addr[1], stop_addr[1])
        self.rleg = Leg('right', start_addr[2], stop_addr[2])

    def calibrate(self):

        # if(self.lleg.angle <= abs(self.angle)):
        #     self.lleg.lock = False
        # if(self.rleg.angle <= abs(self.angle)):
        #     self.rleg.lock = False
        while((abs(self.angle) > self.lleg.angle and self.lleg.angle > 0) or (abs(self.angle) > self.rleg.angle and self.rleg.angle > 0)):
                if(self.angle != 0):
                    self.angle = self.angle + (0-self.angle)/abs(self.angle)
                    move_angle(self.point_zero, self.angle, self.stop_addr)
                    time.sleep(.005)
                    # if(self.lleg.angle + self.rleg.angle == 0):
                    #     self.rleg.lock = True
                    #     self.lleg.lock = True

        while(self.angle != 0 or self.lleg.angle != 0 or self.rleg.angle != 0):
            #while((self.angle > self.lleg.angle and self.lleg.angle > 0) or (self.angle > self.rleg.angle and self.rleg.angle > 0)):
            if(self.angle != 0):
                self.angle = self.angle + (0-self.angle)/abs(self.angle)
            if(self.lleg.angle + self.rleg.angle == 0):
                self.rleg.lock = True
                self.lleg.lock = True
            if(self.lleg.angle != 0 and self.lleg.lock):
                self.lleg.calibrate()
            if(self.rleg.angle != 0 and self.rleg.lock):
                self.rleg.calibrate()
            time.sleep(.005)
            #move_angle(self.addr)
            #print('p: {}, l: {}, r: {}'.format(self.angle, self.lleg.angle, self.rleg.angle))
            self.last = 0

    def move(self, mode, way):

        if(self.lleg.lock):
            self.lleg.move(self.last, mode)

        if(self.rleg.lock):
            self.rleg.move(self.last, mode)

        self.angle = self.angle + 2*self.modes[self.last]

        move_angle(self.point_zero, way*self.angle, self.stop_addr)

        if(self.angle >= self.max):
            self.last = 1
        elif(self.angle <= self.min):
            self.last = 3
        elif(self.angle == 0 and self.last == 1):
            self.last = 2
        elif(self.angle == 0 and self.last == 3):
            self.last = 4
        elif(abs(self.angle) == int(self.max/2) and self.last == 0):
            self.last = 5

        if(self.last == 2 or self.last == 4 or self.last == 5):

            if(self.lleg.angle + self.rleg.angle == 0):
                self.rleg.lock = True
                self.lleg.lock = True

    def move_up(self, mode):

        self.lleg.move(self.last, self.lleg.mode)
        move_angle(self.lleg.point_zero, self.lleg.angle, self.lleg.stop_addr)
        self.rleg.move(self.last, self.rleg.mode)
        move_angle(self.rleg.point_zero, self.rleg.angle, self.rleg.stop_addr)
        self.angle = self.angle + self.modes[self.last]
        move_angle(self.point_zero, self.angle, self.stop_addr)
        time.sleep(.3)
        if(abs(self.angle) == 22 and self.last == 0):
            self.last = 5


"""
Interfaces for help menus
"""
import curses
import json
from os.path import join, abspath, dirname

from utility import draw_table_centered

class Controls:
    """
    Help menu for inventory and game map
    """

    def __init__(self, stdscr, menu_type: str):
        self.stdscr = stdscr
        with open(join(dirname(abspath(__file__)), '..', 'resources',
                       'controls.json')) as items:
            self.items = json.load(items)[menu_type]

    def print(self):
        """
        print interface to window
        """
        self.stdscr.clear()
        height, width = self.stdscr.getmaxyx()
        window = curses.newwin(height, width, 0, 0)
        draw_table_centered(window, ['Primary', 'Secondary', 'Action'],
                            self.items)
        window.addstr(1, 1, 'Keymap')
        window.refresh()
        window.getch()


import requests
from ast import literal_eval

class Authenticate(object):

    def __init__(self):
        super().__init__()

    def authentication(self):
        '''
        Hits Authentication endpoint and gets Session Token, access ID and access Key
        are generated in Cedato platform. API Key must be base64 encoded, format is encoded AccessKey:Secret
        '''

        payload = {'grant_type': 'client_credentials'}
        header = {
            'accept': 'application/json',
            'api-version': '1',
            'authorization': 'Basic {0}', # insert your base64 encrypted AccessKey:Secret here
            'content-type': 'application/x-www-form-urlencoded'
        }
        session_url = 'https://api.cedato.com/api/token'
        response = requests.post(session_url, data=payload, headers=header)
        token = literal_eval(response.text)['data']['access_token']
        return token


# -*- coding: utf-8 -*-
#
# Base class for all computational classes in Syncopy
#

# Builtin/3rd party package imports
import os
import sys
import psutil
import h5py
import time
import numpy as np
from abc import ABC, abstractmethod
from copy import copy
from glob import glob
from numpy.lib.format import open_memmap
from tqdm.auto import tqdm
if sys.platform == "win32":
    # tqdm breaks term colors on Windows - fix that (tqdm issue #446)
    import colorama
    colorama.deinit()
    colorama.init(strip=False)

# Local imports
from .tools import get_defaults
from syncopy import __storage__, __dask__, __path__
from syncopy.shared.errors import SPYIOError, SPYValueError, SPYParallelError, SPYWarning
if __dask__:
    import dask.distributed as dd
    import dask.bag as db
    # # In case of problems w/worker-stealing, uncomment the following lines
    # import dask
    # dask.config.set(distributed__scheduler__work_stealing=False)

__all__ = []

class ComputationalRoutine(ABC):
    """Abstract class for encapsulating sequential/parallel algorithms

    A Syncopy compute class consists of a
    :class:`ComputationalRoutine`-subclass that binds a static
    :func:`computeFunction` and provides the class method
    :meth:`process_metadata`.

    Requirements for :meth:`computeFunction`:

    * First positional argument is a :class:`numpy.ndarray`, the keywords
      `chunkShape` and `noCompute` are supported
    * Returns a :class:`numpy.ndarray` if `noCompute` is `False` and expected
      shape and numerical type of output array otherwise.

    Requirements for :class:`ComputationalRoutine`:

    * Child of :class:`ComputationalRoutine`, binds :func:`computeFunction`
      as static method
    * Provides class method :func:`process_metadata`

    For details on writing compute classes and metafunctions for Syncopy, please
    refer to :doc:`/developer/compute_kernels`.
    """

    # Placeholder: the actual workhorse
    @staticmethod
    def computeFunction(arr, *argv, chunkShape=None, noCompute=None, **kwargs):
        """Computational core routine

        Parameters
        ----------
        arr : :class:`numpy.ndarray`
           Numerical data from a single trial
        *argv : tuple
           Arbitrary tuple of positional arguments
        chunkShape : None or tuple
           Mandatory keyword. If not `None`, represents global block-size of
           processed trial.
        noCompute : None or bool
           Preprocessing flag. If `True`, do not perform actual calculation but
           instead return expected shape and :class:`numpy.dtype` of output
           array.
        **kwargs: dict
           Other keyword arguments.

        Returns
        -------
        out Shape : tuple, if ``noCompute == True``
           expected shape of output array
        outDtype : :class:`numpy.dtype`, if ``noCompute == True``
           expected numerical type of output array
        res : :class:`numpy.ndarray`, if ``noCompute == False``
           Result of processing input `arr`

        Notes
        -----
        This concrete method is a placeholder that is intended to be
        overloaded.

        See also
        --------
        ComputationalRoutine : Developer documentation: :doc:`/developer/compute_kernels`.
        """
        return None

    def __init__(self, *argv, **kwargs):
        """
        Instantiate a :class:`ComputationalRoutine` subclass

        Parameters
        ----------
        *argv : tuple
           Tuple of positional arguments passed on to :meth:`computeFunction`
        **kwargs : dict
           Keyword arguments passed on to :meth:`computeFunction`

        Returns
        -------
        obj : instance of :class:`ComputationalRoutine`-subclass
           Usable class instance for processing Syncopy data objects.
        """

        # list of positional arguments to `computeFunction` for all workers, format:
        # ``self.argv = [3, [0, 1, 1], ('a', 'b', 'c')]`` (compare to `self.ArgV` below)
        self.argv = list(argv)

        # list of positional keyword arguments split up for each worker w/format:
        # ``self.ArgV = [(3,0,'a'), (3,1,'b'), (3,1,'c')`` (compare `self.argv` above)
        self.ArgV = None

        # dict of default keyword values accepted by `computeFunction`
        self.defaultCfg = get_defaults(self.computeFunction)

        # dict of actual keyword argument values to `computeFunction` provided by user
        self.cfg = copy(self.defaultCfg)
        for key in set(self.cfg.keys()).intersection(kwargs.keys()):
            self.cfg[key] = kwargs[key]

        # binary flag: if `True`, average across trials, do nothing otherwise
        self.keeptrials = None

        # full shape of final output dataset (all trials, all chunks, etc.)
        self.outputShape = None

        # numerical type of output dataset
        self.dtype = None

        # list of dicts encoding header info of raw binary input files (experimental!)
        self.hdr = None

        # list of trial numbers to process (either `data.trials` or `data._selection.trials`)
        self.trialList = None

        # list of index-tuples for extracting trial-chunks from input HDF5 dataset
        # >>> MUST be ordered, no repetitions! <<<
        # indices are ABSOLUTE, i.e., wrt entire dataset, not just current trial!
        self.sourceLayout = None

        # list of index-tuples for re-ordering NumPy arrays extracted w/`self.sourceLayout`
        # >>> can be unordered w/repetitions <<<
        # indices are RELATIVE, i.e., wrt current trial!
        self.sourceSelectors = None

        # list of index-tuples for storing trial-chunk result in output dataset
        # >>> MUST be ordered, no repetitions! <<<
        # indices are ABSOLUTE, i.e., wrt entire dataset, not just current trial
        self.targetLayout = None

        # list of shape-tuples of trial-chunk results
        self.targetShapes = None

        # binary flag: if `True`, use fancy array indexing via `np.ix_` to extract
        # data from input via `self.sourceLayout` + `self.sourceSelectors`; if `False`,
        # only use `self.sourceLayout` (selections ordered, no reps)
        self.useFancyIdx = None

        # integer, max. memory footprint of largest input array piece (in bytes)
        self.chunkMem = None

        # directory for storing source-HDF5 files making up virtual output dataset
        self.virtualDatasetDir = None

        # h5py layout encoding shape/geometry of file sources within virtual output dataset
        self.VirtualDatasetLayout = None

        # name of output dataset
        self.datasetName = None

        # tmp holding var for preserving original access mode of `data`
        self.dataMode = None

        # time (in seconds) b/w querying state of futures ('pending' -> 'finished')
        self.sleepTime = 0.1

        # if `True`, enforces use of single-threaded scheduler in `compute_parallel`
        self.parallelDebug = False

        # format string for tqdm progress bars in sequential and parallel computations
        self.tqdmFormat = "{desc}: {percentage:3.0f}% |{bar}| {n_fmt}/{total_fmt} [{elapsed}<{remaining}]"

        # maximal acceptable size (in MB) of any provided positional argument
        self._maxArgSize = 100

        # counter and maximal recursion depth for calling `self._sizeof`
        self._callMax = 10000
        self._callCount = 0

    def initialize(self, data, chan_per_worker=None, keeptrials=True):
        """
        Perform dry-run of calculation to determine output shape

        Parameters
        ----------
        data : syncopy data object
           Syncopy data object to be processed (has to be the same object
           that is passed to :meth:`compute` for the actual calculation).
        chan_per_worker : None or int
           Number of channels to be processed by each worker (only relevant in
           case of concurrent processing). If `chan_per_worker` is `None` (default)
           by-trial parallelism is used, i.e., each worker processes
           data corresponding to a full trial. If `chan_per_worker > 0`, trials
           are split into channel-groups of size `chan_per_worker` (+ rest if the
           number of channels is not divisible by `chan_per_worker` without
           remainder) and workers are assigned by-trial channel-groups for
           processing.
        keeptrials : bool
            Flag indicating whether to return individual trials or average

        Returns
        -------
        Nothing : None

        Notes
        -----
        This class method **has** to be called prior to performing the actual
        computation realized in :meth:`computeFunction`.

        See also
        --------
        compute : core routine performing the actual computation
        """

        # First store `keeptrial` keyword value (important for output shapes below)
        self.keeptrials = keeptrials

        # Determine if data-selection was provided; if so, extract trials and check
        # whether selection requires fancy array indexing
        if data._selection is not None:
            self.trialList = data._selection.trials
            self.useFancyIdx = data._selection._useFancy
        else:
            self.trialList = list(range(len(data.trials)))
            self.useFancyIdx = False
        numTrials = len(self.trialList)

        # If lists/tuples are in positional arguments, ensure `len == numTrials`
        # Scalars are duplicated to fit trials, e.g., ``self.argv = [3, [0, 1, 1]]``
        # then ``argv = [[3, 3, 3], [0, 1, 1]]``
        for ak, arg in enumerate(self.argv):

            # Ensure arguments are within reasonable size for distribution across workers
            # (protect against circular object references by imposing max. calls)
            self._callCount = 0
            argsize = self._sizeof(arg)
            if argsize > self._maxArgSize:
                lgl = "positional arguments less than 100 MB each"
                act = "positional argument with memory footprint of {0:4.2f} MB"
                raise SPYValueError(legal=lgl, varname="argv", actual=act.format(argsize))

            if isinstance(arg, (list, tuple)):
                if len(arg) != numTrials:
                    lgl = "list/tuple of positional arguments for each trial"
                    act = "length of list/tuple does not correspond to number of trials"
                    raise SPYValueError(legal=lgl, varname="argv", actual=act)
                continue
            elif isinstance(arg, np.ndarray):
                if arg.size == numTrials:
                    msg = "found NumPy array with size == #Trials. " +\
                        "Regardless, every worker will receive an identical copy " +\
                        "of this array. To propagate elements across workers, use " +\
                        "a list or tuple instead!"
                    SPYWarning(msg)
            self.argv[ak] = [arg] * numTrials

        # Prepare dryrun arguments and determine geometry of trials in output
        dryRunKwargs = copy(self.cfg)
        dryRunKwargs["noCompute"] = True
        chk_list = []
        dtp_list = []
        trials = []
        for tk, trialno in enumerate(self.trialList):
            trial = data._preview_trial(trialno)
            trlArg = tuple(arg[tk] for arg in self.argv)
            chunkShape, dtype = self.computeFunction(trial,
                                                     *trlArg,
                                                     **dryRunKwargs)
            chk_list.append(list(chunkShape))
            dtp_list.append(dtype)
            trials.append(trial)

        # The aggregate shape is computed as max across all chunks
        chk_arr = np.array(chk_list)
        if np.unique(chk_arr[:, 0]).size > 1 and not self.keeptrials:
            err = "Averaging trials of unequal lengths in output currently not supported!"
            raise NotImplementedError(err)
        if np.any([dtp_list[0] != dtp for dtp in dtp_list]):
            lgl = "unique output dtype"
            act = "{} different output dtypes".format(np.unique(dtp_list).size)
            raise SPYValueError(legal=lgl, varname="dtype", actual=act)
        chunkShape = tuple(chk_arr.max(axis=0))
        self.outputShape = (chk_arr[:, 0].sum(),) + chunkShape[1:]
        self.cfg["chunkShape"] = chunkShape
        self.dtype = np.dtype(dtp_list[0])

        # Ensure channel parallelization can be done at all
        if chan_per_worker is not None and "channel" not in data.dimord:
            msg = "input object does not contain `channel` dimension for parallelization!"
            SPYWarning(msg)
            chan_per_worker = None
        if chan_per_worker is not None and self.keeptrials is False:
            msg = "trial-averaging does not support channel-block parallelization!"
            SPYWarning(msg)
            chan_per_worker = None
        if data._selection is not None:
            if chan_per_worker is not None and data._selection.channel != slice(None, None, 1):
                msg = "channel selection and simultaneous channel-block " +\
                    "parallelization not yet supported!"
                SPYWarning(msg)
                chan_per_worker = None

        # Allocate control variables
        trial = trials[0]
        trlArg0 = tuple(arg[0] for arg in self.argv)
        chunkShape0 = chk_arr[0, :]
        lyt = [slice(0, stop) for stop in chunkShape0]
        sourceLayout = []
        targetLayout = []
        targetShapes = []
        ArgV = []

        # If parallelization across channels is requested the first trial is
        # split up into several chunks that need to be processed/allocated
        if chan_per_worker is not None:

            # Set up channel-chunking
            nChannels = data.channel.size
            rem = int(nChannels % chan_per_worker)
            n_blocks = [chan_per_worker] * int(nChannels//chan_per_worker) + [rem] * int(rem > 0)
            inchanidx = data.dimord.index("channel")

            # Perform dry-run w/first channel-block of first trial to identify
            # changes in output shape w.r.t. full-trial output (`chunkShape`)
            shp = list(trial.shape)
            idx = list(trial.idx)
            shp[inchanidx] = n_blocks[0]
            idx[inchanidx] = slice(0, n_blocks[0])
            trial.shape = tuple(shp)
            trial.idx = tuple(idx)
            res, _ = self.computeFunction(trial, *trlArg0, **dryRunKwargs)
            outchan = [dim for dim in res if dim not in chunkShape0]
            if len(outchan) != 1:
                lgl = "exactly one output dimension to scale w/channel count"
                act = "{0:d} dimensions affected by varying channel count".format(len(outchan))
                raise SPYValueError(legal=lgl, varname="chan_per_worker", actual=act)
            outchanidx = res.index(outchan[0])

            # Get output chunks and grid indices for first trial
            chanstack = 0
            blockstack = 0
            for block in n_blocks:
                shp = list(trial.shape)
                idx = list(trial.idx)
                shp[inchanidx] = block
                idx[inchanidx] = slice(blockstack, blockstack + block)
                trial.shape = tuple(shp)
                trial.idx = tuple(idx)
                res, _ = self.computeFunction(trial, *trlArg0, **dryRunKwargs)
                lyt[outchanidx] = slice(chanstack, chanstack + res[outchanidx])
                targetLayout.append(tuple(lyt))
                targetShapes.append(tuple([slc.stop - slc.start for slc in lyt]))
                sourceLayout.append(trial.idx)
                ArgV.append(trlArg0)
                chanstack += res[outchanidx]
                blockstack += block

        # Simple: consume all channels simultaneously, i.e., just take the entire trial
        else:
            targetLayout.append(tuple(lyt))
            targetShapes.append(chunkShape0)
            sourceLayout.append(trial.idx)
            ArgV.append(trlArg0)

        # Construct dimensional layout of output
        stacking = targetLayout[0][0].stop
        for tk in range(1, len(self.trialList)):
            trial = trials[tk]
            trlArg = tuple(arg[tk] for arg in self.argv)
            chkshp = chk_list[tk]
            lyt = [slice(0, stop) for stop in chkshp]
            lyt[0] = slice(stacking, stacking + chkshp[0])
            stacking += chkshp[0]
            if chan_per_worker is None:
                targetLayout.append(tuple(lyt))
                targetShapes.append(tuple([slc.stop - slc.start for slc in lyt]))
                sourceLayout.append(trial.idx)
                ArgV.append(trlArg)
            else:
                chanstack = 0
                blockstack = 0
                for block in n_blocks:
                    shp = list(trial.shape)
                    idx = list(trial.idx)
                    shp[inchanidx] = block
                    idx[inchanidx] = slice(blockstack, blockstack + block)
                    trial.shape = tuple(shp)
                    trial.idx = tuple(idx)
                    res, _ = self.computeFunction(trial, *trlArg, **dryRunKwargs) # FauxTrial
                    lyt[outchanidx] = slice(chanstack, chanstack + res[outchanidx])
                    targetLayout.append(tuple(lyt))
                    targetShapes.append(tuple([slc.stop - slc.start for slc in lyt]))
                    sourceLayout.append(trial.idx)
                    chanstack += res[outchanidx]
                    blockstack += block
                    ArgV.append(trlArg)

        # If the determined source layout contains unordered lists and/or index
        # repetitions, set `self.useFancyIdx` to `True` and prepare a separate
        # `sourceSelectors` list that is used in addition to `sourceLayout` for
        # data extraction.
        # In this case `sourceLayout` uses ABSOLUTE indices (indices wrt to size
        # of ENTIRE DATASET) that are SORTED W/O REPS to extract a NumPy array
        # of appropriate size from HDF5.
        # Then `sourceLayout` uses RELATIVE indices (indices wrt to size of CURRENT
        # TRIAL) that can be UNSORTED W/REPS to actually perform the requested
        # selection on the NumPy array extracted w/`sourceLayout`.
        for grd in sourceLayout:
            if any([np.diff(sel).min() <= 0 if isinstance(sel, list)
                    and len(sel) > 1 else False for sel in grd]):
                self.useFancyIdx = True
                break
        if self.useFancyIdx:
            sourceSelectors = []
            for gk, grd in enumerate(sourceLayout):
                ingrid = list(grd)
                sigrid = []
                for sk, sel in enumerate(grd):
                    if isinstance(sel, list):
                        selarr = np.array(sel, dtype=np.intp)
                    else: # sel is a slice
                        step = sel.step
                        if sel.step is None:
                            step = 1
                        selarr = np.array(list(range(sel.start, sel.stop, step)), dtype=np.intp)
                    if selarr.size > 0:
                        sigrid.append(np.array(selarr) - selarr.min())
                        ingrid[sk] = slice(selarr.min(), selarr.max() + 1, 1)
                    else:
                        sigrid.append([])
                        ingrid[sk] = []
                sourceSelectors.append(tuple(sigrid))
                sourceLayout[gk] = tuple(ingrid)
        else:
            sourceSelectors = [Ellipsis] * len(sourceLayout)

        # Store determined shapes and grid layout
        self.sourceLayout = sourceLayout
        self.sourceSelectors = sourceSelectors
        self.targetLayout = targetLayout
        self.targetShapes = targetShapes
        self.ArgV = ArgV

        # Compute max. memory footprint of chunks
        if chan_per_worker is None:
            self.chunkMem = np.prod(self.cfg["chunkShape"]) * self.dtype.itemsize
        else:
            self.chunkMem = max([np.prod(shp) for shp in self.targetShapes]) * self.dtype.itemsize

        # Get data access mode (only relevant for parallel reading access)
        self.dataMode = data.mode

    def compute(self, data, out, parallel=False, parallel_store=None,
                method=None, mem_thresh=0.5, log_dict=None, parallel_debug=False):
        """
        Central management and processing method

        Parameters
        ----------
        data : syncopy data object
           Syncopy data object to be processed (has to be the same object
           that was used by :meth:`initialize` in the pre-calculation
           dry-run).
        out : syncopy data object
           Empty object for holding results
        parallel : bool
           If `True`, processing is performed in parallel (i.e.,
           :meth:`computeFunction` is executed concurrently across trials).
           If `parallel` is `False`, :meth:`computeFunction` is executed
           consecutively trial after trial (i.e., the calculation realized
           in :meth:`computeFunction` is performed sequentially).
        parallel_store : None or bool
           Flag controlling saving mechanism. If `None`,
           ``parallel_store = parallel``, i.e., the compute-paradigm
           dictates the employed writing method. Thus, in case of parallel
           processing, results are written in a fully concurrent
           manner (each worker saves its own local result segment on disk as
           soon as it is done with its part of the computation). If `parallel_store`
           is `False` and `parallel` is `True` the processing result is saved
           sequentially using a mutex. If both `parallel` and `parallel_store`
           are `False` standard single-process HDF5 writing is employed for
           saving the result of the (sequential) computation.
        method : None or str
           If `None` the predefined methods :meth:`compute_parallel` or
           :meth:`compute_sequential` are used to control the actual computation
           (specifically, calling :meth:`computeFunction`) depending on whether
           `parallel` is `True` or `False`, respectively. If `method` is a
           string, it has to specify the name of an alternative (provided)
           class method that is invoked using `getattr`.
        mem_thresh : float
           Fraction of available memory required to perform computation. By
           default, the largest single trial result must not occupy more than
           50% (``mem_thresh = 0.5``) of available single-machine or worker
           memory (if `parallel` is `False` or `True`, respectively).
        log_dict : None or dict
           If `None`, the `log` properties of `out` is populated with the employed
           keyword arguments used in :meth:`computeFunction`.
           Otherwise, `out`'s `log` properties are filled  with items taken
           from `log_dict`.
        parallel_debug : bool
           If `True`, concurrent processing is performed using a single-threaded
           scheduler, i.e., all parallel computing task are run in the current
           Python thread permitting usage of tools like `pdb`/`ipdb`, `cProfile`
           and the like in :meth:`computeFunction`.
           Note that enabling parallel debugging effectively runs the given computation
           on the calling local machine thereby requiring sufficient memory and
           CPU capacity.

        Returns
        -------
        Nothing : None
           The result of the computation is available in `out` once
           :meth:`compute` terminated successfully.

        Notes
        -----
        This routine calls several other class methods to perform all necessary
        pre- and post-processing steps in a fully automatic manner without
        requiring any user-input. Specifically, the following class methods
        are invoked consecutively (in the given order):

        1. :meth:`preallocate_output` allocates a (virtual) HDF5 dataset
           of appropriate dimension for storing the result
        2. :meth:`compute_parallel` (or :meth:`compute_sequential`) performs
           the actual computation via concurrently (or sequentially) calling
           :meth:`computeFunction`
        3. :meth:`process_metadata` attaches all relevant meta-information to
           the result `out` after successful termination of the calculation
        4. :meth:`write_log` stores employed input arguments in `out.cfg`
           and `out.log` to reproduce all relevant computational steps that
           generated `out`.

        See also
        --------
        initialize : pre-calculation preparations
        preallocate_output : storage provisioning
        compute_parallel : concurrent computation using :meth:`computeFunction`
        compute_sequential : sequential computation using :meth:`computeFunction`
        process_metadata : management of meta-information
        write_log : log-entry organization
        """

        # By default, use VDS storage for parallel computing
        if parallel_store is None:
            parallel_store = parallel

        # Do not spill trials on disk if they're supposed to be removed anyway
        if parallel_store and not self.keeptrials:
            msg = "trial-averaging only supports sequential writing!"
            SPYWarning(msg)
            parallel_store = False

        # Concurrent processing requires some additional prep-work...
        if parallel:

            # First and foremost, make sure a dask client is accessible
            try:
                client = dd.get_client()
            except ValueError as exc:
                msg = "parallel computing client: {}"
                raise SPYIOError(msg.format(exc.args[0]))

            # Check if the underlying cluster hosts actually usable workers
            if not len(client.cluster.workers):
                raise SPYParallelError("No active workers found in distributed computing cluster",
                                       client=client)

            # Note: `dask_jobqueue` may not be available even if `__dask__` is `True`,
            # hence the `__name__` shenanigans instead of a simple `isinstance`
            if isinstance(client.cluster, dd.LocalCluster):
                memAttr = "memory_limit"
            elif client.cluster.__class__.__name__ == "SLURMCluster":
                memAttr = "worker_memory"
            else:
                msg = "`ComputationalRoutine` only supports `LocalCluster` and " +\
                    "`SLURMCluster` dask cluster objects. Proceed with caution. "
                SPYWarning(msg)
                memAttr = None

            # Check if trials actually fit into memory before we start computation
            if memAttr:
                wrk_size = max(getattr(wrkr, memAttr) for wrkr in client.cluster.workers.values())
                if self.chunkMem >= mem_thresh * wrk_size:
                    self.chunkMem /= 1024**3
                    wrk_size /= 1000**3
                    msg = "Single-trial result sizes ({0:2.2f} GB) larger than available " +\
                        "worker memory ({1:2.2f} GB) currently not supported"
                    raise NotImplementedError(msg.format(self.chunkMem, wrk_size))

            # In some cases distributed dask workers suffer from spontaneous
            # dementia and forget the `sys.path` of their parent process. Fun!
            def init_syncopy(dask_worker):
                spy_path = os.path.abspath(os.path.split(__path__[0])[0])
                if spy_path not in sys.path:
                    sys.path.insert(0, spy_path)
            client.register_worker_callbacks(init_syncopy)

            # Store provided debugging state
            self.parallelDebug = parallel_debug

        # For sequential processing, just ensure enough memory is available
        else:
            mem_size = psutil.virtual_memory().available
            if self.chunkMem >= mem_thresh * mem_size:
                self.chunkMem /= 1024**3
                mem_size /= 1024**3
                msg = "Single-trial result sizes ({0:2.2f} GB) larger than available " +\
                      "memory ({1:2.2f} GB) currently not supported"
                raise NotImplementedError(msg.format(self.chunkMem, mem_size))

        # Create HDF5 dataset of appropriate dimension
        self.preallocate_output(out, parallel_store=parallel_store)

        # The `method` keyword can be used to override the `parallel` flag
        if method is None:
            if parallel:
                computeMethod = self.compute_parallel
            else:
                computeMethod = self.compute_sequential
        else:
            computeMethod = getattr(self, "compute_" + method, None)

        # Ensure `data` is openend read-only to permit (potentially concurrent)
        # reading access to backing device on disk
        data.mode = "r"

        # Take care of `VirtualData` objects
        self.hdr = getattr(data, "hdr", None)

        # Perform actual computation
        computeMethod(data, out)

        # Reset data access mode
        data.mode = self.dataMode

        # Attach computed results to output object
        out.data = h5py.File(out.filename, mode="r+")[self.datasetName]

        # Store meta-data, write log and get outta here
        self.process_metadata(data, out)
        self.write_log(data, out, log_dict)

    def preallocate_output(self, out, parallel_store=False):
        """
        Storage allocation and provisioning

        Parameters
        ----------
        out : syncopy data object
           Empty object for holding results
        parallel_store : bool
           If `True`, a directory for virtual source files is created
           in Syncopy's temporary on-disk storage (defined by `syncopy.__storage__`).
           Otherwise, a dataset of appropriate type and shape is allocated
           in a new regular HDF5 file created inside Syncopy's temporary
           storage folder.

        Returns
        -------
        Nothing : None

        See also
        --------
        compute : management routine controlling memory pre-allocation
        """

        # Set name of target HDF5 dataset in output object
        self.datasetName = "data"

        # In case parallel writing via VDS storage is requested, prepare
        # directory for by-chunk HDF5 files and construct virutal HDF layout
        if parallel_store:
            vdsdir = os.path.splitext(os.path.basename(out.filename))[0]
            self.virtualDatasetDir = os.path.join(__storage__, vdsdir)
            os.mkdir(self.virtualDatasetDir)

            layout = h5py.VirtualLayout(shape=self.outputShape, dtype=self.dtype)
            for k, idx in enumerate(self.targetLayout):
                fname = os.path.join(self.virtualDatasetDir, "{0:d}.h5".format(k))
                layout[idx] = h5py.VirtualSource(fname, "chk", shape=self.targetShapes[k])
            self.VirtualDatasetLayout = layout

        # Create regular HDF5 dataset for sequential writing
        else:

            # The shape of the target depends on trial-averaging
            if not self.keeptrials:
                shp = self.cfg["chunkShape"]
            else:
                shp = self.outputShape
            with h5py.File(out.filename, mode="w") as h5f:
                h5f.create_dataset(name=self.datasetName,
                                   dtype=self.dtype, shape=shp)

    def compute_parallel(self, data, out):
        """
        Concurrent computing kernel

        Parameters
        ----------
        data : syncopy data object
           Syncopy data object to be processed
        out : syncopy data object
           Empty object for holding results

        Returns
        -------
        Nothing : None

        Notes
        -----
        This method mereley acts as a concurrent wrapper for :meth:`computeFunction`
        by passing along all necessary information for parallel execution and
        storage of results using a dask bag of dictionaries. The actual reading
        of source data and writing of results is managed by the decorator
        :func:`syncopy.shared.parsers.unwrap_io`. Note that this routine first
        builds an entire parallel instruction tree and only kicks off execution
        on the cluster at the very end of the calculation command assembly.

        See also
        --------
        compute : management routine invoking parallel/sequential compute kernels
        compute_sequential : serial processing counterpart of this method
        """

        # Prepare to write chunks concurrently
        if self.virtualDatasetDir is not None:
            outfilename = os.path.join(self.virtualDatasetDir, "{0:d}.h5")
            outdsetname = "chk"

        # Write chunks sequentially
        else:
            outfilename = out.filename
            outdsetname = self.datasetName

        # Construct a dask bag with all necessary components for parallelization
        mainBag = db.from_sequence([{"hdr": self.hdr,
                                     "keeptrials": self.keeptrials,
                                     "infile": data.filename,
                                     "indset": data.data.name,
                                     "ingrid": self.sourceLayout[chk],
                                     "sigrid": self.sourceSelectors[chk],
                                     "fancy": self.useFancyIdx,
                                     "vdsdir": self.virtualDatasetDir,
                                     "outfile": outfilename.format(chk),
                                     "outdset": outdsetname,
                                     "outgrid": self.targetLayout[chk],
                                     "outshape": self.targetShapes[chk],
                                     "dtype": self.dtype}
                                     for chk in range(len(self.sourceLayout))],
                                   npartitions=len(self.sourceLayout))

        # Convert by-worker argv-list to dask bags to distribute across cluster
        # Format: ``ArgV = [(3, 0, 'a'), (3, 0, 'a'), (3, 1, 'b'), (3, 1, 'b')]``
        # then ``list(zip(*ArgV)) = [(3, 3, 3, 3), (0, 0, 1, 1), ('a', 'a', 'b', 'b')]``
        bags = []
        for arg in zip(*self.ArgV):
            bags.append(db.from_sequence(arg))

        # Map all components (channel-trial-blocks) onto `computeFunction`
        results = mainBag.map(self.computeFunction, *bags, **self.cfg)

        # Let the fun begin...
        if not self.parallelDebug:

            # Make sure that all futures are executed (i.e., data is actually written)
            # Note 1: `dd.progress` does not correctly track worker progress hence
            #         the custom-tailored `while` formulation: that periodically
            #         checks in on the status of all allocated futures
            #         -> Do not use this `dd.progress(futures, notebook=False)`
            # Note 2: the while loop below does not run indefinitely - erring or
            #         stalling futures get status 'error' or 'waiting'. After
            #         some time the status of all futures is one of 'finished',
            #         'error' or 'waiting', but none is 'pending' any more.
            futures = dd.client.futures_of(results.persist())
            totalTasks = len(futures)
            pbar = tqdm(total=totalTasks, bar_format=self.tqdmFormat)
            cnt = 0
            while any(f.status == "pending" for f in futures):
                time.sleep(self.sleepTime)
                new = max(0, sum([f.status == "finished" for f in futures]) - cnt)
                cnt += new
                pbar.update(new)
            pbar.close()

            # Avoid race condition: give futures time to perform switch from 'pending'
            # to 'finished' so that `finishedTasks` is computed correctly
            time.sleep(self.sleepTime)

            # If number of 'finished' tasks is less than expected, go into
            # problem analysis mode: all futures that erred hav an `.exception`
            # method which can be used to track down the worker it was executed by
            # Once we know the worker, we can point to the right log file. If
            # futures were cancelled (by the user or the SLURM controller),
            # `.exception` is `None` and we can't relialby track down the
            # respective executing worker
            finishedTasks = sum([f.status == "finished" for f in futures])
            if finishedTasks < totalTasks:
                client = dd.get_client()
                schedulerLog = list(client.cluster.get_logs(cluster=False, scheduler=True, workers=False).values())[0]
                erredFutures = [f for f in futures if f.status == "error"]
                msg = "Parallel computation failed: {}/{} tasks failed or stalled.\n"
                msg = msg.format(totalTasks - finishedTasks, totalTasks)
                msg += "Concurrent computing scheduler log below: \n\n"
                msg += schedulerLog + "\n"

                # If we're working w/`SLURMCluster`, perform the Herculean task of
                # tracking down which dask worker was executed by which SLURM job...
                if client.cluster.__class__.__name__ == "SLURMCluster":
                    try:
                        erredJobs = [f.exception().last_worker.identity()["id"] for f in erredFutures]
                    except AttributeError:
                        erredJobs = []
                    erredJobs = list(set(erredJobs))
                    erredJobIDs = [client.cluster.workers[job].job_id for job in erredJobs]
                    slurmFiles = client.cluster.job_header.split("--output=")[1].replace("%j", "{}")
                    slurmOutDir = os.path.split(slurmFiles)[0]
                    errFiles = glob(slurmOutDir + os.sep + "*.err")
                    if len(erredFutures) or len(errFiles):
                        msg += "Please consult the following SLURM log files for details:\n"
                        msg += "".join(slurmFiles.format(id) + "\n" for id in erredJobIDs)
                        msg += "".join(errfile + "\n" for errfile in errFiles)
                    else:
                        msg += "Please check SLURM logs in {}".format(slurmOutDir)

                # In case of a `LocalCluster`, syphon worker logs
                else:
                    msg += "\nParallel worker logs below: \n"
                    workerLogs = client.cluster.get_logs(cluster=False, scheduler=False, workers=True).values()
                    for wLog in workerLogs:
                        if "Failed" in wLog:
                            msg += wLog

                raise SPYParallelError(msg, client=client)

        # If debugging is requested, drop existing client and enforce use of
        # single-threaded scheduler
        else:
            results.compute(scheduler="single-threaded")

        # When writing concurrently, now's the time to finally create the virtual dataset
        if self.virtualDatasetDir is not None:
            with h5py.File(out.filename, mode="w") as h5f:
                h5f.create_virtual_dataset(self.datasetName, self.VirtualDatasetLayout)

        # If trial-averaging was requested, normalize computed sum to get mean
        if not self.keeptrials:
            with h5py.File(out.filename, mode="r+") as h5f:
                h5f[self.datasetName][()] /= len(self.trialList)
                h5f.flush()

        return

    def compute_sequential(self, data, out):
        """
        Sequential computing kernel

        Parameters
        ----------
        data : syncopy data object
           Syncopy data object to be processed
        out : syncopy data object
           Empty object for holding results

        Returns
        -------
        Nothing : None

        Notes
        -----
        This method most closely reflects classic iterative process execution:
        trials in `data` are passed sequentially to :meth:`computeFunction`,
        results are stored consecutively in a regular HDF5 dataset (that was
        pre-allocated by :meth:`preallocate_output`). Since the calculation result
        is immediately stored on disk, propagation of arrays across routines
        is avoided and memory usage is kept to a minimum.

        See also
        --------
        compute : management routine invoking parallel/sequential compute kernels
        compute_parallel : concurrent processing counterpart of this method
        """

        # Initialize on-disk backing device (either HDF5 file or memmap)
        if self.hdr is None:
            try:
                sourceObj = h5py.File(data.filename, mode="r")[data.data.name]
                isHDF = True
            except OSError:
                sourceObj = open_memmap(data.filename, mode="c")
                isHDF = False
            except Exception as exc:
                raise exc

        # Iterate over (selected) trials and write directly to target HDF5 dataset
        fmt = "{desc}: {percentage:3.0f}% |{bar}| {n_fmt}/{total_fmt} [{elapsed}<{remaining}]"
        with h5py.File(out.filename, "r+") as h5fout:
            target = h5fout[self.datasetName]

            for nblock in tqdm(range(len(self.trialList)), bar_format=self.tqdmFormat):

                # Extract respective indexing tuples from constructed lists
                ingrid = self.sourceLayout[nblock]
                sigrid = self.sourceSelectors[nblock]
                outgrid = self.targetLayout[nblock]
                argv = self.ArgV[nblock]

                # Catch empty source-array selections; this workaround is not
                # necessary for h5py version 2.10+ (see https://github.com/h5py/h5py/pull/1174)
                if any([not sel for sel in ingrid]):
                    res = np.empty(self.targetShapes[nblock], dtype=self.dtype)
                else:
                    # Get source data as NumPy array
                    if self.hdr is None:
                        if isHDF:
                            if self.useFancyIdx:
                                arr = np.array(sourceObj[tuple(ingrid)])[np.ix_(*sigrid)]
                            else:
                                arr = np.array(sourceObj[tuple(ingrid)])
                        else:
                            if self.useFancyIdx:
                                arr = sourceObj[np.ix_(*ingrid)]
                            else:
                                arr = np.array(sourceObj[ingrid])
                        sourceObj.flush()
                    else:
                        idx = ingrid
                        if self.useFancyIdx:
                            idx = np.ix_(*ingrid)
                        stacks = []
                        for fk, fname in enumerate(data.filename):
                            stacks.append(np.memmap(fname, offset=int(self.hdr[fk]["length"]),
                                                    mode="r", dtype=self.hdr[fk]["dtype"],
                                                    shape=(self.hdr[fk]["M"], self.hdr[fk]["N"]))[idx])
                        arr = np.vstack(stacks)[ingrid]

                    # Perform computation
                    res = self.computeFunction(arr, *argv, **self.cfg)

                # Either write result to `outgrid` location in `target` or add it up
                if self.keeptrials:
                    target[outgrid] = res
                else:
                    target[()] = np.nansum([target, res], axis=0)

                # Flush every iteration to avoid memory leakage
                h5fout.flush()

            # If trial-averaging was requested, normalize computed sum to get mean
            if not self.keeptrials:
                target[()] /= len(self.trialList)

        # If source was HDF5 file, close it to prevent access errors
        if isHDF:
            sourceObj.file.close()

        return

    def write_log(self, data, out, log_dict=None):
        """
        Processing of output log

        Parameters
        ----------
        data : syncopy data object
           Syncopy data object that has been processed
        out : syncopy data object
           Syncopy data object holding calculation results
        log_dict : None or dict
           If `None`, the `log` properties of `out` is populated with the employed
           keyword arguments used in :meth:`computeFunction`.
           Otherwise, `out`'s `log` properties are filled  with items taken
           from `log_dict`.

        Returns
        -------
        Nothing : None

        See also
        --------
        process_metadata : Management of meta-information
        """

        # Copy log from source object and write header
        out._log = str(data._log) + out._log
        logHead = "computed {name:s} with settings\n".format(name=self.computeFunction.__name__)

        # Prepare keywords used by `computeFunction` (sans implementation-specific stuff)
        cfg = dict(self.cfg)
        for key in ["noCompute", "chunkShape"]:
            cfg.pop(key)

        # Write log and store `cfg` constructed above in corresponding prop of `out`
        if log_dict is None:
            log_dict = cfg
        logOpts = ""
        for k, v in log_dict.items():
            logOpts += "\t{key:s} = {value:s}\n".format(key=k,
                                                        value=str(v) if len(str(v)) < 80
                                                        else str(v)[:30] + ", ..., " + str(v)[-30:])
        out.log = logHead + logOpts
        out.cfg = cfg

    @abstractmethod
    def process_metadata(self, data, out):
        """
        Meta-information manager

        Parameters
        ----------
        data : syncopy data object
           Syncopy data object that has been processed
        out : syncopy data object
           Syncopy data object holding calculation results

        Returns
        -------
        Nothing : None

        Notes
        -----
        This routine is an abstract method and is thus intended to be overloaded.
        Consult the developer documentation (:doc:`/developer/compute_kernels`) for
        further details.

        See also
        --------
        write_log : Logging of calculation parameters
        """
        pass

    def _sizeof(self, obj):
        """
        Estimate memory consumption of Python objects

        Parameters
        ----------
        obj : Python object
           Any valid Python object whose memory footprint is of interest.

        Returns
        -------
        objsize : float
           Approximate memory footprint of `obj` in megabytes (MB).

        Notes
        -----
        Memory consumption is is estimated by recursively calling :meth:`sys.getsizeof`.
        Circular object references are followed up to a (preset) maximal recursion
        depth. This method was inspired by a routine in
        `Nifty <https://github.com/mwojnars/nifty/blob/master/util.py>`_.
        """

        # Protect against circular object references by adhering to max. no. of
        # recursive calls `self._callMax`
        self._callCount += 1
        if self._callCount >= self._callMax:
            lgl = "minimally nested positional arguments"
            act = "argument with nesting depth >= {}"
            raise SPYValueError(legal=lgl, varname="argv", actual=act.format(self._callMax))

        # Use `sys.getsizeof` to estimate memory consumption of primitive objects
        objsize = sys.getsizeof(obj) / 1024**2
        if isinstance(obj, dict):
            return objsize + sum(list(map(self._sizeof, obj.keys()))) + sum(list(map(self._sizeof, obj.values())))
        if isinstance(obj, (list, tuple, set)):
            return objsize + sum(list(map(self._sizeof, obj)))
        return objsize


'''
This file is modified from
https://github.com/NIRVANALAN/gcn_analysis/blob/master/notebook/Plantenoid%20Citation%20Data%20Format%20Transformation.ipynb

'''
import networkx as nx
import pickle as pkl
import pickle
import sys
from scipy.sparse.linalg.eigen.arpack import eigsh
import scipy.sparse as sp
import time
import argparse
import numpy as np
import pdb
from collections import defaultdict
import torch
import torch.nn.functional as F
import torch.optim as optim
import numpy as np

def encode_onehot(labels):
    classes = set(labels)
    classes_dict = {c: np.identity(len(classes))[i, :] for i, c in
                    enumerate(classes)}
    labels_onehot = np.array(list(map(classes_dict.get, labels)),
                             dtype=np.int32)
    return labels_onehot

def get_features_and_labels(path, dataset, save_path):

    idx_features_labels = np.genfromtxt("{}{}.content".format(path, dataset), dtype=np.dtype(str))
    features = sp.csr_matrix(idx_features_labels[:, 1:-1], dtype=np.float32)
    # features = normalize(features) # no normalization in plantoid

    labels = encode_onehot(idx_features_labels[:, -1])
    # build graph
    idx = np.array(idx_features_labels[:, 0], dtype=np.int32)
    idx_map = {j: i for i, j in enumerate(idx)}
    edges_unordered = np.genfromtxt("{}{}.cites".format(path, dataset),
                                    dtype=np.int32)
    edges = np.array(list(map(idx_map.get, edges_unordered.flatten())),
                    dtype=np.int32).reshape(edges_unordered.shape)
    adj = sp.coo_matrix((np.ones(edges.shape[0]), (edges[:, 0], edges[:, 1])),
                        shape=(labels.shape[0], labels.shape[0]),
                        dtype=np.float32)

    # build symmetric adjacency matrix
    adj = adj + adj.T.multiply(adj.T > adj) - adj.multiply(adj.T > adj)
    idx_train = range(140)
    idx_val = range(200, 500)
    idx_test = range(500, 1500)

    pickle.dump(features[idx_train], open(f"{save_path}/ind.cora.x", "wb"))
    d = sp.vstack((features[:idx_test[0]], features[idx_test[-1]+1:]))
    pickle.dump(d, open(f"{save_path}/ind.cora.allx", "wb"))
    pickle.dump(features[idx_test], open(f"{save_path}/ind.cora.tx", "wb"))

    pickle.dump(labels[idx_train], open(f"{save_path}/ind.cora.y", "wb"))
    pickle.dump(labels[idx_test], open(f"{save_path}/ind.cora.ty", "wb"))
    pickle.dump(np.vstack((labels[:idx_test[0]], labels[idx_test[-1]+1:])),
                open(f"{save_path}/ind.cora.ally", "wb"))

    with open(f'{save_path}/ind.cora.test.index', 'w') as f:
        for item in list(idx_test):
            f.write("%s\n" % item)

    # ori_graph
    from collections import defaultdict
    array_adj = np.argwhere(adj.toarray())
    ori_graph = defaultdict(list)
    for edge in array_adj:
        ori_graph[edge[0]].append(edge[1])
    pickle.dump(ori_graph, open(f"{save_path}/ind.cora.graph", "wb"))

    return features, labels, adj

def parse_index_file(filename):
    """Parse index file."""
    index = []
    for line in open(filename):
        index.append(int(line.strip()))
    return index

def sample_mask(idx, l):
    """Create mask."""
    mask = np.zeros(l)
    mask[idx] = 1
    return np.array(mask, dtype=np.bool)

def check_files(features, labels, adj, dataset):
    dataset = 'cora'
    names = ['x', 'y', 'tx', 'ty', 'allx', 'ally', 'graph']
    objects = []
    for i in range(len(names)):
        with open("./data/cora_nonorm/ind.{}.{}".format(dataset, names[i]), 'rb') as f:
            if sys.version_info > (3, 0):
                objects.append(pkl.load(f, encoding='latin1'))
            else:
                objects.append(pkl.load(f))

    x, y, tx, ty, allx, ally, graph = tuple(objects)
    test_idx_reorder = parse_index_file(
        "./data/cora_nonorm/ind.{}.test.index".format(dataset))
    test_idx_range = np.sort(test_idx_reorder)

    p_features = sp.vstack(
        (allx[:test_idx_range[0]], tx, allx[test_idx_range[0]:])).tolil()
    p_features[test_idx_reorder, :] = features[test_idx_range, :]

    o_adj = nx.adjacency_matrix(nx.from_dict_of_lists(graph))

    o_labels = np.vstack((ally[:test_idx_range[0]], ty, ally[test_idx_range[0]:]))
    o_labels[test_idx_reorder, :] = labels[test_idx_range, :]

    idx_test = test_idx_range.tolist()
    idx_train = range(len(y))
    idx_val = range(len(y), len(y)+500)

    train_mask = sample_mask(idx_train, labels.shape[0])
    val_mask = sample_mask(idx_val, labels.shape[0])
    test_mask = sample_mask(idx_test, labels.shape[0])

    y_train = np.zeros(labels.shape)
    y_val = np.zeros(labels.shape)
    y_test = np.zeros(labels.shape)
    y_train[train_mask, :] = labels[train_mask, :]
    y_val[val_mask, :] = labels[val_mask, :]
    y_test[test_mask, :] = labels[test_mask, :]
    assert (p_features.nonzero()[0] == features.nonzero()[0]).all()
    assert (p_features.nonzero()[1] == features.nonzero()[1]).all()
    assert (adj.shape == o_adj.shape)
    print('all files are checked....')

if __name__ == '__main__':
    path = "./data/cora_ori/cora/"
    dataset = "cora"
    save_path = "./data/cora_ori"
    feats, labels, adj = get_features_and_labels(path, dataset, save_path)
    check_files(feats, labels, adj, dataset)


import random
import time

ans = "yes"

while ans == "yes" or ans == "Yes":
    time.sleep(1)
    value = random.randint(1,6)
    print(value)

    if value == 6:
        print("you have won")
    else:
        print("you have lost")

    ans = input("\n Do You want to play Again Yes/NO \n")


from os import remove
from os.path import abspath, dirname, join
fac_path = join(
    dirname(dirname(abspath(__file__))),
    'facades'
)
plat_path = join(
    dirname(dirname(abspath(__file__))),
    'platforms'
)

# remove Dummy facade
remove(join(fac_path, 'dummy.py'))

# remove Dummy platform modules
for plat in {'android', 'ios', 'win', 'macosx', 'linux'}:
    remove(join(plat_path, plat, 'dummy.py'))

with open(join(fac_path, '__old_init__.py')) as f:
    facades_old = f.read()
remove(join(fac_path, '__old_init__.py'))

# restore the original facades __init__.py
with open(join(fac_path, '__init__.py'), 'w') as f:
    f.write(''.join(facades_old))


"""
Created on Tue Nov 12 11:06:44 2019
@author: Amit Ojha
"""

import pandas as pd
import logging
logger = logging.getLogger('sialogger')

"""
Create the Linkset
"""
def create_linkset(row):

    num_rev = int(row.Review_Count)
    link = str(row.Review_Link_Href)
    link2 = []
    pages = int(num_rev/10)+1
    cutoff = link.find("show_all_btm?ie=UTF8&reviewerType=all_reviews")
    link1 = link[:cutoff]
    for i in range(1,pages+1):
        temp = 'paging_btm_next_2?ie=UTF8&reviewerType=all_reviews&pageNumber='+str(i)
        link2.append(temp)
    linkset = [link1+x for x in link2]
    return linkset

"""
Scrape Reviews
"""
def scrap_reviews(link, driver):

    driver.get(link)
    ids = driver.find_elements_by_xpath("//*[contains(@id,'customer_review-')]")
    comment_ids = []
    for i in ids:
        comment_ids.append(i.get_attribute('id'))

    authorname = []
    title = []
    body = []
    date = []
    usefullness = []

    for x in comment_ids:
        try:
            authorname_element = driver.find_elements_by_xpath('//*[@id="' + x +'"]/div[1]/a/div[2]/span')[0]
            authornametemp = authorname_element.text
            authorname.append(authornametemp)
        except Exception as e:
            logger.info("Exception is {}".format(e))
            authorname.append(None)

        try:
            title_element = driver.find_elements_by_xpath('//*[@id="' + x +'"]/div[2]/a[2]/span')[0]
            titletemp = title_element.text
            title.append(titletemp)
        except Exception as e:
            logger.info("Exception is {}".format(e))
            title.append(None)

        try:
            date_element = driver.find_elements_by_xpath('//*[@id="' + x +'"]/span')[0]
            datetemp = date_element.text
            date.append(datetemp)
        except Exception as e:
            logger.info("Exception is {}".format(e))
            date.append(None)

        try:
            usefullness_element = driver.find_elements_by_xpath('//*[@id="' + x +'"]/div[5]/div/span[1]/div[1]/span')[0]
            usefullnesstemp = usefullness_element.text
            usefullness.append(usefullnesstemp)
        except Exception as e:
            logger.info("Exception is {}".format(e))
            usefullness.append(None)

        try:
            body_element = driver.find_elements_by_xpath('//*[@id="' + x +'"]/div[4]/span/span')[0]
            bodytemp = body_element.text
            body.append(bodytemp)
        except Exception as e:
            logger.info("Exception is {}".format(e))
            body.append(None)
    final_data = pd.DataFrame({'review_link':link, 'author':authorname, 'title':title, 'date':date, 'usefullness':usefullness, 'body':body})
    return final_data


from jak_podjade.state import State
from jak_podjade.utils import print_menu, handle_user_input, printing_results
from jak_podjade.request import load_traffic_for_stop, load_all_stops
from jak_podjade.constant import TRAM_NUMBERS
from jak_podjade.validation import validate_input

state = State(False)
all_stops = load_all_stops()

"""
Główna pętla aplikacji zarządzająca jej wykonaniem.
Odpowiada za wywołanie najważniejszych funkcji.
"""
while not state.should_exit:
    print_menu()
    option = input('Choose: ')
    user_input_map = handle_user_input(state, option)
    if user_input_map is not None:
        if not user_input_map["Stop"]:
            print("\nYou have to select stop! Try again!\n")
            continue

        user_input_map["Stop"] = validate_input(user_input_map["Stop"], all_stops.keys())

        if user_input_map["LineNumber"] and user_input_map["LineNumber"] not in TRAM_NUMBERS:
            print("\nWrong line number! Try again!\n")
            continue

        if user_input_map["Direction"]:
            user_input_map["Direction"] = validate_input(user_input_map["Direction"], all_stops.keys())

        print(user_input_map)

        result_lines = load_traffic_for_stop(user_input_map, all_stops[user_input_map["Stop"]])
        printing_results(result_lines)


from pyramid.httpexceptions import HTTPFound
from pyramid.request import Request
from pyramid.response import Response
from pyramid.view import view_config

from billtracker.data import repository

@view_config(route_name='home', renderer='../templates/home/default.pt')
def home(_: Request):
    user_id = 1  # probably get from a cookie?

    user = repository.get_user_by_id(user_id)
    return {
        'user': user,
    }

@view_config(route_name='details',
             renderer='../templates/home/details.pt',
             request_method='GET')
def details_get(request: Request):
    bill_id = int(request.matchdict.get('bill_id'))
    bill = repository.get_bill_by_id(bill_id)
    if not bill:
        return Response(status=404)

    user_id = 1  # probably get from a cookie?
    user = repository.get_user_by_id(user_id)

    return {
        'user': user,
        'bill': bill,
        'error': None
    }

@view_config(route_name='details',
             renderer='../templates/home/details.pt',
             request_method='POST')
def details_post(request: Request):
    bill_id = int(request.matchdict.get('bill_id'))
    bill = repository.get_bill_by_id(bill_id)
    if not bill:
        return Response(status=404)

    user_id = 1  # probably get from a cookie?
    user = repository.get_user_by_id(user_id)

    amount = int(request.POST.get('amount', -1))
    if amount < 0 or amount > bill.total-bill.paid:
        error = 'Your amount must be more the zero and less than what you owe.'

        return {
            'user': user,
            'bill': bill,
            'error': error,
            'amount': amount
        }

    repository.add_payment(amount, bill_id)

    return HTTPFound(location='/bill/{}'.format(bill_id))


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

"""Main script.

SPDX-FileCopyrightText: 2016-2021 Uwe Krien <krien@uni-bremen.de>

SPDX-License-Identifier: MIT
"""
__copyright__ = "Uwe Krien <krien@uni-bremen.de>"
__license__ = "MIT"

import logging
import multiprocessing
import os
import traceback
from collections import namedtuple
from datetime import datetime
from functools import partial

import pandas as pd

from deflex.postprocessing.basic import get_all_results
from deflex.scenario_tools.scenario_io import create_scenario
from deflex.tools.files import dict2file

def stopwatch():
    """Track the running time."""
    if not hasattr(stopwatch, "start"):
        stopwatch.start = datetime.now()
    return str(datetime.now() - stopwatch.start)[:-7]

def model_multi_scenarios(
    scenarios, cpu_fraction=0.2, log_file=None, results=False
):
    """
    Model multi scenarios in parallel. Keep in mind that the memory usage
    is the critical resource for large models. So start with a low
    cpu_fraction to avoid memory errors.

    Parameters
    ----------
    scenarios : iterable
        Multiple scenarios to be modelled in parallel.
    cpu_fraction : float
        Fraction of available cpu cores to use for the parallel modelling.
        A resulting dezimal number of cores will be rounded up to an integer.
    log_file : str
        Filename to store the log file.
    results : bool
        Store an spreadsheet results file (default: False).

    Examples
    --------
    >>> from deflex import fetch_test_files, TEST_PATH
    >>> fn1 = fetch_test_files("de03_fictive_csv")
    >>> fn2 = fetch_test_files("de03_fictive_broken.xlsx")
    >>> my_log_file = os.path.join(TEST_PATH, "my_log_file.csv")
    >>> my_scenarios = [fn1, fn2]
    >>> model_multi_scenarios(my_scenarios, log_file=my_log_file)
    >>> my_log = pd.read_csv(my_log_file, index_col=[0])
    >>> good = my_log.loc["de03_fictive_csv"]
    >>> rv = good["return_value"]
    >>> datetime.strptime(rv, "%Y-%m-%d %H:%M:%S.%f").year > 2019
    True
    >>> good["trace"]
    nan
    >>> os.path.basename(good["dump"])
    'de03_fictive_csv.dflx'
    >>> good["results"]
    False
    >>> broken = my_log.loc["de03_fictive_broken.xlsx"]
    >>> broken["return_value"].replace("'", "")  # doctest: +ELLIPSIS
    'ValueError(Missing time series for geothermal (capacity: 12.56) in DE02...
    >>> broken["trace"]  # doctest: +ELLIPSIS
    'Traceback (most recent call last)...
    >>> broken["dump"]
    nan
    >>> os.remove(my_log_file)
    >>> os.remove(good["dump"])
    """
    start = datetime.now()
    maximal_number_of_cores = int(
        round(multiprocessing.cpu_count() * cpu_fraction + 0.4999)
    )
    logging.info(f"Multiprocessing will use {maximal_number_of_cores} cores.")
    p = multiprocessing.Pool(maximal_number_of_cores)
    bms = partial(batch_model_scenario, results=results, flat_tuple=True)
    logs = p.map(bms, scenarios)
    p.close()
    p.join()
    out = namedtuple(
        "out",
        ["name", "return_value", "trace", "dump", "results", "start_time"],
    )
    logs = [
        out(
            name=lo[0],
            return_value=lo[1],
            trace=lo[2],
            dump=lo[3],
            results=lo[4],
            start_time=lo[5],
        )
        for lo in logs
    ]
    failing = {
        log.name: log.return_value
        for log in logs
        if isinstance(log.return_value, BaseException)
    }
    logger = pd.DataFrame()
    for log in logs:
        logger.loc[log.name, "start"] = start
        logger.loc[log.name, "start_time"] = log.start_time
        if isinstance(log.return_value, BaseException):
            logger.loc[log.name, "return_value"] = repr(log.return_value)
        else:
            logger.loc[log.name, "return_value"] = log.return_value
        logger.loc[log.name, "trace"] = log.trace
        logger.loc[log.name, "dump"] = log.dump
        logger.loc[log.name, "results"] = log.results

    if log_file is None:
        log_file = os.path.join(
            os.path.expanduser("~"), ".deflex", "log_deflex.csv"
        )
    os.makedirs(os.path.dirname(log_file), exist_ok=True)

    logger.to_csv(log_file)

    if len(failing) < 1:
        logging.info("Finished all scenarios without errors")
    else:
        logging.info(failing)

def batch_model_scenario(
    path, file_type=None, ignore_errors=True, flat_tuple=False, **kwargs
):
    """
    Model a single scenario in batch mode. By default errors will be ignored
    and returned together with the traceback.

    Parameters
    ----------
    path : str
        A valid deflex scenario.
    file_type : str or None
        Type of the input data. Valid values are 'csv', 'xlsx', None. If the
        input is non the path should end on 'csv', '.xlsx'.
    ignore_errors : bool
        Set True to stop the script if an error occurs for debugging. By
        default errors are ignored and returned.
    flat_tuple : bool
        Return a normal tuple instead of a named tuple. This is needed for
        multi-process use. (default: False)

    Other Parameters
    ----------------
    dump : str or bool
        Path to store the dump file. If True the results will be stored along
        with the scenarios using the same name and the suffix `.dflx`. If False
        no dump will be stored (default: True).
    results : str or bool
        Path to store the results in an spreadsheet. If True the results will
        be stored along with the scenarios using the same name and the suffix
        `_results.xlsx`. If False no results will be stored (default: False).
    solver : str
        The solver to use for the optimisation (default: cbc).

    Returns
    -------
    namedtuple

    Examples
    --------
    >>> from deflex import fetch_test_files
    >>> fi = fetch_test_files("de02_heat_csv")
    >>> r = batch_model_scenario(fi, ignore_errors=False)  # doctest: +ELLIPSIS
    Welcome to the CBC MILP ...
    >>> r.name
    'de02_heat_csv'
    >>> my_dump_file = r.dump
    >>> os.path.basename(my_dump_file)
    'de02_heat_csv.dflx'
    >>> r.trace
    >>> r.return_value.year > 2019
    True
    >>> f_wrong = os.path.join("wrong_file.xlsx")
    >>> r = batch_model_scenario(f_wrong)
    >>> r.name
    'wrong_file.xlsx'
    >>> repr(r.return_value)
    "FileNotFoundError(2, 'No such file or directory')"
    >>> r.results
    >>> r.trace  # doctest: +ELLIPSIS
    'Traceback (most recent call last):...
    >>> os.remove(my_dump_file)
    """
    out = namedtuple(
        "out",
        ["name", "return_value", "trace", "dump", "results", "start_time"],
    )
    name = os.path.basename(path)
    logging.info("Next scenario: %s", name)
    start_time = datetime.now()

    if ignore_errors:
        try:
            back = model_scenario(path, file_type, **kwargs)
            rv = None
        except Exception as e:
            back = None
            rv = out(
                name=name,
                return_value=e,
                trace=traceback.format_exc(),
                dump=None,
                results=None,
                start_time=start_time,
            )
    else:
        back = model_scenario(path, file_type, **kwargs)
        rv = None

    if rv is None:
        rv = out(
            name=name,
            return_value=datetime.now(),
            trace=None,
            dump=back.dump,
            results=back.results,
            start_time=start_time,
        )
    if flat_tuple is True:
        rv = tuple([getattr(rv, f) for f in rv._fields])
    return rv

def model_scenario(
    path=None, file_type=None, dump=True, results=False, solver="cbc"
):
    """
    Compute a deflex scenario with the full work flow:

        * creating a scenario
        * loading the input data
        * computing the scenario
        * storing the results

    Parameters
    ----------
    path : str or None
        File or directory with a valid deflex scenario. If no path is given an
        energy system (es) has to be passed.
    file_type : str or None
        Type of the input data. Valid values are 'csv', 'xlsx', None. If the
        input is non the path should end on 'csv' or '.xlsx'.
    dump : str or bool
        Path to store the dump file. If True the results will be stored along
        with the scenarios using the same name and the suffix `.dflx`. If False
        no dump will be stored (default: True).
    results : str or bool
        Path to store the results in an spreadsheet. If True the results will
        be stored along with the scenarios using the same name and the suffix
        `_results.xlsx`. If False no results will be stored (default: False).
    solver : str
        The solver to use for the optimisation (default: cbc).

    Returns
    -------

    Examples
    --------
    >>> from deflex import fetch_test_files, TEST_PATH
    >>> fn = fetch_test_files("de02_no-heat.xlsx")
    >>> r = model_scenario(fn, file_type="xlsx", dump=True
    ...     )  # doctest: +ELLIPSIS
    Welcome to the CBC MILP ...
    >>> os.remove(fn.replace(".xlsx", ".dflx"))
    """
    stopwatch()

    out = namedtuple(
        "out",
        ["dump", "results"],
    )

    if dump is None and results is None:
        msg = (
            "You cannot compute a scenario without storing or dumping the "
            "results in any form,\nSet 'dump' or 'results' to True or define "
            "a dump path or a results path to store the results or dump the "
            "scenario."
        )
        raise AttributeError(msg)

    meta = {
        "model_base": "deflex",
        "solver": solver,
        "start_time": datetime.now(),
    }
    logging.info("Start modelling: %s", stopwatch())

    if file_type is None:
        if "xlsx" in os.path.basename(path):
            file_type = "xlsx"
        else:
            file_type = "csv"

    sc = create_scenario(path, file_type)

    # If a meta table exists in the table collection update meta dict
    sc.meta.update(meta)

    # Use name from meta or from filename
    sc.meta["auto_name"] = (
        os.path.basename(path) + "_" + datetime.now().strftime("%Y%d%m_%H%M%S")
    )
    if "name" not in sc.meta:
        sc.meta["name"] = sc.meta["auto_name"]

    logging.info("Solve the optimisation model: %s", stopwatch())
    sc.compute(solver=solver)

    logging.info("Solved. Dump results: %s", stopwatch())

    if dump is True:
        if file_type == "xlsx":
            dump = path.replace(".xlsx", ".dflx")
        else:
            dump = path + ".dflx"

    if dump is not None:
        os.makedirs(os.path.dirname(dump), exist_ok=True)
        logging.info("Dump file to %s", dump)
        sc.meta["end_time"] = datetime.now()
        if dump[-5:] != ".dflx":
            dump += ".dflx"
        sc.dump(dump)

    if results is True:
        if file_type == "xlsx":
            results = path.replace(".xlsx", "_results.xlsx")
        else:
            results = path + "_results"

    if results:
        os.makedirs(os.path.dirname(results), exist_ok=True)
        res = sc.results
        res["input_data"] = sc.input_data
        all_results = get_all_results(res)
        dict2file(all_results, results, file_type, drop_empty_columns=True)
        logging.info("Results have been written to %s", results)

    logging.info(
        "%s - deflex scenario finished without errors: %s",
        stopwatch(),
        sc.meta["name"],
    )
    return out(dump=dump, results=results)

if __name__ == "__main__":
    pass


# -*- coding: utf-8 -*-
__author__ = 'Lvv'

import sys

sys.path.append('../')

from SpiderInterface.field.FieldBase import FieldBase

class wei_bo_coment(FieldBase):
    def __init__(self, datasource, version=1, level=1, **kwargs):
        FieldBase.__init__(self, datasource, version, level)
        # 用户名
        self.yong_hu_ming = None
        # 标识
        self.biao_shi = None
        # 评论内容
        self.ping_lun_nei_rong = None
        # 用户ID
        self.yong_hu_ID = None
        # 评论时间
        self.ping_lun_shi_jian = None

        self.types['yong_hu_ming'] = unicode
        self.types['biao_shi'] = unicode
        self.types['ping_lun_nei_rong'] = unicode
        self.types['yong_hu_ID'] = unicode
        self.types['ping_lun_shi_jian'] = unicode

    def makemap(self):
        return {
            u'用户名': self.yong_hu_ming,
            u'标识': self.biao_shi,
            u'评论内容': self.ping_lun_nei_rong,
            u'用户ID': self.yong_hu_ID,
            u'评论时间': self.ping_lun_shi_jian,
        }