import os
import collections
import uuid
import json

from qtpy import QtWidgets, QtCore, QtGui

from openpype.lib import FileDefItem
from openpype.tools.utils import (
    paint_image_with_color,
    ClickableLabel,
)
# TODO change imports
from openpype.tools.resources import get_image
from openpype.tools.utils import (
    IconButton,
    PixmapLabel
)

ITEM_ID_ROLE = QtCore.Qt.UserRole + 1
ITEM_LABEL_ROLE = QtCore.Qt.UserRole + 2
ITEM_ICON_ROLE = QtCore.Qt.UserRole + 3
FILENAMES_ROLE = QtCore.Qt.UserRole + 4
DIRPATH_ROLE = QtCore.Qt.UserRole + 5
IS_DIR_ROLE = QtCore.Qt.UserRole + 6
IS_SEQUENCE_ROLE = QtCore.Qt.UserRole + 7
EXT_ROLE = QtCore.Qt.UserRole + 8

def convert_bytes_to_json(bytes_value):
    if isinstance(bytes_value, QtCore.QByteArray):
        # Raw data are already QByteArray and we don't have to load them
        encoded_data = bytes_value
    else:
        encoded_data = QtCore.QByteArray.fromRawData(bytes_value)
    stream = QtCore.QDataStream(encoded_data, QtCore.QIODevice.ReadOnly)
    text = stream.readQString()
    try:
        return json.loads(text)
    except Exception:
        return None

def convert_data_to_bytes(data):
    bytes_value = QtCore.QByteArray()
    stream = QtCore.QDataStream(bytes_value, QtCore.QIODevice.WriteOnly)
    stream.writeQString(json.dumps(data))
    return bytes_value

class SupportLabel(QtWidgets.QLabel):
    pass

class DropEmpty(QtWidgets.QWidget):
    _empty_extensions = "Any file"

    def __init__(self, single_item, allow_sequences, extensions_label, parent):
        super(DropEmpty, self).__init__(parent)

        drop_label_widget = QtWidgets.QLabel("Drag & Drop files here", self)

        items_label_widget = SupportLabel(self)
        items_label_widget.setWordWrap(True)

        layout = QtWidgets.QVBoxLayout(self)
        layout.setContentsMargins(0, 0, 0, 0)
        layout.addSpacing(20)
        layout.addWidget(
            drop_label_widget, 0, alignment=QtCore.Qt.AlignCenter
        )
        layout.addSpacing(30)
        layout.addStretch(1)
        layout.addWidget(
            items_label_widget, 0, alignment=QtCore.Qt.AlignCenter
        )
        layout.addSpacing(10)

        for widget in (
            drop_label_widget,
            items_label_widget,
        ):
            widget.setAlignment(QtCore.Qt.AlignCenter)
            widget.setAttribute(QtCore.Qt.WA_TranslucentBackground)

        update_size_timer = QtCore.QTimer()
        update_size_timer.setInterval(10)
        update_size_timer.setSingleShot(True)

        update_size_timer.timeout.connect(self._on_update_size_timer)

        self._update_size_timer = update_size_timer

        if extensions_label and not extensions_label.startswith(" "):
            extensions_label = " " + extensions_label

        self._single_item = single_item
        self._extensions_label = extensions_label
        self._allow_sequences = allow_sequences
        self._allowed_extensions = set()
        self._allow_folders = None

        self._drop_label_widget = drop_label_widget
        self._items_label_widget = items_label_widget

        self.set_allow_folders(False)

    def set_extensions(self, extensions):
        if extensions:
            extensions = {
                ext.replace(".", "")
                for ext in extensions
            }
        if extensions == self._allowed_extensions:
            return
        self._allowed_extensions = extensions

        self._update_items_label()

    def set_allow_folders(self, allowed):
        if self._allow_folders == allowed:
            return

        self._allow_folders = allowed
        self._update_items_label()

    def _update_items_label(self):
        allowed_items = []
        if self._allow_folders:
            allowed_items.append("folder")

        if self._allowed_extensions:
            allowed_items.append("file")
            if self._allow_sequences:
                allowed_items.append("sequence")

        if not self._single_item:
            allowed_items = [item + "s" for item in allowed_items]

        if not allowed_items:
            self._drop_label_widget.setVisible(False)
            self._items_label_widget.setText(
                "It is not allowed to add anything here!"
            )
            return

        self._drop_label_widget.setVisible(True)
        items_label = "Multiple "
        if self._single_item:
            items_label = "Single "

        if len(allowed_items) == 1:
            extensions_label = allowed_items[0]
        elif len(allowed_items) == 2:
            extensions_label = " or ".join(allowed_items)
        else:
            last_item = allowed_items.pop(-1)
            new_last_item = " or ".join([last_item, allowed_items.pop(-1)])
            allowed_items.append(new_last_item)
            extensions_label = ", ".join(allowed_items)

        allowed_items_label = extensions_label

        items_label += allowed_items_label
        label_tooltip = None
        if self._allowed_extensions:
            items_label += " of\n{}".format(
                ", ".join(sorted(self._allowed_extensions))
            )

        if self._extensions_label:
            label_tooltip = items_label
            items_label = self._extensions_label

        if self._items_label_widget.text() == items_label:
            return

        self._items_label_widget.setToolTip(label_tooltip)
        self._items_label_widget.setText(items_label)
        self._update_size_timer.start()

    def resizeEvent(self, event):
        super(DropEmpty, self).resizeEvent(event)
        self._update_size_timer.start()

    def _on_update_size_timer(self):
        """Recalculate height of label with extensions.

        Dynamic QLabel with word wrap does not handle properly it's sizeHint
        calculations on show. This way it is recalculated. It is good practice
        to trigger this method with small offset using '_update_size_timer'.
        """

        width = self._items_label_widget.width()
        height = self._items_label_widget.heightForWidth(width)
        self._items_label_widget.setMinimumHeight(height)
        self._items_label_widget.updateGeometry()

    def paintEvent(self, event):
        super(DropEmpty, self).paintEvent(event)

        pen = QtGui.QPen()
        pen.setBrush(QtCore.Qt.darkGray)
        pen.setStyle(QtCore.Qt.DashLine)
        pen.setWidth(1)

        content_margins = self.layout().contentsMargins()
        rect = self.rect()
        left_m = content_margins.left() + pen.width()
        top_m = content_margins.top() + pen.width()
        new_rect = QtCore.QRect(
            left_m,
            top_m,
            (
                rect.width()
                - (left_m + content_margins.right() + pen.width())
            ),
            (
                rect.height()
                - (top_m + content_margins.bottom() + pen.width())
            )
        )

        painter = QtGui.QPainter(self)
        painter.setRenderHint(QtGui.QPainter.Antialiasing)
        painter.setPen(pen)
        painter.drawRect(new_rect)

class FilesModel(QtGui.QStandardItemModel):
    def __init__(self, single_item, allow_sequences):
        super(FilesModel, self).__init__()

        self._id = str(uuid.uuid4())
        self._single_item = single_item
        self._multivalue = False
        self._allow_sequences = allow_sequences

        self._items_by_id = {}
        self._file_items_by_id = {}
        self._filenames_by_dirpath = collections.defaultdict(set)
        self._items_by_dirpath = collections.defaultdict(list)

        self.rowsAboutToBeRemoved.connect(self._on_about_to_be_removed)
        self.rowsInserted.connect(self._on_insert)

    @property
    def id(self):
        return self._id

    def _on_about_to_be_removed(self, parent_index, start, end):
        """Make sure that removed items are removed from items mapping.

        Connected with '_on_insert'. When user drag item and drop it to same
        view the item is actually removed and creted again but it happens in
        inner calls of Qt.
        """

        for row in range(start, end + 1):
            index = self.index(row, 0, parent_index)
            item_id = index.data(ITEM_ID_ROLE)
            if item_id is not None:
                self._items_by_id.pop(item_id, None)

    def _on_insert(self, parent_index, start, end):
        """Make sure new added items are stored in items mapping.

        Connected to '_on_about_to_be_removed'. Some items are not created
        using '_create_item' but are recreated using Qt. So the item is not in
        mapping and if it would it would not lead to same item pointer.
        """

        for row in range(start, end + 1):
            index = self.index(start, end, parent_index)
            item_id = index.data(ITEM_ID_ROLE)
            if item_id not in self._items_by_id:
                self._items_by_id[item_id] = self.item(row)

    def set_multivalue(self, multivalue):
        """Disable filtering."""

        if self._multivalue == multivalue:
            return
        self._multivalue = multivalue

    def add_filepaths(self, items):
        if not items:
            return

        if self._multivalue:
            _items = []
            for item in items:
                if isinstance(item, (tuple, list, set)):
                    _items.extend(item)
                else:
                    _items.append(item)
            items = _items

        file_items = FileDefItem.from_value(items, self._allow_sequences)
        if not file_items:
            return

        if not self._multivalue and self._single_item:
            file_items = [file_items[0]]
            current_ids = list(self._file_items_by_id.keys())
            if current_ids:
                self.remove_item_by_ids(current_ids)

        new_model_items = []
        for file_item in file_items:
            item_id, model_item = self._create_item(file_item)
            new_model_items.append(model_item)
            self._file_items_by_id[item_id] = file_item
            self._items_by_id[item_id] = model_item

        if new_model_items:
            roow_item = self.invisibleRootItem()
            roow_item.appendRows(new_model_items)

    def remove_item_by_ids(self, item_ids):
        if not item_ids:
            return

        items = []
        for item_id in set(item_ids):
            if item_id not in self._items_by_id:
                continue
            item = self._items_by_id.pop(item_id)
            self._file_items_by_id.pop(item_id)
            items.append(item)

        if items:
            for item in items:
                self.removeRows(item.row(), 1)

    def get_file_item_by_id(self, item_id):
        return self._file_items_by_id.get(item_id)

    def _create_item(self, file_item):
        if file_item.is_dir:
            icon_pixmap = paint_image_with_color(
                get_image(filename="folder.png"), QtCore.Qt.white
            )
        else:
            icon_pixmap = paint_image_with_color(
                get_image(filename="file.png"), QtCore.Qt.white
            )

        item = QtGui.QStandardItem()
        item_id = str(uuid.uuid4())
        item.setData(item_id, ITEM_ID_ROLE)
        item.setData(file_item.label or "< empty >", ITEM_LABEL_ROLE)
        item.setData(file_item.filenames, FILENAMES_ROLE)
        item.setData(file_item.directory, DIRPATH_ROLE)
        item.setData(icon_pixmap, ITEM_ICON_ROLE)
        item.setData(file_item.lower_ext, EXT_ROLE)
        item.setData(file_item.is_dir, IS_DIR_ROLE)
        item.setData(file_item.is_sequence, IS_SEQUENCE_ROLE)

        return item_id, item

    def mimeData(self, indexes):
        item_ids = [
            index.data(ITEM_ID_ROLE)
            for index in indexes
        ]

        item_ids_data = convert_data_to_bytes(item_ids)
        mime_data = super(FilesModel, self).mimeData(indexes)
        mime_data.setData("files_widget/internal_move", item_ids_data)

        file_items = []
        for item_id in item_ids:
            file_item = self.get_file_item_by_id(item_id)
            if file_item:
                file_items.append(file_item.to_dict())

        full_item_data = convert_data_to_bytes({
            "items": file_items,
            "id": self._id
        })
        mime_data.setData("files_widget/full_data", full_item_data)
        return mime_data

    def dropMimeData(self, mime_data, action, row, col, index):
        item_ids = convert_bytes_to_json(
            mime_data.data("files_widget/internal_move")
        )
        if item_ids is None:
            return False

        # Find matching item after which will be items moved
        #   - store item before moved items are removed
        root = self.invisibleRootItem()
        if row >= 0:
            src_item = self.item(row)
        else:
            src_item_id = index.data(ITEM_ID_ROLE)
            src_item = self._items_by_id.get(src_item_id)

        src_row = None
        if src_item:
            src_row = src_item.row()

        # Take out items that should be moved
        items = []
        for item_id in item_ids:
            item = self._items_by_id.get(item_id)
            if item:
                self.takeRow(item.row())
                items.append(item)

        # Skip if there are not items that can be moved
        if not items:
            return False

        # Calculate row where items should be inserted
        row_count = root.rowCount()
        if src_row is None:
            src_row = row_count

        if src_row > row_count:
            src_row = row_count

        root.insertRow(src_row, items)
        return True

