from torch import nn
import torch
import numpy as np
from torch.nn import functional as F
from torch.autograd import Variable
class FocalLoss(nn.Module):
    def __init__(self, class_num, alpha=0.5, gamma=2, use_alpha=True, size_average=True, ignore_index=None):
        super(FocalLoss, self).__init__()
        self.class_num = class_num
        self.alpha = alpha
        self.gamma = gamma
        self.ignore_index = ignore_index

        if isinstance(alpha, (list, np.ndarray)):
            self.alpha = torch.tensor(alpha).float()
        elif isinstance(alpha, (int, float)):
            assert alpha < 1
            self.alpha = torch.zeros(class_num)
            self.alpha[0] += alpha
            self.alpha[1:] += (1-alpha)
            assert len(self.alpha)==class_num  # alpha=[1,1,1,...] <--> alpha=0.5 <--> no weight
        self.softmax = nn.Softmax(dim=1)
        self.size_average = size_average

        self.alpha = self.alpha / self.alpha.sum() * class_num # normalization

    def forward(self, pred, target):
        if isinstance(self.ignore_index, int) and self.ignore_index<self.class_num:
            mask = (target != self.ignore_index)
            target = target.masked_select(mask)
            pred = pred.masked_select(mask.unsqueeze(1)).view(-1, self.class_num)
        prob = self.softmax(pred.view(-1,self.class_num))
        prob = prob.clamp(min=1e-9,max=1.0)

        target_ = torch.zeros(target.size(0),self.class_num)
        target_.scatter_(1, target.view(-1, 1), 1.)

        prob_log = prob.log() #+ 1e-10
        batch_loss = - self.alpha * torch.pow(1-prob,self.gamma) * prob_log * target_
        #batch_loss = - torch.pow(1-prob,self.gamma) * prob_log * target_
        batch_loss = batch_loss.sum(dim=1)
        if self.size_average:
            loss = batch_loss.mean()
        else:
            loss = batch_loss.sum()
        return loss

class LSR(nn.Module):

    def __init__(self, e=0.1, reduction='mean'):
        super().__init__()

        self.log_softmax = nn.LogSoftmax(dim=1)
        self.e = e
        self.reduction = reduction

    def _one_hot(self, labels, classes, value=1):
        """
            Convert labels to one hot vectors

        Args:
            labels: torch tensor in format [label1, label2, label3, ...]
            classes: int, number of classes
            value: label value in one hot vector, default to 1

        Returns:
            return one hot format labels in shape [batchsize, classes]
        """

        one_hot = torch.zeros(labels.size(0), classes)

        #labels and value_added  size must match
        labels = labels.view(labels.size(0), -1)
        value_added = torch.Tensor(labels.size(0), 1).fill_(value)

        value_added = value_added.to(labels.device)
        one_hot = one_hot.to(labels.device)

        one_hot.scatter_add_(1, labels, value_added)

        return one_hot

    def _smooth_label(self, target, length, smooth_factor):
        """convert targets to one-hot format, and smooth
        them.
        Args:
            target: target in form with [label1, label2, label_batchsize]
            length: length of one-hot format(number of classes)
            smooth_factor: smooth factor for label smooth

        Returns:
            smoothed labels in one hot format
        """
        one_hot = self._one_hot(target, length, value=1 - smooth_factor)
        one_hot += smooth_factor / (length - 1)

        return one_hot.to(target.device)

    def forward(self, x, target):

        if x.size(0) != target.size(0):
            raise ValueError('Expected input batchsize ({}) to match target batch_size({})'
                    .format(x.size(0), target.size(0)))

        if x.dim() < 2:
            raise ValueError('Expected input tensor to have least 2 dimensions(got {})'
                    .format(x.size(0)))

        if x.dim() != 2:
            raise ValueError('Only 2 dimension tensor are implemented, (got {})'
                    .format(x.size()))

        smoothed_target = self._smooth_label(target, x.size(1), self.e)
        x = self.log_softmax(x)
        loss = torch.sum(- x * smoothed_target, dim=1)

        if self.reduction == 'none':
            return loss

        elif self.reduction == 'sum':
            return torch.sum(loss)

        elif self.reduction == 'mean':
            return torch.mean(loss)

        else:
            raise ValueError('unrecognized option, expect reduction to be one of none, mean, sum')

if __name__ == '__main__':
    label = torch.tensor([0])
    pred = torch.tensor([[ 0.1094,  0.0912,  0.1766]])
    pred2 = torch.tensor([[1,0,0]]).float()

    loss_fn = FocalLoss(alpha=[1, 1, 1], gamma=0, class_num=3)
    print("alpha:", loss_fn.alpha)
    loss_1 = loss_fn(pred, label)
    print(loss_1)
    loss_2 = loss_fn(pred2, label)
    print(loss_2)


import tweepy
import time

consumer_key = ' '
consumer_secret = ' '

key = ''
secret = ''

auth = tweepy.OAuthHandler(consumer_key, consumer_secret)
auth.set_access_token(key, secret)

api = tweepy.API(auth)
api.update_status('Tweeter bot reporting for duty')

FILE_NAME = 'last_seen.txt'

def read_last_seen(FILE_NAME):
    file_read = open(FILE_NAME, 'r')
    last_seen_id = int(file_read.read().strip())
    file_read.close()
    return last_seen_id

def write_last_seen(FILE_NAME, last_seen_id):
    file_write = open(FILE_NAME, 'w')
    file_write.write(str(last_seen_id))
    file_write.close()
    return

tweets = api.mentions_timeline(read_last_seen(FILE_NAME), tweet_mode="extended")

def reply():
    for tweet in reversed(tweets):
        print(str(tweet.id) + " - " + tweet.full_text)
        if '#covid' in tweet.full_text.lower():
            print(str(tweet.id) + " - " + tweet.full_text)
            api.update_status('@' + tweet.user.screen_name + "Amplifying he tweet retweet and link", tweet.id)
            api.create_favorite(tweet.id)
            api.retweet(tweet.id)
            write_last_seen(tweet.id)

while True:
    reply()
    time.sleep(10)

# def main():
#     print("Starting my task")
#     while True:
#         check_hastag(api, ["urgent", "remdesivir", "bed", "icu", "hospital", "covid-19", "o2", "patient", "covid",
#                            "bangalore", "covid"])
#         time.sleep(60)
#
#
# if __name__ == "__main__":
#     main()


import os
from datetime import datetime
import wx.adv
import wx.grid
import wx.lib.agw.aui as aui
import wx.lib.fancytext as fancy

from profile_ import Profile
from cultural import Outline, WellTrajectory
from frames.frame_utilities import GetFilePath
from frames.frame_design import SectionSeparator, PropertySizer, AlignedFlexSizer, Latex2HTML, GAP, INTER_GAP
from widgets.customized_tree_ctrl import CustomizedTreeCtrl
from widgets.customized_menu import CustomMenu, CustomMenuItem
from widgets.tab_art import AuiModifiedTabArt
from widgets.insert_controls import ButtonsInsertCtrl, ArrowButtonsInsertCtrl

from widgets.text_button_control import TextButtonCtrl, ID_RIGHT, ID_LEFT
from frames.import_frames import ProfileImportFrame

import variable_mgr as vm

from _errors import LimitError
from _ids import *
import _icons as ico

GREY = wx.Colour(230, 230, 230)

# ======================================================================================================================
# Customized Controls containing information from VariableManager
# ======================================================================================================================
class PropertyCtrl:
    def __init__(self, property_):

        self._property = property_
        self._align_row = True

    def AlignRow(self):
        return self._align_row

    def ChangeProperty(self, property_):
        self._property = property_

    def GetErrorLabel(self):
        return self._property.GetComboLabel()

    def GetFrameLabel(self, idx=None):
        return self._property.GetFrameLabel(idx)

    def GetLimits(self):
        return self._property.GetLimits()

    def GetUnit(self, idx=None):
        return self._property.GetUnit(idx)

class PropertyArrowButtonsInsertCtrl(ArrowButtonsInsertCtrl, PropertyCtrl):
    def __init__(self, parent, property_, entity_mgr, tree, type_=None, lock=False, hierarchical=False, btn_bitmaps=()):
        ArrowButtonsInsertCtrl.__init__(self, parent, btn_bitmaps=btn_bitmaps)
        PropertyCtrl.__init__(self, property_)

        self.custom_textctrl.SetToolTip(self._property.GetToolTip())

        self._entity_mgr = entity_mgr
        self._tree = tree
        self._type = type_
        self._lock = lock
        self._hierarchical = hierarchical

        self.Bind(wx.EVT_BUTTON, self.OnInsertItem, self.arrow)

    # events -----------------------------------------------------------------------------------------------------------
    def OnInsertItem(self, event):
        item = self._tree.GetSelection()
        if not item:
            return

        data = item.GetData()
        if not data.IsPointer():
            return

        entity = self._entity_mgr.GetEntity(*data.GetPointer())

        if (self._type is not None) and (not entity.IsType(self._type)):
            return

        self.Insert(entity.GetName(), entity.GetBitmap(), entity.GetPointer())
        self.Lock()

    # methods ----------------------------------------------------------------------------------------------------------
    def BindMethod(self, idx, method):
        self.Bind(wx.EVT_BUTTON, method, self._buttons[idx])

    def Default(self):
        self.Clear()

    def DisableButton(self, idx):
        self._buttons[idx].Disable()

    def GetProperty(self):
        return self._property.FromFrame(self.GetClientData())

    def Lock(self):
        if self._lock:
            self.arrow.Enable(False)
            self.custom_textctrl.Enable(False)

    def SetProperty(self, property_):
        pointer = self._property.ToFrame(property_)

        if pointer is not None:

            # crude method for distinguishing between pointer and other insert methods
            if len(pointer) == 2:

                try:
                    entity = self._entity_mgr.GetEntity(*pointer)
                    self.Insert(entity.GetName(), entity.GetBitmap(), entity.GetPointer())
                    self.Lock()

                except KeyError:

                    self.Clear()  # entity has been deleted

            else:  # property_ contains label, bitmap and data

                self.Insert(*pointer)

        elif self._hierarchical:  # clear text

            self.Clear()

class PropertyBitmapComboBox(wx.adv.BitmapComboBox, PropertyCtrl):
    def __init__(self, parent, property_):  # client_data=None
        wx.adv.BitmapComboBox.__init__(self, parent, value='', style=wx.CB_READONLY)
        PropertyCtrl.__init__(self, property_)

        self.SetToolTip(self._property.GetToolTip())

        self.Initialise()

    def ChangeProperty(self, property_):
        self._property = property_
        self.Clear()
        self.Initialise()

    def Default(self):
        self.SetProperty(None)

    def GetData(self):
        return [self.GetClientData(i) for i in range(0, self.GetCount())]

    def GetProperty(self):
        return self._property.FromFrame(self.GetSelection())

    def GetSelectionData(self):
        map = self._property.GetClientDataMap()

        try:
            return map[self.GetSelection()]
        except KeyError:
            raise

    def GetText(self):
        return self.GetString(self.GetSelection())

    def Initialise(self):
        for (label, bitmap) in zip(self._property.GetChoices(), self._property.GetChoiceBitmaps()):
            self.Append(label, bitmap, None)

    def SetData(self, client_data):
        for i in range(self.GetCount()):
            self.SetClientData(i, client_data[i])

    def SetProperty(self, property_):
        self.SetSelection(self._property.ToFrame(property_))  # method okay, using undetected overload

class PropertyButtonsInsertCtrl(ButtonsInsertCtrl, PropertyCtrl):
    def __init__(self, parent, property_, btn_bitmaps):
        ButtonsInsertCtrl.__init__(self, parent, btn_bitmaps=btn_bitmaps)
        PropertyCtrl.__init__(self, property_)

        self.custom_textctrl.SetToolTip(self._property.GetToolTip())

    def BindMethod(self, idx, method):
        self.Bind(wx.EVT_BUTTON, method, self._buttons[idx])

    def Default(self):
        self.SetProperty(None)

    def GetProperty(self):
        return self._property.FromFrame(self.GetClientData())

    def SetProperty(self, property_):
        label, bitmap, data = property_
        self.Insert(label, bitmap, data)

