<commit_before>#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
#  IkaLog
#  ======
#  Copyright (C) 2015 ExceptionError
#  Copyright (C) 2015 Takeshi HASEGAWA
#
#  Licensed under the Apache License, Version 2.0 (the "License");
#  you may not use this file except in compliance with the License.
#  You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
#  Unless required by applicable law or agreed to in writing, software
#  distributed under the License is distributed on an "AS IS" BASIS,
#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#  See the License for the specific language governing permissions and
#  limitations under the License.
#

import random
import socket
import sys
import traceback

from ikalog.constants import *
from ikalog.utils import *

# Needed in GUI mode
try:
    import wx
except:
    pass

class BoyomiClient(object):
    ''' 棒読みちゃんにコマンドを送信するクラスです
    http://chi.usamimi.info/Program/Application/BouyomiChan/
    '''

    def __init__(self, host='127.0.0.1', port=50001):
        self.host = host
        self.port = port

    def _connect(self):
        self._socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self._socket.connect((self.host, self.port))

    def _close(self):
        self._socket.close()

    def _send(self, data):
        self._socket.send(data)

    def _command(self, command):
        data = bytearray()
        data.append((command >> 0) & 0xFF)
        data.append((command >> 8) & 0xFF)
        self._connect()
        self._send(data)
        self._close()

    def pause(self):
        self._command(16)

    def resume(self):
        self._command(32)

    def skip(self):
        self._command(48)

    def clear(self):
        self._command(64)

    def read(self, message):
        self.talk(-1, -1, -1, 0, message)

    def talk(self, speed, tone, volume, voice, message):
        data = bytearray()
        # command
        data.append(1)
        data.append(0)
        # speed
        data.append((speed >> 0) & 0xFF)
        data.append((speed >> 8) & 0xFF)
        # tone
        data.append((tone >> 0) & 0xFF)
        data.append((tone >> 8) & 0xFF)
        # volume
        data.append((volume >> 0) & 0xFF)
        data.append((volume >> 8) & 0xFF)
        # voice
        data.append((voice >> 0) & 0xFF)
        data.append((voice >> 8) & 0xFF)
        # encode
        data.append(0)
        # length
        message_data = message.encode('utf-8')
        length = len(message_data)
        data.append((length >> 0) & 0xFF)
        data.append((length >> 8) & 0xFF)
        data.append((length >> 16) & 0xFF)
        data.append((length >> 24) & 0xFF)
        self._connect()
        self._send(data)
        self._send(message_data)
        self._close()

class BoyomiDictionary(object):
    _config = {
        'lobby_matching': ['しばらく待ちましょう'],
        'lobby_matched': ['メンバーが揃いました'],
        'start': ['{map}で{rule}が始まります'],
        'go_sign': ['ゲームスタート！'],
        'killed': ['倒した！', 'やったぁ'],
        'dead': ['あああああああ', 'ぎゃああああ', 'うわああああ'],
        'death_reason_identified': ['{reason}で倒された'],
        'finish': ['ゲーム終了！'],
        'individual_result_win': ['勝った！', 'やったぜ'],
        'individual_result_lose': ['負けた！', 'くやしい'],
        'individual_result_unknown': ['よくわからない', 'どういうことだ？'],
        'individual_kill_death': ['{kill}キル{death}デス'],
        'session_end': ['おつかれさまでした'],
    }

    def __init__(self, config):
        for key in self._config.keys():
            if key in config:
                self._config[key] = config[key]

    def text(self, key):
        return random.choice(self._config.get(key, ['']))

    def get_config(self):
        return self._config

class Boyomi(object):
    '''
    棒読みクライアント
    '''
    custom_read = {
        'initialize': '棒読みちゃん読み上げテストです',
        'nzap85': 'エヌザップ85',
        'nzap89': 'エヌザップ89',
        'bamboo14mk1': '14式竹筒銃・こう',
        'liter3k': 'リッター3ケー',
        'liter3k_custom': 'リッター3ケーカスタム',
        'liter3k_scope': '3ケースコープ',
        'liter3k_scope_custom': '3ケースコープカスタム',
        'squiclean_a': 'スクイックリンアルファ',
        'squiclean_b': 'スクイックリンベータ',
        'rapid_elite': 'ラピッドブラスターエリート',
        'unknown': '未知の武器',
    }

    def __init__(self, host='127.0.0.1', port=50001, dictionary={}):
        self._enabled = True

        self._dict = BoyomiDictionary(dictionary)
        self._client = BoyomiClient(host, port)
        self._read(self.custom_read['initialize'])

    def config_key(self):
        return 'boyomi'

    def set_config(self, config):
        dictionary = config.get(self.config_key(), {})
        self._dict = BoyomiDictionary(dictionary)

    def get_config(self, config):
        boyomi = self._dict.get_config()
        config[self.config_key()] = boyomi
        return config

    def _read(self, message):
        if (self._client is None) or (not self._enabled):
            return

        try:
            self._client.read(message)
        except ConnectionRefusedError:
            error = '「{message}」を読み上げることができませんでした'.format(message=message)
            IkaUtils.dprint(error)

    def _text(self, key):
        return self._dict.text(key)

    def _read_text(self, key):
        self._read(self._text(key))

    def on_lobby_matching(self, context):
        self._read_text('lobby_matching')

    def on_lobby_matched(self, context):
        self._read_text('lobby_matched')

    def on_game_start(self, context):
        map_text = IkaUtils.map2text(context['game']['map'], unknown='スプラトゥーン')
        rule_text = IkaUtils.rule2text(context['game']['rule'], unknown='ゲーム')
        self._read(
            self._text('start').format(map=map_text, rule=rule_text)
        )

    def on_game_go_sign(self, context):
        self._read_text('go_sign')

    def on_game_killed(self, context):
        self._read_text('killed')

    def on_game_dead(self, context):
        self._read_text('dead')

    def on_game_death_reason_identified(self, context):
        reason = context['game']['last_death_reason']
        label = self._death_reason_label(reason)
        self._client.read(
            self._text('death_reason_identified').format(reason=label)
        )

    def _death_reason_label(self, reason):
        if reason in self.custom_read:
            return self.custom_read[reason]
        if reason in weapons:
            return weapons[reason]['ja']
        if reason in sub_weapons:
            return sub_weapons[reason]['ja']
        if reason in special_weapons:
            return special_weapons[reason]['ja']
        return self.custom_read['unknown']

    def on_game_finish(self, context):
        self._read_text('finish')

    def on_game_individual_result(self, context):
        map = IkaUtils.map2text(context['game']['map'])
        rule = IkaUtils.rule2text(context['game']['rule'])
        won = IkaUtils.getWinLoseText(
            context['game']['won'],
            win_text=self._text('individual_result_win'),
            lose_text=self._text('individual_result_lose'),
            unknown_text=self._text('individual_result_unknown')
        )
        self._read(won)
        me = IkaUtils.getMyEntryFromContext(context)
        kill = me['kills']
        death = me['deaths']
        self._read(
            self._text('individual_kill_death').format(kill=kill, death=death)
        )

    def on_game_session_end(self, context):
        self._read_text('session_end')

    def initialize_client(self):
        try:
            self._client = BoyomiClient(self._host, int(self._port))
        except:
            IkaUtils.dprint('棒読みちゃんの設定が反映できませんでした。設定値を見直してください。')
            self.dprint(traceback.format_exc())
            self._client = None
            return False
        return True

    def apply_ui(self):
        self._enabled = self.check_enable.GetValue()
        self._host = self.edit_host.GetValue()
        self._port = self.edit_port.GetValue()
        self.initialize_client()

    def refresh_ui(self):
        self.check_enable.SetValue(self._enabled)

        if not self._host is None:
            self.edit_host.SetValue(self._host)
        else:
            self.edit_host.SetValue('')

        if not self._port is None:
            self.edit_port.SetValue(str(self._port))
        else:
            self.edit_port.SetValue('50001')

        self._internal_update = False

    def on_config_reset(self, context=None):
        self._enabled = False
        self._host = '127.0.0.1'
        self._port = '50001'

    def on_config_load_from_context(self, context):
        self.on_config_reset(context)

        try:
            conf = context['config']['boyomi']
        except:
            conf = {}

        if 'Enable' in conf:
            self._enabled = conf['Enable']

        if 'host' in conf:
            self._host = conf['host']

        if 'port' in conf:
            try:
                self._port = int(conf['port'])
            except ValueError:
                IkaUtils.dprint('%s: port must be an integer' % self)
                self._port = 50001

        self.refresh_ui()
        self.initialize_client()
        return True

    def on_config_save_to_context(self, context):
        context['config']['boyomi'] = {
            'Enable': self._enabled,
            'host': self._host,
            'port': self._port,
        }

    def on_config_apply(self, context):
        self.apply_ui()

    def on_test_button_click(self, event):
        self._read(self.custom_read['initialize'])

    def on_option_tab_create(self, notebook):
        self.panel = wx.Panel(notebook, wx.ID_ANY)
        self.page = notebook.InsertPage(0, self.panel, '棒読みちゃん')
        self.layout = wx.BoxSizer(wx.VERTICAL)

        self.check_enable = wx.CheckBox(
            self.panel, wx.ID_ANY, '棒読みちゃんで実況する')
        self.edit_host = wx.TextCtrl(self.panel, wx.ID_ANY, 'host')
        self.edit_port = wx.TextCtrl(self.panel, wx.ID_ANY, 'port')
        self.button_test = wx.Button( self.panel, wx.ID_ANY, 'テスト')

        try:
            layout = wx.GridSizer(2, 4)
        except:
            layout = wx.GridSizer(2)

        layout.Add(wx.StaticText(self.panel, wx.ID_ANY, u'ホスト'))
        layout.Add(self.edit_host)
        layout.Add(wx.StaticText(self.panel, wx.ID_ANY, u'ポート'))
        layout.Add(self.edit_port)

        self.layout.Add(self.check_enable)
        self.layout.Add(layout, flag=wx.EXPAND)
        self.layout.Add(self.button_test)

        self.panel.SetSizer(self.layout)

        self.button_test.Bind(wx.EVT_BUTTON, self.on_test_button_click)
<commit_msg>Fixed a trying to always Speech when it is dead<commit_after>#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
#  IkaLog
#  ======
#  Copyright (C) 2015 ExceptionError
#  Copyright (C) 2015 Takeshi HASEGAWA
#
#  Licensed under the Apache License, Version 2.0 (the "License");
#  you may not use this file except in compliance with the License.
#  You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
#  Unless required by applicable law or agreed to in writing, software
#  distributed under the License is distributed on an "AS IS" BASIS,
#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#  See the License for the specific language governing permissions and
#  limitations under the License.
#