class FilesProxyModel(QtCore.QSortFilterProxyModel):
    def __init__(self, *args, **kwargs):
        super(FilesProxyModel, self).__init__(*args, **kwargs)
        self._allow_folders = False
        self._allowed_extensions = None
        self._multivalue = False

    def set_multivalue(self, multivalue):
        """Disable filtering."""

        if self._multivalue == multivalue:
            return
        self._multivalue = multivalue
        self.invalidateFilter()

    def set_allow_folders(self, allow=None):
        if allow is None:
            allow = not self._allow_folders

        if allow == self._allow_folders:
            return
        self._allow_folders = allow
        self.invalidateFilter()

    def set_allowed_extensions(self, extensions=None):
        if extensions is not None:
            _extensions = set()
            for ext in set(extensions):
                if not ext.startswith("."):
                    ext = ".{}".format(ext)
                _extensions.add(ext.lower())
            extensions = _extensions

        if self._allowed_extensions != extensions:
            self._allowed_extensions = extensions
            self.invalidateFilter()

    def are_valid_files(self, filepaths):
        for filepath in filepaths:
            if os.path.isfile(filepath):
                _, ext = os.path.splitext(filepath)
                if ext.lower() in self._allowed_extensions:
                    return True

            elif self._allow_folders:
                return True
        return False

    def filter_valid_files(self, filepaths):
        filtered_paths = []
        for filepath in filepaths:
            if os.path.isfile(filepath):
                _, ext = os.path.splitext(filepath)
                if ext.lower() in self._allowed_extensions:
                    filtered_paths.append(filepath)

            elif self._allow_folders:
                filtered_paths.append(filepath)
        return filtered_paths

    def filterAcceptsRow(self, row, parent_index):
        # Skip filtering if multivalue is set
        if self._multivalue:
            return True

        model = self.sourceModel()
        index = model.index(row, self.filterKeyColumn(), parent_index)
        # First check if item is folder and if folders are enabled
        if index.data(IS_DIR_ROLE):
            if not self._allow_folders:
                return False
            return True

        # Check if there are any allowed extensions
        if self._allowed_extensions is None:
            return False

        if index.data(EXT_ROLE) not in self._allowed_extensions:
            return False
        return True

    def lessThan(self, left, right):
        left_comparison = left.data(DIRPATH_ROLE)
        right_comparison = right.data(DIRPATH_ROLE)
        if left_comparison == right_comparison:
            left_comparison = left.data(ITEM_LABEL_ROLE)
            right_comparison = right.data(ITEM_LABEL_ROLE)

        if sorted((left_comparison, right_comparison))[0] == left_comparison:
            return True
        return False

class ItemWidget(QtWidgets.QWidget):
    context_menu_requested = QtCore.Signal(QtCore.QPoint)

    def __init__(
        self, item_id, label, pixmap_icon, is_sequence, multivalue, parent=None
    ):
        self._item_id = item_id

        super(ItemWidget, self).__init__(parent)

        self.setAttribute(QtCore.Qt.WA_TranslucentBackground)

        icon_widget = PixmapLabel(pixmap_icon, self)
        label_widget = QtWidgets.QLabel(label, self)

        label_size_hint = label_widget.sizeHint()
        height = label_size_hint.height()
        actions_menu_pix = paint_image_with_color(
            get_image(filename="menu.png"), QtCore.Qt.white
        )

        split_btn = ClickableLabel(self)
        split_btn.setFixedSize(height, height)
        split_btn.setPixmap(actions_menu_pix)
        if multivalue:
            split_btn.setVisible(False)
        else:
            split_btn.setVisible(is_sequence)

        layout = QtWidgets.QHBoxLayout(self)
        layout.setContentsMargins(5, 5, 5, 5)
        layout.addWidget(icon_widget, 0)
        layout.addWidget(label_widget, 1)
        layout.addWidget(split_btn, 0)

        split_btn.clicked.connect(self._on_actions_clicked)

        self._icon_widget = icon_widget
        self._label_widget = label_widget
        self._split_btn = split_btn
        self._actions_menu_pix = actions_menu_pix
        self._last_scaled_pix_height = None

    def _update_btn_size(self):
        label_size_hint = self._label_widget.sizeHint()
        height = label_size_hint.height()
        if height == self._last_scaled_pix_height:
            return
        self._last_scaled_pix_height = height
        self._split_btn.setFixedSize(height, height)
        pix = self._actions_menu_pix.scaled(
            height, height,
            QtCore.Qt.KeepAspectRatio,
            QtCore.Qt.SmoothTransformation
        )
        self._split_btn.setPixmap(pix)

    def showEvent(self, event):
        super(ItemWidget, self).showEvent(event)
        self._update_btn_size()

    def resizeEvent(self, event):
        super(ItemWidget, self).resizeEvent(event)
        self._update_btn_size()

    def _on_actions_clicked(self):
        pos = self._split_btn.rect().bottomLeft()
        point = self._split_btn.mapToGlobal(pos)
        self.context_menu_requested.emit(point)

class InViewButton(IconButton):
    pass

class FilesView(QtWidgets.QListView):
    """View showing instances and their groups."""

    remove_requested = QtCore.Signal()
    context_menu_requested = QtCore.Signal(QtCore.QPoint)

    def __init__(self, *args, **kwargs):
        super(FilesView, self).__init__(*args, **kwargs)

        self.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers)
        self.setSelectionMode(
            QtWidgets.QAbstractItemView.ExtendedSelection
        )
        self.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
        self.setAcceptDrops(True)
        self.setDragEnabled(True)
        self.setDragDropMode(QtWidgets.QAbstractItemView.InternalMove)

        remove_btn = InViewButton(self)
        pix_enabled = paint_image_with_color(
            get_image(filename="delete.png"), QtCore.Qt.white
        )
        pix_disabled = paint_image_with_color(
            get_image(filename="delete.png"), QtCore.Qt.gray
        )
        icon = QtGui.QIcon(pix_enabled)
        icon.addPixmap(pix_disabled, QtGui.QIcon.Disabled, QtGui.QIcon.Off)
        remove_btn.setIcon(icon)
        remove_btn.setEnabled(False)

        remove_btn.clicked.connect(self._on_remove_clicked)
        self.customContextMenuRequested.connect(self._on_context_menu_request)

        self._remove_btn = remove_btn
        self._multivalue = False

    def setSelectionModel(self, *args, **kwargs):
        """Catch selection model set to register signal callback.

        Selection model is not available during initialization.
        """

        super(FilesView, self).setSelectionModel(*args, **kwargs)
        selection_model = self.selectionModel()
        selection_model.selectionChanged.connect(self._on_selection_change)

    def set_multivalue(self, multivalue):
        """Disable remove button on multivalue."""

        self._multivalue = multivalue
        self._remove_btn.setVisible(not multivalue)

    def update_remove_btn_visibility(self):
        model = self.model()
        visible = False
        if not self._multivalue and model:
            visible = model.rowCount() > 0
        self._remove_btn.setVisible(visible)

    def has_selected_item_ids(self):
        """Is any index selected."""
        for index in self.selectionModel().selectedIndexes():
            instance_id = index.data(ITEM_ID_ROLE)
            if instance_id is not None:
                return True
        return False

    def get_selected_item_ids(self):
        """Ids of selected instances."""

        selected_item_ids = set()
        for index in self.selectionModel().selectedIndexes():
            instance_id = index.data(ITEM_ID_ROLE)
            if instance_id is not None:
                selected_item_ids.add(instance_id)
        return selected_item_ids

    def has_selected_sequence(self):
        for index in self.selectionModel().selectedIndexes():
            if index.data(IS_SEQUENCE_ROLE):
                return True
        return False

    def event(self, event):
        if event.type() == QtCore.QEvent.KeyPress:
            if (
                event.key() == QtCore.Qt.Key_Delete
                and self.has_selected_item_ids()
            ):
                self.remove_requested.emit()
                return True

        return super(FilesView, self).event(event)

    def _on_context_menu_request(self, pos):
        index = self.indexAt(pos)
        if index.isValid():
            point = self.viewport().mapToGlobal(pos)
            self.context_menu_requested.emit(point)

    def _on_selection_change(self):
        self._remove_btn.setEnabled(self.has_selected_item_ids())

    def _on_remove_clicked(self):
        self.remove_requested.emit()

    def _update_remove_btn(self):
        """Position remove button to bottom right."""

        viewport = self.viewport()
        height = viewport.height()
        pos_x = viewport.width() - self._remove_btn.width() - 5
        pos_y = height - self._remove_btn.height() - 5
        self._remove_btn.move(max(0, pos_x), max(0, pos_y))

    def resizeEvent(self, event):
        super(FilesView, self).resizeEvent(event)
        self._update_remove_btn()

    def showEvent(self, event):
        super(FilesView, self).showEvent(event)
        self._update_remove_btn()
        self.update_remove_btn_visibility()