class PropertyCheckBox(wx.CheckBox, PropertyCtrl):
    def __init__(self, parent, property_):
        wx.CheckBox.__init__(self, parent, label=property_.GetLabel())
        PropertyCtrl.__init__(self, property_)

        self.SetToolTip(self._property.GetToolTip())

        self._align_row = False

    def Default(self):
        self.SetValue(False)

    def GetProperty(self):
        return self._property.FromFrame(self.IsChecked())

    def SetProperty(self, property_):
        self.SetValue(self._property.ToFrame(property_))

class PropertyChoice(wx.Choice, PropertyCtrl):
    def __init__(self, parent, property_):
        wx.Choice.__init__(self, parent)
        PropertyCtrl.__init__(self, property_)

        self.Initialise()

    def ChangeProperty(self, property_):
        self._property = property_
        self.Clear()
        self.Initialise()

    def Default(self):
        self.SetProperty(None)

    def GetProperty(self):
        return self._property.FromFrame(self.GetSelection())

    def GetData(self):
        return [self.GetClientData(i) for i in range(0, self.GetCount())]

    def Initialise(self):
        for label in self._property.GetChoices():
            self.Append(label)

    def SetProperty(self, property_):
        self.SetSelection(self._property.ToFrame(property_))

class PropertyDatePickerCtrl(wx.adv.DatePickerCtrl, PropertyCtrl):
    def __init__(self, parent, property_):
        wx.adv.DatePickerCtrl.__init__(self, parent, dt=wx.DateTime.Now(), style=wx.adv.DP_DROPDOWN)
        PropertyCtrl.__init__(self, property_)

        self.SetToolTip(self._property.GetToolTip())

    def Default(self):
        self.SetProperty(datetime.now())

    def GetProperty(self):
        return self._property.FromFrame(self.GetValue())

    def SetProperty(self, property_):
        self.SetValue(self._property.ToFrame(property_))

class PropertyColourPickerCtrl(wx.ColourPickerCtrl, PropertyCtrl):
    def __init__(self, parent, property_):
        wx.ColourPickerCtrl.__init__(self, parent)
        PropertyCtrl.__init__(self, property_)

        self.SetToolTip(self._property.GetToolTip())

    def Default(self):
        self.SetProperty(wx.Colour(0, 0, 0))

    def GetProperty(self):
        return self._property.FromFrame(self.GetColour())

    def SetProperty(self, property_):
        self.SetColour(self._property.ToFrame(property_))

class PropertyRadioBox(wx.RadioBox, PropertyCtrl):
    def __init__(self, parent, property_):
        wx.RadioBox.__init__(self, parent, label=property_.GetLabel(), choices=property_.GetChoices())
        PropertyCtrl.__init__(self, property_)

        self._align_row = False

    def Default(self):
        self.SetSelection(0)

    def GetProperty(self):
        return self._property.FromFrame(self.GetSelection())

    def SetProperty(self, property_):
        self.SetSelection(self._property.ToFrame(property_))

class PropertyTextButtonCtrl(TextButtonCtrl, PropertyCtrl):
    def __init__(self, parent, property_, side=ID_RIGHT):
        TextButtonCtrl.__init__(self, parent, bitmap=property_.GetBitmap(), side=side)
        PropertyCtrl.__init__(self, property_)

        self.text_ctrl.SetToolTip(self._property.GetToolTip())

    def Default(self):
        self.SetProperty(None)

    def GetProperty(self):
        return self._property.FromFrame(self.GetValue())

    def SetProperty(self, property_):
        self.SetValue(self._property.ToFrame(property_))

class PropertyTextCtrl(wx.TextCtrl, PropertyCtrl):
    def __init__(self, parent, property_):
        wx.TextCtrl.__init__(self, parent)
        PropertyCtrl.__init__(self, property_)

        tooltip = self._property.GetToolTip()
        if tooltip is not None:
            self.SetToolTip(tooltip)

    def Default(self):
        self.SetProperty(None)

    def GetProperty(self):
        return self._property.FromFrame(self.GetValue())

    def SetProperty(self, property_):
        self.SetValue(self._property.ToFrame(property_))

# ======================================================================================================================
# Generic panel classes and methods
# ======================================================================================================================
class PropertyPanel(wx.Panel):
    def __init__(self, parent):
        super().__init__(parent)

    def GetRequiredSize(self, texts):
        f = self.GetFont()
        dc = wx.WindowDC(self)
        dc.SetFont(f)

        w_max = 0
        h_max = 0

        for text in tuple(texts):
            if text is None:
                continue

            if not isinstance(text, str):  # text is a type Class Unit
                text = text.Get()

            w, h = dc.GetTextExtent(text)
            w_max = max(w, w_max)
            h_max = max(h, h_max)

        return w_max, h_max

    @staticmethod
    def ErrorCheckInput(ctrl):
        label = ctrl.GetErrorLabel()

        try:

            if isinstance(ctrl, PropertyTextCtrl):

                value = ctrl.GetValue()

                if isinstance(value, str):

                    if value:  # string not empty

                        value = float(value)

                        minimum, maximum = ctrl.GetLimits()

                        if minimum is not None and value < minimum:
                            raise LimitError('{} must be greater than {}.'.format(label, minimum))

                        if maximum is not None and value > maximum:
                            raise LimitError('{} must be less than {}.'.format(label, maximum))

        except ValueError:
            raise ValueError('{} must be a number.'.format(label))

class GridPanel(PropertyPanel):
    def __init__(self, parent, rows, cols):
        super().__init__(parent)

        self._ctrls = []
        self.ctrl_sizer = PropertySizer(rows, cols)
        self.sizer = wx.BoxSizer(wx.VERTICAL)

    def AddCtrl(self, ctrl, label=None, unit=None):
        self._ctrls.append(ctrl)

        if ctrl.AlignRow():
            self.ctrl_sizer.AddCtrl(self, ctrl, label=label, unit=unit)
        else:  # RadioBox and CheckBox
            self.sizer.Add(ctrl, 1, wx.EXPAND | wx.ALL, GAP)

    def BindMethodToCtrls(self, wx_event, method, from_=None, to=None):
        for ctrl in self.GetCtrls(from_, to):
            self.Bind(wx_event, method, ctrl)

    def DefaultCtrls(self, from_=None, to=None):
        for ctrl in self.GetCtrls(from_, to):
            ctrl.Default()

    def EnableCtrls(self, state=True, from_=None, to=None):
        for ctrl in self.GetCtrls(from_, to):
            ctrl.Enable(state)

    def Get(self):
        values = []

        for ctrl in self._ctrls:

            box = None

            try:

                self.ErrorCheckInput(ctrl)
                values.append(ctrl.GetProperty())

            except ValueError as e:

                box = wx.MessageDialog(self, message=str(e), caption='Value Error')

            except LimitError as e:

                box = wx.MessageDialog(self, message=str(e), caption='Limit Error')

            if box is not None:
                box.ShowModal()
                box.Destroy()
                raise ValueError()

        return values

    def GetCtrls(self, from_=None, to=None):
        if from_ is None and to is None:
            ctrls = self._ctrls
        elif to is None:
            ctrls = self._ctrls[from_:]
        elif from_ is None:
            ctrls = self._ctrls[:to]
        else:
            ctrls = self._ctrls[from_:to]

        return ctrls

    def Realize(self, col=1):

        if col >= 0:
            self.ctrl_sizer.AddGrowableCol(col, 1)

        self.SetSizer(self.sizer)
        self.sizer.Fit(self)

    def Set(self, *values):
        for ctrl, value in zip(self._ctrls, values):
            ctrl.SetProperty(value)

class DynamicPanel(GridPanel):
    def __init__(self, parent, property_, rows, cols):
        super().__init__(parent, rows+1, cols)

        self.selection = PropertyBitmapComboBox(self, property_)
        self._ctrls.append(self.selection)
        self.ctrl_sizer.AddCtrl(self, self.selection)

        self._labels = []
        self._units = []

        # used for special cases of external control enabling
        self._to = 0

        # events -------------------------------------------------------------------------------------------------------
        self.Bind(wx.EVT_COMBOBOX, self.OnComboBox, self.selection)

    # events -----------------------------------------------------------------------------------------------------------
    def OnComboBox(self, event):
        idx = event.GetInt()
        self.UpdateText(idx)

        # clearing every input but the selection
        for ctrl in self._ctrls[1:]:
            ctrl.Default()

    # external functions -----------------------------------------------------------------------------------------------
    def AddCtrl(self, ctrl, label=None, unit=None):
        w, h = self.GetRequiredSize(ctrl.GetFrameLabel())
        self._labels.append(wx.StaticText(self, label='', size=wx.Size(w, h)))

        w, h = self.GetRequiredSize(ctrl.GetUnit())
        self._units.append(wx.StaticText(self, label='', size=wx.Size(w, h)))

        self._ctrls.append(ctrl)
        self.ctrl_sizer.AddCtrl(self, ctrl, label=self._labels[-1], unit=self._units[-1])

    def ChangeProperties(self, *properties):
        for (ctrl, property_) in zip(self._ctrls, properties):
            ctrl.ChangeProperty(property_)

    def Clear(self):
        self.selection.SetSelection(-1)  # method okay, using undetected overload

        for (ctrl, label, unit) in zip(self._ctrls[1:], self._labels, self._units):
            ctrl.Default()
            label.SetLabel('')
            unit.SetLabel('')
            ctrl.Enable(False)

    def EnableAvailableCtrls(self, state):
        self.EnableCtrls(state, from_=0, to=self._to)

    def GetSelection(self):
        return self.selection.GetSelection()

    def UpdateText(self, idx):
        if idx == -1:
            self.Clear()
            return

        self._to = 1

        # self._ctrls[0] is self.selection. Added for input/output, but not dynamic
        for (ctrl, label, unit) in zip(self._ctrls[1:], self._labels, self._units):
            label_str = ctrl.GetFrameLabel(idx)
            unit_str = ctrl.GetUnit(idx)

            if label_str:
                label.SetLabel(ctrl.GetFrameLabel(idx))
            else:
                label.SetLabel('')

            if unit_str:
                unit.SetLabel(ctrl.GetUnit(idx))
            else:
                unit.SetLabel('')

            if label_str or unit_str:
                ctrl.Enable(True)
                self._to += 1

            else:
                ctrl.Enable(False)

class SectionPanel(GridPanel):
    def __init__(self, parent, rows, cols, label, bitmap=None):
        super().__init__(parent, rows, cols)

        self.sizer.Add(SectionSeparator(self, label=label, bitmap=bitmap), 0, wx.EXPAND | (wx.ALL & ~wx.TOP), GAP)
        self.sizer.Add(self.ctrl_sizer, 1, wx.EXPAND | (wx.ALL & ~wx.TOP), GAP)

class DynamicSectionPanel(DynamicPanel):
    def __init__(self, parent, property_, rows, cols, label, bitmap=None):
        super().__init__(parent, property_, rows, cols)

        self.sizer.Add(SectionSeparator(self, label=label, bitmap=bitmap), 0, wx.EXPAND | (wx.ALL & ~wx.TOP), GAP)
        self.sizer.Add(self.ctrl_sizer, 1, wx.EXPAND | (wx.ALL & ~wx.TOP), GAP)

class DerivedPanel(SectionPanel):
    def __init__(self, parent, rows, cols, label, bitmap, derived=False):
        super().__init__(parent, rows, cols, label, bitmap)