import random
import socket
import sys
import traceback

from ikalog.constants import *
from ikalog.utils import *

# Needed in GUI mode
try:
    import wx
except:
    pass

class BoyomiClient(object):
    ''' 棒読みちゃんにコマンドを送信するクラスです
    http://chi.usamimi.info/Program/Application/BouyomiChan/
    '''

    def __init__(self, host='127.0.0.1', port=50001):
        self.host = host
        self.port = port

    def _connect(self):
        self._socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self._socket.connect((self.host, self.port))

    def _close(self):
        self._socket.close()

    def _send(self, data):
        self._socket.send(data)

    def _command(self, command):
        data = bytearray()
        data.append((command >> 0) & 0xFF)
        data.append((command >> 8) & 0xFF)
        self._connect()
        self._send(data)
        self._close()

    def pause(self):
        self._command(16)

    def resume(self):
        self._command(32)

    def skip(self):
        self._command(48)

    def clear(self):
        self._command(64)

    def read(self, message):
        self.talk(-1, -1, -1, 0, message)

    def talk(self, speed, tone, volume, voice, message):
        data = bytearray()
        # command
        data.append(1)
        data.append(0)
        # speed
        data.append((speed >> 0) & 0xFF)
        data.append((speed >> 8) & 0xFF)
        # tone
        data.append((tone >> 0) & 0xFF)
        data.append((tone >> 8) & 0xFF)
        # volume
        data.append((volume >> 0) & 0xFF)
        data.append((volume >> 8) & 0xFF)
        # voice
        data.append((voice >> 0) & 0xFF)
        data.append((voice >> 8) & 0xFF)
        # encode
        data.append(0)
        # length
        message_data = message.encode('utf-8')
        length = len(message_data)
        data.append((length >> 0) & 0xFF)
        data.append((length >> 8) & 0xFF)
        data.append((length >> 16) & 0xFF)
        data.append((length >> 24) & 0xFF)
        self._connect()
        self._send(data)
        self._send(message_data)
        self._close()

class BoyomiDictionary(object):
    _config = {
        'lobby_matching': ['しばらく待ちましょう'],
        'lobby_matched': ['メンバーが揃いました'],
        'start': ['{map}で{rule}が始まります'],
        'go_sign': ['ゲームスタート！'],
        'killed': ['倒した！', 'やったぁ'],
        'dead': ['あああああああ', 'ぎゃああああ', 'うわああああ'],
        'death_reason_identified': ['{reason}で倒された'],
        'finish': ['ゲーム終了！'],
        'individual_result_win': ['勝った！', 'やったぜ'],
        'individual_result_lose': ['負けた！', 'くやしい'],
        'individual_result_unknown': ['よくわからない', 'どういうことだ？'],
        'individual_kill_death': ['{kill}キル{death}デス'],
        'session_end': ['おつかれさまでした'],
    }

    def __init__(self, config):
        for key in self._config.keys():
            if key in config:
                self._config[key] = config[key]

    def text(self, key):
        return random.choice(self._config.get(key, ['']))

    def get_config(self):
        return self._config

class Boyomi(object):
    '''
    棒読みクライアント
    '''
    custom_read = {
        'initialize': '棒読みちゃん読み上げテストです',
        'nzap85': 'エヌザップ85',
        'nzap89': 'エヌザップ89',
        'bamboo14mk1': '14式竹筒銃・こう',
        'liter3k': 'リッター3ケー',
        'liter3k_custom': 'リッター3ケーカスタム',
        'liter3k_scope': '3ケースコープ',
        'liter3k_scope_custom': '3ケースコープカスタム',
        'squiclean_a': 'スクイックリンアルファ',
        'squiclean_b': 'スクイックリンベータ',
        'rapid_elite': 'ラピッドブラスターエリート',
        'unknown': '未知の武器',
    }

    def __init__(self, host='127.0.0.1', port=50001, dictionary={}):
        self._enabled = True

        self._dict = BoyomiDictionary(dictionary)
        self._client = BoyomiClient(host, port)
        self._read(self.custom_read['initialize'])

    def config_key(self):
        return 'boyomi'

    def set_config(self, config):
        dictionary = config.get(self.config_key(), {})
        self._dict = BoyomiDictionary(dictionary)

    def get_config(self, config):
        boyomi = self._dict.get_config()
        config[self.config_key()] = boyomi
        return config

    def _read(self, message):
        if (self._client is None) or (not self._enabled):
            return

        try:
            self._client.read(message)
        except ConnectionRefusedError:
            error = '「{message}」を読み上げることができませんでした'.format(message=message)
            IkaUtils.dprint(error)

    def _text(self, key):
        return self._dict.text(key)

    def _read_text(self, key):
        self._read(self._text(key))

    def on_lobby_matching(self, context):
        self._read_text('lobby_matching')

    def on_lobby_matched(self, context):
        self._read_text('lobby_matched')

    def on_game_start(self, context):
        map_text = IkaUtils.map2text(context['game']['map'], unknown='スプラトゥーン')
        rule_text = IkaUtils.rule2text(context['game']['rule'], unknown='ゲーム')
        self._read(
            self._text('start').format(map=map_text, rule=rule_text)
        )

    def on_game_go_sign(self, context):
        self._read_text('go_sign')

    def on_game_killed(self, context):
        self._read_text('killed')

    def on_game_dead(self, context):
        self._read_text('dead')

    def on_game_death_reason_identified(self, context):
        reason = context['game']['last_death_reason']
        label = self._death_reason_label(reason)
        self._read(
            self._text('death_reason_identified').format(reason=label)
        )

    def _death_reason_label(self, reason):
        if reason in self.custom_read:
            return self.custom_read[reason]
        if reason in weapons:
            return weapons[reason]['ja']
        if reason in sub_weapons:
            return sub_weapons[reason]['ja']
        if reason in special_weapons:
            return special_weapons[reason]['ja']
        return self.custom_read['unknown']

    def on_game_finish(self, context):
        self._read_text('finish')

    def on_game_individual_result(self, context):
        map = IkaUtils.map2text(context['game']['map'])
        rule = IkaUtils.rule2text(context['game']['rule'])
        won = IkaUtils.getWinLoseText(
            context['game']['won'],
            win_text=self._text('individual_result_win'),
            lose_text=self._text('individual_result_lose'),
            unknown_text=self._text('individual_result_unknown')
        )
        self._read(won)
        me = IkaUtils.getMyEntryFromContext(context)
        kill = me['kills']
        death = me['deaths']
        self._read(
            self._text('individual_kill_death').format(kill=kill, death=death)
        )

    def on_game_session_end(self, context):
        self._read_text('session_end')

    def initialize_client(self):
        try:
            self._client = BoyomiClient(self._host, int(self._port))
        except:
            IkaUtils.dprint('棒読みちゃんの設定が反映できませんでした。設定値を見直してください。')
            self.dprint(traceback.format_exc())
            self._client = None
            return False
        return True

    def apply_ui(self):
        self._enabled = self.check_enable.GetValue()
        self._host = self.edit_host.GetValue()
        self._port = self.edit_port.GetValue()
        self.initialize_client()

    def refresh_ui(self):
        self.check_enable.SetValue(self._enabled)

        if not self._host is None:
            self.edit_host.SetValue(self._host)
        else:
            self.edit_host.SetValue('')

        if not self._port is None:
            self.edit_port.SetValue(str(self._port))
        else:
            self.edit_port.SetValue('50001')

        self._internal_update = False

    def on_config_reset(self, context=None):
        self._enabled = False
        self._host = '127.0.0.1'
        self._port = '50001'

    def on_config_load_from_context(self, context):
        self.on_config_reset(context)

        try:
            conf = context['config']['boyomi']
        except:
            conf = {}

        if 'Enable' in conf:
            self._enabled = conf['Enable']

        if 'host' in conf:
            self._host = conf['host']

        if 'port' in conf:
            try:
                self._port = int(conf['port'])
            except ValueError:
                IkaUtils.dprint('%s: port must be an integer' % self)
                self._port = 50001

        self.refresh_ui()
        self.initialize_client()
        return True

    def on_config_save_to_context(self, context):
        context['config']['boyomi'] = {
            'Enable': self._enabled,
            'host': self._host,
            'port': self._port,
        }

    def on_config_apply(self, context):
        self.apply_ui()

    def on_test_button_click(self, event):
        self._read(self.custom_read['initialize'])

    def on_option_tab_create(self, notebook):
        self.panel = wx.Panel(notebook, wx.ID_ANY)
        self.page = notebook.InsertPage(0, self.panel, '棒読みちゃん')
        self.layout = wx.BoxSizer(wx.VERTICAL)

        self.check_enable = wx.CheckBox(
            self.panel, wx.ID_ANY, '棒読みちゃんで実況する')
        self.edit_host = wx.TextCtrl(self.panel, wx.ID_ANY, 'host')
        self.edit_port = wx.TextCtrl(self.panel, wx.ID_ANY, 'port')
        self.button_test = wx.Button( self.panel, wx.ID_ANY, 'テスト')

        try:
            layout = wx.GridSizer(2, 4)
        except:
            layout = wx.GridSizer(2)

        layout.Add(wx.StaticText(self.panel, wx.ID_ANY, u'ホスト'))
        layout.Add(self.edit_host)
        layout.Add(wx.StaticText(self.panel, wx.ID_ANY, u'ポート'))
        layout.Add(self.edit_port)

        self.layout.Add(self.check_enable)
        self.layout.Add(layout, flag=wx.EXPAND)
        self.layout.Add(self.button_test)

        self.panel.SetSizer(self.layout)

        self.button_test.Bind(wx.EVT_BUTTON, self.on_test_button_click)
<|endoftext|>


<commit_before>from distutils.command.config import config

import numpy
import copy

from PyQt5 import QtCore
from PyQt5 import QtGui
from PyQt5 import QtWidgets

from bumps import options
from bumps import fitters

import sas.qtgui.Utilities.LocalConfig as LocalConfig
import sas.qtgui.Utilities.ObjectLibrary as ObjectLibrary
import sas.qtgui.Utilities.GuiUtils as GuiUtils

from sas.qtgui.Perspectives.Fitting.FittingWidget import FittingWidget
from sas.qtgui.Perspectives.Fitting.ConstraintWidget import ConstraintWidget
from sas.qtgui.Perspectives.Fitting.FittingOptions import FittingOptions
from sas.qtgui.Perspectives.Fitting.GPUOptions import GPUOptions