class FilesWidget(QtWidgets.QFrame):
    value_changed = QtCore.Signal()

    def __init__(self, single_item, allow_sequences, extensions_label, parent):
        super(FilesWidget, self).__init__(parent)
        self.setAcceptDrops(True)

        empty_widget = DropEmpty(
            single_item, allow_sequences, extensions_label, self
        )

        files_model = FilesModel(single_item, allow_sequences)
        files_proxy_model = FilesProxyModel()
        files_proxy_model.setSourceModel(files_model)
        files_view = FilesView(self)
        files_view.setModel(files_proxy_model)

        layout = QtWidgets.QStackedLayout(self)
        layout.setContentsMargins(0, 0, 0, 0)
        layout.setStackingMode(QtWidgets.QStackedLayout.StackAll)
        layout.addWidget(empty_widget)
        layout.addWidget(files_view)
        layout.setCurrentWidget(empty_widget)

        files_proxy_model.rowsInserted.connect(self._on_rows_inserted)
        files_proxy_model.rowsRemoved.connect(self._on_rows_removed)
        files_view.remove_requested.connect(self._on_remove_requested)
        files_view.context_menu_requested.connect(
            self._on_context_menu_requested
        )

        self._in_set_value = False
        self._single_item = single_item
        self._multivalue = False

        self._empty_widget = empty_widget
        self._files_model = files_model
        self._files_proxy_model = files_proxy_model
        self._files_view = files_view

        self._widgets_by_id = {}

        self._layout = layout

    def _set_multivalue(self, multivalue):
        if self._multivalue is multivalue:
            return
        self._multivalue = multivalue
        self._files_view.set_multivalue(multivalue)
        self._files_model.set_multivalue(multivalue)
        self._files_proxy_model.set_multivalue(multivalue)
        self.setEnabled(not multivalue)

    def set_value(self, value, multivalue):
        self._in_set_value = True

        widget_ids = set(self._widgets_by_id.keys())
        self._remove_item_by_ids(widget_ids)

        self._set_multivalue(multivalue)

        self._add_filepaths(value)

        self._in_set_value = False

    def current_value(self):
        model = self._files_proxy_model
        item_ids = set()
        for row in range(model.rowCount()):
            index = model.index(row, 0)
            item_ids.add(index.data(ITEM_ID_ROLE))

        file_items = []
        for item_id in item_ids:
            file_item = self._files_model.get_file_item_by_id(item_id)
            if file_item is not None:
                file_items.append(file_item.to_dict())

        if not self._single_item:
            return file_items
        if file_items:
            return file_items[0]

        empty_item = FileDefItem.create_empty_item()
        return empty_item.to_dict()

    def set_filters(self, folders_allowed, exts_filter):
        self._files_proxy_model.set_allow_folders(folders_allowed)
        self._files_proxy_model.set_allowed_extensions(exts_filter)
        self._empty_widget.set_extensions(exts_filter)
        self._empty_widget.set_allow_folders(folders_allowed)

    def _on_rows_inserted(self, parent_index, start_row, end_row):
        for row in range(start_row, end_row + 1):
            index = self._files_proxy_model.index(row, 0, parent_index)
            item_id = index.data(ITEM_ID_ROLE)
            if item_id in self._widgets_by_id:
                continue
            label = index.data(ITEM_LABEL_ROLE)
            pixmap_icon = index.data(ITEM_ICON_ROLE)
            is_sequence = index.data(IS_SEQUENCE_ROLE)

            widget = ItemWidget(
                item_id,
                label,
                pixmap_icon,
                is_sequence,
                self._multivalue
            )
            widget.context_menu_requested.connect(
                self._on_context_menu_requested
            )
            self._files_view.setIndexWidget(index, widget)
            self._files_proxy_model.setData(
                index, widget.sizeHint(), QtCore.Qt.SizeHintRole
            )
            self._widgets_by_id[item_id] = widget

        if not self._in_set_value:
            self.value_changed.emit()

        self._update_visibility()

    def _on_rows_removed(self, parent_index, start_row, end_row):
        available_item_ids = set()
        for row in range(self._files_proxy_model.rowCount()):
            index = self._files_proxy_model.index(row, 0)
            item_id = index.data(ITEM_ID_ROLE)
            available_item_ids.add(index.data(ITEM_ID_ROLE))

        widget_ids = set(self._widgets_by_id.keys())
        for item_id in available_item_ids:
            if item_id in widget_ids:
                widget_ids.remove(item_id)

        for item_id in widget_ids:
            widget = self._widgets_by_id.pop(item_id)
            widget.setVisible(False)
            widget.deleteLater()

        if not self._in_set_value:
            self.value_changed.emit()
        self._update_visibility()

    def _on_split_request(self):
        if self._multivalue:
            return

        item_ids = self._files_view.get_selected_item_ids()
        if not item_ids:
            return

        for item_id in item_ids:
            file_item = self._files_model.get_file_item_by_id(item_id)
            if not file_item:
                return

            new_items = file_item.split_sequence()
            self._add_filepaths(new_items)
        self._remove_item_by_ids(item_ids)

    def _on_remove_requested(self):
        if self._multivalue:
            return

        items_to_delete = self._files_view.get_selected_item_ids()
        if items_to_delete:
            self._remove_item_by_ids(items_to_delete)

    def _on_context_menu_requested(self, pos):
        if self._multivalue:
            return

        menu = QtWidgets.QMenu(self._files_view)

        if self._files_view.has_selected_sequence():
            split_action = QtWidgets.QAction("Split sequence", menu)
            split_action.triggered.connect(self._on_split_request)
            menu.addAction(split_action)

        remove_action = QtWidgets.QAction("Remove", menu)
        remove_action.triggered.connect(self._on_remove_requested)
        menu.addAction(remove_action)

        menu.popup(pos)

    def dragEnterEvent(self, event):
        if self._multivalue:
            return

        mime_data = event.mimeData()
        if mime_data.hasUrls():
            filepaths = []
            for url in mime_data.urls():
                filepath = url.toLocalFile()
                if os.path.exists(filepath):
                    filepaths.append(filepath)

            if self._files_proxy_model.are_valid_files(filepaths):
                event.setDropAction(QtCore.Qt.CopyAction)
                event.accept()

        full_data_value = mime_data.data("files_widget/full_data")
        if self._handle_full_data_drag(full_data_value):
            event.setDropAction(QtCore.Qt.CopyAction)
            event.accept()

    def dragLeaveEvent(self, event):
        event.accept()

    def dropEvent(self, event):
        if self._multivalue:
            return

        mime_data = event.mimeData()
        if mime_data.hasUrls():
            event.accept()
            filepaths = []
            for url in mime_data.urls():
                filepath = url.toLocalFile()
                if os.path.exists(filepath):
                    filepaths.append(filepath)

            # Filter filepaths before passing it to model
            filepaths = self._files_proxy_model.filter_valid_files(filepaths)
            if filepaths:
                self._add_filepaths(filepaths)

        if self._handle_full_data_drop(
            mime_data.data("files_widget/full_data")
        ):
            event.setDropAction(QtCore.Qt.CopyAction)
            event.accept()

        super(FilesWidget, self).dropEvent(event)

    def _handle_full_data_drag(self, value):
        if value is None:
            return False

        full_data = convert_bytes_to_json(value)
        if full_data is None:
            return False

        if full_data["id"] == self._files_model.id:
            return False
        return True

    def _handle_full_data_drop(self, value):
        if value is None:
            return False

        full_data = convert_bytes_to_json(value)
        if full_data is None:
            return False

        if full_data["id"] == self._files_model.id:
            return False

        for item in full_data["items"]:
            filepaths = [
                os.path.join(item["directory"], filename)
                for filename in item["filenames"]
            ]
            filepaths = self._files_proxy_model.filter_valid_files(filepaths)
            if filepaths:
                self._add_filepaths(filepaths)

        if self._copy_modifiers_enabled():
            return False
        return True

    def _copy_modifiers_enabled(self):
        if (
            QtWidgets.QApplication.keyboardModifiers()
            & QtCore.Qt.ControlModifier
        ):
            return True
        return False

    def _add_filepaths(self, filepaths):
        self._files_model.add_filepaths(filepaths)

    def _remove_item_by_ids(self, item_ids):
        self._files_model.remove_item_by_ids(item_ids)

    def _update_visibility(self):
        files_exists = self._files_proxy_model.rowCount() > 0
        if files_exists:
            current_widget = self._files_view
        else:
            current_widget = self._empty_widget
        self._layout.setCurrentWidget(current_widget)
        self._files_view.update_remove_btn_visibility()


# -*- coding: utf-8 -*-
# vim:et:ft=python:nowrap:sts=4:sw=4:ts=4
#############################################################################

'''
Flight Data Utilities: Aircraft Configuration Information
'''

#############################################################################
# Flap Selections

##### FIXME: Can we avoid doing this model specific level?
####FLAP_MODEL_MAP = {
####    'CRJ 100LR': (0, 20, 30, 45),               # FAA TCDS A21EA Rev 31
####}

FLAP_SERIES_MAP = {
    '1900':      (0, 10, 20, 35),               # FAA TCDS A24CE Rev 106
    '1900C':     (0, 10, 20, 35),               # FAA TCDS A24CE Rev 106
    '1900D':     (0, 17.5, 35),                 # FAA TCDS A24CE Rev 106
    'A300B2':    (0, 8, 15, 25),                # FAA TCDS A35EU Rev 26
    'A300B2K':   (0, 8, 15, 25),                # FAA TCDS A35EU Rev 26
    'A300B4(F)': (0, 8, 15, 25),                # FAA TCDS A35EU Rev 26
    'A300F4':    (0, 15, 20, 40),               # FAA TCDS A35EU Rev 26
    'A340-200':  (0, 17, 22, 26, 32),           # FAA TCDS A43NM Rev 7
    'A340-300':  (0, 17, 22, 26, 32),           # FAA TCDS A43NM Rev 7
    'A340-500':  (0, 17, 22, 29, 34),           # FAA TCDS A43NM Rev 7 & FDS Customer #47 A330/A340 Flight Controls
    'A340-600':  (0, 17, 22, 29, 34),           # FAA TCDS A43NM Rev 7
    'ATR42-200': (0, 15, 30, 45),               # FAA TCDS A53EU Rev 21 (45 = Emergency)
    'ATR42-300': (0, 15, 30, 45),               # FAA TCDS A53EU Rev 21 (45 = Emergency)
    'ATR42-320': (0, 15, 30, 45),               # FAA TCDS A53EU Rev 21 (45 = Emergency)
    'ATR42-500': (0, 15, 25, 35),               # FAA TCDS A53EU Rev 21 (35 = Effective 33)
    'ATR72-100': (0, 15, 28),                   # FAA TCDS A53EU Rev 21
    'ATR72-200': (0, 15, 28),                   # FAA TCDS A53EU Rev 21
    'ATR72-210': (0, 15, 33),                   # FAA TCDS A53EU Rev 21 (-500 is -212A!)
    'DHC-8-100': (0, 5, 15, 35),                # FAA TCDS A13NM Rev 20
    'DHC-8-200': (0, 5, 15, 35),                # FAA TCDS A13NM Rev 20
    'DHC-8-300': (0, 5, 10, 15, 35),            # FAA TCDS A13NM Rev 20
    'DHC-8-400': (0, 5, 10, 15, 35),            # FAA TCDS A13NM Rev 20
    'ERJ-135BJ': (0, 9, 22, 45),                # FAA TCDS T00011AT Rev 29
    ####'Citation Sovereign': (0, 7, 15, 35),   # Smart Cockpit Citation Sovereign Flight Controls Mode; 680; FIXME
    ####'Citation Bravo':     (0, 15, 40),      # FAA TCDS A22CE Rev 65 Smart Cockpit Citation Bravo Limitations Model 550; FIXME
    ####'Citation CJ1':       (0, 15, 35, 60),  # FAA TCDS A1WI Rev 21 & Smart Cockpit Citation CJ1 Limitations (60 = prohibited in flight) Model 525A; FIXME
    ####'Citation CJ3':       (0, 15, 35, 55),  # FAA TCDS A1WI Rev 21 & Smart Cockpit Citation CJ3 Limitations (55 = Prohibited in flight) Model 525B; FIXME
    ####'Citation X':         (0, 5, 15, 35),   # Smart Cockpit Citation X Limitations Model 750; FIXME
    ####'Citation XLS':       (0, 7, 15, 35),   # FAA TCDS A22CE Rev 65; FIXME
}

FLAP_FAMILY_MAP = {
    'A310':    (0, 15, 20, 40),                        # FAA TCDS A35EU Rev 26
    'A318':    (0, 10, 15, 20, 40),                    # FAA TCDS A28NM Rev 11
    'A319':    (0, 10, 15, 20, 40),                    # FAA TCDS A28NM Rev 11
    'A320':    (0, 10, 15, 20, 35),                    # FAA TCDS A28NM Rev 11
    'A321':    (0, 10, 14, 21, 25),                    # FAA TCDS A28NM Rev 11
    'A330':    (0, 8, 14, 22, 32),                     # Smart Cockpit A330 General Limitions Rev 19
    'A380':    (0, 8, 17, 26, 33),                     # Smart Cockpit A380 Briefing For Pilots
    'BAE 146': (0, 18, 24, 30, 33),                    # FAA TCDS A49EU Rev 17 (Includes RJ85 & RJ100)
    'B727':    (0, 2, 5, 15, 25, 30, 40),              # Smart Cockpit B727 Flight Controls
    'B737 Classic': (0, 1, 2, 5, 10, 15, 25, 30, 40),  # Smart Cockpit B737E Flight Controls 9.10.13
    'B737 NG': (0, 1, 2, 5, 10, 15, 25, 30, 40),       # Smart Cockpit B_NG Flight Controls (1)
    'B747':    (0, 1, 5, 10, 20, 25, 30),              # Smart Cockpit B747-400 Flight Controls 9.10.8
    'B757':    (0, 1, 5, 15, 20, 25, 30),              # Smart Cockpit B757-200RR Flight Controls 9.10.8
    'B767':    (0, 1, 5, 15, 20, 25, 30),              # Smart Cockpit B767-300GE Flight Controls 9.10.10
    'B777':    (0, 1, 5, 15, 20, 25, 30),              # Smart Cockpit B777 Flight Controls 9.10.7
    'B787':    (0, 1, 5, 15, 20, 25, 30),              # FAA TCDS T00021SE Rev 6
    'CL600':   (0, 20, 30, 45),                        # FAA TCDS A21EA Rev 31
    'CL850':   (0, 8, 20, 30, 45),                     # FAA TCDS A21EA Rev 31
    'CRJ 100/200': (0, 8, 20, 30, 45),                 # FAA TCDS A21EA Rev 31
    'CRJ 700':   (0, 1, 8, 20, 30, 45),                # FAA TCDS A21EA Rev 31 & Smart Cockpit CRJ-700/900 Flight Controls Rev 3
    'CRJ 900':   (0, 1, 8, 20, 30, 45),                # FAA TCDS A21EA Rev 31 & Smart Cockpit CRJ-700/900 Flight Controls Rev 3
    'DC-9':    (0, 13, 20, 25, 30, 40),                # FAA TCDS A6WE Rev 28 (DC-9-81 & DC-9-82)
    'ERJ-135/145': (0, 9, 18, 22, 45),                 # FAA TCDS T00011AT Rev 29
    ####'ERJ-170/175': (0, 5, 10, 20, 35),             # FAA TCDS A56NM Rev 8; FIXME
    'ERJ190':  (0, 7, 10, 20, 37),                     # FAA TCDS A57NM Rev 9 & Smart Cockpit Embraer_190 Flight Controls & FAA TCDS A57NM Rev 9
    'F27':     (0, 5, 10, 15, 20, 25, 35),             # FAA TCDS A-817 Rev 21
    'F28':     (0, 8, 15, 25, 42),                     # FAA TCDS A20EU Rev 14
    'Falcon':  (0, 9, 20, 40),                         # FAA TCDS A59NM Rev 1 & Smart Cockpit 7X Flight Controls Issue 2
    'G550':    (0, 10, 20, 39),                        # FAA TCDS A12EA Rev 40
    'G-IV':    (0, 10, 20, 39),                        # FAA TCDS A12EA Rev 40
    'G-V':     (0, 10, 20, 39),                        # FAA TCDS A12EA Rev 40
    'Global':  (0, 6, 16, 30),                         # FAA TCDS T00003NY Rev 16 & Smart Cockpit G5000 Limitations Vol 1
    'L100':    (0, 50, 100),                           # FAA TCDS A1SO Rev 16 (100% = 36)
    'Learjet': (0, 8, 20, 40),                         # FAA TCDS T00008WI Rev 17
    'L1011':   (0, 4, 10, 14, 18, 22, 33),             # FAA TCDS A23WE Rev 19
    ####'MD-11':   (0, 15, 22, 25, 28, 35, 50),        # FAA TCDS A22WE Rev 12; FIXME
}