class HierarchicalPanel(wx.Panel):
    def __init__(self, parent, panel, *args, label=None, bitmap=None, has_parent=True):
        super().__init__(parent)

        self.use_self = SectionSeparator(self, label=label, bitmap=bitmap, checkbox=has_parent)
        self.ctrls = panel(self, *args)  # GridPanel or DynamicPanel

        self.sizer = wx.BoxSizer(wx.VERTICAL)
        self.sizer.Add(self.use_self, 0, wx.EXPAND | wx.ALL, GAP)
        self.ctrls.sizer.Add(self.ctrls.ctrl_sizer, 0, wx.EXPAND | (wx.ALL & ~wx.TOP), GAP)
        self.sizer.Add(self.ctrls, 0, wx.EXPAND | wx.ALL, GAP)

        # events -------------------------------------------------------------------------------------------------------
        if has_parent:
            self.Bind(wx.EVT_CHECKBOX, self.OnChecked, self.use_self.checkbox)
            self.use_self.SetValue(True)
            self.OnChecked(self.use_self)

    # events -----------------------------------------------------------------------------------------------------------
    def OnChecked(self, event):
        self.ctrls.EnableCtrls(event.IsChecked())

    # external functions -----------------------------------------------------------------------------------------------
    def AddCtrl(self, ctrl, label=None, unit=None):
        self.ctrls.AddCtrl(ctrl, label=label, unit=unit)

    def EnableCheckBox(self, state):
        self.use_self.EnableCheckBox(state)

    def EnableCtrls(self, state=True, from_=None, to=None):
        self.ctrls.EnableCtrls(state, from_=from_, to=to)

    def EnableAvailableCtrls(self, state):
        self.ctrls.EnableAvailableCtrls(state)

    def Get(self):
        return (self.use_self.IsChecked(), *self.ctrls.Get())

    def IsChecked(self):
        return self.use_self.IsChecked()

    def Realize(self):
        self.ctrls.Realize()
        self.SetSizer(self.sizer)
        self.sizer.Fit(self)

    def Set(self, use_self, *values):
        self.use_self.SetValue(use_self)
        self.OnChecked(self.use_self)

        self.ctrls.Set(*values)

    def SetUseSelf(self, state):
        self.use_self.SetValue(state)

    def UseSelf(self):
        return self.use_self.IsChecked()

# ======================================================================================================================
# Generic AUI classes and methods
# ======================================================================================================================
class PropertiesAUIPanel(wx.Panel):
    def __init__(self, parent, min_size=(200, 200)):
        super().__init__(parent)

        # preparing aui-manager ----------------------------------------------------------------------------------------
        self._mgr = aui.AuiManager()
        self._mgr.SetManagedWindow(self)
        self.SetMinSize(size=min_size)

        self.notebook = PropertiesNotebook(self)

    def AddPage(self, parent_panel, *panels, proportions=(), title='', bitmap=wx.NullBitmap):

        sizer = wx.BoxSizer(wx.VERTICAL)

        if not proportions:
            proportions = [0 for _ in panels]

        for panel, proportion in zip(panels[:-1], proportions[:-1]):
            sizer.Add(panel, proportion, wx.EXPAND | (wx.ALL & ~wx.BOTTOM), INTER_GAP)

        sizer.Add(panels[-1], proportions[-1], wx.EXPAND | wx.ALL, INTER_GAP)

        parent_panel.SetSizer(sizer)
        sizer.Fit(parent_panel)

        # TODO: this causes a matplotlib wxAssertionError. which I believe to be a bug in matplotlib
        self.notebook.AddPage(parent_panel, title, bitmap=bitmap)

    def Realize(self):
        self._mgr.AddPane(self.notebook, aui.AuiPaneInfo().Name('panel').CenterPane().PaneBorder(False))
        self._mgr.Update()

class PropertiesNotebook(aui.AuiNotebook):
    def __init__(self, parent):
        super().__init__(parent)
        self.SetAGWWindowStyleFlag(aui.AUI_NB_TOP)
        self.ChangeTabArt()

    def ChangeTabArt(self):
        art = AuiModifiedTabArt()

        # colours ------------------------------------------------------------------------------------------------------
        colour = GREY
        art.SetBaseColour(colour)
        art._border_colour = wx.Colour(150, 150, 150)
        art._border_pen = wx.Pen(wx.Colour(150, 150, 150))

        art._background_top_colour = colour
        art._background_bottom_colour = colour

        art._tab_top_start_colour = colour
        art._tab_top_end_colour = colour
        art._tab_bottom_start_colour = colour
        art._tab_bottom_end_colour = colour

        art._tab_inactive_top_colour = colour
        art._tab_inactive_bottom_colour = colour

        art._tab_text_colour = lambda page: page.text_colour
        art._tab_disabled_text_colour = wx.SystemSettings.GetColour(wx.SYS_COLOUR_GRAYTEXT)

        self.SetArtProvider(art)

# ======================================================================================================================
# CustomTreeCtrl Panels
# ======================================================================================================================
class SelectionTree(wx.Panel):
    def __init__(self, parent, object_menu_page, expand_collapse=False):
        super().__init__(parent, style=wx.SIMPLE_BORDER)

        self.tree = CustomizedTreeCtrl(self, agw_style=wx.TR_HAS_BUTTONS | wx.TR_MULTIPLE)

        self._mirror_tree = object_menu_page.tree  # object_menu_page tree mirrored for population
        self.tree.SetImageList(self._mirror_tree.GetImageList())

        # workaround to not have ugly greyed out disabled pictures
        self.tree._grayedImageList = self.tree.GetImageList()

        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(self.tree, 1, wx.EXPAND)
        self.SetSizer(sizer)

        self.SetMinSize(wx.Size(150, 0))

        # events -------------------------------------------------------------------------------------------------------
        if expand_collapse:
            self.Bind(wx.EVT_TREE_ITEM_RIGHT_CLICK, self.OnRightClick, self.tree)

    # events -----------------------------------------------------------------------------------------------------------
    def OnRightClick(self, event):
        context_menu = CustomMenu(self)
        context_menu.AppendCollapseItem(self.OnCollapseAll)
        context_menu.AppendExpandItem(self.OnExpandAll)
        context_menu.CustomPopup()

    def OnCollapseAll(self, event):
        self.tree.CollapseAll()

    def OnExpandAll(self, event):
        self.tree.ExpandAll()

    # external methods -------------------------------------------------------------------------------------------------
    def Populate(self, mirror_items, types, entity=None, entity_mgr=None, root_title=None, ct_type=1, progress=False):
        self.tree.Freeze()

        # called in case tree reloaded
        self.tree.DeleteAllItems()

        if root_title is None:
            self.tree.SetAGWWindowStyleFlag(self.tree.GetAGWWindowStyleFlag() | wx.TR_HIDE_ROOT)
            root = self.tree.AddRoot('')
        else:
            root = self.tree.AddRoot(root_title)

        for mirror_item in mirror_items:
            item = self.tree.AppendItem(root, mirror_item.GetText(), data=mirror_item.GetData())
            self.tree.SetItemImage(item, mirror_item.GetImage(), wx.TreeItemIcon_Normal)
            self.tree.SetItemImage(item, mirror_item.GetImage(), wx.TreeItemIcon_Expanded)

            self.AddChildren(item, mirror_item, types, entity=entity, entity_mgr=entity_mgr,
                             ct_type=ct_type, progress=progress)

        self.tree.ExpandAll()

        self.tree.Thaw()

    def AddChildren(self, parent, mirror_parent, types, entity=None, entity_mgr=None, ct_type=1, progress=False):
        """
        Add children to the mirrored item

        Parameters
        ----------
        parent : wx.lib.agw.GenericTreeItem
            Parent item in CustomTreeCtrl to add children to
        mirror_parent : wx.lib.agw.GenericTreeItem
            Parent from object_menu_page being mirror in the tree
        types : tuple
            Tuple of types to test against
        entity : Entity
            Class Entity
        entity_mgr : EntityManager
            Class EntityManager
        ct_type : int
            ct_type from CustomTreeCtrl
        progress : bool
            Whether to progress through the tree and show all items in a hierarchy after the first item with ct_type>0
        """

        child, cookie = self._mirror_tree.GetFirstChild(mirror_parent)

        while child:
            data = child.GetData()

            item = self.tree.AppendItem(parent, child.GetText(), ct_type=1, data=data)
            self.tree.SetItemImage(item, child.GetImage(), wx.TreeItemIcon_Normal)
            self.tree.SetItemImage(item, child.GetImage(), wx.TreeItemIcon_Expanded)

            # if an entity and entity mgr is passed, ensure the entity itself is not populated, and potential child
            # entities which already have a parent of the entity type does not allow checking.
            # if the item is already a child of entity then check it
            disable = False

            if (entity is not None) and (not data.IsFolder()):
                item_entity = entity_mgr.GetEntity(*data.GetPointer())

                if item_entity is entity:
                    disable = True

                if data.GetType() in types:

                    if item_entity.HasParent(entity.GetType()):

                        if entity.IsParentOf(*data.GetPointer()):
                            self.tree.CheckItem2(item, True)

                        else:

                            if not item_entity.AllowMultipleParents(entity.GetType()):
                                disable = True

                    elif entity.HasParent(item_entity.GetType()):

                        if item_entity.IsParentOf(*entity.GetPointer()):

                            disable = True

            # change ct_type and progress
            if data.GetType() in types:

                self.tree.SetItemType(item, ct_type=ct_type)

                if disable:
                    self.tree.EnableItem(item, False, torefresh=True)

                if progress:
                    self.AddChildren(item, child, types, entity=entity, entity_mgr=entity_mgr,
                                     ct_type=ct_type, progress=progress)

            else:
                self.tree.SetItemType(item, ct_type=0)
                self.AddChildren(item, child, types, entity=entity, entity_mgr=entity_mgr,
                                 ct_type=ct_type, progress=progress)

            child, cookie = self._mirror_tree.GetNextChild(mirror_parent, cookie)

    def CheckItemsById(self, ids, parent=None):
        if parent is None:
            parent = self.tree.GetRootItem()

            if (not isinstance(ids, tuple)) and (not isinstance(ids, list)):
                ids = (ids,)

        child, cookie = self.tree.GetFirstChild(parent)

        while child:
            data = child.GetData()
            if data.GetId() in ids:
                self.tree.CheckItem2(child, True)

            self.CheckItemsById(ids, child)
            child, cookie = self.tree.GetNextChild(parent, cookie)

    def GetPointers(self):
        return [d.GetPointer() for d in self.tree.GetData()]

    def GetItems(self):
        return self.tree.GetItems()

    # returns a list of all items in the tree that are checked (checkbox and radiobutton)
    def GetCheckedItems(self):
        return self.tree.GetCheckedItems()

    def SetImageList(self, il):
        self.tree.SetImageList(il)

class ProcessorFlowTree(wx.Panel):
    def __init__(self, parent):
        super().__init__(parent, style=wx.SIMPLE_BORDER)

        self.tree = CustomizedTreeCtrl(self, agw_style=wx.TR_HAS_BUTTONS | wx.TR_MULTIPLE)

        # Create an image list to add icons next to an item ------------------------------------------------------------
        il = wx.ImageList(16, 16)
        il.Add(ico.processor_16x16.GetBitmap())
        il.Add(ico.pipeline_16x16.GetBitmap())

        self.tree.SetImageList(il)

        # workaround to not have ugly greyed out disabled pictures
        self.tree._grayedImageList = self.tree.GetImageList()

        # sizing -------------------------------------------------------------------------------------------------------
        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(self.tree, 1, wx.EXPAND)
        self.SetSizer(sizer)

        self.SetMinSize(wx.Size(150, 0))

    def CheckItemById(self, id_, parent=None):
        if parent is None:
            parent = self.tree.GetRootItem()

        child, cookie = self.tree.GetFirstChild(parent)

        while child:
            if child.GetData() == id_:
                self.tree.CheckItem2(child, True)

            self.CheckItemById(id_, child)
            child, cookie = self.tree.GetNextChild(parent, cookie)

    # returns a list of all items in the tree that are checked (checkbox and radiobutton)
    def GetCheckedItems(self):
        return self.tree.GetCheckedItems()

    def Populate(self, entity, entity_mgr):
        # called in case tree reloaded
        self.tree.DeleteAllItems()

        root = self.tree.AddRoot('Primary down-stream node')

        parents = entity_mgr.GetEntities([p for p in entity.GetNetworkParents()])

        for parent in parents:

            image = 0 if entity.IsProcessor() else 1

            item = self.tree.AppendItem(root, parent.GetName(), ct_type=2, data=parent.GetId())
            self.tree.SetItemImage(item, image, wx.TreeItemIcon_Normal)
            self.tree.SetItemImage(item, image, wx.TreeItemIcon_Expanded)

        self.tree.ExpandAll()