class FittingWindow(QtWidgets.QTabWidget):
    """
    """
    tabsModifiedSignal = QtCore.pyqtSignal()
    fittingStartedSignal = QtCore.pyqtSignal(list)
    fittingStoppedSignal = QtCore.pyqtSignal(list)

    name = "Fitting" # For displaying in the combo box in DataExplorer
    def __init__(self, parent=None, data=None):

        super(FittingWindow, self).__init__()

        self.parent = parent
        self._data = data

        # List of active fits
        self.tabs = []

        # Max index for adding new, non-clashing tab names
        self.maxIndex = 1

        # The default optimizer
        self.optimizer = 'Levenberg-Marquardt'

        # Dataset index -> Fitting tab mapping
        self.dataToFitTab = {}

        # The tabs need to be closeable
        self.setTabsClosable(True)

        # The tabs need to be movabe
        self.setMovable(True)

        self.communicate = self.parent.communicator()

        # Initialize the first tab
        self.addFit(None)

        # Deal with signals
        self.tabCloseRequested.connect(self.tabCloses)
        self.communicate.dataDeletedSignal.connect(self.dataDeleted)
        self.fittingStartedSignal.connect(self.onFittingStarted)
        self.fittingStoppedSignal.connect(self.onFittingStopped)

        self.communicate.copyFitParamsSignal.connect(self.onParamCopy)
        self.communicate.pasteFitParamsSignal.connect(self.onParamPaste)
        self.communicate.copyExcelFitParamsSignal.connect(self.onExcelCopy)
        self.communicate.copyLatexFitParamsSignal.connect(self.onLatexCopy)
        self.communicate.SaveFitParamsSignal.connect(self.onParamSave)

        # Perspective window not allowed to close by default
        self._allow_close = False

        # Fit options - uniform for all tabs
        self.fit_options = options.FIT_CONFIG
        self.fit_options_widget = FittingOptions(self, config=self.fit_options)
        self.fit_options.selected_id = fitters.LevenbergMarquardtFit.id

        # Listen to GUI Manager signal updating fit options
        self.fit_options_widget.fit_option_changed.connect(self.onFittingOptionsChange)

        # GPU Options
        self.gpu_options_widget = GPUOptions(self)

        self.updateWindowTitle()

        # Add new tab mini-button
        self.plusButton = QtWidgets.QToolButton(self)
        self.plusButton.setText("+")
        self.setCornerWidget(self.plusButton)
        self.plusButton.setToolTip("Add a new Fit Page")
        self.plusButton.clicked.connect(lambda: self.addFit(None))

    def updateWindowTitle(self):
        """
        Update the window title with the current optimizer name
        """
        self.optimizer = self.fit_options.selected_name
        self.setWindowTitle('Fit panel - Active Fitting Optimizer: %s' % self.optimizer)

    def setClosable(self, value=True):
        """
        Allow outsiders to close this widget
        """
        assert isinstance(value, bool)

        self._allow_close = value

    def onParamCopy(self):
        self.currentTab.onCopyToClipboard("")

    def onParamPaste(self):
        self.currentTab.onParameterPaste()

    def onExcelCopy(self):
        self.currentTab.onCopyToClipboard("Excel")

    def onLatexCopy(self):
        self.currentTab.onCopyToClipboard("Latex")

    def serializeAllFitpage(self):
        # serialize all active fitpages and return
        # a dictionary: {data_id: fitpage_state}
        params = {}
        for i, tab in enumerate(self.tabs):
            tab_data = self.getSerializedFitpage(tab)
            if 'data_id' not in tab_data: continue
            id = tab_data['data_id'][0]
            if isinstance(id, list):
                for i in id:
                    if i in params:
                        params[i].append(tab_data)
                    else:
                        params[i] = [tab_data]
            else:
                if id in params:
                    params[id].append(tab_data)
                else:
                    params[id] = [tab_data]
        return params

    def serializeCurrentFitpage(self):
        # serialize current(active) fitpage
        return self.getSerializedFitpage(self.currentTab)

    def getSerializedFitpage(self, tab):
        """
        get serialize requested fit tab
        """
        fitpage_state = tab.getFitPage()
        fitpage_state += tab.getFitModel()
        # put the text into dictionary
        line_dict = {}
        for line in fitpage_state:
            #content = line.split(',')
            if len(line) > 1:
                line_dict[line[0]] = line[1:]
        return line_dict

    def currentTabDataId(self):
        """
        Returns the data ID of the current tab
        """
        tab_id = []
        if not self.currentTab.data:
            return tab_id
        for item in self.currentTab.all_data:
            data = GuiUtils.dataFromItem(item)
            tab_id.append(data.id)

        return tab_id

    def updateFromParameters(self, parameters):
        """
        Pass the update parameters to the current fit page
        """
        self.currentTab.createPageForParameters(parameters)

    def onParamSave(self):
        self.currentTab.onCopyToClipboard("Save")

    def closeEvent(self, event):
        """
        Overwrite QDialog close method to allow for custom widget close
        """
        # Invoke fit page events
        if self._allow_close:
            # reset the closability flag
            self.setClosable(value=False)
            # Tell the MdiArea to close the container
            self.parentWidget().close()
            event.accept()
        else:
            # Maybe we should just minimize
            self.setWindowState(QtCore.Qt.WindowMinimized)
            event.ignore()

    def addFit(self, data, is_batch=False, tab_index=None):
        """
        Add a new tab for passed data
        """
        if tab_index is None:
            tab_index = self.maxIndex
        else:
            self.maxIndex = tab_index
        tab	= FittingWidget(parent=self.parent, data=data, tab_id=tab_index)
        tab.is_batch_fitting = is_batch

        # Add this tab to the object library so it can be retrieved by scripting/jupyter
        tab_name = self.getTabName(is_batch=is_batch)
        ObjectLibrary.addObject(tab_name, tab)
        self.tabs.append(tab)
        if data:
            self.updateFitDict(data, tab_name)
        #self.maxIndex += 1
        self.maxIndex = tab_index + 1

        icon = QtGui.QIcon()
        if is_batch:
            icon.addPixmap(QtGui.QPixmap("src/sas/qtgui/images/icons/layers.svg"))
        self.addTab(tab, icon, tab_name)
        # Show the new tab
        self.setCurrentWidget(tab);
        # Notify listeners
        self.tabsModifiedSignal.emit()

    def addConstraintTab(self):
        """
        Add a new C&S fitting tab
        """
        tabs = [isinstance(tab, ConstraintWidget) for tab in self.tabs]
        if any(tabs):
            # We already have a C&S tab: show it
            self.setCurrentIndex(tabs.index(True))
            return
        tab	= ConstraintWidget(parent=self)
        # Add this tab to the object library so it can be retrieved by scripting/jupyter
        tab_name = self.getCSTabName() # TODO update the tab name scheme
        ObjectLibrary.addObject(tab_name, tab)
        self.tabs.append(tab)
        icon = QtGui.QIcon()
        icon.addPixmap(QtGui.QPixmap("src/sas/qtgui/images/icons/link.svg"))
        self.addTab(tab, icon, tab_name)

        # This will be the last tab, so set the index accordingly
        self.setCurrentIndex(self.count()-1)

    def updateFitDict(self, item_key, tab_name):
        """
        Create a list if none exists and append if there's already a list
        """
        item_key_str = str(item_key)
        if item_key_str in list(self.dataToFitTab.keys()):
            self.dataToFitTab[item_key_str].append(tab_name)
        else:
            self.dataToFitTab[item_key_str] = [tab_name]

    def getTabName(self, is_batch=False):
        """
        Get the new tab name, based on the number of fitting tabs so far
        """
        page_name = "BatchPage" if is_batch else "FitPage"
        page_name = page_name + str(self.maxIndex)
        return page_name

    def getCSTabName(self):
        """
        Get the new tab name, based on the number of fitting tabs so far
        """
        page_name = "Const. & Simul. Fit"
        return page_name

    def deleteAllTabs(self):
        """
        Explicitly deletes all the fittabs, leaving nothing.
        This is in preparation for the project load step.
        """
        for tab_index in range(len(self.tabs)):
            self.closeTabByIndex(tab_index)

    def closeTabByIndex(self, index):
        """
        Close/delete a tab with the given index.
        No checks on validity of the index.
        """
        try:
            ObjectLibrary.deleteObjectByRef(self.tabs[index])
            self.removeTab(index)
            del self.tabs[index]
            self.tabsModifiedSignal.emit()
        except IndexError:
            # The tab might have already been deleted previously
            pass

    def resetTab(self, index):
        """
        Adds a new tab and removes the last tab
        as a way of resetting the fit tabs
        """
        # If data on tab empty - do nothing
        if index in self.tabs and not self.tabs[index].data:
            return
        # Add a new, empy tab
        self.addFit(None)
        # Remove the previous last tab
        self.tabCloses(index)

    def tabCloses(self, index):
        """
        Update local bookkeeping on tab close
        """
        # don't remove the last tab
        if len(self.tabs) <= 1:
            self.resetTab(index)
            return
        self.closeTabByIndex(index)

    def closeTabByName(self, tab_name):
        """
        Given name of the fitting tab - close it
        """
        for tab_index in range(len(self.tabs)):
            if self.tabText(tab_index) == tab_name:
                self.tabCloses(tab_index)
        pass # debug hook

    def dataDeleted(self, index_list):
        """
        Delete fit tabs referencing given data
        """
        if not index_list or not self.dataToFitTab:
            return
        for index_to_delete in index_list:
            index_to_delete_str = str(index_to_delete)
            orig_dict = copy.deepcopy(self.dataToFitTab)
            for tab_key in orig_dict.keys():
                if index_to_delete_str in tab_key:
                    for tab_name in orig_dict[tab_key]:
                        self.closeTabByName(tab_name)
                    self.dataToFitTab.pop(tab_key)

    def allowBatch(self):
        """
        Tell the caller that we accept multiple data instances
        """
        return True

    def isSerializable(self):
        """
        Tell the caller that this perspective writes its state
        """
        return True

    def setData(self, data_item=None, is_batch=False, tab_index=None):
        """
        Assign new dataset to the fitting instance
        Obtain a QStandardItem object and dissect it to get Data1D/2D
        Pass it over to the calculator
        """
        assert data_item is not None

        if not isinstance(data_item, list):
            msg = "Incorrect type passed to the Fitting Perspective"
            raise AttributeError(msg)

        if not isinstance(data_item[0], QtGui.QStandardItem):
            msg = "Incorrect type passed to the Fitting Perspective"
            raise AttributeError(msg)

        if is_batch:
            # Just create a new fit tab. No empty batchFit tabs
            self.addFit(data_item, is_batch=is_batch)
            return

        items = [data_item] if is_batch else data_item
        for data in items:
            # Find the first unassigned tab.
            # If none, open a new tab.
            available_tabs = [tab.acceptsData() for tab in self.tabs]

            if tab_index is not None:
                self.addFit(data, is_batch=is_batch, tab_index=tab_index)
                return
            if numpy.any(available_tabs):
                first_good_tab = available_tabs.index(True)
                self.tabs[first_good_tab].data = data
                tab_name = str(self.tabText(first_good_tab))
                self.updateFitDict(data, tab_name)
            else:
                self.addFit(data, is_batch=is_batch)

    def onFittingOptionsChange(self, fit_engine):
        """
        React to the fitting algorithm change by modifying window title
        """
        fitter = [f.id for f in options.FITTERS if f.name == str(fit_engine)][0]
        # set the optimizer
        self.fit_options.selected_id = str(fitter)
        # Update the title
        self.updateWindowTitle()

    def onFittingStarted(self, tabs_for_fitting=None):
        """
        Notify tabs listed in tabs_for_fitting
        that the fitting thread started
        """
        assert(isinstance(tabs_for_fitting, list))
        assert(len(tabs_for_fitting)>0)

        for tab_object in self.tabs:
            if not isinstance(tab_object, FittingWidget):
                continue
            page_name = "Page%s"%tab_object.tab_id
            if any([page_name in tab for tab in tabs_for_fitting]):
                tab_object.disableInteractiveElements()

        pass

    def onFittingStopped(self, tabs_for_fitting=None):
        """
        Notify tabs listed in tabs_for_fitting
        that the fitting thread stopped
        """
        assert(isinstance(tabs_for_fitting, list))
        assert(len(tabs_for_fitting)>0)

        for tab_object in self.tabs:
            if not isinstance(tab_object, FittingWidget):
                continue
            page_name = "Page%s"%tab_object.tab_id
            if any([page_name in tab for tab in tabs_for_fitting]):
                tab_object.enableInteractiveElements()

    def getCurrentStateAsXml(self):
        """
        Returns an XML version of the current state
        """
        state = {}
        for tab in self.tabs:
            pass
        return state

    @property
    def currentTab(self):
        """
        Returns the tab widget currently shown
        """
        return self.currentWidget()

    def getFitTabs(self):
        """
        Returns the list of fitting tabs
        """
        return [tab for tab in self.tabs if isinstance(tab, FittingWidget)]

    def getActiveConstraintList(self):
        """
        Returns a list of the constraints for all fitting tabs. Constraints
        are a tuple of strings (parameter, expression) e.g. ('M1.scale',
        'M2.scale + 2')
        """
        constraints = []
        for tab in self.getFitTabs():
            tab_name = tab.modelName()
            tab_constraints = tab.getConstraintsForModel()
            constraints.extend((tab_name + "." + par, expr) for par, expr in
                               tab_constraints)
        return constraints

    def getSymbolDictForConstraints(self):
        """
        Returns a dictionary containing all the symbols in  all constrained tabs
        and their values.
        """
        symbol_dict = {}
        for tab in self.getFitTabs():
            symbol_dict.update(tab.getSymbolDict())
        return symbol_dict

    def getConstraintTab(self):
        """
        Returns the constraint tab, or None if no constraint tab is active
        """
        if any(isinstance(tab, ConstraintWidget) for tab in self.tabs):
            constraint_tab = next(tab for tab in self.tabs if isinstance(tab,
                                                                     ConstraintWidget))
        else:
            constraint_tab = None
        return constraint_tab