#############################################################################
# Slat Selections

SLAT_SERIES_MAP = {
    'A300B2':    (0, 20, 25),   # FAA TCDS A35EU Rev 26; FIXME: B2-203!
    'A300B2K':   (0, 16, 25),   # FAA TCDS A35EU Rev 26
    'A300B4(F)': (0, 16, 25),   # FAA TCDS A35EU Rev 26
    'A300F4':    (0, 15, 30),   # FAA TCDS A35EU Rev 26
    'A340-200':  (0, 20, 24),   # FAA TCDS A43NM Rev 7
    'A340-300':  (0, 20, 24),   # FAA TCDS A43NM Rev 7
    'A340-500':  (0, 21, 24),   # FAA TCDS A43NM Rev 7 & FDS Customer #47 A330/A340 Flight Controls
    'A340-600':  (0, 20, 23),   # FAA TCDS A43NM Rev 7
    ####'Citation X': (Not Extended, Extended),    # Smart Cockpit Citation X Limitations Model 750; FIXME
}

SLAT_FAMILY_MAP = {
    'A310':    (0, 15, 20, 30),    # FAA TCDS A35EU Rev 26
    'A318':    (0, 18, 22, 27),    # FAA TCDS A28NM Rev 11
    'A319':    (0, 18, 22, 27),    # FAA TCDS A28NM Rev 11
    'A320':    (0, 18, 22, 27),    # FAA TCDS A28NM Rev 11
    'A321':    (0, 18, 22, 27),    # FAA TCDS A28NM Rev 11
    'A330':    (0, 16, 20, 23),    # Smart Cockpit A330 General Limitions Rev 19
    'A380':    (0, 20, 23),        # Smart Cockpit A380 Briefing For Pilots
    'B787':    (0, 50, 100),       # Boeing 787 Operations Manual
    'CRJ 700': (0, 20, 25),        # FAA TCDS A21EA Rev 31 & Smart Cockpit CRJ-700/900 Flight Controls Rev 3
    'CRJ 900': (0, 20, 25),        # FAA TCDS A21EA Rev 31 & Smart Cockpit CRJ-700/900 Flight Controls Rev 3
    'DC-9':    (0, 17.8, 21),      # FAA TCDS A6WE Rev 28 (DC-9-81 & DC-9-82)
    'ERJ190':  (0, 15, 25),        # FAA TCDS A57NM Rev 9 & Smart Cockpit Embraer_190 Flight Controls
    ####'Falcon': (Not Extended, Extended),   # FAA TCDS A59NM Rev 1 & Smart Cockpit 7X Flight Controls Issue 2; FIXME
	#### Note: Slats Extended: Inboard Down = 20 deg, Median & Outboard Down = 35 deg
    'Global': (0, 20),             # (Not Extended, Extended) FAA TCDS T00003NY Rev 16 & Smart Cockpit G5000 Limitations Vol 1 & LFL doc
}

#############################################################################
# Aileron Selections

AILERON_SERIES_MAP = {
    'A340-500': (0, 10),        # FAA TCDS A43NM Rev 7 & AHY A330/A340 Flight Controls
}

AILERON_FAMILY_MAP = {
    'A330': (0, 5, 10),         # Smart Cockpit A330 General Limitions Rev 19
    'A380': (0, 5, 10),         # Smart Cockpit A380 Briefing For Pilots
}

#############################################################################
# Conf Selections

# Notes:
# - The series conf map will take precedence over the family conf map.
# - If using flap and slat to determine conf, only create a tuple of length 2
# - Each entry is of the form -- indication: (slat, flap, aileron)

CONF_SERIES_MAP = {
    'A340-200': {
        '0':    (0, 0),        # FAA TCDS A43NM Rev 7
        '1':    (20, 0),       # FAA TCDS A43NM Rev 7
        '1+F':  (20, 17),      # FAA TCDS A43NM Rev 7 (ECAM Indication = 1+F)
        '2':    (24, 22),      # FAA TCDS A43NM Rev 7
        '3':    (24, 26),      # FAA TCDS A43NM Rev 7
        'Full': (24, 32),      # FAA TCDS A43NM Rev 7
    },
    'A340-300': {
        '0':    (0, 0),        # FAA TCDS A43NM Rev 7
        '1':    (20, 0),       # FAA TCDS A43NM Rev 7
        '1+F':  (20, 17),      # FAA TCDS A43NM Rev 7 (ECAM Indication = 1+F)
        '2':    (24, 22),      # FAA TCDS A43NM Rev 7
        '3':    (24, 26),      # FAA TCDS A43NM Rev 7
        'Full': (24, 32),      # FAA TCDS A43NM Rev 7
    },
    'A340-500': {
        '0':    (0, 0, 0),     # FAA TCDS A43NM Rev 7
        '1':    (21, 0, 0),    # FAA TCDS A43NM Rev 7 & FDS Customer #47 A330/A340 Flight Controls
        '1+F':  (21, 17, 10),  # FAA TCDS A43NM Rev 7 (ECAM Indication = 1+F)
        '1*':   (24, 17, 10),  # FAA TCDS A43NM Rev 7 (ECAM Indication = 2)
        '2':    (24, 22, 10),  # FAA TCDS A43NM Rev 7 & FDS Customer #47 A330/A340 Flight Controls
        '3':    (24, 29, 10),  # FAA TCDS A43NM Rev 7 & FDS Customer #47 A330/A340 Flight Controls
        'Full': (24, 34, 10),  # FAA TCDS A43NM Rev 7 & FDS Customer #47 A330/A340 Flight Controls
    },
    'A340-600': {
        '0':    (0, 0),        # FAA TCDS A43NM Rev 7
        '1':    (20, 0),       # FAA TCDS A43NM Rev 7
        '1+F':  (20, 17),      # FAA TCDS A43NM Rev 7 (ECAM Indication = 1+F)
        '2':    (23, 22),      # FAA TCDS A43NM Rev 7
        '3':    (23, 29),      # FAA TCDS A43NM Rev 7
        'Full': (23, 34),      # FAA TCDS A43NM Rev 7
    },
}

CONF_FAMILY_MAP = {
    'A318': {
        '0':    (0, 0),        # FAA TCDS A28NM Rev 11
        '1':    (18, 0),       # FAA TCDS A28NM Rev 11
        '1+F':  (18, 10),      # FAA TCDS A28NM Rev 11 (ECAM Indication = 1+F)
        '2':    (22, 15),      # FAA TCDS A28NM Rev 11
        '3':    (22, 20),      # FAA TCDS A28NM Rev 11
        'Full': (27, 40),      # FAA TCDS A28NM Rev 11
    },
    'A319': {
        '0':    (0, 0),        # FAA TCDS A28NM Rev 11
        '1':    (18, 0),       # FAA TCDS A28NM Rev 11
        '1+F':  (18, 10),      # FAA TCDS A28NM Rev 11 (ECAM Indication = 1+F)
        '2':    (22, 15),      # FAA TCDS A28NM Rev 11
        '3':    (22, 20),      # FAA TCDS A28NM Rev 11
        'Full': (27, 40),      # FAA TCDS A28NM Rev 11
    },
    'A320': {
        '0':    (0, 0),        # FAA TCDS A28NM Rev 11
        '1':    (18, 0),       # FAA TCDS A28NM Rev 11
        '1+F':  (18, 10),      # FAA TCDS A28NM Rev 11 (ECAM Indication = 1+F)
        '2':    (22, 15),      # FAA TCDS A28NM Rev 11
        '3':    (22, 20),      # FAA TCDS A28NM Rev 11
        'Full': (27, 35),      # FAA TCDS A28NM Rev 11
    },
    'A321': {
        '0':    (0, 0),        # FAA TCDS A28NM Rev 11
        '1':    (18, 0),       # FAA TCDS A28NM Rev 11
        '1+F':  (18, 10),      # FAA TCDS A28NM Rev 11 (ECAM Indication = 1+F)
        '2':    (22, 14),      # FAA TCDS A28NM Rev 11
        '3':    (22, 21),      # FAA TCDS A28NM Rev 11
        'Full': (27, 25),      # FAA TCDS A28NM Rev 11
    },
    'A330': {
        '0':    (0, 0, 0),     # Smart Cockpit A330 General Limitions Rev 4
        '1':    (16, 0, 0),    # Smart Cockpit A330 General Limitions Rev 4
        '1+F':  (16, 8, 5),    # Smart Cockpit A330 General Limitions Rev 4 (ECAM Indication = 1+F)
        '1*':   (20, 8, 10),   # Smart Cockpit A330 General Limitions Rev 4 (ECAM Indication = 2)
        '2':    (20, 14, 10),  # Smart Cockpit A330 General Limitions Rev 4
        '2*':   (23, 14, 10),  # Smart Cockpit A330 General Limitions Rev 4 (ECAM Indication = 3)
        '3':    (23, 22, 10),  # Smart Cockpit A330 General Limitions Rev 4
        'Full': (23, 32, 10),  # Smart Cockpit A330 General Limitions Rev 4
    },
    'A380': {
        '0':    (0, 0, 0),     # Smart Cockpit A380 Briefing For Pilots
        '1':    (20, 0, 0),    # Smart Cockpit A380 Briefing For Pilots
        '1+F':  (20, 8, 5),    # Smart Cockpit A380 (ECAM Indication = 1+F)
        '2':    (20, 17, 5),   # Smart Cockpit A380 Briefing For Pilots
        '3':    (23, 26, 5),   # Smart Cockpit A380 Briefing For Pilots
        'Full': (23, 33, 10),  # Smart Cockpit A380 Briefing For Pilots
    },
    'B787': { # Flap settings must be integers.
        0:    (0, 0),
        1:    (50, 0),
        5:    (50, 5),
        15:   (50, 15),
        20:   (50, 20),
        25:   (100, 20),
        30:   (100, 30),
    },
    'CRJ 700': {
        '0':    (0, 0),        # FAA TCDS A21EA Rev 31 & Smart Cockpit CRJ-700/900 Flight Controls Rev 3
        '1':    (20, 0),       # FAA TCDS A21EA Rev 31 & Smart Cockpit CRJ-700/900 Flight Controls Rev 3
        '8':    (20, 8),       # FAA TCDS A21EA Rev 31 & Smart Cockpit CRJ-700/900 Flight Controls Rev 3
        '20':   (20, 20),      # FAA TCDS A21EA Rev 31 & Smart Cockpit CRJ-700/900 Flight Controls Rev 3
        '30':   (25, 30),      # FAA TCDS A21EA Rev 31 & Smart Cockpit CRJ-700/900 Flight Controls Rev 3
        '45':   (25, 45),      # FAA TCDS A21EA Rev 31 & Smart Cockpit CRJ-700/900 Flight Controls Rev 3
    },
    'CRJ 900': {
        '0':    (0, 0),        # FAA TCDS A21EA Rev 31 & Smart Cockpit CRJ-700/900 Flight Controls Rev 3
        '1':    (20, 0),       # FAA TCDS A21EA Rev 31 & Smart Cockpit CRJ-700/900 Flight Controls Rev 3
        '8':    (20, 8),       # FAA TCDS A21EA Rev 31 & Smart Cockpit CRJ-700/900 Flight Controls Rev 3
        '20':   (20, 20),      # FAA TCDS A21EA Rev 31 & Smart Cockpit CRJ-700/900 Flight Controls Rev 3
        '30':   (25, 30),      # FAA TCDS A21EA Rev 31 & Smart Cockpit CRJ-700/900 Flight Controls Rev 3
        '45':   (25, 45),      # FAA TCDS A21EA Rev 31 & Smart Cockpit CRJ-700/900 Flight Controls Rev 3
    },
    'ERJ190': {
        '0':    (0, 0),        # Smart Cockpit Embraer 190 Flight Controls & FAA TCDS A57NM Rev 9
        '1':    (15, 7),       # Smart Cockpit Embraer 190 Flight Controls & FAA TCDS A57NM Rev 9
        '2':    (15, 10),      # Smart Cockpit Embraer 190 Flight Controls & FAA TCDS A57NM Rev 9
        '3':    (15, 20),      # Smart Cockpit Embraer 190 Flight Controls & FAA TCDS A57NM Rev 9
        '4':    (25, 20),      # Smart Cockpit Embraer 190 Flight Controls & FAA TCDS A57NM Rev 9
        '5':    (25, 20),      # Smart Cockpit Embraer 190 Flight Controls & FAA TCDS A57NM Rev 9
        'Full': (25, 37),      # Smart Cockpit Embraer 190 Flight Controls & FAA TCDS A57NM Rev 9
    },
    ####'Falcon': {
    ####    '0'  : (0, 0),         # FAA TCDS A59NM Rev 1 & Smart Cockpit 7X Flight Controls Issue 2; FIXME
    ####    'SF1': (Extended, 9),  # FAA TCDS A59NM Rev 1 & Smart Cockpit 7X Flight Controls Issue 2; FIXME
    ####    'SF2': (Extended, 20), # FAA TCDS A59NM Rev 1 & Smart Cockpit 7X Flight Controls Issue 2; FIXME
    ####    'SF3': (Extended, 40), # FAA TCDS A59NM Rev 1 & Smart Cockpit 7X Flight Controls Issue 2; FIXME
	#### Note: Slats Extended: Inboard Down = 20 deg, Median & Outboard Down = 35 deg
    ####},
}