# ======================================================================================================================
# Free text input panels (requires overwritten Get & Set not checking for numbers)
# ======================================================================================================================
class NamePanel(GridPanel):
    def __init__(self, parent):
        super().__init__(parent, 1, 2)

        self.sizer.Add(self.ctrl_sizer, 1, wx.EXPAND | wx.ALL, GAP)
        self.AddCtrl(PropertyTextCtrl(self, vm.Name()))

        self.Realize()

    def Get(self):
        return self._ctrls[0].GetProperty()

    def Set(self, name):
        self._ctrls[0].SetProperty(name)

class EvaluationPanel(GridPanel):
    def __init__(self, parent, property_):
        super().__init__(parent, 1, 2)

        self.sizer.Add(self.ctrl_sizer, 1, wx.EXPAND | wx.ALL, GAP)
        self.arrow = PropertyTextButtonCtrl(self, property_, side=ID_LEFT)
        self.AddCtrl(self.arrow)

        self.Realize()

    def Append(self, expression):
        self.Set(self.Get() + expression)

    def Get(self):
        return self._ctrls[0].GetProperty()

    def Set(self, evaluation):
        self._ctrls[0].SetProperty(evaluation)

class ScalerEvaluationPanel(EvaluationPanel):
    def __init__(self, parent):
        super().__init__(parent, vm.ScalerEvaluation())

class SummaryEvaluationPanel(EvaluationPanel):
    def __init__(self, parent):
        super().__init__(parent, vm.SummaryEvaluation())

# ======================================================================================================================
# Import panels (Either from ObjectMenu or from a ProfileImportFrame)
# ======================================================================================================================
class AnaloguePanel(GridPanel):
    def __init__(self, parent, entity_mgr, tree):
        super().__init__(parent, 1, 2)

        bitmaps = (ico.window_trend_chart_16x16.GetBitmap(),)
        self.analogue = PropertyArrowButtonsInsertCtrl(self, vm.Analogue(), entity_mgr, tree, type_=ID_ANALOGUE, lock=True, btn_bitmaps=bitmaps)
        self.AddCtrl(self.analogue)

        self.sizer.Add(self.ctrl_sizer, 1, wx.EXPAND | wx.ALL, GAP)
        self.Realize()

    def Insert(self, id_):
        self.analogue.SetProperty(id_)

class ScalingEvaluationPanel(HierarchicalPanel):
    def __init__(self, parent, entity_mgr, tree, has_parent=True):
        super().__init__(parent, GridPanel, 1, 2, label='Scaling evaluation', bitmap=ico.scaling_chart_16x16.GetBitmap(), has_parent=has_parent)

        self.AddCtrl(PropertyArrowButtonsInsertCtrl(self.ctrls, vm.Scaling(), entity_mgr, tree,
                                                    type_=ID_SCALING, hierarchical=True))

        self.Realize()

class HistoryPanel(SectionPanel):
    def __init__(self, parent):
        super().__init__(parent, 1, 2, 'History', ico.history_match_16x16.GetBitmap())

        self.history = PropertyButtonsInsertCtrl(self, vm.History(), btn_bitmaps=(ico.project_open_16x16.GetBitmap(),))
        self.AddCtrl(self.history)

        self.Realize()

        # events -------------------------------------------------------------------------------------------------------
        self.history.BindMethod(0, self.OnBrowseButton)

    def OnBrowseButton(self, event):
        profile = Profile()
        frame = ProfileImportFrame(self, profile)
        frame.ShowModal()

        if frame.IsImported():
            self.Set(profile, frame.GetPath())

    def Get(self):
        data = self.history.GetClientData()
        if data is not None:
            profile, path = data
        else:
            profile, path = None, None

        return profile, path

    def Set(self, profile, path):
        if path is None:
            return

        label = os.path.basename(path)
        bitmap = ico.history_match_16x16.GetBitmap()
        self.history.Insert(label, bitmap, (profile, path))

class HistoryFitPanel(SectionPanel):
    def __init__(self, parent):
        super().__init__(parent, 1, 2, 'History and fitting', ico.history_match_16x16.GetBitmap())

        bitmaps = (ico.project_open_16x16.GetBitmap(), ico.window_fit_chart_16x16.GetBitmap(),)
        self.history = PropertyButtonsInsertCtrl(self, vm.HistoryFit(), btn_bitmaps=bitmaps)
        self.AddCtrl(self.history)

        self.Realize()

        # events -------------------------------------------------------------------------------------------------------
        self.history.BindMethod(0, self.OnBrowseButton)

    def OnBrowseButton(self, event):
        profile = Profile()
        frame = ProfileImportFrame(self, profile)
        frame.ShowModal()

        if frame.IsImported():
            self.Set(profile, frame.GetPath())

    def Get(self):
        data = self.history.GetClientData()
        if data is not None:
            profile, path = data
        else:
            profile, path = None, None

        return profile, path

    def Set(self, profile, path):
        if path is None:
            return

        label = os.path.basename(path)
        bitmap = ico.history_match_16x16.GetBitmap()
        self.history.Insert(label, bitmap, (profile, path))

class CulturalPanel(SectionPanel):
    def __init__(self, parent, bitmap):
        super().__init__(parent, 1, 2, 'Cultural', bitmap)

        self.cultural = PropertyButtonsInsertCtrl(self, vm.Cultural(), btn_bitmaps=(ico.project_open_16x16.GetBitmap(),))
        self.AddCtrl(self.cultural)

        self.Realize()

    def Get(self):
        data = self.cultural.GetClientData()
        if data is not None:
            cultural, path = data
        else:
            cultural, path = None, None

        return cultural, path

    def Set(self, cultural, path):
        if path is None:
            return

        label = os.path.basename(path)
        bitmap = ico.polygon_16x16.GetBitmap()
        self.cultural.Insert(label, bitmap, (cultural, path))

class CulturalTrajectoryPanel(CulturalPanel):
    def __init__(self, parent):
        super().__init__(parent, ico.polygon_16x16.GetBitmap())

        # events -------------------------------------------------------------------------------------------------------
        self.cultural.BindMethod(0, self.OnBrowseButton)

    def OnBrowseButton(self, event):
        filepath = GetFilePath(self)
        if filepath is None:
            return

        cultural = WellTrajectory()
        cultural.ReadDEV(filepath)
        self.Set(cultural, filepath)

class CulturalOutlinePanel(CulturalPanel):
    def __init__(self, parent):
        super().__init__(parent, ico.polygon_16x16.GetBitmap())

        # events -------------------------------------------------------------------------------------------------------
        self.cultural.BindMethod(0, self.OnBrowseButton)

    def OnBrowseButton(self, event):
        filepath = GetFilePath(self)
        if filepath is None:
            return

        cultural = Outline()
        cultural.ReadASCII(filepath)
        self.Set(cultural, filepath)

class CulturalPointPanel(CulturalPanel):
    def __init__(self, parent):
        super().__init__(parent, ico.polygon_16x16.GetBitmap())

        # events -------------------------------------------------------------------------------------------------------
        self.cultural.BindMethod(0, self.OnBrowseButton)

    def OnBrowseButton(self, event):
        pass
        # filepath = GetFilePath(self)
        # if filepath is None:
        #     return
        #
        # cultural = Outline()
        # cultural.ReadASCII(filepath)
        # self.Set(cultural, filepath)

class PredictionPanel(HierarchicalPanel):
    def __init__(self, parent, entity_mgr, tree, disable_dca=True, has_parent=True):
        super().__init__(parent, GridPanel, 3, 3, label='Prediction', bitmap=ico.analogue_16x16.GetBitmap(), has_parent=has_parent)

        # used for access to tree
        self._entity_mgr = entity_mgr
        self._tree = tree  # object_menu.<page>.tree for access to selection when clicking on ArrowInsertControl
        self._prediction = None

        bitmaps = (ico.project_open_16x16.GetBitmap(), ico.window_trend_chart_16x16.GetBitmap())

        self.prediction = PropertyBitmapComboBox(self.ctrls, vm.Prediction())
        self.typecurve = PropertyArrowButtonsInsertCtrl(self.ctrls, vm.Typecurve(), entity_mgr, tree, type_=ID_TYPECURVE, btn_bitmaps=bitmaps)
        self.occurrence = PropertyTextCtrl(self.ctrls, vm.ProbabilityOfOccurrence())

        self.ctrls.AddCtrl(self.prediction)
        self.ctrls.AddCtrl(self.typecurve)
        self.ctrls.AddCtrl(self.occurrence)

        if disable_dca:
            self.typecurve.DisableButton(1)

        self.Realize()

        # events -------------------------------------------------------------------------------------------------------
        self.Bind(wx.EVT_COMBOBOX, self.OnComboBox, self.prediction)
        self.typecurve.BindMethod(0, self.OnBrowseButton)

    # events -----------------------------------------------------------------------------------------------------------
    def OnComboBox(self, event):
        self.SaveState()

        i = event.GetInt()
        self._prediction = self.prediction.GetClientData(i)

        self.LoadState()

    def OnBrowseButton(self, event):
        profile = Profile()
        frame = ProfileImportFrame(self, profile)
        frame.ShowModal()

        if frame.IsImported():
            self.InsertImport(profile, frame.GetPath())

    # external functions -----------------------------------------------------------------------------------------------
    def Get(self):
        self.SaveState()
        return (self.use_self.IsChecked(),  *self.prediction.GetData())

    def GetFunctions(self):
        return self._prediction.GetFunctions()

    def GetPrediction(self):
        pointer = None
        functions = self._prediction.GetFunctions()
        profile = None
        label = None

        typecurve = self.typecurve.GetClientData()

        if typecurve is not None:
            type_ = self._prediction.GetType()

            # work-around for loading typecurves that have been insert via arrow
            if isinstance(typecurve, tuple) and isinstance(typecurve[0], int):
                type_ = ID_PREDICTION_TYPECURVE

            # gather client data
            if type_ == ID_PREDICTION_TYPECURVE:
                pointer = typecurve

            elif type_ == type_ == ID_PREDICTION_FUNCTION:
                label = typecurve

            else:
                profile, label = typecurve
        else:
            type_ = None

        occurrence = self.occurrence.GetProperty()

        return type_, pointer, functions, profile, label, occurrence

    def LoadState(self):
        self.SetPrediction(*self._prediction.Get())

    def SaveState(self):
        self._prediction.Set(*self.GetPrediction())

    def Set(self, use_self, *values):
        for i, value in enumerate(values):
            self.prediction.SetClientData(i, value.Copy())

        self.use_self.SetValue(use_self)
        self.OnChecked(self.use_self)

        # initialize as Mid
        self.prediction.SetProperty(1)
        self._prediction = self.prediction.GetClientData(1)

        self.LoadState()

    def SetFunctions(self, functions):
        self._prediction.SetFunctions(functions)
        self.InsertFunctions()

    def SetPrediction(self, *values):
        type_, pointer, functions, profile, path, occurrence = values

        if type_ is not None:
            if type_ == ID_PREDICTION_TYPECURVE:
                self.InsertTypecurve(pointer)

            elif type_ == ID_PREDICTION_FUNCTION:
                self.InsertFunctions()

            else:
                self.InsertImport(profile, path)
        else:
            self.typecurve.Clear()

        self.occurrence.SetProperty(occurrence)

    def InsertFunctions(self):
        label = 'DCA ({})'.format(self.prediction.GetText())
        bitmap = ico.hyperbolic_dca_16x16.GetBitmap()
        self.typecurve.Insert(label, bitmap, ID_PREDICTION_FUNCTION)
        self._prediction.SetType(ID_PREDICTION_FUNCTION)

    def InsertImport(self, profile, path):
        if path is None:
            return

        label = os.path.basename(path)
        bitmap = ico.history_chart_16x16.GetBitmap()
        self.typecurve.Insert(label, bitmap, (profile, path))
        self._prediction.SetType(ID_PREDICTION_IMPORT)

    def InsertTypecurve(self, pointer):
        if pointer is not None:

            try:
                entity = self._entity_mgr.GetEntity(*pointer)
            except KeyError:
                return

            self.typecurve.Insert(entity.GetName(), entity.GetBitmap(), entity.GetPointer())
            self._prediction.SetType(ID_PREDICTION_TYPECURVE)