<commit_msg>fixed code style issues in FittingPerspective<commit_after>from distutils.command.config import config

import numpy
import copy

from PyQt5 import QtCore
from PyQt5 import QtGui
from PyQt5 import QtWidgets

from bumps import options
from bumps import fitters

import sas.qtgui.Utilities.LocalConfig as LocalConfig
import sas.qtgui.Utilities.ObjectLibrary as ObjectLibrary
import sas.qtgui.Utilities.GuiUtils as GuiUtils

from sas.qtgui.Perspectives.Fitting.FittingWidget import FittingWidget
from sas.qtgui.Perspectives.Fitting.ConstraintWidget import ConstraintWidget
from sas.qtgui.Perspectives.Fitting.FittingOptions import FittingOptions
from sas.qtgui.Perspectives.Fitting.GPUOptions import GPUOptions

class FittingWindow(QtWidgets.QTabWidget):
    """
    """
    tabsModifiedSignal = QtCore.pyqtSignal()
    fittingStartedSignal = QtCore.pyqtSignal(list)
    fittingStoppedSignal = QtCore.pyqtSignal(list)

    name = "Fitting" # For displaying in the combo box in DataExplorer
    def __init__(self, parent=None, data=None):

        super(FittingWindow, self).__init__()

        self.parent = parent
        self._data = data

        # List of active fits
        self.tabs = []

        # Max index for adding new, non-clashing tab names
        self.maxIndex = 1

        # The default optimizer
        self.optimizer = 'Levenberg-Marquardt'

        # Dataset index -> Fitting tab mapping
        self.dataToFitTab = {}

        # The tabs need to be closeable
        self.setTabsClosable(True)

        # The tabs need to be movabe
        self.setMovable(True)

        self.communicate = self.parent.communicator()

        # Initialize the first tab
        self.addFit(None)

        # Deal with signals
        self.tabCloseRequested.connect(self.tabCloses)
        self.communicate.dataDeletedSignal.connect(self.dataDeleted)
        self.fittingStartedSignal.connect(self.onFittingStarted)
        self.fittingStoppedSignal.connect(self.onFittingStopped)

        self.communicate.copyFitParamsSignal.connect(self.onParamCopy)
        self.communicate.pasteFitParamsSignal.connect(self.onParamPaste)
        self.communicate.copyExcelFitParamsSignal.connect(self.onExcelCopy)
        self.communicate.copyLatexFitParamsSignal.connect(self.onLatexCopy)
        self.communicate.SaveFitParamsSignal.connect(self.onParamSave)

        # Perspective window not allowed to close by default
        self._allow_close = False

        # Fit options - uniform for all tabs
        self.fit_options = options.FIT_CONFIG
        self.fit_options_widget = FittingOptions(self, config=self.fit_options)
        self.fit_options.selected_id = fitters.LevenbergMarquardtFit.id

        # Listen to GUI Manager signal updating fit options
        self.fit_options_widget.fit_option_changed.connect(self.onFittingOptionsChange)

        # GPU Options
        self.gpu_options_widget = GPUOptions(self)

        self.updateWindowTitle()

        # Add new tab mini-button
        self.plusButton = QtWidgets.QToolButton(self)
        self.plusButton.setText("+")
        self.setCornerWidget(self.plusButton)
        self.plusButton.setToolTip("Add a new Fit Page")
        self.plusButton.clicked.connect(lambda: self.addFit(None))

    def updateWindowTitle(self):
        """
        Update the window title with the current optimizer name
        """
        self.optimizer = self.fit_options.selected_name
        self.setWindowTitle('Fit panel - Active Fitting Optimizer: %s' % self.optimizer)

    def setClosable(self, value=True):
        """
        Allow outsiders to close this widget
        """
        assert isinstance(value, bool)

        self._allow_close = value

    def onParamCopy(self):
        self.currentTab.onCopyToClipboard("")

    def onParamPaste(self):
        self.currentTab.onParameterPaste()

    def onExcelCopy(self):
        self.currentTab.onCopyToClipboard("Excel")

    def onLatexCopy(self):
        self.currentTab.onCopyToClipboard("Latex")

    def serializeAllFitpage(self):
        # serialize all active fitpages and return
        # a dictionary: {data_id: fitpage_state}
        params = {}
        for i, tab in enumerate(self.tabs):
            tab_data = self.getSerializedFitpage(tab)
            if 'data_id' not in tab_data: continue
            id = tab_data['data_id'][0]
            if isinstance(id, list):
                for i in id:
                    if i in params:
                        params[i].append(tab_data)
                    else:
                        params[i] = [tab_data]
            else:
                if id in params:
                    params[id].append(tab_data)
                else:
                    params[id] = [tab_data]
        return params

    def serializeCurrentFitpage(self):
        # serialize current(active) fitpage
        return self.getSerializedFitpage(self.currentTab)

    def getSerializedFitpage(self, tab):
        """
        get serialize requested fit tab
        """
        fitpage_state = tab.getFitPage()
        fitpage_state += tab.getFitModel()
        # put the text into dictionary
        line_dict = {}
        for line in fitpage_state:
            #content = line.split(',')
            if len(line) > 1:
                line_dict[line[0]] = line[1:]
        return line_dict

    def currentTabDataId(self):
        """
        Returns the data ID of the current tab
        """
        tab_id = []
        if not self.currentTab.data:
            return tab_id
        for item in self.currentTab.all_data:
            data = GuiUtils.dataFromItem(item)
            tab_id.append(data.id)

        return tab_id

    def updateFromParameters(self, parameters):
        """
        Pass the update parameters to the current fit page
        """
        self.currentTab.createPageForParameters(parameters)

    def onParamSave(self):
        self.currentTab.onCopyToClipboard("Save")

    def closeEvent(self, event):
        """
        Overwrite QDialog close method to allow for custom widget close
        """
        # Invoke fit page events
        if self._allow_close:
            # reset the closability flag
            self.setClosable(value=False)
            # Tell the MdiArea to close the container
            self.parentWidget().close()
            event.accept()
        else:
            # Maybe we should just minimize
            self.setWindowState(QtCore.Qt.WindowMinimized)
            event.ignore()

    def addFit(self, data, is_batch=False, tab_index=None):
        """
        Add a new tab for passed data
        """
        if tab_index is None:
            tab_index = self.maxIndex
        else:
            self.maxIndex = tab_index
        tab	= FittingWidget(parent=self.parent, data=data, tab_id=tab_index)
        tab.is_batch_fitting = is_batch

        # Add this tab to the object library so it can be retrieved by scripting/jupyter
        tab_name = self.getTabName(is_batch=is_batch)
        ObjectLibrary.addObject(tab_name, tab)
        self.tabs.append(tab)
        if data:
            self.updateFitDict(data, tab_name)
        #self.maxIndex += 1
        self.maxIndex = tab_index + 1

        icon = QtGui.QIcon()
        if is_batch:
            icon.addPixmap(QtGui.QPixmap("src/sas/qtgui/images/icons/layers.svg"))
        self.addTab(tab, icon, tab_name)
        # Show the new tab
        self.setCurrentWidget(tab);
        # Notify listeners
        self.tabsModifiedSignal.emit()

    def addConstraintTab(self):
        """
        Add a new C&S fitting tab
        """
        tabs = [isinstance(tab, ConstraintWidget) for tab in self.tabs]
        if any(tabs):
            # We already have a C&S tab: show it
            self.setCurrentIndex(tabs.index(True))
            return
        tab	= ConstraintWidget(parent=self)
        # Add this tab to the object library so it can be retrieved by scripting/jupyter
        tab_name = self.getCSTabName() # TODO update the tab name scheme
        ObjectLibrary.addObject(tab_name, tab)
        self.tabs.append(tab)
        icon = QtGui.QIcon()
        icon.addPixmap(QtGui.QPixmap("src/sas/qtgui/images/icons/link.svg"))
        self.addTab(tab, icon, tab_name)

        # This will be the last tab, so set the index accordingly
        self.setCurrentIndex(self.count()-1)

    def updateFitDict(self, item_key, tab_name):
        """
        Create a list if none exists and append if there's already a list
        """
        item_key_str = str(item_key)
        if item_key_str in list(self.dataToFitTab.keys()):
            self.dataToFitTab[item_key_str].append(tab_name)
        else:
            self.dataToFitTab[item_key_str] = [tab_name]

    def getTabName(self, is_batch=False):
        """
        Get the new tab name, based on the number of fitting tabs so far
        """
        page_name = "BatchPage" if is_batch else "FitPage"
        page_name = page_name + str(self.maxIndex)
        return page_name

    def getCSTabName(self):
        """
        Get the new tab name, based on the number of fitting tabs so far
        """
        page_name = "Const. & Simul. Fit"
        return page_name

    def deleteAllTabs(self):
        """
        Explicitly deletes all the fittabs, leaving nothing.
        This is in preparation for the project load step.
        """
        for tab_index in range(len(self.tabs)):
            self.closeTabByIndex(tab_index)

    def closeTabByIndex(self, index):
        """
        Close/delete a tab with the given index.
        No checks on validity of the index.
        """
        try:
            ObjectLibrary.deleteObjectByRef(self.tabs[index])
            self.removeTab(index)
            del self.tabs[index]
            self.tabsModifiedSignal.emit()
        except IndexError:
            # The tab might have already been deleted previously
            pass

    def resetTab(self, index):
        """
        Adds a new tab and removes the last tab
        as a way of resetting the fit tabs
        """
        # If data on tab empty - do nothing
        if index in self.tabs and not self.tabs[index].data:
            return
        # Add a new, empy tab
        self.addFit(None)
        # Remove the previous last tab
        self.tabCloses(index)

    def tabCloses(self, index):
        """
        Update local bookkeeping on tab close
        """
        # don't remove the last tab
        if len(self.tabs) <= 1:
            self.resetTab(index)
            return
        self.closeTabByIndex(index)

    def closeTabByName(self, tab_name):
        """
        Given name of the fitting tab - close it
        """
        for tab_index in range(len(self.tabs)):
            if self.tabText(tab_index) == tab_name:
                self.tabCloses(tab_index)
        pass # debug hook

    def dataDeleted(self, index_list):
        """
        Delete fit tabs referencing given data
        """
        if not index_list or not self.dataToFitTab:
            return
        for index_to_delete in index_list:
            index_to_delete_str = str(index_to_delete)
            orig_dict = copy.deepcopy(self.dataToFitTab)
            for tab_key in orig_dict.keys():
                if index_to_delete_str in tab_key:
                    for tab_name in orig_dict[tab_key]:
                        self.closeTabByName(tab_name)
                    self.dataToFitTab.pop(tab_key)

    def allowBatch(self):
        """
        Tell the caller that we accept multiple data instances
        """
        return True

    def isSerializable(self):
        """
        Tell the caller that this perspective writes its state
        """
        return True

    def setData(self, data_item=None, is_batch=False, tab_index=None):
        """
        Assign new dataset to the fitting instance
        Obtain a QStandardItem object and dissect it to get Data1D/2D
        Pass it over to the calculator
        """
        assert data_item is not None

        if not isinstance(data_item, list):
            msg = "Incorrect type passed to the Fitting Perspective"
            raise AttributeError(msg)

        if not isinstance(data_item[0], QtGui.QStandardItem):
            msg = "Incorrect type passed to the Fitting Perspective"
            raise AttributeError(msg)

        if is_batch:
            # Just create a new fit tab. No empty batchFit tabs
            self.addFit(data_item, is_batch=is_batch)
            return

        items = [data_item] if is_batch else data_item
        for data in items:
            # Find the first unassigned tab.
            # If none, open a new tab.
            available_tabs = [tab.acceptsData() for tab in self.tabs]

            if tab_index is not None:
                self.addFit(data, is_batch=is_batch, tab_index=tab_index)
                return
            if numpy.any(available_tabs):
                first_good_tab = available_tabs.index(True)
                self.tabs[first_good_tab].data = data
                tab_name = str(self.tabText(first_good_tab))
                self.updateFitDict(data, tab_name)
            else:
                self.addFit(data, is_batch=is_batch)

    def onFittingOptionsChange(self, fit_engine):
        """
        React to the fitting algorithm change by modifying window title
        """
        fitter = [f.id for f in options.FITTERS if f.name == str(fit_engine)][0]
        # set the optimizer
        self.fit_options.selected_id = str(fitter)
        # Update the title
        self.updateWindowTitle()

    def onFittingStarted(self, tabs_for_fitting=None):
        """
        Notify tabs listed in tabs_for_fitting
        that the fitting thread started
        """
        assert(isinstance(tabs_for_fitting, list))
        assert(len(tabs_for_fitting)>0)

        for tab_object in self.tabs:
            if not isinstance(tab_object, FittingWidget):
                continue
            page_name = "Page%s"%tab_object.tab_id
            if any([page_name in tab for tab in tabs_for_fitting]):
                tab_object.disableInteractiveElements()

        pass

    def onFittingStopped(self, tabs_for_fitting=None):
        """
        Notify tabs listed in tabs_for_fitting
        that the fitting thread stopped
        """
        assert(isinstance(tabs_for_fitting, list))
        assert(len(tabs_for_fitting)>0)

        for tab_object in self.tabs:
            if not isinstance(tab_object, FittingWidget):
                continue
            page_name = "Page%s"%tab_object.tab_id
            if any([page_name in tab for tab in tabs_for_fitting]):
                tab_object.enableInteractiveElements()

    def getCurrentStateAsXml(self):
        """
        Returns an XML version of the current state
        """
        state = {}
        for tab in self.tabs:
            pass
        return state

    @property
    def currentTab(self):
        """
        Returns the tab widget currently shown
        """
        return self.currentWidget()

    def getFitTabs(self):
        """
        Returns the list of fitting tabs
        """
        return [tab for tab in self.tabs if isinstance(tab, FittingWidget)]

    def getActiveConstraintList(self):
        """
        Returns a list of the constraints for all fitting tabs. Constraints
        are a tuple of strings (parameter, expression) e.g. ('M1.scale',
        'M2.scale + 2')
        """
        constraints = []
        for tab in self.getFitTabs():
            tab_name = tab.modelName()
            tab_constraints = tab.getConstraintsForModel()
            constraints.extend((tab_name + "." + par, expr)
                               for par, expr in tab_constraints)
        return constraints

    def getSymbolDictForConstraints(self):
        """
        Returns a dictionary containing all the symbols in  all constrained tabs
        and their values.
        """
        symbol_dict = {}
        for tab in self.getFitTabs():
            symbol_dict.update(tab.getSymbolDict())
        return symbol_dict

    def getConstraintTab(self):
        """
        Returns the constraint tab, or None if no constraint tab is active
        """
        if any(isinstance(tab, ConstraintWidget) for tab in self.tabs):
            constraint_tab = next(tab
                                  for tab in self.tabs
                                  if isinstance(tab, ConstraintWidget))
        else:
            constraint_tab = None
        return constraint_tab