#############################################################################
# Imports

import logging
import numpy as np

from itertools import imap

#############################################################################
# Globals

logger = logging.getLogger(name=__name__)

#############################################################################
# Accessors

def get_flap_detents():
    '''
    Get all flap combinations from all supported aircraft types

    :returns: list of detent values
    :rtype: list
    '''
    all_detents = set()
    for detents in FLAP_SERIES_MAP.itervalues():
        all_detents.update(detents)
    for detents in FLAP_FAMILY_MAP.itervalues():
        all_detents.update(detents)
    return sorted(all_detents)

def get_conf_detents():
    '''
    Get all conf combinations from all supported aircraft types

    :returns: list of detent values
    :rtype: list
    '''
    all_detents = set()
    for detents in imap(dict.keys, CONF_SERIES_MAP.itervalues()):
        all_detents.update(detents)
    for detents in imap(dict.keys, CONF_FAMILY_MAP.itervalues()):
        all_detents.update(detents)
    return sorted(all_detents)

def get_slat_detents():
    '''
    Get all slat detents from all supported aircraft types

    :returns: list of detent values
    :rtype: list
    '''
    all_detents = set()
    for detents in SLAT_SERIES_MAP.itervalues():
        all_detents.update(detents)
    for detents in SLAT_FAMILY_MAP.itervalues():
        all_detents.update(detents)
    return sorted(all_detents)

def get_flap_map(series=None, family=None):
    '''
    Accessor for fetching flap mapping parameters.

    :param series: Aircraft series e.g. B737-300
    :type series: String
    :param family: Aircraft family e.g. B737
    :type family: String
    :raises: KeyError if no mapping found
    :returns: list of detent values
    :rtype: list
    '''
    if series in FLAP_SERIES_MAP:
        return FLAP_SERIES_MAP[series]
    if family in FLAP_FAMILY_MAP:
        return FLAP_FAMILY_MAP[family]
    raise KeyError("No flap mapping for series '%s', family '%s'." %
                   (series, family))

def get_flap_values_mapping(series, family, flap_param=None):
    '''
    Get the flap mapping for the aircraft type. Should this not be
    available, rounds the available flap array to the nearest 5 degrees.

    If flap_param is None, raises KeyError as no fallback available.

    Returns the values mapping:
    { int(flap angle) : str(flap angle) }

    :param series: Aircraft Series with .value attribute
    :type series: Attribute
    :param family: Aircraft Family with .value attribute
    :type family: Attribute
    :param flap_param: Recorded flap parameter.
    :type flap_param: Parameter
    :returns: Values Mapping for each flap setting for provided aircraft type
    :rtype: Dict
    '''
    try:
        flap_steps = get_flap_map(series.value, family.value)
    except KeyError:
        # no flaps mapping, round to nearest 5 degrees
        logger.warning("No flap settings for series '%s' family '%s' - "
                       "rounding to nearest 5", series.value, family.value)
        if flap_param is None:
            raise
        # round to nearest 5 degrees (as per round_to_nearest)
        step = 5.0
        array = np.ma.round(flap_param.array / step) * step
        flap_steps = [int(f) for f in np.ma.unique(array)
                      if f is not np.ma.masked]
    return {int(f): str(f) for f in flap_steps}

def get_slat_map(series=None, family=None):
    '''
    Accessor for fetching slat mapping parameters.

    :param series: Aircraft series e.g. B737-300
    :type series: String
    :param family: Aircraft family e.g. B737
    :type family: String
    :raises: KeyError if no mapping found
    :returns: list of detent values
    :rtype: list
    '''
    if series in SLAT_SERIES_MAP:
        return SLAT_SERIES_MAP[series]
    if family in SLAT_FAMILY_MAP:
        return SLAT_FAMILY_MAP[family]
    raise KeyError("No slat mapping for series '%s', family '%s'." %
                   (series, family))

def get_aileron_map(series=None, family=None):
    '''
    Accessor for fetching aileron mapping parameters. Note this is used for
    generating the Flaperon parameter.

    :param series: Aircraft series e.g. A340-500
    :type series: String
    :param family: Aircraft family e.g. A330
    :type family: String
    :raises: KeyError if no mapping found
    :returns: list of detent values
    :rtype: list
    '''
    if series in AILERON_SERIES_MAP:
        return AILERON_SERIES_MAP[series]
    if family in AILERON_FAMILY_MAP:
        return AILERON_FAMILY_MAP[family]
    raise KeyError("No aileron mapping for series '%s', family '%s'" %
                   (series, family))

def get_conf_map(series=None, family=None):
    '''
    Accessor for fetching conf mapping parameters.

    Return is a dictionary of state: tuple where tuple can contain either
    (slat, flap) or (slat, flap, aileron) depending on Aircraft requirements.

    :param series: Aircraft series e.g. B737-300
    :type series: String
    :param family: Aircraft family e.g. B737
    :type family: String
    :raises: KeyError if no mapping found
    :returns: conf mapping
    :rtype: dict
    '''
    if series in CONF_SERIES_MAP:
        return CONF_SERIES_MAP[series]
    if family in CONF_FAMILY_MAP:
        return CONF_FAMILY_MAP[family]
    raise KeyError("No conf mapping for series '%s', family '%s'." %
                   (series, family))


import random
while True:
    user_choice=input("Enter teh choice from the given set ( rock,paper,scissors)")
    computer_actions=["rock","paper","scissor"]
    comp_choice=random.choice(computer_actions)
    print(f"\n You choice{user_choice} \n computer choice {comp_choice}.\n")
    if user_choice==comp_choice:
        print(f"Both players selected { user_choice} \n Tie!!!!!")
    elif comp_choice=="rock":
        if user_choice=="scissor":
            print("Rock smashes scissors! You loss!")
        else:
            print("Paper covers rock! You Win")
    elif comp_choice=="scissor":
        if user_choice=="paper":
            print("scissors cuts paper! You loss!")
        else:
            print("Rock smashes scissors! You Win")
    elif comp_choice=="paper":
        if user_choice=="scissor":
            print("scissors cuts paper! You Win !")
        else:
            print("Paper covers rock! You loss")
    play_again=input=("Play again ? (y/n):")
    if play_again.lower()!="y":
        break

    #or convert choice into number using random variable
    """randNO=random.randint(1,3)  if randNO==1: comp='rock' like this"""


import configparser
import psycopg2
from sql_queries import copy_table_queries, insert_table_queries

def load_staging_tables(cur, conn):

    """ queries that loads data from S3 buckets
to Redshift"""

    for query in copy_table_queries:
        cur.execute(query)
        conn.commit()

def insert_tables(cur, conn):

    """ INSERT statements from staging tables to
the dimension and fact tables"""

    for query in insert_table_queries:
        cur.execute(query)
        conn.commit()

def main():
    """
    Extract songs metadata and user activity data from S3, transform it using a staging table, and load it into dimensional tables for analysis
    """
    config = configparser.ConfigParser()
    config.read('dwh.cfg')

    conn = psycopg2.connect("host={} dbname={} user={} password={} port={}".format(*config['CLUSTER'].values()))
    cur = conn.cursor()

#    load_staging_tables(cur, conn)
    insert_tables(cur, conn)

    conn.close()

if __name__ == "__main__":
    main()


import argparse
import re
import sys
from typing import Optional

import boto3  # type: ignore
from botocore.exceptions import NoCredentialsError  # type: ignore

pattern_with_minutes = r"(\d{4}-\d{2}-\d{2}T\d{2}:\d{2})"  # "YYYY-MM-DDTHH:MM"
pattern_without_minutes = r"(\d{4}-\d{2}-\d{2}T\d{2})"  # "YYYY-MM-DDTHH"
iso_date_with_minutes = re.compile(pattern_with_minutes)
iso_date_without_minutes = re.compile(pattern_without_minutes)

def list_objects(bucket_name: str, prefix: Optional[str] = None) -> list[str]:
    """
    Lists all objects in the specified S3 bucket matching the given prefix.
    Returns a list of object keys.
    """
    s3 = boto3.client("s3")
    keys: list[str] = []

    paginator = s3.get_paginator("list_objects_v2")
    page_iterator = paginator.paginate(Bucket=bucket_name, Prefix=prefix)

    for page in page_iterator:
        if "Contents" in page:
            keys.extend(obj["Key"] for obj in page["Contents"])
    return keys

def filter_objects(object_keys: list[str]) -> list[str]:
    """
    Filters the list of object keys based on the provided regex pattern.
    Returns a list of filtered object keys.
    """
    return [key for key in object_keys if detect_timestamps_without_minutes(key)]