class ScenarioPanel(GridPanel):
    def __init__(self, parent, entity_mgr, tree):
        super().__init__(parent, 1, 2)

        self.AddCtrl(PropertyArrowButtonsInsertCtrl(self, vm.Scenario(), entity_mgr, tree, type_=ID_SCENARIO, lock=True))
        self.sizer.Add(self.ctrl_sizer, 1, wx.EXPAND | wx.ALL, GAP)

        self.Realize()

    def Insert(self, id_):
        self._ctrls[0].SetProperty(id_)

class HistorySimulationPanel(GridPanel):
    def __init__(self, parent, entity_mgr, tree):
        super().__init__(parent, 1, 2)

        self.AddCtrl(PropertyArrowButtonsInsertCtrl(self, vm.HistorySimulation(), entity_mgr, tree, type_=ID_HISTORY))
        self.sizer.Add(self.ctrl_sizer, 1, wx.EXPAND | wx.ALL, GAP)

        self.Realize()

# ======================================================================================================================
# Input panels in alphabetical order
# ======================================================================================================================
class AvailabilityPanel(HierarchicalPanel):
    def __init__(self, parent, has_parent=True):
        super().__init__(parent, GridPanel, 1, 3, label='Availability', bitmap=ico.uptime_16x16.GetBitmap(), has_parent=has_parent)

        self.AddCtrl(PropertyTextCtrl(self.ctrls, vm.Availability()))

        self.Realize()

class ConstrainedModelPanel(SectionPanel):
    def __init__(self, parent, has_parent=True):
        super().__init__(parent, 2, 3, 'Modelling', bitmap=ico.flow_constraint_16x16.GetBitmap())

        self.AddCtrl(PropertyTextCtrl(self, vm.Availability()))
        self.AddCtrl(PropertyCheckBox(self, vm.SimulateConstrainted()))

        self.Realize()

class FlowConstraintPanel(SectionPanel):
    def __init__(self, parent, unit_system):
        super().__init__(parent, 4, 3, 'Flow constraints', ico.flow_constraint_16x16.GetBitmap())

        self.oil = PropertyTextCtrl(self, vm.OilConstraint(unit_system))
        self.gas = PropertyTextCtrl(self, vm.GasConstraint(unit_system))
        self.water = PropertyTextCtrl(self, vm.WaterConstraint(unit_system))
        self.liquid = PropertyTextCtrl(self, vm.LiquidConstraint(unit_system))

        self.AddCtrl(self.oil)
        self.AddCtrl(self.gas)
        self.AddCtrl(self.water)
        self.AddCtrl(self.liquid)

        self.Realize()

class FlowSplitPanel(SectionPanel):
    def __init__(self, parent):
        super().__init__(parent, 6, 3, 'Flow split', ico.flow_constraint_16x16.GetBitmap())

        self.AddCtrl(PropertyBitmapComboBox(self, vm.SplitType()))
        self.AddCtrl(PropertyTextCtrl(self, vm.OilSplit()))
        self.AddCtrl(PropertyTextCtrl(self, vm.GasSplit()))
        self.AddCtrl(PropertyTextCtrl(self, vm.WaterSplit()))
        self.AddCtrl(PropertyTextCtrl(self, vm.InjectionGasSplit()))
        self.AddCtrl(PropertyTextCtrl(self, vm.InjectionWaterSplit()))

        self.Realize()

class LiftGasConstraintPanel(SectionPanel):
    def __init__(self, parent, unit_system):
        super().__init__(parent, 1, 3, 'Lift gas constraint', ico.lift_gas_constraint_16x16.GetBitmap())

        self.AddCtrl(PropertyTextCtrl(self, vm.LiftGasConstraint(unit_system)))

        self.Realize()

class GasLiftPanel(HierarchicalPanel):
    def __init__(self, parent, unit_system, has_parent=True):
        super().__init__(parent, GridPanel, 1, 3, label='Gas-lift', bitmap=ico.lift_gas_rate_16x16.GetBitmap(), has_parent=has_parent)

        self.AddCtrl(PropertyTextCtrl(self.ctrls, vm.TargetGasLiquidRatio(unit_system)))

        self.Realize()

class InflowingPhasePanel(SectionPanel):
    def __init__(self, parent):
        super().__init__(parent, 5, 1, 'In-flowing phases', ico.fluids_16x16.GetBitmap())

        self.AddCtrl(PropertyCheckBox(self, vm.OilInflow()))
        self.AddCtrl(PropertyCheckBox(self, vm.GasInflow()))
        self.AddCtrl(PropertyCheckBox(self, vm.WaterInflow()))
        self.AddCtrl(PropertyCheckBox(self, vm.InjectionGasInflow()))
        self.AddCtrl(PropertyCheckBox(self, vm.InjectionWaterInflow()))

        self.Realize(col=-1)

class InjectionConstraintPanel(SectionPanel):
    def __init__(self, parent, unit_system):
        super().__init__(parent, 2, 3, 'Injection constraints', ico.injection_constraint_16x16.GetBitmap())

        self.AddCtrl(PropertyTextCtrl(self, vm.InjectionGasConstraint(unit_system)))
        self.AddCtrl(PropertyTextCtrl(self, vm.InjectionWaterConstraint(unit_system)))

        self.Realize()

class InjectionFluidPanel(HierarchicalPanel):
    def __init__(self, parent, unit_system, has_parent=True):
        super().__init__(parent, GridPanel, 2, 3, label='Injection fluids', bitmap=ico.fluids_injection_16x16.GetBitmap(), has_parent=has_parent)

        self.AddCtrl(PropertyTextCtrl(self.ctrls, vm.InjectionGasFVF(unit_system)))
        self.AddCtrl(PropertyTextCtrl(self.ctrls, vm.InjectionWaterFVF(unit_system)))

        self.Realize()

class InjectionPhasePanel(GridPanel):
    def __init__(self, parent):
        super().__init__(parent, 1, 2)

        self.radio = PropertyRadioBox(self, vm.InjectionPhase())  # used for access in events
        self.AddCtrl(self.radio)

        self.Realize()

    def GetSelection(self):
        return self._ctrls[0].GetSelection()

class InjectionPotentialPanel(HierarchicalPanel):
    def __init__(self, parent, unit_system, has_parent=True):
        super().__init__(parent, GridPanel, 2, 3, label='Injection potentials', bitmap=ico.wag_injection_rate_16x16.GetBitmap(), has_parent=has_parent)

        self.AddCtrl(PropertyTextCtrl(self.ctrls, vm.GasInjectionPotentialConstant(unit_system)))
        self.AddCtrl(PropertyTextCtrl(self.ctrls, vm.WaterInjectionPotentialConstant(unit_system)))

        self.Realize()

class LicensePanel(SectionPanel):
    def __init__(self, parent):
        super().__init__(parent, 1, 2, 'License', ico.event_16x16.GetBitmap())

        self.AddCtrl(PropertyDatePickerCtrl(self, vm.License()))

        self.Realize()

class ProductionPhasePanel(GridPanel):
    def __init__(self, parent):
        super().__init__(parent, 1, 2)

        self.AddCtrl(PropertyRadioBox(self, vm.ProductionPhase()))

        self.Realize()

    def GetSelection(self):
        return self._ctrls[0].GetSelection()

class ReservoirFluidPanel(HierarchicalPanel):
    def __init__(self, parent, unit_system, has_parent=True):
        super().__init__(parent, GridPanel, 4, 3, label='Reservoir fluids', bitmap=ico.fluids_16x16.GetBitmap(), has_parent=has_parent)

        self.AddCtrl(PropertyTextCtrl(self.ctrls, vm.OilFVF(unit_system)))
        self.AddCtrl(PropertyTextCtrl(self.ctrls, vm.GasFVF(unit_system)))
        self.AddCtrl(PropertyTextCtrl(self.ctrls, vm.WaterFVF(unit_system)))
        self.AddCtrl(PropertyTextCtrl(self.ctrls, vm.SolutionGasOilRatio(unit_system)))

        self.Realize()

class RiskingPanel(DerivedPanel):
    def __init__(self, parent, derived=False):
        super().__init__(parent, 2, 3, 'Risking', ico.risking_16x16.GetBitmap(), derived=derived)

        self.AddCtrl(PropertyTextCtrl(self, vm.Maturity()))
        self.AddCtrl(PropertyTextCtrl(self, vm.ProbabilityOfSuccess()))

        self.Realize()
        self.EnableCtrls(not derived)

class VolumePanel(DerivedPanel):
    def __init__(self, parent, unit_system, derived=False):
        super().__init__(parent, 2, 3, 'Volumes', ico.stoiip_16x16.GetBitmap(), derived=derived)

        self.AddCtrl(PropertyTextCtrl(self, vm.STOIIP(unit_system)))

        self.Realize()
        self.EnableCtrls(not derived)

class WagPanel(HierarchicalPanel):
    def __init__(self, parent, has_parent=True):
        super().__init__(parent, GridPanel, 2, 3, label='WAG', bitmap=ico.injector_wag_16x16.GetBitmap(), has_parent=has_parent)

        self.AddCtrl(PropertyTextCtrl(self.ctrls, vm.WAGCycleDuration()))
        self.AddCtrl(PropertyTextCtrl(self.ctrls, vm.WAGCycles()))

        self.Realize()

class WellSpacingPanel(HierarchicalPanel):
    def __init__(self, parent, unit_system, has_parent=True):
        super().__init__(parent, GridPanel, 2, 3, label='Well spacing', bitmap=ico.well_spacing_16x16.GetBitmap(), has_parent=has_parent)

        self.AddCtrl(PropertyBitmapComboBox(self.ctrls, vm.DevelopmentLayout()))
        self.AddCtrl(PropertyTextCtrl(self.ctrls, vm.WellSpacing(unit_system)))

        self.Realize()

# ======================================================================================================================
# Panels with buttons
# ======================================================================================================================
class VoidagePanel(SectionPanel):
    def __init__(self, parent, unit_system):
        super().__init__(parent, 1, 2, 'Voidage replacement', ico.wag_voidage_replacement_16x16.GetBitmap())

        self._proportions = {}

        self.ratio = PropertyTextButtonCtrl(self, vm.VoidageProportion(unit_system))
        self.AddCtrl(self.ratio)

        self.Realize()

    def GetProportions(self):
        return self._proportions

    def Get(self):
        try:
            self.ErrorCheckInput(self.ratio)
        except ValueError:
            raise

        return self.ratio.GetProperty(), self._proportions

    def Set(self, ratio, proportions):
        self.ratio.SetProperty(ratio)
        self._proportions = proportions

# ======================================================================================================================
# Uncertainty panels
# ======================================================================================================================
class UncertainParameter:
    """
    Used as client data to the BitmapComboBox in UncertainParameterPanel to keep track of input
    """
    def __init__(self):
        self._value = None
        self._distribution = None
        self._parameters = [None, None, None]

    def Set(self, value, distribution, parameters):
        self._value = value
        self._distribution = distribution
        self._parameters = parameters

    def GetValue(self):
        return self._value

    def SetValue(self, value):
        self._value = value

    def GetUncertainty(self):
        return (self._distribution, *self._parameters)

    def SetUncertainty(self, distribution, parameters):
        self._distribution = distribution
        self._parameters = parameters

class UncertaintyPanel(HierarchicalPanel):
    def __init__(self, parent, has_parent=True):
        super().__init__(parent, DynamicPanel, vm.Distribution(), 3, 3, label='Uncertainty',
                         bitmap=ico.uncertainty_16x16.GetBitmap(), has_parent=has_parent)

        self.AddCtrl(PropertyTextCtrl(self.ctrls, vm.DistributionParameter1()))
        self.AddCtrl(PropertyTextCtrl(self.ctrls, vm.DistributionParameter2()))
        self.AddCtrl(PropertyTextCtrl(self.ctrls, vm.DistributionParameter3()))