<|endoftext|>


<commit_before>import csv
from indra.util.aws import get_s3_client
from indra.statements import Phosphorylation
from indra.sources.phosphoelm.api import PhosphoElmProcessor, \
    _get_json_from_entry_rows, s3_bucket, ppelm_s3_key
from nose.plugins.attrib import attr

columns = ['acc', 'sequence', 'position', 'code', 'pmids', 'kinases',
           'source', 'species', 'entry_date']
non_human_no_kinase = ['O08539',
                       'FAKEGSKG', '6', 'S', '17114649', '', 'HTP',
                       'Mus musculus', '2005-03-14 12:16:11.108314+01']
human_no_kinase = ['O14543',
                   'MVTHSKFPAAGMSRPLDTSLRLKTFSSKSEYQL', '31', 'Y',
                   '12783885', '', 'LTP', 'Homo sapiens',
                   '2006-10-17 12:06:48.271076+02']
human_kinase1 = ['O14543',
                 'FAKESEQUENCESRPLDTSLRLKTFSSKSEYQL',
                 '31', 'Y', '12783885', 'Lck', 'LTP', 'Homo sapiens',
                 '2006-10-17 12:06:48.16767+02']
human_kinase2 = ['O14746', 'FAKEPRCRAVRSLL', '12', 'S', '10224060',
                 'PKB_group', 'LTP', 'Homo sapiens',
                 '2004-12-31 00:00:00+01']

raw_data = [columns, non_human_no_kinase, human_no_kinase, human_kinase1,
            human_kinase2]

@attr('nonpublic')
def test_download_from_s3():
    s3 = get_s3_client(False)
    s3_obj = s3.get_object(Bucket=s3_bucket, Key=ppelm_s3_key)
    csv_reader = csv.reader(
        s3_obj['Body'].read().decode('utf8').splitlines(True),
        delimiter='\t'
    )
    ppelm_json = _get_json_from_entry_rows(csv_reader)
    assert ppelm_json

def test_json_processing():
    test_json = _get_json_from_entry_rows(iter(raw_data))
    assert len(test_json) == len(raw_data) - 1, len(test_json)
    assert set(columns) == set(test_json[0].keys())

def test_keep_empty():
    pep = PhosphoElmProcessor(
        phosphoelm_data=_get_json_from_entry_rows(iter(raw_data))
    )
    pep.process_phosphorylations(True)
    stmts = pep.statements
    assert len(stmts) == 3
    assert all(isinstance(st, Phosphorylation) for st in stmts)
    assert all(st.evidence[0].source_api == 'phospho.ELM' for st in stmts)
    assert all(len(st.evidence[0].annotations['sequence']) > 0
               for st in stmts)

def test_not_empty():
    pep = PhosphoElmProcessor(
        phosphoelm_data=_get_json_from_entry_rows(iter(raw_data))
    )
    pep.process_phosphorylations()
    stmts = pep.statements
    assert len(stmts) == 2
<commit_msg>Add tests for some special cases<commit_after>import csv
from indra.util.aws import get_s3_client
from indra.statements import Phosphorylation
from indra.sources.phosphoelm.api import PhosphoElmProcessor, \
    _get_json_from_entry_rows, s3_bucket, ppelm_s3_key
from nose.plugins.attrib import attr