def detect_timestamps_without_minutes(key: str) -> Optional[str]:
    """
    Returns the string if it contains a YYYY-MM-DDTHH timestamp.
    Otherwise, return None
    """
    match_with_minutes = iso_date_with_minutes.search(key)
    match_without_minutes = iso_date_without_minutes.search(key)

    return key if match_without_minutes and not match_with_minutes else None

def update_timestamp(string) -> str:
    """Updates YYYY-MM-DDTHH timestamps with a :MM field set to the start of the hour"""
    if iso_date_with_minutes.search(string):
        return string
    return re.sub(r"(^.*\d{4}-\d{2}-\d{2}T\d{2})(.*$)", r"\1:00\2", string)

def rename_objects(bucket_name: str, object_keys: list[str]) -> None:
    """
    Renames the objects in the specified S3 bucket by adding ":00" to the object key.
    """
    s3 = boto3.client("s3")

    for key in object_keys:
        new_key = update_timestamp(key)

        print(f"Copying: {key} to {new_key}")
        s3.copy_object(
            Bucket=bucket_name,
            CopySource={"Bucket": bucket_name, "Key": key},
            Key=new_key,
        )

        print(f"Removing old key: {key}")
        s3.delete_object(Bucket=bucket_name, Key=key)

        print(f"Renamed object: {key} to {new_key}")

def process_objects(bucket_name: str, prefix: str, dry_run: bool) -> None:
    """
    Process objects in the specified S3 bucket by listing, filtering, and renaming them.
    """
    # List all objects in the bucket
    object_keys = list_objects(bucket_name, prefix)
    print(f"Number of keys found in the bucket: {len(object_keys)}")

    # Filter the objects based on regex pattern
    filtered_keys = filter_objects(object_keys)
    print("Filtered keys are: ")
    print(filtered_keys)
    print(f"Number of keys without minutes: {len(filtered_keys)}")

    # Rename the filtered objects
    if not dry_run:
        rename_objects(bucket_name, filtered_keys)

def main():
    parser = argparse.ArgumentParser()
    parser.add_argument(
        "--bucket",
        type=str,
        required=True,
        help="The S3 bucket name like: my-s3-bucket",
    )
    parser.add_argument(
        "--prefix",
        type=str,
        default="",
        required=False,
        help="The S3 bucket prefix to rename like: diagnostics.zarr",
    )
    parser.add_argument(
        "--dry-run",
        default=True,
        required=False,
        help="Do a dry run - everything up to renaming objects in the S3 bucket",
        action=argparse.BooleanOptionalAction,
    )
    args = parser.parse_args()

    if args.dry_run:
        print(
            "Dry run - Objects won't be renamed in S3. \n\n"
            "Use --no-dry-run once you've confirmed the results are as desired.\n"
        )

    try:
        process_objects(args.bucket, args.prefix, args.dry_run)
    except NoCredentialsError:
        sys.exit("Error: Unable to locate credentials")


from django.shortcuts import render, get_object_or_404, redirect
from django.http import JsonResponse
from django.utils import timezone
from django.contrib import messages
from django.core.exceptions import ObjectDoesNotExist

from django.views.generic import *
from .forms import *

class ClientMixin(object):
    def get_context_data(self, *args, **kwargs):
        context = super().get_context_data(**kwargs)
        context['items'] = Item.objects.all()
        qs = Order.objects.filter(
            user=self.request.user, ordered=False)
        if qs.exists():
            context['count'] = qs[0].items.count()
        else:
            context['count'] = 0
        print(context['count'], 'rrr')
        context['object'] = Order.objects.get(
            user=self.request.user, ordered=False)

        return context

class HomeView(ClientMixin, TemplateView):
    template_name = 'home.html'

class ItemDetailView(ClientMixin, DetailView):
    model = Item
    template_name = 'itemdetail.html'
    context_object_name = 'itemdetail'

    def get_context_data(self, *args, **kwargs):
        context = super().get_context_data(**kwargs)
        context['item_id'] = self.kwargs['pk']
        print(context['item_id'])
        return context

class OrderSummeryView(ClientMixin, TemplateView):
    template_name = 'ordersummery.html'

    # def get(self, *args, **kwargs):
    #     try:
    #         order = Order.objects.get(user=self.request.user, ordered=False)
    #         context = {
    #             'object': order
    #         }
    #         return render(self.request, self.template_name, context)
    #     except ObjectDoesNotExist:
    #         messages.error(self.request, 'you do not have active order')
    #         return redirect('/')

def add_to_cart(request, pk):
    item = get_object_or_404(Item, pk=pk)
    print(item.title)
    print(item, 'narendra')
    order_item, created = OrderItem.objects.get_or_create(
        item=item, user=request.user, ordered=False)
    print(order_item, created, 'ram thapa')
    order_qs = Order.objects.filter(user=request.user, ordered=False)
    print(order_qs, 'order_qs')

    if order_qs.exists():
        order = order_qs[0]
        print(order, 'order')
        if order.items.filter(item__id=item.id).exists():
            order_item.quantity += 1
            order_item.save()
            messages.info(request, 'this item quantity was updated')
            return redirect('jqueryapp:ordersummery')
        else:

            order.items.add(order_item)
            messages.info(request, 'this item was added to your cart')
            return redirect('jqueryapp:ordersummery')
    else:
        ordered_date = timezone.now()
        order = Order.objects.create(
            user=request.user, ordered_date=ordered_date)
        order.items.add(order_item)
        messages.info(request, 'this item was added to your cart')
    return redirect('jqueryapp:ordersummery')

def remove_from_cart(request, pk):
    item = get_object_or_404(Item, pk=pk)
    order_qs = Order.objects.filter(user=request.user, ordered=False)
    if order_qs.exists():
        order = order_qs[0]
        if order.items.filter(item__id=item.id).exists():
            order_item = OrderItem.objects.filter(
                item=item, user=request.user, ordered=False)[0]
            order.items.remove(order_item)
            messages.info(request, 'this item was removed from your cart')
            return redirect('jqueryapp:ordersummery')
        else:
            messages.info(request, 'this item was not in your cart')
            return redirect('jqueryapp:ordersummery')
    else:
        messages.info(request, 'you donot have an active order')

        return redirect('jqueryapp:ordersummery')

def remove_single_item_from_cart(request, pk):
    item = get_object_or_404(Item, pk=pk)
    order_qs = Order.objects.filter(user=request.user, ordered=False)
    if order_qs.exists():
        order = order_qs[0]
        if order.items.filter(item__id=item.id).exists():
            order_item = OrderItem.objects.filter(
                item=item, user=request.user, ordered=False)[0]
            if order_item.quantity > 1:
                order_item.quantity -= 1
                order_item.save()

            else:
                order.items.remove(order_item)

            messages.info(request, 'the item quantity was updated')

            return redirect('jqueryapp:ordersummery')
        else:
            messages.info(request, 'this item was not in your cart')
            return redirect('jqueryapp:product', pk=pk)
    else:
        messages.info(request, 'you donot have an active order')

        return redirect('jqueryapp:product', pk=pk)

class CheckOutView(ClientMixin, TemplateView):
    template_name = 'checkout.html'


#!/usr/bin/env python3

import sys
import statistics
import pandas as pd
from plotnine import *

def main():
  file = sys.argv[1]
  plotCsv(file)

def plotCsv(file):
  print("Plotting results")
  data = pd.read_csv(file, decimal=",", sep=";")
  plot = ggplot(data, aes(x='factor(algorithm)', y='nm', fill='factor(algorithm)')) + geom_violin(draw_quantiles=[0.25, 0.5, 0.75], show_legend=False) + scale_y_continuous(trans="log1p", breaks = [0, 1, 5, 10, 20, 50, 100, 1000]) + ylab("Number of move actions") + xlab("Algorithm") + scale_fill_manual(values=cb_palette()) + theme_light()
  plot.save(file + "_mv.pdf")
  plot = ggplot(data, aes(x='factor(algorithm)', y='nu', fill='factor(algorithm)')) + geom_violin(draw_quantiles=[0.25, 0.5, 0.75], show_legend=False) + scale_y_continuous(trans="log1p", breaks = [0, 1, 5, 10, 20, 50, 100, 1000]) + ylab("Number of update actions") + xlab("Algorithm") + scale_fill_manual(values=cb_palette()) + theme_light()
  plot.save(file + "_upd.pdf")
  plot = ggplot(data, aes(x='factor(algorithm)', y='s', fill='factor(algorithm)')) + geom_violin(draw_quantiles=[0.25, 0.5, 0.75], show_legend=False) + scale_y_continuous(trans="log1p", breaks = [0, 1, 5, 10, 20, 50, 100, 1000]) + ylab("Number of actions") + xlab("Algorithm") + scale_fill_manual(values=cb_palette()) + theme_light()
  plot.save(file + "_size.pdf")
  data['runtime'] = data.apply (lambda row: statistics.median([row['t'], row['t.1'], row['t.2'], row['t.3'], row['t.4']]), axis = 1)
  plot = ggplot(data, aes(x='factor(algorithm)', y='runtime / 1_000_000_000', fill='factor(algorithm)')) + geom_violin(draw_quantiles=[0.25, 0.5, 0.75], show_legend=False) + scale_y_continuous(trans="log10", breaks = [0, 0.5, 1, 10, 100, 1000]) + ylab("Runtime (s)") + xlab("Algorithm") + scale_fill_manual(values=cb_palette()) + theme_light()
  plot.save(file + "_runtime.pdf")

def cb_palette():
  return ["#E69F00", "#56B4E9", "#009E73", "#F0E442", "#0072B2", "#D55E00", "#CC79A7"]

if __name__ == "__main__":
    main()


from PIL import Image

image1 = Image.open('Desert.jpg')
image2 = Image.open('Lighthouse.jpg')
image3 = Image.open('Koala.jpg')

print('------------------')

print('Image1 Size ', image1.size)

print('------------------')

print('Image2 Size ', image2.size)
# --------------------Image-1-----------------
# ---------Cropping---------

# i am putting image 2 into image 1
area = (100, 100, 200, 200)
image4 = image2.crop(area)
# image4.show()
print('Image 3 size ', image4.size)

# ---------Merging---------

image1, image4

area = (824, 100, 924, 200)
image1.paste(image4, area)

# image1.show()

# --------------------Image-2-----------------

# -----------Cropping-------------

area = (400, 250, 700, 550)
image5 = image3.crop(area)
print(image5.size)

# ---------Merging---------

image1, image5
area = (100, 100, 400, 400)
image1.paste(image5, area)
image1.show()


from an_adventure import start
import argparse

def main():

    parser = argparse.ArgumentParser(description="Starts an adventure!")
    parser.add_argument("-s", "--skip-intro", dest="skipIntro", action="store_true", default=False, help="skip the introduction")

    args = vars(parser.parse_args())

    start(**args)

if __name__ == "__main__":
    main()


from django.shortcuts import render
from django.http import HttpResponse, JsonResponse
from django.db import connection
import mysql.connector
import json
from PIL import Image
from io import BytesIO
import base64
import os
import torch
import yolov5
import random
from .models import User
import datetime

from rest_framework.decorators import api_view
from rest_framework.response import Response

# Create your views here.

@api_view(['GET'])
def apiOverView(request):

    print("jee")
    # api_urls = {
    #    'list':'/task-list/',
    #    'create':'/task-create/'
    # }

    return JsonResponse({'data': 'bien'}, safe=False)