class UncertainParameterPanel(wx.Panel):
    def __init__(self, parent, has_parent=True):
        super().__init__(parent)

        self.sizer = wx.BoxSizer(wx.VERTICAL)

        self._properties = None
        self._current_idx = None

        self.selection = wx.adv.BitmapComboBox(self, style=wx.CB_READONLY)
        self.value = HierarchicalPanel(self, GridPanel, 1, 3, label='Value', has_parent=has_parent)
        self.uncer = UncertaintyPanel(self, has_parent=has_parent)

        # used to control unit of value TextCtrl
        self.unit = None

        # sizing -------------------------------------------------------------------------------------------------------
        selection_sizer = AlignedFlexSizer([wx.StaticText(self, label='Parameter:')], [self.selection])
        self.sizer.Add(selection_sizer, 0, wx.EXPAND | (wx.ALL & ~wx.BOTTOM), GAP)
        self.sizer.Add(self.value, 0, wx.EXPAND | (wx.ALL & ~wx.BOTTOM), GAP)
        self.sizer.Add(self.uncer, 0, wx.EXPAND | (wx.ALL & ~wx.BOTTOM), GAP)

        # events -------------------------------------------------------------------------------------------------------
        self.Bind(wx.EVT_COMBOBOX, self.OnComboBox, self.selection)

    # events -----------------------------------------------------------------------------------------------------------
    def OnComboBox(self, event):
        self.SaveState()

        idx = event.GetInt()

        # update the fancytext unit
        unit = self._properties.GetUnit(idx)
        if unit is not None:
            unit, _ = Latex2HTML(unit)
            self.unit.SetBitmap(wx.NullBitmap)  # avoid duplication of bitmap

            if unit:  # avoid little black dot if unit is an empty string (unitless variables)
                self.unit.SetBitmap(fancy.RenderToBitmap(unit))

        self.LoadState(idx)

        self.uncer.ctrls.UpdateText(self.uncer.ctrls.GetSelection())

        self._current_idx = idx

        # enable/disable value
        self.value.EnableCtrls(self.value.UseSelf())

        # disable all uncertainties after having loaded, in-case it is unchecked hierarchically
        self.uncer.EnableCtrls(False)

        # enable based on existing selections
        if self.uncer.UseSelf():
            if self.uncer.ctrls.GetSelection() > -1:
                self.uncer.EnableAvailableCtrls(True)
            else:
                self.uncer.EnableCtrls(True, to=1)

    # external functions -----------------------------------------------------------------------------------------------
    def EnableCtrls(self, state):
        # remainder of ctrls are disabled by default if self.selection cannot be accessed.
        self.selection.Enable(state)

    def GetUncertainties(self):
        self.SaveState()

        uncertainties = []
        for i, _ in enumerate(self._properties.GetVariables()):
            data = self.selection.GetClientData(i)
            uncertainties.append(data.GetUncertainty())

        return (self.uncer.IsChecked(), *uncertainties)

    def GetValues(self):
        self.SaveState()

        values = []
        for i, _ in enumerate(self._properties.GetVariables()):
            data = self.selection.GetClientData(i)
            values.append(data.GetValue())

        return (self.value.IsChecked(), *values)

    def Initialise(self, collection):
        for property_ in collection.GetVariables():
            label = property_.GetComboLabel()
            bitmap = property_.GetBitmap()

            self.selection.Append(label, bitmap, UncertainParameter())

        w, h = self.value.ctrls.GetRequiredSize(collection.GetUnit())
        self.unit = fancy.StaticFancyText(self.value.ctrls, wx.ID_ANY, ' ', size=wx.Size(w, h))
        self.value.AddCtrl(PropertyTextCtrl(self.value.ctrls, vm.UncertainValue()), unit=self.unit)
        self._properties = collection

    def LoadState(self, idx):
        data = self.selection.GetClientData(idx)

        # load values
        self.value.Set(self.value.UseSelf(), data.GetValue())

        # load uncertainty
        self.uncer.Set(self.uncer.UseSelf(), *data.GetUncertainty())

    def Realize(self):
        self.value.Realize()
        self.uncer.Realize()
        self.SetSizer(self.sizer)
        self.sizer.Fit(self)

    def SaveState(self):
        idx = self._current_idx
        if idx is None:
            return

        data = self.selection.GetClientData(idx)

        # save values and uncertainty
        try:
            _, value = self.value.Get()
            uncer = self.uncer.Get()
        except ValueError:
            return

        data.SetValue(value)
        data.SetUncertainty(uncer[1], uncer[2:])  # 0 is use_self

    def SetUncertainties(self, use_self, *uncertainties):
        for i, uncer in enumerate(uncertainties):
            data = self.selection.GetClientData(i)
            data.SetUncertainty(*uncer)

        self.uncer.Set(use_self)
        self.uncer.EnableCtrls(False)

    def SetValues(self, use_self, *values):
        for i, value in enumerate(values):
            data = self.selection.GetClientData(i)
            data.SetValue(value)

        self.value.Set(use_self)
        self.value.EnableCtrls(False)

class ScalingPanel(UncertainParameterPanel):
    def __init__(self, parent, has_parent=True):
        super().__init__(parent, has_parent=has_parent)

        properties = vm.VariableCollection(vm.CumulativeScaler(),
                                           vm.RateScaler(),
                                           vm.FFWScaler(),
                                           vm.FFGScaler(),
                                           vm.OnsetScaler(),
                                           vm.InitialWCTScaler())

        self.Initialise(properties)
        self.Realize()

class StaticPanel(UncertainParameterPanel):
    def __init__(self, parent, unit_system, has_parent=True):
        super().__init__(parent, has_parent=has_parent)

        properties = vm.VariableCollection(vm.CompletedLength(unit_system),
                                           vm.HydrocarbonFeet(unit_system),
                                           vm.HydrocarbonPoreVolume(unit_system),
                                           vm.Permeability(unit_system),
                                           vm.OilDensity(unit_system))

        self.Initialise(properties)
        self.Realize()

# ======================================================================================================================
# Properties panel (generic for the most common entity frames)
# ======================================================================================================================
class PropertiesPanel(wx.Panel):
    def __init__(self, parent):
        super().__init__(parent)
        self.SetBackgroundColour(wx.WHITE)

        self.name = NamePanel(self)

        # sizing input -------------------------------------------------------------------------------------------------
        self.sizer = wx.BoxSizer(wx.VERTICAL)
        self.sizer.Add(self.name, 0, wx.EXPAND)
        self.SetSizer(self.sizer)

    def SaveName(self, entity):
        entity.SetName(self.name.Get())

    def Load(self, name):
        self.name.Set(name)

# ======================================================================================================================
# Analogue panels
# ======================================================================================================================
class ModelPanel(DynamicSectionPanel):
    def __init__(self, parent):
        super().__init__(parent, vm.PlaceholderMethod(), 4, 2, label='Model')

        self.AddCtrl(PropertyTextCtrl(self, vm.PlaceholderInput()))
        self.AddCtrl(PropertyTextCtrl(self, vm.PlaceholderParameter1()))
        self.AddCtrl(PropertyTextCtrl(self, vm.PlaceholderParameter2()))
        self.AddCtrl(PropertyTextCtrl(self, vm.PlaceholderParameter3()))

        self.Realize()

# ======================================================================================================================
# Typecurve panels
# ======================================================================================================================
class IncludeModelPanel(GridPanel):
    def __init__(self, parent):
        super().__init__(parent, 1, 2)

        self.check = PropertyCheckBox(self, vm.IncludeModel())  # used for access in events
        self.AddCtrl(self.check)

        self.Realize()

class MergePanel(DynamicSectionPanel):
    def __init__(self, parent):
        super().__init__(parent, vm.MergeType(), 2, 2, label='Merge', bitmap=ico.merge_16x16.GetBitmap())

        self.AddCtrl(PropertyTextCtrl(self, vm.MergePoint()))
        self.AddCtrl(PropertyTextCtrl(self, vm.MergeRate()))

        self.Realize()

class ModifierPanel(SectionPanel):
    def __init__(self, parent):
        super().__init__(parent, 2, 2, 'Modify', ico.modify_16x16.GetBitmap())

        self.AddCtrl(PropertyTextCtrl(self, vm.Multiplier()))
        self.AddCtrl(PropertyTextCtrl(self, vm.Addition()))

        self.Realize()

class RunPanel(SectionPanel):
    def __init__(self, parent):
        super().__init__(parent, 1, 8, label='Run', bitmap=ico.run_16x16.GetBitmap())

        self.point = PropertyBitmapComboBox(self, vm.RunFrom())
        self.axis = PropertyBitmapComboBox(self, vm.RunFromSpecific())
        self.value = PropertyTextCtrl(self, vm.RunFromValue())
        self.run_to = PropertyTextButtonCtrl(self, vm.RunTo())

        self.AddCtrl(self.point)
        self.AddCtrl(self.axis)
        self.AddCtrl(self.value)
        self.AddCtrl(self.run_to)

        self.Realize(col=7)

# ======================================================================================================================
# Scenario panels
# ======================================================================================================================
class DurationPanel(SectionPanel):
    def __init__(self, parent):
        super().__init__(parent, 2, 2, 'Duration', ico.duration_16x16.GetBitmap())

        self.AddCtrl(PropertyDatePickerCtrl(self, vm.StartDate()))
        self.AddCtrl(PropertyDatePickerCtrl(self, vm.EndDate()))

        self.Realize()

class EventPanel(DynamicSectionPanel):
    def __init__(self, parent):
        super().__init__(parent, vm.EventTrigger(), 2, 3, label='Event', bitmap=ico.event_16x16.GetBitmap())

        self.AddCtrl(PropertyDatePickerCtrl(self, vm.EventDate()))
        self.AddCtrl(PropertyTextCtrl(self, vm.OffsetYears()))

        self.Realize()

# ======================================================================================================================
# Simulation panels
# ======================================================================================================================
class PlateauPanel(SectionPanel):
    def __init__(self, parent, unit_system):
        super().__init__(parent, 2, 3, 'Plateaus', ico.plateau_chart_16x16.GetBitmap())

        self.AddCtrl(PropertyTextCtrl(self, vm.TargetOilPlateau(unit_system)))
        self.AddCtrl(PropertyTextCtrl(self, vm.TargetGasPlateau(unit_system)))

        self.Realize()

class TimelinePanel(DynamicSectionPanel):
    def __init__(self, parent):
        super().__init__(parent, vm.Frequency(), 2, 3, 'Timeline', ico.event_16x16.GetBitmap())

        self.AddCtrl(PropertyTextCtrl(self, vm.TimeDelta()))

        self.Realize()

class SamplingPanel(SectionPanel):
    def __init__(self, parent):
        super().__init__(parent, 2, 2, 'Sampling', ico.swanson_distribution_16x16.GetBitmap())

        self.AddCtrl(PropertyTextCtrl(self, vm.Samples()))
        self.AddCtrl(PropertyCheckBox(self, vm.SaveAllSamples()))

        self.Realize()

# ======================================================================================================================
# Scaling Panels
# ======================================================================================================================
class ScalerSelectionPanel(GridPanel):
    # designed to be sub-classed
    def __init__(self, parent):
        super().__init__(parent, 1, 2)

        self.selection = PropertyBitmapComboBox(self, vm.ScalerSelection())
        self.AddCtrl(self.selection)
        self.sizer.Add(self.ctrl_sizer, 1, wx.EXPAND | wx.ALL, GAP)

        self.Realize()

# ======================================================================================================================
# Re-sample Panels
# ======================================================================================================================
class ResamplePanel(HierarchicalPanel):
    def __init__(self, parent):
        super().__init__(parent, GridPanel, 4, 3, label='Re-sample', bitmap=ico.dates_16x16.GetBitmap())

        self.AddCtrl(PropertyDatePickerCtrl(self.ctrls, vm.StartDate()))
        self.AddCtrl(PropertyDatePickerCtrl(self.ctrls, vm.EndDate()))

        self.frequency = PropertyBitmapComboBox(self.ctrls, vm.Frequency())
        self.timestep = PropertyTextCtrl(self.ctrls, vm.TimeStep())
        self.AddCtrl(self.frequency)
        self.AddCtrl(self.timestep)

        self.ctrls.Enable(False)
        self.timestep.Enable(False)
        self.SetUseSelf(False)

        self.Realize()

        self.Bind(wx.EVT_COMBOBOX, self.OnComboBox, self.frequency)

    def OnChecked(self, event):
        """
        Overwritten usage of HierarchicalPanel. Workaround to enable ctrls without enabling self.timestep
        :param event: wx.EVT class
        :return:
        """
        self.ctrls.Enable(event.IsChecked())

    def OnComboBox(self, event):
        id_ = event.GetInt()

        if id_ == 3:
            self.timestep.Enable(True)
        else:
            self.timestep.Clear()
            self.timestep.Enable(False)