columns = ['acc', 'sequence', 'position', 'code', 'pmids', 'kinases',
           'source', 'species', 'entry_date']
non_human_no_kinase = ['O08539',
                       'FAKEGSKG', '6', 'S', '17114649', '', 'HTP',
                       'Mus musculus', '2005-03-14 12:16:11.108314+01']
human_no_kinase = ['O14543',
                   'MVTHSKFPAAGMSRPLDTSLRLKTFSSKSEYQL', '31', 'Y',
                   '12783885', '', 'LTP', 'Homo sapiens',
                   '2006-10-17 12:06:48.271076+02']
human_kinase1 = ['O14543',
                 'FAKESEQUENCESRPLDTSLRLKTFSSKSEYQL',
                 '31', 'Y', '12783885', 'Lck', 'LTP', 'Homo sapiens',
                 '2006-10-17 12:06:48.16767+02']
human_kinase2 = ['O14746', 'FAKEPRCRAVRSLL', '12', 'S', '10224060',
                 'PKB_group', 'LTP', 'Homo sapiens',
                 '2004-12-31 00:00:00+01']

raw_data = [columns, non_human_no_kinase, human_no_kinase, human_kinase1,
            human_kinase2]

@attr('nonpublic')
def test_download_from_s3():
    s3 = get_s3_client(False)
    s3_obj = s3.get_object(Bucket=s3_bucket, Key=ppelm_s3_key)
    csv_reader = csv.reader(
        s3_obj['Body'].read().decode('utf8').splitlines(True),
        delimiter='\t'
    )
    ppelm_json = _get_json_from_entry_rows(csv_reader)
    assert ppelm_json

def test_json_processing():
    test_json = _get_json_from_entry_rows(iter(raw_data))
    assert len(test_json) == len(raw_data) - 1, len(test_json)
    assert set(columns) == set(test_json[0].keys())

def test_keep_empty():
    pep = PhosphoElmProcessor(
        phosphoelm_data=_get_json_from_entry_rows(iter(raw_data))
    )
    pep.process_phosphorylations(True)
    stmts = pep.statements
    assert len(stmts) == 3
    assert all(isinstance(st, Phosphorylation) for st in stmts)
    assert all(st.evidence[0].source_api == 'phospho.ELM' for st in stmts)
    assert all(len(st.evidence[0].annotations['sequence']) > 0
               for st in stmts)

def test_not_empty():
    pep = PhosphoElmProcessor(
        phosphoelm_data=_get_json_from_entry_rows(iter(raw_data))
    )
    pep.process_phosphorylations()
    stmts = pep.statements
    assert len(stmts) == 2

def test_special_cases():
    # See http://phospho.elm.eu.org/kinases.html for list of kinases
    _, ag = PhosphoElmProcessor._get_enzyme('Aurora A')
    assert list(ag.db_refs.items())[0] == ('HGNC', '11393')

    _, ag = PhosphoElmProcessor._get_enzyme('CCDPK')  # is non-human
    assert ag is None

    _, ag = PhosphoElmProcessor._get_enzyme('MAP2K_group')
    assert list(ag.db_refs.items())[0] == ('FPLX', 'MAP2K')

    _, ag = PhosphoElmProcessor._get_enzyme('PDHK4')
    assert list(ag.db_refs.items())[0] == ('HGNC', '8812')

    # PDKC is probably a typo in the kinase table at
    # http://phospho.elm.eu.org/kinases.html but it is unclear what was
    # meant by the name from the source material
    _, ag = PhosphoElmProcessor._get_enzyme('PDKC')
    assert list(ag.db_refs.items())[0] == ('TEXT', 'PDKC')

    _, ag = PhosphoElmProcessor._get_enzyme('PKA_alpha')
    assert list(ag.db_refs.items())[0] == ('HGNC', '9380')

    _, ag = PhosphoElmProcessor._get_enzyme('PKC_zeta')
    assert list(ag.db_refs.items())[0] == ('HGNC', '9412')

    _, ag = PhosphoElmProcessor._get_enzyme('RSK-5')
    assert list(ag.db_refs.items())[0] == ('HGNC', '10434')

    _, ag = PhosphoElmProcessor._get_enzyme('Titin kinase')
    assert list(ag.db_refs.items())[0] == ('HGNC', '12403')

<|endoftext|>


<commit_before>import datetime
from urlparse import urljoin
from django.core.management.base import BaseCommand, CommandError
from django.conf import settings
from locksmith.common import apicall