def doYolo(img):
    print('1')
    # model = torch.hub.load('ultralytics/yolov5', 'yolov5s')
    # model = torch.load(os.getcwd() + '/yolov5s.pt')
    # torch.hub.set_dir(os.getcwd())
    # model = torch.hub.load('ultralytics/yolov5', 'custom', path=os.getcwd() + '/yolov5s.pt',trust_repo=True, source='local')
    # model = torch.hub.load(os.getcwd()+'/ultralytics_yolov5_master', 'custom', path= os.getcwd() + '/yolov5s.pt', source='local', skip_validation=True)

    model = yolov5.load(os.getcwd() + '/best200epochsSGD.pt')
    print(img)

    results = model(img)
    detectedImage = Image.fromarray(results.render()[0])
    # results.show()
    size = len(results.pandas().xyxy[0])

    resultGood = []
    resultBad = []

    tobyteGood = []
    tobyteBad = []
    namesGood = []
    namesBad = []

    names = []
    average = 0

    for x in range(size):
        average += results.pandas().xyxy[0].confidence[x]
        if average < 0.85:
            continue

        left = results.pandas().xyxy[0].xmin[x]
        top = results.pandas().xyxy[0].ymin[x]
        right = results.pandas().xyxy[0].xmax[x]
        bottom = results.pandas().xyxy[0].ymax[x]

        imgtemp = detectedImage
        imgcrop = imgtemp.crop((left, top, right, bottom))

        if results.pandas().xyxy[0].name[x].split('ME')[0] == '':
            namesBad.append(results.pandas().xyxy[0].name[x])
            tobyteBad.append(imgcrop)
        else:
            namesGood.append(results.pandas().xyxy[0].name[x])
            tobyteGood.append(imgcrop)

    buffered = BytesIO()

    detectedImage.save(buffered, format="JPEG")
    img_str = base64.b64encode(buffered.getvalue())
    img_base64 = bytes("data:image/jpeg;base64,", encoding='utf-8') + img_str
    final = img_base64.decode('UTF-8')

    resultGood.append(final)

    for x in range(len(tobyteGood)):
        buffered = BytesIO()
        tobyteGood[x].save(buffered, format="JPEG")
        img_str = base64.b64encode(buffered.getvalue())
        img_base64 = bytes("data:image/jpeg;base64,",
                           encoding='utf-8') + img_str
        finalcrop = img_base64.decode('UTF-8')
        resultGood.append(finalcrop)

    for x in range(len(tobyteBad)):
        buffered = BytesIO()
        tobyteBad[x].save(buffered, format="JPEG")
        img_str = base64.b64encode(buffered.getvalue())
        img_base64 = bytes("data:image/jpeg;base64,",
                           encoding='utf-8') + img_str
        finalcrop = img_base64.decode('UTF-8')
        resultBad.append(finalcrop)

    if len(resultGood) > 1 or len(resultBad) > 0:
        average = average / ((len(resultGood) - 1) + len(resultBad))

    return resultGood, resultBad,round(average, 2), namesGood, namesBad

@api_view(['POST'])
def detect(request):
    data = request.data

    data = json.dumps(data)
    data = json.loads(data)

    if data['image'].split('/')[1].split(';')[0] == 'jpeg':
        imgb64 = data['image'].split('data:image/jpeg;base64,')[1]
    if data['image'].split('/')[1].split(';')[0] == 'png':
        imgb64 = data['image'].split('data:image/png;base64,')[1]
    im = Image.open(BytesIO(base64.b64decode(imgb64)))
    #im = Image.open(os.getcwd() + '/image.jpg')
    resultGood, resultBad,average, namesGood, namesBad = doYolo(im)

    print(average, namesGood, namesBad)
    return JsonResponse(
        {
            'resultGood': resultGood,
            'resultBad': resultBad,
            'count': (len(resultGood) - 1) + len(resultBad),
            'average': average,
            'namesGood': namesGood,
            'namesBad': namesBad
        }, safe=False)

def doYoloState(img):
    model = yolov5.load(os.getcwd() + '/temp.pt')
    results = model(img)
    #detectedImage = Image.fromarray(results.render()[0])

    average = results.pandas().xyxy[0].confidence[0]

    arr = ['Bueno', 'Malo']
    val = random.randint(0, 1)
    state = arr[val]

    #buffered = BytesIO()
    #detectedImage.save(buffered, format="JPEG")
    #img_str = base64.b64encode(buffered.getvalue())
    #img_base64 = bytes("data:image/jpeg;base64,", encoding='utf-8') + img_str
    #final = img_base64.decode('UTF-8')

    return round(average, 2), state

@api_view(['POST'])
def detectState(request):
    data = request.data

    data = json.dumps(data)
    data = json.loads(data)

    imgb64 = data['image'].split('data:image/jpeg;base64,')[1]
    im = Image.open(BytesIO(base64.b64decode(imgb64)))

    average, state = doYoloState(im)

    return JsonResponse(
        {
            'average': average,
            'state': state
        }, safe=False)

@api_view(['POST'])
def registerUser(request):
    try:
        data = request.data
        print(data['email'])

        cursor = connection.cursor()
        mySql_insert_query = """INSERT INTO User (email, password, enterprise)
                               VALUES
                               (%s, %s, %s) """

        record = (data['email'], data['password'], data['enterprise'])
        cursor.execute(mySql_insert_query, record)

        connection.commit()

        return HttpResponse(status=201)
    except mysql.connector.Error as error:
        return HttpResponse(status=500)

@api_view(['POST'])
def registerDetection(request):
    try:
        data = request.data
        cursor = connection.cursor()
        mySql_insert_query = """INSERT INTO DetectionResult (user_id, count, percentage, date, names, nametype,images)
                               VALUES
                               (%s, %s, %s, %s, %s, %s, %s) """

        record = (data['user_id'], data['count'], data['percentage'], datetime.datetime.now(),  data['names'], data['nametype'],data['images'])
        cursor.execute(mySql_insert_query, record)

        connection.commit()

        return HttpResponse(status=201)
    except mysql.connector.Error as error:
        return HttpResponse(status=500)

@api_view(['POST'])
def loginUser(request):
    try:
        data = request.data
        cursor = connection.cursor()
        sql_select_query = """select * from User where email = %s and password = %s"""
        # set variable in query
        record = (data['email'], data['password'])
        cursor.execute(sql_select_query, record)

        print(data)

        result = cursor.fetchall()

        print(result[0][0])

        if len(result) < 1:
            return JsonResponse({'error': 'ERROR'}, safe=False)
        else:
            res = {'id': result[0][0], 'email': result[0][1], 'enterprise': result[0][3]}
            return JsonResponse(
                {
                    'data': res
                }, safe=False)

    except mysql.connector.Error as error:
        return JsonResponse({'error': 'ERROR'}, safe=False)

def dictfetchall(cursor):
    "Return all rows from a cursor as a dict"
    columns = [col[0] for col in cursor.description]
    return [
        dict(zip(columns, row))
        for row in cursor.fetchall()
    ]

@api_view(['POST'])
def getDetection(request):
    try:
        data = request.data

        cursor = connection.cursor()
        sql_select_query = """select * from DetectionResult where user_id = %s and date = %s and nametype = %s"""
        record = (data['user_id'], data['date'], data['producttype'])
        cursor.execute(sql_select_query, record)
        res = dictfetchall(cursor)

        totallist = res

        if len(res) < 1:
            return JsonResponse({'error': 'ERROR'}, safe=False)
        else:
            #res = {'id': result[0][0], 'email': result[0][1], 'enterprise': result[0][3]}
            return JsonResponse(
                {
                    'data': json.dumps(totallist, default=str)
                    #json.dumps({'data': res.decode('utf-8')})
                }, safe=False)

    except mysql.connector.Error as error:
        return JsonResponse({'error': 'ERROR'}, safe=False)


from django.shortcuts import get_object_or_404
from products.models import Product

def bag_contents(request):

    bag_items = []
    id_selector = ""
    total = 0
    product_count = 0
    grand_total = 0
    bag = request.session.get('bag', {})

    # product has no sizes
    for item_id, item_data in bag.items():
        if isinstance(item_data, int):
            product = get_object_or_404(Product, pk=item_id)
            total = item_data * product.price
            grand_total += item_data * product.price
            product_count += item_data
            bag_items.append({
                'item_id': item_id, #product id
                'price': product.price, #price
                'quantity': item_data, #quantity
                'product':product, #product
                'total': total, #qty*price
                'id_selector': item_id, #product id
            })
        # product has many sizes
        else:
            product = get_object_or_404(Product, pk=item_id)
            for size, quantity in item_data['items_by_size'].items():
                # id selector will hold product id along with size
                # this if for correct rendering ang jquery element lookup
                id_selector = "id_" + str(item_id) + "_size_" + str(size)
                total = quantity * product.price
                product_count += quantity
                grand_total += quantity * product.price
                bag_items.append({
                    'item_id': item_id,
                    'price': product.price,
                    'quantity': quantity,
                    'product': product,
                    'size': size,
                    'total': total,
                    'id_selector': id_selector,

                })

    context = {
        'bag_items': bag_items,
        'total': total,
        'product_count': product_count,
        'grand_total': grand_total,
        'id_selector': id_selector,
    }

    return context


import cv2
import os
import pickle
from video_seg_main import CreateDatasetArgs
from preprocess_utils import *

def read_frames_from_file(filename, desired_dim=None, frames=None) -> list:
    final_list = list()
    pics_names = os.listdir(filename)
    if not frames:
        frames = range(len(pics_names))
    for pic_ind in frames:
        pic_name = pics_names[pic_ind]
        pic_tmp = cv2.imread(os.path.join(filename, pic_name))
        gray_tmp = cv2.cvtColor(pic_tmp, cv2.COLOR_BGR2GRAY)
        if desired_dim:
            gray_tmp = cv2.resize(gray_tmp, desired_dim)
        final_list.append(gray_tmp)
    return final_list

def load_dataset(filename, number_of_videos=np.inf, desired_dim=None, frames=None) -> list:
    annotations_path = os.path.join(filename, 'Annotations', '480p')
    JPEG_path = os.path.join(filename, 'JPEGImages', '480p')
    annotations_list = os.listdir(annotations_path)
    if len(annotations_list) < number_of_videos:
        number_of_videos = len(annotations_list)
    dataset = []
    for video_idx in range(number_of_videos):
        annototion_vid_path = os.path.join(annotations_path, annotations_list[video_idx])
        JPEG_vid_path = os.path.join(JPEG_path, annotations_list[video_idx])

        annotation_list = read_frames_from_file(annototion_vid_path, frames=frames, desired_dim=desired_dim)
        # for current use we want the segmentaion to be only 0 or 1
        annotation_list = [np.where(frame > 0, 1, 0) for frame in annotation_list]
        JPEG_list = read_frames_from_file(JPEG_vid_path, frames=frames, desired_dim=desired_dim)

        dataset.append((JPEG_list, annotation_list))
    return dataset

def train_test_split(dataset, train_part=0.8):
    number_of_videos = len(dataset)
    if type(dataset) is dict:
        indexes = list(dataset.keys())
    else:
        indexes = list(range(number_of_videos))
    np.random.shuffle(indexes)
    train_indexes = indexes[0:int(np.round(train_part * number_of_videos))]
    test_indexes = indexes[int(np.round(train_part * number_of_videos)) + 1::]

    train_dict = {}
    test_dict = {}
    results_dict = {}

    for idx in train_indexes:
        train_dict[idx] = dataset[idx]

    for idx in test_indexes:
        test_dict[idx] = dataset[idx]
        results_dict[idx] = list()

    return train_dict, test_dict, results_dict

def train_test_combine(dataset1, dataset2):
    return {**dataset1,**dataset2}