# ======================================================================================================================
# Variable Panels
# ======================================================================================================================
class LineOptionsPanel(SectionPanel):
    def __init__(self, parent):
        super().__init__(parent, 3, 2, 'Line Options', ico.event_16x16.GetBitmap())

        self.AddCtrl(PropertyBitmapComboBox(self, vm.VariableLinestyle()))
        self.AddCtrl(PropertyBitmapComboBox(self, vm.VariableDrawstyle()))
        self.AddCtrl(PropertyColourPickerCtrl(self, vm.VariableColour()))

        self.Realize()

class SummaryIconPanel(GridPanel):
    def __init__(self, parent):
        super().__init__(parent, 1, 2)

        self.sizer.Add(self.ctrl_sizer, 1, wx.EXPAND | wx.ALL, GAP)
        self.selection = PropertyBitmapComboBox(self, vm.SummaryIcon())
        self.AddCtrl(self.selection)

        self.Realize()

class SummaryConversionPanel(SectionPanel):
    def __init__(self, parent):
        super().__init__(parent, 4, 3, 'Conversion', bitmap=ico.specific_point_16x16.GetBitmap())

        self.function = PropertyBitmapComboBox(self, vm.SummaryFunction())
        self.point = PropertyBitmapComboBox(self, vm.SummaryPoint())

        self.AddCtrl(self.function)
        self.AddCtrl(self.point)
        self.AddCtrl(PropertyDatePickerCtrl(self, vm.SummaryPointDate()))
        self.AddCtrl(PropertyTextCtrl(self, vm.SummaryPointTime()))

        self.EnableCtrls(False, from_=1)

        self.Realize()

        # events -------------------------------------------------------------------------------------------------------
        self.Bind(wx.EVT_COMBOBOX, self.OnFunctionComboBox, self.function)
        self.Bind(wx.EVT_COMBOBOX, self.OnPointComboBox, self.point)

    # events -----------------------------------------------------------------------------------------------------------
    def OnFunctionComboBox(self, event, idx=None):
        if idx is None:
            if event is None:
                return

            idx = event.GetInt()

        if idx == 0:
            self.EnableCtrls(True, from_=1, to=2)

            point = self.point.GetSelection()
            if point != -1:
                self.OnPointComboBox(None, point)

        else:
            self.EnableCtrls(False, from_=1)

    def OnPointComboBox(self, event, idx=None):
        if idx is None:
            if event is None:
                return

            idx = event.GetInt()

        self.EnableCtrls(False, from_=2)

        if idx == 2:  # date
            self.EnableCtrls(True, from_=2, to=3)

        elif idx == 3:  # time
            self.EnableCtrls(True, from_=3)

# ======================================================================================================================
# Settings Panels
# ======================================================================================================================
class NormalSizeOptionsPanel(SectionPanel):
    def __init__(self, parent):
        super().__init__(parent, 5, 2, 'Normal mode', ico.window_16x16.GetBitmap())

        self.AddCtrl(PropertyBitmapComboBox(self, vm.SettingsLinewidth()))
        self.AddCtrl(PropertyBitmapComboBox(self, vm.SettingsMarkerSize()))
        self.AddCtrl(PropertyBitmapComboBox(self, vm.SettingsTickLabelSize()))
        self.AddCtrl(PropertyBitmapComboBox(self, vm.SettingsLabelSize()))
        self.AddCtrl(PropertyBitmapComboBox(self, vm.SettingsLegendSize()))

        self.Realize()

class PresentSizeOptionsPanel(SectionPanel):
    def __init__(self, parent):
        super().__init__(parent, 5, 2, 'Presentation mode', ico.window_16x16.GetBitmap())

        self.AddCtrl(PropertyBitmapComboBox(self, vm.SettingsLinewidth()))
        self.AddCtrl(PropertyBitmapComboBox(self, vm.SettingsMarkerSize()))
        self.AddCtrl(PropertyBitmapComboBox(self, vm.SettingsTickLabelSize()))
        self.AddCtrl(PropertyBitmapComboBox(self, vm.SettingsLabelSize()))
        self.AddCtrl(PropertyBitmapComboBox(self, vm.SettingsLegendSize()))

        self.Realize()

class UnitSystemPanel(SectionPanel):
    def __init__(self, parent):
        super().__init__(parent, 1, 2, 'Unit system', ico.settings_16x16.GetBitmap())

        self.AddCtrl(PropertyBitmapComboBox(self, vm.SettingsUnitSystem()))
        self.EnableCtrls(False)

        self.Realize()

class EnsembleCasePanel(SectionPanel):
    def __init__(self, parent):
        super().__init__(parent, 3, 2, 'Cases', ico.profiles_chart_16x16.GetBitmap())

        self.AddCtrl(PropertyBitmapComboBox(self, vm.SettingsLowCase()))
        self.AddCtrl(PropertyBitmapComboBox(self, vm.SettingsMidCase()))
        self.AddCtrl(PropertyBitmapComboBox(self, vm.SettingsHighCase()))

        self.Realize()

class EnsembleShadingPanel(SectionPanel):
    def __init__(self, parent):
        super().__init__(parent, 3, 2, 'Shading', ico.prediction_16x16.GetBitmap())

        self.AddCtrl(PropertyBitmapComboBox(self, vm.SettingsShadingResolution()))
        self.AddCtrl(PropertyBitmapComboBox(self, vm.SettingsShadingLow()))
        self.AddCtrl(PropertyBitmapComboBox(self, vm.SettingsShadingHigh()))

        self.Realize()

# ======================================================================================================================
# Duplicate Panels
# ======================================================================================================================
class DuplicatePanel(SectionPanel):
    def __init__(self, parent):
        super().__init__(parent, 2, 2, 'Duplicate', ico.swanson_distribution_16x16.GetBitmap())

        self.AddCtrl(PropertyTextCtrl(self, vm.Duplicates()))
        self.AddCtrl(PropertyCheckBox(self, vm.DuplicateAsControlled()))

        self.Realize()


import dateparser
import yaml
import subprocess
import tempfile
import os
import pandas as pd
import requests
import os.path as osp
import sys
import hashlib
import constants as C
from typing import Dict, Any, List

def read_config(fn: str) -> Dict[str, Any]:
    with open(fn, "r") as f:
        config = yaml.safe_load(f)

    return config

def get_dates(config: Dict[str, Any]) -> List[Dict[str, int]]:
    def formatter(s: str) -> Dict[str, int]:
        ps = dateparser.parse(s)
        return dict(year=ps.year, month=ps.month)

    return [formatter(d) for d in config["dates"]]

def get_sites(
    config: Dict[str, Any],
) -> List[str]:
    sheet_id = config["sheet_details"]["id"]
    gid = config["sheet_details"]["gid"]
    sheet_base = C.SheetRelated.sheet_url.format(sheet_id, gid)
    tmp = tempfile.NamedTemporaryFile(delete=False)
    _ = subprocess.run(["curl", "-L", sheet_base, "--output", tmp.name])
    df = pd.read_csv(tmp.name, header=0, index_col=0)
    sites = df["ID"].values.tolist()
    sites = [s for s in sites if s != ""]
    os.unlink(tmp.name)
    return sites

def send_message(config: Dict[str, Any], html_message: str):
    settings = config["email"]
    reqs = requests.post(
        f"https://api.mailgun.net/v3/{settings['domain']}/messages",
        auth=("api", settings["api"]),
        data={
            "from": "<{}>".format(settings["from"]),
            "to": settings["to"],
            "subject": "Campsite Availability",
            "bcc": settings["bcc"],
            "html": f"{html_message}",
        },
    )
    return reqs

def availability_dict_to_html(
    availability_dict: Dict[str, str], site_urls: None | Dict[str, str] = None
) -> str:
    html = ""
    for ground_name, ground_sites in availability_dict.items():
        if len(ground_sites) < 1:
            continue

        html += "
Campground: {}
".format(ground_name)
        html += "
"
        for site_name, site_dates in ground_sites.items():
            if site_urls is not None:
                html += "
Campsite: {}
".format(
                    site_urls[site_name], site_name
                )
            else:
                html += "
Campsite: {}
".format(site_urls[site_name], site_name)
            html += "
"
            for date in site_dates:
                html += "
{}
".format(date)
            html += "
"
        html += "
"
    return html

def check_html(config: Dict[str, Any], new_html: str) -> bool:
    def hasher(string: str) -> str:
        return hashlib.md5(string.encode()).hexdigest()

    html_dir = config["directories"]["html"]
    old_fn = osp.join(html_dir, "latest_hash.dat")

    new_hash = hasher(new_html)

    if not osp.isdir(html_dir):
        print("[ERROR] : {} does not exists".format(html_dir))
        sys.exit(-1)
    if osp.exists(old_fn):
        with open(old_fn, "r+") as of:
            old_hash = of.readlines()[0]
    else:
        with open(old_fn, "w+") as of:
            of.writelines(new_hash)
        return True

    if new_hash == old_hash:
        return False
    else:
        with open(old_fn, "w+") as of:
            of.writelines(new_hash)
        return True


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

from webserver import db
from webserver.models import Menu
from webserver.tests import build_menu
from webserver.tests import delete_menus
from webserver.tests.functional import FunctionalTest

class Exists(FunctionalTest):
    """ Check if the webservice exists """

    @classmethod
    def setup_class(cls):
        """ Add database fixtures """

        build_menu(id=5)
        db.session.commit()

    @classmethod
    def teardown_class(cls):
        """ Clear database fixtures """

        delete_menus()
        db.session.commit()

    def test_exists(self):
        """ PUT /menus/id: exists """

        # Check request
        response = self.put('/menus/5')
        assert response.status_code != 404
        assert response.status_code != 500

class UnknownParameters(FunctionalTest):
    """ Check with no datas """

    @classmethod
    def setup_class(cls):
        """ Add database fixtures """

        build_menu(id=15)
        db.session.commit()

    @classmethod
    def teardown_class(cls):
        """ Clear database fixtures """

        delete_menus()
        db.session.commit()

    def test_unkown_id(self):
        """ PUT /menus/id: with unkown id """

        # Check request
        response = self.put('/menus/5')
        assert response.status_code == 404
        assert response.data == "Le menu n'existe pas."

class InvalidParameters(FunctionalTest):
    """ Check with no datas """

    @classmethod
    def setup_class(cls):
        """ Add database fixtures """

        build_menu(id=5)
        db.session.commit()

    @classmethod
    def teardown_class(cls):
        """ Clear database fixtures """

        delete_menus()
        db.session.commit()

    def test_invalid_name(self):
        """ PUT /menus/id: with invalid name """

        # Prepare data
        data = dict()
        data['name'] = 1111

        # Check request
        response = self.put('/menus/5', data=data)
        assert response.status_code == 400
        assert response.data == "Le nom du menu doit être une chaine de caractère."

class Update(FunctionalTest):
    """ Check with valid data """

    @classmethod
    def setup_class(cls):
        """ Add database fixtures """

        build_menu(id=5)
        db.session.commit()

    @classmethod
    def teardown_class(cls):
        """ Clear database fixtures """

        delete_menus()
        db.session.commit()

    def test_update(self):
        """ PUT /menus/id: with valid data """

        # Prepare data
        data = dict()
        data['name'] = "Menu des 4 saisons"

        # Check request
        response = self.put('/menus/5', data=data)
        assert response.status_code == 200

        # Check received data
        result = self.parse(response.data)
        assert 'id' in result

        # Check in database
        menu = db.session.query(Menu).get(result['id'])
        assert menu.name == "Menu des 4 saisons"


from util import get_soup, lst2txt