class Command(BaseCommand):
    help = "Push a given day's logs up to the analytics hub"
    args = '[date:YYYY-MM-DD]'
    requires_model_validation = False

    def handle(self, date=None, *args, **options):
        if not date:
            # set date to yesterday if not passed in
            yesterday = datetime.datetime.now() - datetime.timedelta(days=1)
            date = yesterday.strftime('%Y-%m-%d')

        print 'pushing logs for %s' % date

        dt_begin = datetime.datetime.strptime(date, '%Y-%m-%d')
        dt_end = dt_begin + datetime.timedelta(days=1)

        # construct database query
        qs = LogModel.objects.extra(where=["date_trunc('day', {0}) = '{1}'".format(DATE_FIELD, date)]).order_by()
        results = qs.values(ENDPOINT_FIELD, USER_FIELD).annotate(calls=Count('id'))

        results = db.logs.group(['key', 'method'],
                               {"timestamp" {"$gte": dt_begin, "$lt": dt_end},
                               {"count": 0},
                               "function (obj, prev) {prev.count += 1;}")

        endpoint = urljoin(settings.LOCKSMITH_HUB_URL, 'report_calls/')

        # report results
        for item in results:
            apicall(endpoint, settings.LOCKSMITH_SIGNING_KEY,
                    api=settings.LOCKSMITH_API_NAME, date=date,
                    endpoint=item['method'], key=item['key'],
                    calls=item['count'])
<commit_msg>fix syntax error<commit_after>import datetime
from urlparse import urljoin
from django.core.management.base import BaseCommand, CommandError
from django.conf import settings
from locksmith.common import apicall

class Command(BaseCommand):
    help = "Push a given day's logs up to the analytics hub"
    args = '[date:YYYY-MM-DD]'
    requires_model_validation = False

    def handle(self, date=None, *args, **options):
        if not date:
            # set date to yesterday if not passed in
            yesterday = datetime.datetime.now() - datetime.timedelta(days=1)
            date = yesterday.strftime('%Y-%m-%d')

        print 'pushing logs for %s' % date

        dt_begin = datetime.datetime.strptime(date, '%Y-%m-%d')
        dt_end = dt_begin + datetime.timedelta(days=1)

        # construct database query
        qs = LogModel.objects.extra(where=["date_trunc('day', {0}) = '{1}'".format(DATE_FIELD, date)]).order_by()
        results = qs.values(ENDPOINT_FIELD, USER_FIELD).annotate(calls=Count('id'))

        results = db.logs.group(['key', 'method'],
                               {"timestamp" {"$gte": dt_begin, "$lt": dt_end}},
                               {"count": 0},
                               "function (obj, prev) {prev.count += 1;}")

        endpoint = urljoin(settings.LOCKSMITH_HUB_URL, 'report_calls/')

        # report results
        for item in results:
            apicall(endpoint, settings.LOCKSMITH_SIGNING_KEY,
                    api=settings.LOCKSMITH_API_NAME, date=date,
                    endpoint=item['method'], key=item['key'],
                    calls=item['count'])
<|endoftext|>


<commit_before>import logging
import json

from datetime import date, timedelta
from functools import partial
from django.contrib import messages
from django.core.exceptions import PermissionDenied
from django.core.urlresolvers import reverse
from django.http.response import Http404
from django.http import HttpResponse, HttpResponseRedirect
from django.shortcuts import render, get_object_or_404, redirect
from django.contrib.auth.decorators import permission_required, login_required
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger

from .models import Event, EventGroup, Person
from .forms import EventForm, EventGroupForm
from talks.api import serializers
from talks.events.forms import PersonForm

logger = logging.getLogger(__name__)

def homepage(request):
    today = date.today()
    tomorrow = today + timedelta(days=1)
    events = Event.published.filter(start__gte=today,
                                    start__lt=tomorrow).order_by('start')
    event_groups = EventGroup.objects.for_events(events)
    conferences = filter(lambda eg: eg.group_type == EventGroup.CONFERENCE,
                         event_groups)
    series = filter(lambda eg: eg.group_type == EventGroup.SEMINAR,
                    event_groups)
    group_no_type = filter(lambda eg: not eg.group_type,
                           event_groups)
    context = {
        'events': events,
        'event_groups': event_groups,
        'conferences': conferences,
        'group_no_type': group_no_type,
        'series': series,
        'default_collection': None,
    }
    if request.tuser:
        # Authenticated user
        collection = request.tuser.default_collection
        context['default_collection'] = collection
        context['user_events'] = collection.get_events()
        context['user_event_groups'] = collection.get_event_groups()
    return render(request, 'front.html', context)

def upcoming_events(request):
    today = date.today()
    events = Event.published.filter(start__gte=today).order_by('start')
    return _events_list(request, events)

def events_for_year(request, year):
    events = Event.published.filter(start__year=year)
    return _events_list(request, events)

def events_for_month(request, year, month):
    events = Event.published.filter(start__year=year,
                                    start__month=month)
    return _events_list(request, events)

def events_for_day(request, year, month, day):
    events = Event.published.filter(start__year=year,
                                    start__month=month,
                                    start__day=day)
    return _events_list(request, events)

def _events_list(request, events):
    context = {'events': events}
    return render(request, 'events/events.html', context)

def show_event(request, event_slug):
    try:
        # TODO depending if user is admin or not,
        # we should use Event.published here...
        ev = Event.objects.select_related(
            'speakers',
            'location',
            'group',
            'department_organiser').get(slug=event_slug)
    except Event.DoesNotExist:
        raise Http404
    context = {
        'event': ev,
        'url': request.build_absolute_uri(reverse('show-event', args=[ev.slug])),
        'location': ev.api_location,
        'speakers': ev.speakers.all(),
    }
    return render(request, 'events/event.html', context)

@login_required
@permission_required('events.change_event', raise_exception=PermissionDenied)
def edit_event(request, event_slug):
    event = get_object_or_404(Event, slug=event_slug)
    if not event.user_can_edit(request.user):
        raise PermissionDenied
    form = EventForm(request.POST or None, instance=event, prefix='event')
    context = {
        'event': event,
        'event_form': form,
        'is_editing': True
    }
    if request.method == 'POST':
        if form.is_valid():
            event = form.save()
            messages.success(request, "Event was updated")
            return redirect(event.get_absolute_url())
        else:
            messages.warning(request, "Please correct errors below")
    return render(request, "events/event_form.html", context)

@login_required
@permission_required("events.add_event", raise_exception=PermissionDenied)
def create_event(request, group_slug=None):
    initial = None
    event_group = None
    logger.debug("group_id:%s", group_slug)
    if group_slug:
        event_group = get_object_or_404(EventGroup, slug=group_slug)
        initial = {
            'group': event_group,
        }

    PrefixedEventForm = partial(EventForm, prefix='event', initial=initial)

    if request.method == 'POST':
        context = {
            'event_form': PrefixedEventForm(request.POST),
        }
        forms_valid = context['event_form'].is_valid()
        if forms_valid:
            logging.debug("form is valid")
            event = context['event_form'].save()
            event.editor_set.add(request.user)
            event.save()
            messages.success(request, "New event has been created")
            if 'another' in request.POST:
                if event_group:
                    logger.debug("redirecting to create-event-in-group")
                    # Adding more events, redirect to the create event in existing group form
                    return HttpResponseRedirect(reverse('create-event-in-group', args=(event_group.slug,)))
                else:
                    logger.debug("redirecting to create-event")
                    return HttpResponseRedirect(reverse('create-event'))
            else:
                return HttpResponseRedirect(reverse('show-event', args=(event.slug,)))
        else:
            logging.debug("form is NOT valid")
            messages.warning(request, "Please correct errors below")
    else:
        context = {
            'event_form': PrefixedEventForm(),
            'is_editing': False
        }
    return render(request, 'events/event_form.html', context)

@login_required
@permission_required('events.delete_event', raise_exception=PermissionDenied)
def delete_event(request, event_slug):
    event = get_object_or_404(Event, slug=event_slug)

    # only super users or editors can delete an event
    if not event.user_can_edit(request.user):
        raise PermissionDenied

    # if the user is not a super user, and if the event has already started
    # it should not be possible to delete it
    if not request.user.is_superuser:
        if event.already_started:
            messages.warning(request, "You cannot delete an event that has already started")
            return redirect(event.get_absolute_url())
    context = {
        'event': event,
    }
    if request.method == 'POST':
        event.delete()
        messages.success(request, "Event has been successfully deleted")
        return redirect('contributors-events')
    return render(request, "events/delete_event.html", context)

def list_event_groups(request):
    object_list = EventGroup.objects.all()
    context = {
        'object_list': object_list,
    }
    return render(request, "events/event_group_list.html", context)

def show_event_group(request, event_group_slug):
    group = get_object_or_404(EventGroup, slug=event_group_slug)
    context = {
        'event_group': group,
    }
    return render(request, 'events/event-group.html', context)

@login_required
@permission_required('events.change_eventgroup', raise_exception=PermissionDenied)
def edit_event_group(request, event_group_slug):
    group = get_object_or_404(EventGroup, slug=event_group_slug)
    form = EventGroupForm(request.POST or None, instance=group)
    if request.method == 'POST':
        logging.debug("incoming post: %s", request.POST)
        if form.is_valid():
            event_group = form.save()
            messages.success(request, "Event group was updated")
            return redirect(event_group.get_absolute_url())
        else:
            messages.warning(request, "Please correct errors below")
    context = {
        'form': form,
        'event_group': group,
        'is_editing': True
    }
    return render(request, 'events/event_group_form.html', context)

@login_required
@permission_required('events.add_eventgroup', raise_exception=PermissionDenied)
def create_event_group(request):
    form = EventGroupForm(request.POST or None)
    is_modal = request.GET.get('modal')
    status_code = 200
    if request.method == 'POST':
        if form.is_valid():
            event_group = form.save()
            if is_modal:
                response = json.dumps(serializers.EventGroupSerializer(event_group).data)
                return HttpResponse(response, status=201, content_type='application/json')
            messages.success(request, "Event group was created")
            return redirect(event_group.get_absolute_url())
        else:
            status_code = 400
            messages.warning(request, "Please correct errors below")

    context = {
        'form': form,
        'modal_title': "Add a new event group",
        'is_editing': False
    }

    if is_modal:
        return render(request, 'events/event_group_modal_form.html', context, status=status_code)
    else:
        return render(request, 'events/event_group_form.html', context, status=status_code)

@login_required
@permission_required('events.delete_eventgroup', raise_exception=PermissionDenied)
def delete_event_group(request, event_group_slug):
    event_group = get_object_or_404(EventGroup, slug=event_group_slug)
    context = {
        'event_group': event_group,
        'events': event_group.events.all()
    }
    if request.method == 'POST':
        # first updating all events that were referring to the group to be deleted
        Event.objects.filter(group=event_group).update(group=None)
        event_group.delete()
        messages.success(request, "Event group has been successfully deleted")
        return redirect('contributors-events')
    return render(request, "events/delete_event_group.html", context)

def contributors_home(request):
    return HttpResponseRedirect(reverse('contributors-events'))

@login_required()
@permission_required('events.add_person', raise_exception=PermissionDenied)
def create_person(request):
    form = PersonForm(request.POST or None)
    is_modal = request.GET.get('modal')
    status_code = 200
    if request.method == 'POST':
        if form.is_valid():
            person = form.save()
            if is_modal:
                response = json.dumps(serializers.PersonSerializer(person).data)
                return  HttpResponse(response, status=201, content_type='application/json')
            messages.success(request, "Person was created")
            return redirect(person.get_absolute_url())
        else:
            status_code = 400
            messages.warning(request, "Please correct errors below")
    context = {
        'form': form,
        'modal_title': "Add a new Person",
    }

    if is_modal:
        return render(request, 'events/person_modal_form.html', context, status=status_code)
    else:
        return render(request, 'events/person_form.html', context, status=status_code)

@login_required
@permission_required('events.change_event', raise_exception=PermissionDenied)
def contributors_events(request):
    events_date = request.GET.get('date', None)
    events_status = request.GET.get('status', None)
    events_missing = request.GET.get('missing', None)
    events_editable = request.GET.get('editable', None)

    count = request.GET.get('count', 20)
    page = request.GET.get('page', 1)

    # used to build a URL fragment that does not
    # contain "page" so that we can... paginate
    args = {'count': count}

    if events_editable and not request.user.is_superuser:
        events = Event.objects.filter(editor_set__in=[request.user])
        args['editable'] = 'true'
    else:
        events = Event.objects.all()

    if events_date:
        if events_date == 'future':
            args['date'] = 'future'
            events = events.filter(start__gte=date.today())
        elif events_date == 'past':
            args['date'] = 'past'
            events = events.filter(start__lt=date.today())
    if events_status:
        args['status'] = events_status
        events = events.filter(status=events_status)
    if events_missing :
        if events_missing == 'title':
            args['missing'] = 'title'
            events = events.filter(title_not_announced=True)
        elif events_missing == 'location':
            args['missing'] = 'location'
            events = events.filter(location='')

    paginator = Paginator(events, count)

    try:
        events = paginator.page(page)
    except (PageNotAnInteger, EmptyPage):
        return redirect('contributors-events')

    fragment = '&'.join(["{k}={v}".format(k=k, v=v) for k, v in args.iteritems()])

    context = {
        'events': events,
        'fragment': fragment
    }

    return render(request, 'events/contributors_events.html', context)

@login_required()
@permission_required('events.change_eventgroup', raise_exception=PermissionDenied)
def contributors_eventgroups(request):
    eventgroups = EventGroup.objects.all()
    count = request.GET.get('count',20)
    page = request.GET.get('page', 1)

    paginator = Paginator(eventgroups, count)

    try:
        eventgroups = paginator.page(page)
    except (PageNotAnInteger, EmptyPage):
        return redirect('contributors-eventgroups')

    context = {
        'groups': eventgroups
    }

    return render(request, 'events/contributors_groups.html', context)

@login_required()
@permission_required('events.change_person', raise_exception=PermissionDenied)
def contributors_persons(request):
    persons = Person.objects.all()
    count = request.GET.get('count', 20)
    page = request.GET.get('page', 1)

    paginator = Paginator(persons, count)

    try:
        persons = paginator.page(page)
    except (PageNotAnInteger, EmptyPage):
        return redirect('contributors-persons')

    context = {
        'persons' : persons
    }

    return render(request, 'events/contributors_persons.html', context)

@login_required()
def show_person(request, person_slug):
    person = get_object_or_404(Person, slug=person_slug)

    context = {
        'person': person,
    }
    return render(request, 'events/person.html', context)

@login_required
@permission_required('events.change_person', raise_exception=PermissionDenied)
def edit_person(request, person_slug):
    person = get_object_or_404(Person, slug=person_slug)
    form = PersonForm(request.POST or None, instance=person)
    if request.method == 'POST':
        if form.is_valid():
            person = form.save()
            messages.success(request, "Person was updated")
            return redirect(person.get_absolute_url())
        else:
            messages.warning(request,"Please correct errors below")
    context = {
        'form': form,
        'person': person,
    }
    return render(request, 'events/person_form.html', context)
<commit_msg>Fix topics/speakers by providing initial data<commit_after>import logging
import json

from datetime import date, timedelta
from functools import partial
from django.contrib import messages
from django.core.exceptions import PermissionDenied
from django.core.urlresolvers import reverse
from django.http.response import Http404
from django.http import HttpResponse, HttpResponseRedirect
from django.shortcuts import render, get_object_or_404, redirect
from django.contrib.auth.decorators import permission_required, login_required
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger

from .models import Event, EventGroup, Person
from .forms import EventForm, EventGroupForm
from talks.api import serializers
from talks.events.forms import PersonForm

logger = logging.getLogger(__name__)

def homepage(request):
    today = date.today()
    tomorrow = today + timedelta(days=1)
    events = Event.published.filter(start__gte=today,
                                    start__lt=tomorrow).order_by('start')
    event_groups = EventGroup.objects.for_events(events)
    conferences = filter(lambda eg: eg.group_type == EventGroup.CONFERENCE,
                         event_groups)
    series = filter(lambda eg: eg.group_type == EventGroup.SEMINAR,
                    event_groups)
    group_no_type = filter(lambda eg: not eg.group_type,
                           event_groups)
    context = {
        'events': events,
        'event_groups': event_groups,
        'conferences': conferences,
        'group_no_type': group_no_type,
        'series': series,
        'default_collection': None,
    }
    if request.tuser:
        # Authenticated user
        collection = request.tuser.default_collection
        context['default_collection'] = collection
        context['user_events'] = collection.get_events()
        context['user_event_groups'] = collection.get_event_groups()
    return render(request, 'front.html', context)

def upcoming_events(request):
    today = date.today()
    events = Event.published.filter(start__gte=today).order_by('start')
    return _events_list(request, events)

def events_for_year(request, year):
    events = Event.published.filter(start__year=year)
    return _events_list(request, events)

def events_for_month(request, year, month):
    events = Event.published.filter(start__year=year,
                                    start__month=month)
    return _events_list(request, events)

def events_for_day(request, year, month, day):
    events = Event.published.filter(start__year=year,
                                    start__month=month,
                                    start__day=day)
    return _events_list(request, events)

def _events_list(request, events):
    context = {'events': events}
    return render(request, 'events/events.html', context)

def show_event(request, event_slug):
    try:
        # TODO depending if user is admin or not,
        # we should use Event.published here...
        ev = Event.objects.select_related(
            'speakers',
            'location',
            'group',
            'department_organiser').get(slug=event_slug)
    except Event.DoesNotExist:
        raise Http404
    context = {
        'event': ev,
        'url': request.build_absolute_uri(reverse('show-event', args=[ev.slug])),
        'location': ev.api_location,
        'speakers': ev.speakers.all(),
    }
    return render(request, 'events/event.html', context)

@login_required
@permission_required('events.change_event', raise_exception=PermissionDenied)
def edit_event(request, event_slug):
    event = get_object_or_404(Event, slug=event_slug)
    if not event.user_can_edit(request.user):
        raise PermissionDenied
    # providing data for topics/speakers as it is not straight from the Model
    initial = {'topics': [t.uri for t in event.topics.all()],   # uses GenericRelation
               'speakers': event.speakers.all()}        # different of person_set
    form = EventForm(request.POST or None, instance=event, initial=initial, prefix='event')
    context = {
        'event': event,
        'event_form': form,
        'is_editing': True
    }
    if request.method == 'POST':
        if form.is_valid():
            event = form.save()
            messages.success(request, "Event was updated")
            return redirect(event.get_absolute_url())
        else:
            messages.warning(request, "Please correct errors below")
    return render(request, "events/event_form.html", context)

@login_required
@permission_required("events.add_event", raise_exception=PermissionDenied)
def create_event(request, group_slug=None):
    initial = None
    event_group = None
    logger.debug("group_id:%s", group_slug)
    if group_slug:
        event_group = get_object_or_404(EventGroup, slug=group_slug)
        initial = {
            'group': event_group,
        }

    PrefixedEventForm = partial(EventForm, prefix='event', initial=initial)

    if request.method == 'POST':
        context = {
            'event_form': PrefixedEventForm(request.POST),
        }
        forms_valid = context['event_form'].is_valid()
        if forms_valid:
            logging.debug("form is valid")
            event = context['event_form'].save()
            event.editor_set.add(request.user)
            event.save()
            messages.success(request, "New event has been created")
            if 'another' in request.POST:
                if event_group:
                    logger.debug("redirecting to create-event-in-group")
                    # Adding more events, redirect to the create event in existing group form
                    return HttpResponseRedirect(reverse('create-event-in-group', args=(event_group.slug,)))
                else:
                    logger.debug("redirecting to create-event")
                    return HttpResponseRedirect(reverse('create-event'))
            else:
                return HttpResponseRedirect(reverse('show-event', args=(event.slug,)))
        else:
            logging.debug("form is NOT valid")
            messages.warning(request, "Please correct errors below")
    else:
        context = {
            'event_form': PrefixedEventForm(),
            'is_editing': False
        }
    return render(request, 'events/event_form.html', context)

@login_required
@permission_required('events.delete_event', raise_exception=PermissionDenied)
def delete_event(request, event_slug):
    event = get_object_or_404(Event, slug=event_slug)

    # only super users or editors can delete an event
    if not event.user_can_edit(request.user):
        raise PermissionDenied

    # if the user is not a super user, and if the event has already started
    # it should not be possible to delete it
    if not request.user.is_superuser:
        if event.already_started:
            messages.warning(request, "You cannot delete an event that has already started")
            return redirect(event.get_absolute_url())
    context = {
        'event': event,
    }
    if request.method == 'POST':
        event.delete()
        messages.success(request, "Event has been successfully deleted")
        return redirect('contributors-events')
    return render(request, "events/delete_event.html", context)

def list_event_groups(request):
    object_list = EventGroup.objects.all()
    context = {
        'object_list': object_list,
    }
    return render(request, "events/event_group_list.html", context)

def show_event_group(request, event_group_slug):
    group = get_object_or_404(EventGroup, slug=event_group_slug)
    context = {
        'event_group': group,
    }
    return render(request, 'events/event-group.html', context)

@login_required
@permission_required('events.change_eventgroup', raise_exception=PermissionDenied)
def edit_event_group(request, event_group_slug):
    group = get_object_or_404(EventGroup, slug=event_group_slug)
    form = EventGroupForm(request.POST or None, instance=group)
    if request.method == 'POST':
        logging.debug("incoming post: %s", request.POST)
        if form.is_valid():
            event_group = form.save()
            messages.success(request, "Event group was updated")
            return redirect(event_group.get_absolute_url())
        else:
            messages.warning(request, "Please correct errors below")
    context = {
        'form': form,
        'event_group': group,
        'is_editing': True
    }
    return render(request, 'events/event_group_form.html', context)

@login_required
@permission_required('events.add_eventgroup', raise_exception=PermissionDenied)
def create_event_group(request):
    form = EventGroupForm(request.POST or None)
    is_modal = request.GET.get('modal')
    status_code = 200
    if request.method == 'POST':
        if form.is_valid():
            event_group = form.save()
            if is_modal:
                response = json.dumps(serializers.EventGroupSerializer(event_group).data)
                return HttpResponse(response, status=201, content_type='application/json')
            messages.success(request, "Event group was created")
            return redirect(event_group.get_absolute_url())
        else:
            status_code = 400
            messages.warning(request, "Please correct errors below")

    context = {
        'form': form,
        'modal_title': "Add a new event group",
        'is_editing': False
    }

    if is_modal:
        return render(request, 'events/event_group_modal_form.html', context, status=status_code)
    else:
        return render(request, 'events/event_group_form.html', context, status=status_code)

@login_required
@permission_required('events.delete_eventgroup', raise_exception=PermissionDenied)
def delete_event_group(request, event_group_slug):
    event_group = get_object_or_404(EventGroup, slug=event_group_slug)
    context = {
        'event_group': event_group,
        'events': event_group.events.all()
    }
    if request.method == 'POST':
        # first updating all events that were referring to the group to be deleted
        Event.objects.filter(group=event_group).update(group=None)
        event_group.delete()
        messages.success(request, "Event group has been successfully deleted")
        return redirect('contributors-events')
    return render(request, "events/delete_event_group.html", context)

def contributors_home(request):
    return HttpResponseRedirect(reverse('contributors-events'))

@login_required()
@permission_required('events.add_person', raise_exception=PermissionDenied)
def create_person(request):
    form = PersonForm(request.POST or None)
    is_modal = request.GET.get('modal')
    status_code = 200
    if request.method == 'POST':
        if form.is_valid():
            person = form.save()
            if is_modal:
                response = json.dumps(serializers.PersonSerializer(person).data)
                return  HttpResponse(response, status=201, content_type='application/json')
            messages.success(request, "Person was created")
            return redirect(person.get_absolute_url())
        else:
            status_code = 400
            messages.warning(request, "Please correct errors below")
    context = {
        'form': form,
        'modal_title': "Add a new Person",
    }

    if is_modal:
        return render(request, 'events/person_modal_form.html', context, status=status_code)
    else:
        return render(request, 'events/person_form.html', context, status=status_code)

@login_required
@permission_required('events.change_event', raise_exception=PermissionDenied)
def contributors_events(request):
    events_date = request.GET.get('date', None)
    events_status = request.GET.get('status', None)
    events_missing = request.GET.get('missing', None)
    events_editable = request.GET.get('editable', None)

    count = request.GET.get('count', 20)
    page = request.GET.get('page', 1)

    # used to build a URL fragment that does not
    # contain "page" so that we can... paginate
    args = {'count': count}

    if events_editable and not request.user.is_superuser:
        events = Event.objects.filter(editor_set__in=[request.user])
        args['editable'] = 'true'
    else:
        events = Event.objects.all()

    if events_date:
        if events_date == 'future':
            args['date'] = 'future'
            events = events.filter(start__gte=date.today())
        elif events_date == 'past':
            args['date'] = 'past'
            events = events.filter(start__lt=date.today())
    if events_status:
        args['status'] = events_status
        events = events.filter(status=events_status)
    if events_missing :
        if events_missing == 'title':
            args['missing'] = 'title'
            events = events.filter(title_not_announced=True)
        elif events_missing == 'location':
            args['missing'] = 'location'
            events = events.filter(location='')

    paginator = Paginator(events, count)

    try:
        events = paginator.page(page)
    except (PageNotAnInteger, EmptyPage):
        return redirect('contributors-events')

    fragment = '&'.join(["{k}={v}".format(k=k, v=v) for k, v in args.iteritems()])

    context = {
        'events': events,
        'fragment': fragment
    }

    return render(request, 'events/contributors_events.html', context)

@login_required()
@permission_required('events.change_eventgroup', raise_exception=PermissionDenied)
def contributors_eventgroups(request):
    eventgroups = EventGroup.objects.all()
    count = request.GET.get('count',20)
    page = request.GET.get('page', 1)

    paginator = Paginator(eventgroups, count)

    try:
        eventgroups = paginator.page(page)
    except (PageNotAnInteger, EmptyPage):
        return redirect('contributors-eventgroups')

    context = {
        'groups': eventgroups
    }

    return render(request, 'events/contributors_groups.html', context)

@login_required()
@permission_required('events.change_person', raise_exception=PermissionDenied)
def contributors_persons(request):
    persons = Person.objects.all()
    count = request.GET.get('count', 20)
    page = request.GET.get('page', 1)

    paginator = Paginator(persons, count)

    try:
        persons = paginator.page(page)
    except (PageNotAnInteger, EmptyPage):
        return redirect('contributors-persons')

    context = {
        'persons' : persons
    }

    return render(request, 'events/contributors_persons.html', context)

@login_required()
def show_person(request, person_slug):
    person = get_object_or_404(Person, slug=person_slug)

    context = {
        'person': person,
    }
    return render(request, 'events/person.html', context)

@login_required
@permission_required('events.change_person', raise_exception=PermissionDenied)
def edit_person(request, person_slug):
    person = get_object_or_404(Person, slug=person_slug)
    form = PersonForm(request.POST or None, instance=person)
    if request.method == 'POST':
        if form.is_valid():
            person = form.save()
            messages.success(request, "Person was updated")
            return redirect(person.get_absolute_url())
        else:
            messages.warning(request,"Please correct errors below")
    context = {
        'form': form,
        'person': person,
    }
    return render(request, 'events/person_form.html', context)
<|endoftext|>