def create_dataset(dataset_dir,
                   args: CreateDatasetArgs,
                   override_saved_data=False,
                   save_new_data=True):
    data_pickle_path = DataPickleNamer.create(args.image_size, args.number_of_videos, args.preprocess_type)

    # Load pickle if exists
    if not override_saved_data:
        if not os.path.isdir('data_pickles'):
            os.makedirs('data_pickles')
        if os.path.isfile(data_pickle_path) and DataPickleNamer.check(data_pickle_path, args.image_size, args.number_of_videos):
            with open(data_pickle_path, 'rb') as f:
                train_dict, test_dict, results_dict = pickle.load(f)
            print('Loaded dataset from pickle.')
            return train_dict, test_dict, results_dict

    # Load data
    print('Pickle not loaded. Creating dataset...')
    dataset = load_dataset(dataset_dir, number_of_videos=args.number_of_videos, frames=range(args.video_length+1), desired_dim=args.image_size) \
              + load_dataset(dataset_dir, number_of_videos=args.number_of_videos, frames=range(-(args.video_length+1), 0), desired_dim=args.image_size)

    # Add augmentations
    print('Adding augmentations...')
    n = len(dataset)
    for i in range(n):
        element = dataset[i]
        dataset.append((
            [np.fliplr(f) for f in element[0]],
            [np.fliplr(f) for f in element[1]]
        ))
        dataset.append((
            [np.flipud(f) for f in element[0]],
            [np.flipud(f) for f in element[1]]
        ))
        for k in [1, 2, 3]:
            dataset.append((
                [np.rot90(f, k) for f in element[0]],
                [np.rot90(f, k) for f in element[1]]
            ))

    if args.preprocess_type:
        print('Starting Preprocess...')
        dataset = preprocess(dataset, preprocess_type=args.preprocess_type)
        print('Finished Preprocess.')

    if args.type:
        change_data_type(dataset, args.type)

    train_dict, test_dict, results_dict = train_test_split(dataset, args.train_part)

    # save pickle
    if save_new_data:
        # TODO: this part takes a lot of time, maybe it doesn't override previous data
        with open(data_pickle_path, 'wb+') as f:
            pickle.dump((train_dict, test_dict, results_dict), f)
        print('Saved data to file.')

    return train_dict, test_dict, results_dict

class DataPickleNamer:
    @staticmethod
    def create(desired_dim, number_of_videos, preprocess_type):
        name = 'x'.join(str(d) for d in desired_dim)
        if number_of_videos is not np.inf:
            name = name + '_num=' + str(number_of_videos)
        if preprocess_type is not None:
            name = name + '_pre=' + preprocess_type
        name = name + '.pickle'
        return os.path.join('data_pickles', name)

    @staticmethod
    def check(data_pickle_path, desired_dim, number_of_videos, preprocess_type):
        try:
            data_pickle_name = os.path.basename(data_pickle_path)
            data_pickle_name, path_extention = os.path.splitext(data_pickle_name)

            name_parts = data_pickle_name.split('_')
            data_pickle_name = name_parts[0]
            path_number_of_videos = np.inf
            path_preprocess_type = None
            if len(name_parts) > 1:
                for n in name_parts[1:]:
                    n = n.split('=')
                    if n[0] == 'num':
                        path_number_of_videos = int(n[1])
                    elif n[0] == 'pre':
                        path_preprocess_type = n[1]
            path_desired_dim = tuple([eval(i) for i in data_pickle_name.split('x')])
        except:
            return False
        return (path_extention == '.pickle') \
               and (path_desired_dim == desired_dim) \
               and (path_number_of_videos == number_of_videos) \
               and (preprocess_type == path_preprocess_type)

def save_results_to_date_file(data_to_save, file_name: str = None):
    from datetime import datetime, date

    today = date.today()
    d1 = today.strftime("%d_%m_%Y")

    if not file_name:
        now = datetime.now()
        file_name = now.strftime("%H:%M")

    path = os.path.join('saved_results', d1)
    if not os.path.isdir(path):
        os.mkdir(path)

    path = os.path.join(path, file_name)
    with open(path + '.pickle', 'wb') as handle:
        pickle.dump(data_to_save, handle, protocol=pickle.HIGHEST_PROTOCOL)

if __name__ == '__main__':
    general_DS_folser = os.path.join('D:\Data_Sets', 'DAVIS-2017-trainval-480p', 'DAVIS')

    dataset_dict = load_dataset(general_DS_folser)
    print(dataset_dict.keys())

    plt.imshow(dataset_dict['bear'][0][2], cmap='gray')
    new_dim = (dataset_dict['bear'][0][2].shape[1] // 4, dataset_dict['bear'][0][2].shape[0] // 4)
    resized = cv2.resize(dataset_dict['bear'][0][2], new_dim)
    plt.imshow(resized, cmap='gray')
    plt.show()

    plt.imshow(dataset_dict['bear'][1][2], cmap='gray')
    new_dim = (dataset_dict['bear'][1][2].shape[1] // 4, dataset_dict['bear'][0][2].shape[0] // 4)
    resized = cv2.resize(dataset_dict['bear'][1][2], new_dim)
    plt.imshow(resized, cmap='gray')
    plt.show()

    plt.imshow(dataset_dict['bmx-trees'][0][2], cmap='gray')
    new_dim = (dataset_dict['bmx-trees'][0][2].shape[1] // 4, dataset_dict['bmx-trees'][0][2].shape[0] // 4)
    resized = cv2.resize(dataset_dict['bmx-trees'][0][2], new_dim)
    plt.imshow(resized, cmap='gray')
    plt.show()

    plt.imshow(dataset_dict['bmx-trees'][1][2], cmap='gray')
    new_dim = (dataset_dict['bmx-trees'][1][2].shape[1] // 4, dataset_dict['bmx-trees'][0][2].shape[0] // 4)
    resized = cv2.resize(dataset_dict['bmx-trees'][1][2], new_dim)
    plt.imshow(resized, cmap='gray')
    plt.show()
    print("check in the unils_for_DL file")


# This program asks the user to enter a sequence of numbers. Let's call this sequence A. Thereafter ask the user to enter a sequence of numbers
# B which indicates positions which are most important. We generate a sequence as follows:
# The important numbers specified by B must be at the beginning and in the same order as they were in A.
# The remaining number from A are to be sorted in descending order.

lsta = input("Please enter A: ")
lstb = input("Please enter B: ")
lstA = lsta.split(" ")
lstB = lstb.split(" ")
lstC = []
lstD = []
lstA2 = []

def sequencer():
    for i in range(len(lstB)):
        lstC.append(int(lstA[int(lstB[i]) - 1]))
    for i in range(len(lstB)):
        lstA.remove(str(lstC[i]))
    for i in range(len(lstA)):
        lstA2.append(int(lstA[i]))
    lstA2.sort(reverse=True)
    for i in range(len(lstC)):
        lstD.append(int(lstC[i]))
    for i in range(len(lstA2)):
        lstD.append(int(lstA2[i]))
    print("The sequence you want is:",lstD)

sequencer()


from django.urls import path
from . import views

app_name = 'search'

urlpatterns = [
    path('', views.index, name='search'),
    path('results/user/<slug:login>', views.user_details, name='user'),
    path('results/<slug:by>/<slug:query>/page-<int:page>', views.results, name='results')
]


import matplotlib.pyplot as plt
import torch
from torch import cuda
from torch.utils.data import DataLoader

from util.dataloader import GANImageDataset, ImageTransform, make_datapath_list
from util.model import SAGANDiscriminator, SAGANGenerator, weights_init
from util.train import train_sagan

def main():
    mean = (0.5,)
    std = (0.5,)
    batch_size = 64
    z_dim = 20
    epochs = 300
    torch.backends.cudnn.benchmark = True

    train_img_list = make_datapath_list("pytorch_advanced/gan_generation/data/img_78/")
    transform = ImageTransform(mean, std)
    train_dataset = GANImageDataset(train_img_list, transform)
    train_dataloader = DataLoader(train_dataset, batch_size, shuffle=True, num_workers=2, pin_memory=True)

    generator = SAGANGenerator(z_dim=20, image_size=64)
    discriminator = SAGANDiscriminator(image_size=64)
    generator.apply(weights_init)
    discriminator.apply(weights_init)
    print("ネットワーク初期化完了")

    device = torch.device("cuda:0" if cuda.is_available() else "cpu")
    print("使用デバイス：", device)

    g_lr, d_lr = 0.0001, 0.0004
    beta1, beta2 = 0.0, 0.9
    g_optimizer = torch.optim.Adam(generator.parameters(), g_lr, betas=(beta1, beta2))
    d_optimizer = torch.optim.Adam(discriminator.parameters(), d_lr, betas=(beta1, beta2))

    g, d = train_sagan(
        generator, discriminator, train_dataloader, g_optimizer, d_optimizer, z_dim, epochs, device=device
    )

    fixed_z = torch.randn(batch_size, z_dim, 1, 1)
    fake_images, am1, am2 = g(fixed_z.to(device))
    real_images = next(iter(train_dataloader))

    _ = plt.figure(figsize=(15, 9))
    for i in range(0, 5):
        plt.subplot(3, 5, i + 1)
        plt.imshow(real_images[i][0].cpu().detach().numpy(), "gray")

        plt.subplot(3, 5, 5 + i + 1)
        plt.imshow(fake_images[i][0].cpu().detach().numpy(), "gray")

        plt.subplot(3, 5, 10 + i + 1)
        am = am1[i].view(16, 16, 16, 16)
        am = am[7][7]
        plt.imshow(am.cpu().detach().numpy(), "Reds")
    plt.show()

if __name__ == "__main__":
    main()


import os
import requests
import argparse
import sys
import shutil
import json

def main(args=sys.argv[1:]):
    parser = argparse.ArgumentParser(description="Advent of Code initer.")
    parser.add_argument("-y","--year",help="input year like : YYYY")
    parser.add_argument("-d","--day",help="input day like : D")
    options = parser.parse_args(args)

    work_dir = os.getcwd()
    target_dir = f"{work_dir}/{options.year}/{options.day}"
    url = f"https://adventofcode.com/{options.year}/day/{options.day}/input"

    with open(work_dir+"/config.json","r") as file:
        headers = json.load(file)
        code = requests.get(url, headers=headers)
        open(f"{target_dir}/inputs.txt","wb").write(code.content)
        shutil.copy(f"{work_dir}/base.py",f"{target_dir}/main.py")

if __name__ == '__main__':
    main()


from django.urls import path
from online_shop import views

urlpatterns = [
    path('categories', views.get_categories),
    path('categories/<int:pk>', views.get_category),
    path('categories/<int:pk>/products', views.get_category_products),
    path('products', views.get_products),
    path('products/<int:pk>', views.get_product),
    path('categories/<int:pk1>/products/<int:pk2>', views.get_product_of_category)
]


from django import forms
from django.forms import ModelForm, modelformset_factory, BaseModelFormSet

from CookingHeaven.main.models import Recipe, Ingredient, RecipeStep, Category, Unit

class RecipeCreateUpdateForm(ModelForm):

    def __init__(self, user, *args, **kwargs):
        self.user = user
        super().__init__(*args, **kwargs)
        self.fields['preparation_time'].label = "Preparation time in minutes:"
        self.fields['cooking_time'].label = "Cooking time in minutes:"

    def save(self, commit=True, *args, **kwargs):
        recipe = super().save(commit=False)
        recipe.publisher = self.user
        if commit:
            recipe.save()
            self.save_m2m()
        return recipe

    class Meta:
        model = Recipe
        fields = ('name', 'description', 'photo', 'preparation_time', 'cooking_time', 'category',)

class IngredientCreateForm(ModelForm):
    class Meta:
        model = Ingredient
        fields = ('name', 'amount', 'unit',)

class RecipeStepCreateForm(ModelForm):
    class Meta:
        model = RecipeStep
        fields = ('description',)

class CategoryCreateForm(ModelForm):
    class Meta:
        model = Category
        fields = '__all__'

class UnitCreateForm(ModelForm):
    class Meta:
        model = Unit
        fields = '__all__'

class CustomModelFormSet(BaseModelFormSet):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        for form in self.forms:
            form.empty_permitted = False

class RecipeCategoryFilterForm(forms.Form):
    filter = forms.ChoiceField(choices=[])  # Initialize with empty choices
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.fields['filter'].choices = [(category, category) for category in Category.objects.all()]

IngredientFormset = modelformset_factory(
    Ingredient,
    form=IngredientCreateForm,
    formset=CustomModelFormSet,
    can_delete=True,
    extra=0
)
RecipeStepFormset = modelformset_factory(
    RecipeStep,
    form=RecipeStepCreateForm,
    formset=CustomModelFormSet,
    can_delete=True,
    extra=0
)