soup = get_soup('https://www.op.gg/champions')
all_champions = [n.text for n in soup.find_all('a', {'class': 'css-mtyeel'})]
lst2txt('data/all.txt', all_champions)

roles = ['top', 'jungle', 'mid', 'adc', 'support']
for role in roles:
    soup = get_soup(f'https://www.op.gg/champions?position={role}')

    champions = [n.text for n in soup.find_all('td', {'class': 'css-cym2o0'})]
    lst2txt(f'data/{role}_name.txt', champions)

    champions = [n.find('a').get('href').split('/')[2] for n in soup.find_all('td', {'class': 'css-cym2o0'})]
    lst2txt(f'data/{role}_url.txt', champions)


import pytest

from datahub.company.models import Contact
from datahub.company.test.factories import ContactFactory
from datahub.search.contact.models import Contact as SearchContact

pytestmark = pytest.mark.django_db

def test_contact_dbmodel_to_dict(opensearch):
    """Tests conversion of db model to dict."""
    contact = ContactFactory()

    result = SearchContact.db_object_to_dict(contact)

    keys = {
        '_document_type',
        'id',
        'title',
        'company',
        'created_on',
        'created_by',
        'modified_on',
        'archived',
        'archived_on',
        'archived_reason',
        'archived_by',
        'first_name',
        'last_name',
        'name',
        'name_with_title',
        'job_title',
        'adviser',
        'primary',
        'full_telephone_number',
        'email',
        'address_same_as_company',
        'address_1',
        'address_2',
        'address_area',
        'address_town',
        'address_county',
        'address_country',
        'address_postcode',
        'notes',
        'company_sector',
        'company_uk_region',
        'valid_email',
    }

    assert set(result.keys()) == keys

def test_contact_dbmodels_to_documents(opensearch):
    """Tests conversion of db models to OpenSearch documents."""
    contacts = ContactFactory.create_batch(2)

    result = SearchContact.db_objects_to_documents(contacts)

    assert len(list(result)) == len(contacts)

def test_contact_dbmodels_to_document_without_country(opensearch):
    """
    Tests conversion of db models to OpenSearch documents when
    country is None.
    """
    # We want to bypass any validation
    contact = Contact(
        address_same_as_company=False,
        address_country=None,
    )
    result = SearchContact.to_document(contact)

    assert '_source' in result


from Bot.settings import HEADERS
from bs4 import BeautifulSoup
import requests

def get_url(zodiac, type, time):
    """
    The function allows to get the URL with a horoscope based on user's request
    """
    zodiac_signs_rambler = {'Овен': 'aries', 'Телец': 'taurus', 'Близнецы': 'gemini',
                            'Рак': 'cancer', 'Лев': 'leo', 'Дева': 'virgo',
                            'Весы': 'libra', 'Скорпион': 'scorpio', 'Стрелец': 'sagittarius',
                            'Козерог': 'capricorn', 'Водолей': 'aquarius', 'Рыбы': 'pisces'}
    time_horoscope_rambler = {'Вчера': '/yesterday', 'Сегодня': '', 'Завтра': '/tomorrow',
                              'Неделя': '/weekly', 'Месяц': '/monthly'}
    types_horoscope_rumbler = {'Общий': '', 'Любовный': '/erotic', 'Финансы': '/career'}
    url = 'https://horoscopes.rambler.ru/' + str(zodiac_signs_rambler[zodiac]) + str(
        types_horoscope_rumbler[type]) + str(time_horoscope_rambler[time])
    return url

def get_html(url, params=None):
    """
    The function allows to get the URL's data
    """
    r = requests.get(url, headers=HEADERS, params=params)
    return r

def get_content(html):
    """
    The function collects a horoscope for user
    """
    soup = BeautifulSoup(html, 'html.parser')
    items = soup.find_all('div', class_='_1dQ3')

    target_horoscope = []
    for item in items:
        target_horoscope.append(item.find('span').get_text())
    return target_horoscope[0]

def parse(url):
    """
    The function returns the result of "get_html" and "get_content" functions
    """
    html = get_html(url)
    if html.status_code == 200:
        return get_content(html.text)
    else:
        return 'Error'

if __name__ == "__main__":
    parse(get_url('Рыбы', 'Финансы', 'Сегодня'))


#Ismael Diaz (PSID: 1846093)
#CIS 2348 Final Project Pt. 1
from datetime import date, datetime
import csv

inventory = []
today = date.today()

# populating the master inventory
inputFile = open('ManufacturerList.csv')
reader = csv.reader(inputFile)

for items in reader:
    i = 0
    inventory.insert(i, [items[0], items[1], items[2]])
    i += 1

inputFile.close()

# importing prices and appending to inventory
inputFile = open('PriceList.csv')
reader = csv.reader(inputFile)

for prices in reader:
    for list in inventory:
        i = 0
        if prices[0] == list[0]:
            list.append(prices[1])
        i += 1

inputFile.close()

# importing service dates and appending to inventory
inputFile = open('ServiceDatesList.csv')
reader = csv.reader(inputFile)

for dates in reader:
    for list in inventory:
        i = 0
        if dates[0] == list[0]:
            list.append(dates[1])
        i += 1

inputFile.close()

# appending damaged attribute to the end of list for correct csv writing order
inputFile = open('ManufacturerList.csv')
reader = csv.reader(inputFile)

for damaged in reader:
    for list in inventory:
        if damaged[0] == list[0]:
            list.append(damaged[3])

inputFile.close()

# sorting and writing to fullInventory.csv
outputFile = open('FullInventory.csv', 'w', newline='')
writer = csv.writer(outputFile)

alphaInventory = sorted(inventory, key=lambda x: x[1])
writer.writerows(alphaInventory)

outputFile.close()

# sorting and writing item type inventory list to [itemtype]Inventory.csv
sortedInventory = sorted(inventory, key=lambda x: x[0])
itemInventory = []
itemTypes = []

for index in range(len(sortedInventory)):
    itemInventory.append([sortedInventory[index][0], sortedInventory[index][1], sortedInventory[index][3], sortedInventory[index][4], sortedInventory[index][5]])

i = 0
for list in sortedInventory:
    itemTypes.append(list[2])
    filename = itemTypes[i] + 'Inventory.csv'
    outputFile = open(filename, 'w', newline='')
    writer = csv.writer(outputFile)

    for item in sortedInventory:
        if list[2] == item[2]:
            for a in range(len(sortedInventory)):
                if item[0] == itemInventory[a][0]:
                    writer.writerow(itemInventory[a])

    outputFile.close()
    i += 1

# sorting dates and writing to PastServiceDateInventory.csv
outputFile = open("PastServiceDateInventory.csv", 'w', newline='')
writer = csv.writer(outputFile)
sortedInventory = []

i = 0
for list in inventory:
    dateobject = datetime.strptime(inventory[i][4], "%m/%d/%Y").date()
    sortedInventory.append([inventory[i][0], inventory[i][1], inventory[i][2], inventory[i][3], dateobject, inventory[i][5]])
    i += 1

sortedInventory = sorted(sortedInventory, key=lambda x: x[4])

i = 0
for list in sortedInventory:
    if list[4] < today:
        date = list[4]
        writer.writerow([inventory[i][0], inventory[i][1], inventory[i][2], inventory[i][3], date.strftime("%m/%d/%Y"), inventory[i][5]])

    i += 1
outputFile.close()

# sorting prices and writing to DamagedInventory.csv
outputFile = open('DamagedInventory.csv', 'w', newline='')
writer = csv.writer(outputFile)
sortedInventory = []

for list in inventory:
    sortedInventory.append([list[0], list[1], list[2], int(list[3]), list[4], list[5]])

sortedInventory = sorted(sortedInventory, key=lambda x: x[3], reverse=True)

for list in sortedInventory:
    if list[5] == '':
        writer.writerow([list[0], list[1], list[2], list[3], list[4]])

outputFile.close()

Final Project Part 1/Final Project Part 1.py


import matplotlib.pyplot as plt
from utils.data import readDistanceMatrix
from utils.readFile import readTSPLib
import pandas as pd
import plotly.express as px
import numpy as np
import plotly
from utils.solution import findRadiusPoint

# solutionList = [[0, 3, 7, 15, 23, 25, 33, 39, 53, 59, 69, 95, 104, 110, 115], [1, 2, 8, 10, 20, 22, 26, 50, 52, 79, 81, 92, 102, 114, 118], [16, 19, 42, 43, 49, 64, 68, 74, 97, 106, 117], [4, 36, 38, 56, 62, 66, 72, 82], [12], [6, 37, 40, 55, 71, 73], [11, 63, 76, 87, 94, 96, 108], [13, 14, 27, 28, 29, 31, 44, 58, 60, 75, 77, 80, 85, 86, 91, 93, 119], [5, 9, 34, 35, 46, 47, 54, 61, 70, 83, 88, 89, 98, 100, 101, 103, 105, 109, 111, 113], [17, 18, 21, 24, 30, 32, 41, 45, 48, 51, 57, 65, 67, 78, 84, 90, 99, 107, 112, 116]]
# distanceMatrix = readDistanceMatrix("gr120", "distanceMatrices")

def draw(x, y, solution, depot):
    X = []
    Y = []
    solutionList = solution.solutionList
    coorDepot = x[depot - 1], y[depot - 1]
    for cluster in solutionList:
        X.append([x[point] for point in cluster])
        Y.append([y[point] for point in cluster])

    labels = range(1, len(X)+1)

    fig = plt.figure()
    ax = fig.add_subplot(111)
    for x, y, lab in zip(X, Y, labels):
        ax.scatter(x, y, label=lab)

    kwargs = dict(label="Depot", zorder=3, marker="*", s=100)
    ax.scatter(*coorDepot ,c="tab:red", **kwargs)
    ax.set_title(f" Total distance: {round(solution.costFuction)}")

    colormap = plt.cm.gist_ncar  # nipy_spectral, Set1,Paired
    colorst = [colormap(i) for i in np.linspace(0, 0.9, len(ax.collections))]
    for t, j1 in enumerate(ax.collections):
        j1.set_color(colorst[t])

    ax.legend(fontsize='x-small')
    plt.show()

def drawInteractive(x, y, solutionList, distanceMatrix):
    # data = readTSPLib('gr120.tsp')
    # lat = []
    # lon = []
    # for point in data:
    #     lat.append(data[point].coordinate["lat"])
    #     lon.append(data[point].coordinate["lon"])
    # X = [ ]
    # Y = [ ]
    infoPoint = []
    for idxCluster in range(len(solutionList)):
        # for idxCluster in range(1):
        for point in solutionList[idxCluster]:
            infoPoint.append([x[point], y[point], idxCluster])
    # print(infoPoint)
    df = pd.DataFrame(infoPoint, columns=['x', 'y', "index_cluster"])
    df["index_cluster"] = df["index_cluster"].astype(str)

    fig = px.scatter(df, x="x", y="y",
                     color="index_cluster")

    for idxCluster in range(len(solutionList)):
        print(solutionList[idxCluster])
        PairOfPoints = findRadiusPoint(
            solutionList[idxCluster], distanceMatrix)
        print(PairOfPoints)
        if len(PairOfPoints) == 2:
            firstPoint = PairOfPoints[0]
            secondPoint = PairOfPoints[1]
            name = "Radius cluster: " + str(idxCluster)
            fig.add_scatter(x=[x[firstPoint], x[secondPoint]], y=[
                            y[firstPoint], y[secondPoint]], name=name)

    fig.update_traces(marker=dict(size=20))
    plotly.offline.plot(
        fig, filename='D:\Hoc\\thuc tap\cluster\image\\test.html')
    # fig.show()

# draw(solutionList)


from math import ceil
def solve():
    l = list(map(int, input().split()))
    n = len(l)
    s = {str(l): 0}
    c = 1
    while 1:
        m = max(l)
        i = next(i for i, v in enumerate(l) if v == m)
        add = ceil(m/n)
        l[i] = 0
        for j in range(1, n+1):
            l[(i+j)%n] += min(m, add)
            m -= min(m, add)
        k = str(l)
        if k in s: return (c, c - s[k])
        else: s[k] = c
        c+=1

pt1, pt2 = solve()
print('pt1', pt1)
print('pt2', pt2)