from __future__ import annotations
from business.model.contract_details import (
    IBContractDetails,
)  # allow return same type as class ..... -> FuturesTreeNode
import math
import logging
from collections import defaultdict
from typing import Any, Dict, List, Union

import numpy as np
import pandas as pd
from PyQt5.QtCore import (
    QAbstractItemModel,
    QModelIndex,
    Qt,
    pyqtSlot,
    QVariant,
)

from ibapi.contract import ContractDetails

# create logger
log = logging.getLogger("CellarLogger")

def defaultValue(data: Union[List[List[Any]], None]) -> pd.DataFrame:
    df = pd.DataFrame(
        data=data,
        columns=[
            "order",
            "symbol",
            "localSymbol",
            "contractMonth",
            "contractEndDate",
            "parentId",
            "diff",
            "bid_size",
            "bid",
            "last",
            "ask",
            "ask_size",
            "open",
            "high",
            "low",
            "close",
            "change",
            "volume",
            "avg_volume",
            "option_historical_vol",
            "option_implied_vol",
        ],
    )
    # df.set_index(["symbol", "localSymbol"], inplace=True)
    return df

# header: List[str] = [
#     "symbol",
#     "localSymbol",
#     "contractMonth",
#     "contractEndDate",
#     "diff",
#     "bid_size",
#     "bid",
#     "last",
#     "ask",
#     "ask_size",
#     "open",
#     "high",
#     "low",
#     "close",
#     "change",
#     "volume",
#     "avg_volume",
#     "option_historical_vol",
#     "option_implied_vol",
#     "delete",
# ]

class FuturesTreeModel(QAbstractItemModel):
    def __init__(self):
        super().__init__()
        self._data = defaultValue([])
        self.firstLevel = defaultValue([])

        # header
        # for index, value in enumerate(header):
        #     self.setHeaderData(index, Qt.Horizontal, value)

    # ----------------------------------------------------------
    # Custom methods
    # ----------------------------------------------------------

    # def __indexColumnsCount(self):
    #     return len(self.root.data.index.names)

    # def __dataColumnsCount(self) -> int:
    #     return self._root.data.shape[1]

    # def __allColumnsCount(self) -> int:
    #     return self._root.data.shape[1] + self.__indexColumnsCount()

    def addGroup(
        self, data: Union[List[ContractDetails], List[IBContractDetails]]
    ):
        # log.debug("Running...")
        # log.debug(locals())

        # ITEMS ----------------------------------
        firstIndex: int = -1
        for row in data:

            # add new row to the tree
            self.beginInsertRows(QModelIndex(), 0, 0)

            # find last "order" number
            lastOrderNumber = self._data["order"].max()
            if math.isnan(lastOrderNumber):
                lastOrderNumber = 0

            vals: List[Any] = [
                lastOrderNumber + 1,
                row.contract.symbol,
                row.contract.localSymbol,
                row.contractMonth,
                row.contract.lastTradeDateOrContractMonth,
                firstIndex,
                *np.zeros(15),
            ]
            vals_series = pd.Series(vals, index=self._data.columns)

            self._data = self._data.append(vals_series, ignore_index=True)

            # if this is parent of the group
            if firstIndex == -1:
                parentRow = self._data[
                    (self._data["symbol"] == row.contract.symbol)
                    & (self._data["localSymbol"] == row.contract.localSymbol)
                ]
                if parentRow.empty:
                    raise Exception("THIS SHOULD NOT HAPPENED")
                else:
                    firstIndex = parentRow.index[0]
                    self.firstLevel = self.firstLevel.append(
                        vals_series, ignore_index=True
                    )

            self.endInsertRows()

    # def reset(self):
    #     self.beginResetModel()
    #     self.root = FuturesTreeNode(None)
    #     self.endResetModel()

    def removeFuture(self, ticker: str):
        self.beginRemoveRows(QModelIndex(), 0, 0)
        self._data = self._data[self._data["symbol"] != ticker]
        self.endRemoveRows()

    # ----------------------------------------------------------
    # Signals / Slots
    # ----------------------------------------------------------
    # @pyqtSlot(dict, name="on_update_model")
    # def on_update_model(self, obj: Dict[str, Any]):
    #     # log.debug("Running...")
    #     # log.debug(locals())
    #     log.debug(
    #         f"ticker={obj['ticker']}|localSymbol={obj['localSymbol']}|type={obj['type']}|price={obj['price']}"
    #     )

    #     if obj == {}:
    #         log.info("EMPTY")
    #         return

    #     self.root.updateData(
    #         obj["ticker"], obj["localSymbol"], obj["type"], obj["price"]
    #     )

    #     bbb = QModelIndex()
    #     bbb.row = 1
    #     bbb.column = 1

    #     # self.dataChanged.emit(QModelIndex(), QModelIndex())
    #     self.dataChanged.emit(bbb, bbb)

    # ----------------------------------------------------------
    # Minimum methods to override
    # ----------------------------------------------------------
    def data(self, index: QModelIndex, role: Any) -> Union[str, None]:
        # log.debug("Running...")
        # log.debug(locals())

        # if not index.isValid():
        #     log.error("index invalid - return None")
        #     return None

        if role == Qt.DisplayRole:

            if index.parent().row() != -1:
                log.info(
                    f"index.row: {index.row()}, index.col: {index.column()}, index.parent.row: {index.parent().row()}, index.parent.col: {index.parent().column()}"
                )

            columnIndex: int = index.column()
            rowIndex: int = index.row()

            value = self.firstLevel.iloc[rowIndex, columnIndex]

            # value = self._data.iloc[rowIndex, columnIndex]

            if columnIndex == 3:  # contract-end-date
                return value
            elif columnIndex == 6:  # diff
                return f"{value:.0f}"
            elif columnIndex == 7:  # bid-size
                return f"{value:.0f}"
            elif columnIndex == 8:  # bid
                return f"{value:.2f}"
            if columnIndex == 9:  # last
                return f"{value:.2f}"
            elif columnIndex == 10:  # ask
                return f"{value:.2f}"
            elif columnIndex == 11:  # ask-size
                return f"{value:.0f}"
            elif columnIndex == 12:  # open
                return f"{value:.2f}"
            elif columnIndex == 13:  # high
                return f"{value:.2f}"
            elif columnIndex == 14:  # low
                return f"{value:.2f}"
            elif columnIndex == 15:  # close
                return f"{value:.2f}"
            # elif columnIndex == 14:  # change
            #     return f"{value:.1f}"
            elif columnIndex == 17:  # volume
                return f"{value:.0f}"
            elif columnIndex == 18:  # avg volume
                return f"{value:.0f}"
            elif columnIndex == 19:  # hist volatility
                return f"{value:.2f}"
            elif columnIndex == 20:  # implied volatility
                return f"{value:.2f}"

            return str(value)

        return None

    def rowCount(self, parent: QModelIndex) -> int:
        # log.debug("Running...")
        # log.debug(locals())

        # log.info(
        #     f"isValid: {parent.isValid()}, parent.row: {parent.row()}, parent.col: {parent.column()}"
        # )

        if self._data.empty:
            return 0

        if parent.isValid():
            # number of children in parent
            childRowsCount = self._data[
                self._data["parentId"] == parent.row()
            ].shape[0]
            return childRowsCount
        else:
            # all data
            # return self._data.shape[0]
            # # number of parents
            return self._data[self._data["parentId"] == -1].shape[0]

    def columnCount(self, parent: QModelIndex) -> int:
        # log.debug("Running...")
        # log.debug(locals())

        if self._data.empty:
            return 0

        return self._data.shape[1]

        # if parent.isValid():

        #     print(parent)

        #     return 0
        # else:
        #     return self._data[self._data["parentId"] == -1].shape[1]

    def index(self, row: int, col: int, parent: QModelIndex) -> QModelIndex:
        # log.info("Running...")
        # # log.info(locals())
        # log.info(
        #     f"row: {row}, col: {col}, parent.row: {parent.row()}, parent.col: {parent.column()}"
        # )

        # if col > 0:
        #     return QModelIndex()

        # return self.createIndex(row, col, None)

        # item = self._data.iloc[
        #     row,
        # ]
        # return self.createIndex(row, col, item)

        if not parent.isValid():
            # log.info("not parent.isValid()")
            item = self.firstLevel.iloc[
                row,
            ]
            return self.createIndex(row, col, item["order"])
        else:
            log.info("is parent.isValid()")
            # if row == 0 and parent.row() == 0:
            #     i = 6
            parentDf = self.firstLevel.iloc[parent.row()]
            item = self._data[self._data["parentId"] == parentDf.name].iloc[
                row, col
            ]

            return self.createIndex(row, col, item["order"])

        # if not self.hasIndex(row, col, parent):
        #     return QModelIndex()

        # if parent.column() != 0:
        #     return QModelIndex()

        # if parent.isValid() and parent.column() != 0:
        #     return QModelIndex()

        onlyParents = self._data[self._data["parentId"] == -1]

        item = onlyParents.iloc[
            row,
        ]
        return self.createIndex(row, col, item)

        if not parent.isValid():
            item = onlyParents.iloc[
                row,
            ]
            return self.createIndex(row, col, item)
        else:
            item = self._data[self._data["parentId"] == parent.row()].loc[
                row,
            ]
            return self.createIndex(row, col, item)

        # OLD
        if parent.isValid() and parent.column() != 0:
            return QModelIndex()

        if not self.hasIndex(row, col, parent):
            return QModelIndex()

        if not parent or not parent.isValid():

            # index = (
            #     self._data[self._data["parentId"] == -1]
            #     .reset_index()
            #     .iloc[row,]
            #     .name
            # )

            index = self._data[self._data["parentId"] == -1].iloc[row,].name

            return QAbstractItemModel.createIndex(self, index, col, None)

        else:

            # index = (
            #     self._data[self._data["parentId"] == parent.row()]
            #     .reset_index()
            #     .iloc[row,]
            #     .name
            # )

            index = (
                self._data[self._data["parentId"] == parent.row()]
                .reset_index()
                .iloc[row,]["index"]
            )
            return QAbstractItemModel.createIndex(self, index, col, None)

            return QModelIndex()

        dfRow = self._data.loc[row]
        return QAbstractItemModel.createIndex(self, row, col, dfRow)

        # if not _parent or not _parent.isValid():
        #     parent = self.root
        # else:
        #     parent = _parent.internalPointer()

        # if not QAbstractItemModel.hasIndex(self, row, col, _parent):
        #     return QModelIndex()

        # child = parent.child(row)
        # if child:
        #     return QAbstractItemModel.createIndex(self, row, col, child)
        # else:
        #     return QModelIndex()

    def parent(self, index: QModelIndex) -> QModelIndex:
        # log.debug("Running...")
        # log.debug(locals())

        # log.info(f"index.row: {index.row()}, index.col: {index.column()}")

        # parentId = self._data.iloc[index.row(),]["parentId"]
        parentId = self.firstLevel.iloc[index.row(),]["parentId"]

        if parentId == -1:
            return QModelIndex()
        else:
            parent = self._data.iloc[
                parentId,
            ]
            return self.createIndex(parentId, 0, parent)

        # # OLD 1
        # if not index.isValid():
        #     return QModelIndex()

        # onlyParents = self._data[self._data["parentId"] == -1]

        # parentId = onlyParents.iloc[index.row(),]["parentId"]

        # if parentId == -1:
        #     return QModelIndex()
        # else:
        #     parent = onlyParents.iloc[
        #         parentId,
        #     ]
        #     return self.createIndex(parentId, 0, parent)

        # # OLD
        # if index.isValid():

        #     parentId = self._data.iloc[index.row(),]["parentId"]

        #     if parentId == -1:
        #         return QAbstractItemModel.createIndex(self, -1, -1, None)

        #     parent = self._data.iloc[
        #         parentId,
        #     ]

        #     return QAbstractItemModel.createIndex(self, parentId, 0, parent)

        # else:
        #     return QModelIndex()
        # raise Exception("??????????")
        # return QAbstractItemModel.createIndex(self, -1, -1, None)

    # ----------------------------------------------------------
    # Override - headers
    # ----------------------------------------------------------

    # def setHeaderData(
    #     self,
    #     section: int,
    #     orientation: Qt.Orientation,
    #     value: QVariant,
    #     role=Qt.DisplayRole,
    # ) -> bool:
    #     # log.debug("Running...")
    #     # log.debug(locals())

    #     if orientation == Qt.Horizontal:

    #         # print("Setting value: " + value)
    #         self.header_data[section] = value

    #         return True

    #     else:

    #         aaa = super(FuturesTreeModel, self).setHeaderData(
    #             section, orientation, value, role
    #         )
    #         # print(aaa)
    #         return aaa

    def headerData(
        self, section: int, orientation: Qt.Orientation, role=Qt.DisplayRole
    ) -> object:
        # log.debug("Running...")
        # log.debug(locals())

        # print(section)

        if role == Qt.DisplayRole:
            aaa = self._data.columns[section]
            # print(aaa)
            return aaa

    def flags(self, index):
        return (
            Qt.ItemIsEnabled
            | Qt.ItemIsSelectable
            | Qt.ItemIsEditable
            | Qt.ItemIsDragEnabled
            | Qt.ItemIsDropEnabled
        )

    # # ----------------------------------------------------------
    # # AbstractItemModel - methods
    # # ----------------------------------------------------------

    # def beginInsertColumns(self, parent, first, last):
    #     log.debug("Running...")
    #     log.debug(locals())
    #     super(FuturesTreeModel, self).beginInsertColumns(parent, first, last)

    # def beginInsertRows(self, parent, first, last):
    #     log.debug("Running...")
    #     log.debug(locals())
    #     super(FuturesTreeModel, self).beginInsertRows(parent, first, last)

    # def beginMoveColumns(self, sourceParent, sourceFirst, sourceLast, destinationParent, destinationColumn):
    #     log.debug("Running...")
    #     log.debug(locals())
    #     super(FuturesTreeModel, self).beginMoveColumns(sourceParent, sourceFirst, sourceLast, destinationParent, destinationColumn)

    # def beginMoveRows(self, sourceParent, sourceFirst, sourceLast, destinationParent, destinationRow):
    #     log.debug("Running...")
    #     log.debug(locals())
    #     super(FuturesTreeModel, self).beginMoveRows(sourceParent, sourceFirst, sourceLast, destinationParent, destinationRow)

    # def beginRemoveColumns(self, parent, first, last):
    #     log.debug("Running...")
    #     log.debug(locals())
    #     super(FuturesTreeModel, self).beginRemoveColumns(parent, first, last)

    # def beginRemoveRows(self, parent, first, last):
    #     log.debug("Running...")
    #     log.debug(locals())
    #     super(FuturesTreeModel, self).beginRemoveRows(parent, first, last)

    # def beginResetModel(self):
    #     log.debug("Running...")
    #     log.debug(locals())
    #     super(FuturesTreeModel, self).beginResetModel()

    # def changePersistentIndex(self, a_from, to):
    #     log.debug("Running...")
    #     log.debug(locals())
    #     super(FuturesTreeModel, self).changePersistentIndex(a_from, to)

    # def changePersistentIndexList(self, a_from, to):
    #     log.debug("Running...")
    #     log.debug(locals())
    #     super(FuturesTreeModel, self).changePersistentIndexList(a_from, to)

    # def checkIndex(self, index, options):
    #     log.debug("Running...")
    #     log.debug(locals())
    #     super(FuturesTreeModel, self).checkIndex(index, options)

    # def createIndex(self, row, column, ptr):
    #     # log.debug("Running...")
    #     # log.debug(locals())
    #     aaa = super(FuturesTreeModel, self).createIndex(row, column, ptr)

    #     # log.info(f"row: {row}, col: {column}")
    #     # log.info(ptr)

    #     # log.debug("S - -----------------------")
    #     # print(type(aaa))
    #     # print(aaa)
    #     # log.debug("E - -----------------------")
    #     return aaa

    # def createIndex(self, row, column, id):
    #     log.debug("Running...")
    #     log.debug(locals())
    #     aaa = super(FuturesTreeModel, self).createIndex(row, column, id)

    #     # log.debug("-----------------------")
    #     # print(type(aaa))
    #     # print(aaa)
    #     # log.debug("-----------------------")
    #     return aaa

    # def decodeData(self, row, column, parent, stream):
    #     log.debug("Running...")
    #     log.debug(locals())
    #     super(FuturesTreeModel, self).decodeData(row, column, parent, stream)

    # def encodeData(self, indexes, stream):
    #     log.debug("Running...")
    #     log.debug(locals())
    #     super(FuturesTreeModel, self).encodeData(indexes, stream)

    # def endInsertColumns(self):
    #     log.debug("Running...")
    #     log.debug(locals())
    #     super(FuturesTreeModel, self).endInsertColumns()

    # def endInsertRows(self):
    #     log.debug("Running...")
    #     log.debug(locals())
    #     super(FuturesTreeModel, self).endInsertRows()

    # def endMoveColumns(self):
    #     log.debug("Running...")
    #     log.debug(locals())
    #     super(FuturesTreeModel, self).endMoveColumns()

    # def endMoveRows(self):
    #     log.debug("Running...")
    #     log.debug(locals())
    #     super(FuturesTreeModel, self).endMoveRows()

    # def endRemoveColumns(self):
    #     log.debug("Running...")
    #     log.debug(locals())
    #     super(FuturesTreeModel, self).endRemoveColumns()

    # def endRemoveRows(self):
    #     log.debug("Running...")
    #     log.debug(locals())
    #     super(FuturesTreeModel, self).endRemoveRows()

    # def endResetModel(self):
    #     log.debug("Running...")
    #     log.debug(locals())
    #     super(FuturesTreeModel, self).endResetModel()

    # def hasIndex(self, row, column, parent):
    #     log.debug("Running...")
    #     log.debug(locals())
    #     aaa = super(FuturesTreeModel, self).hasIndex(row, column, parent)
    #     # print("######")
    #     # print(aaa)
    #     # print("######")
    #     return aaa

    # def insertColumn(self, column, parent):
    #     log.debug("Running...")
    #     log.debug(locals())
    #     super(FuturesTreeModel, self).insertColumn(column, parent)

    # def insertRow(self, row, parent):
    #     log.debug("Running...")
    #     log.debug(locals())
    #     super(FuturesTreeModel, self).insertRow(row, parent)

    # def moveColumn(self, sourceParent, sourceColumn, destinationParent, destinationChild):
    #     log.debug("Running...")
    #     log.debug(locals())
    #     super(FuturesTreeModel, self).moveColumn(sourceParent, sourceColumn, destinationParent, destinationChild)

    # def moveRow(self, sourceParent, sourceRow, destinationParent, destinationChild):
    #     log.debug("Running...")
    #     log.debug(locals())
    #     super(FuturesTreeModel, self).moveRow(sourceParent, sourceRow, destinationParent, destinationChild)

    # def persistentIndexList(self):
    #     log.debug("Running...")
    #     log.debug(locals())
    #     super(FuturesTreeModel, self).persistentIndexList()

    # def removeColumn(self, column, parent):
    #     log.debug("Running...")
    #     log.debug(locals())
    #     super(FuturesTreeModel, self).removeColumn(column, parent)

    # def removeRow(self, row, parent):
    #     log.debug("Running...")
    #     log.debug(locals())
    #     super(FuturesTreeModel, self).removeRow(row, parent)

    # # ----------------------------------------------------------
    # # AbstractItemModel - virtual methods
    # # ----------------------------------------------------------

    # def buddy(self, index) -> QModelIndex:
    #     # log.debug("Running...")
    #     # log.debug(locals())
    #     aaa = super(FuturesTreeModel, self).buddy(index)
    #     # print(aaa)
    #     return aaa

    # def canDropMimeData(self, data, action, row, column, parent) -> bool:
    #     log.debug("Running...")
    #     log.debug(locals())
    #     aaa = super(FuturesTreeModel, self).canDropMimeData(data, action, row, column, parent)
    #     # print(aaa)
    #     return aaa

    # def canFetchMore(self, parent) -> bool:
    #     # log.debug("Running...")
    #     # log.debug(locals())

    #     log.info(f"parent.row: {parent.row()}, parent.col: {parent.column()}")

    #     # result = self.hasChildren(parent)
    #     if parent.row() < self.firstLevel.shape[0]:
    #         return True
    #     else:
    #         return False

    # print(result)
    # return result

    # aaa = super(FuturesTreeModel, self).canFetchMore(parent)
    # print(aaa)
    # return aaa

    # # def columnCount(self, parent) -> int:
    # #     log.debug("Running...")
    # #     log.debug(locals())
    # #     print(parent.row())
    # #     print(parent.column())

    # #     aaa = super(FuturesTreeModel, self).columnCount(parent)
    # #     print(aaa)
    # #     return aaa

    # # def data(self, index, role) -> object:
    # #     # log.debug("Running...")
    # #     # log.debug(locals())

    # #     aaa = super(FuturesTreeModel, self).data(index, role)
    # #     # print(aaa)
    # #     return aaa

    # def dropMimeData(self, data, action, row, column, parent) -> bool:
    #     log.debug("Running...")
    #     log.debug(locals())
    #     aaa = super(FuturesTreeModel, self).dropMimeData(data, action, row, column, parent)
    #     # print(aaa)
    #     return aaa

    # def fetchMore(self, parent):
    #     # log.debug("Running...")
    #     # log.debug(locals())

    #     log.info(f"parent.row: {parent.row()}, parent.col: {parent.column()}")

    #     # super(FuturesTreeModel, self).fetchMore(parent)

    # def flags(self, index) -> Qt.ItemFlags:
    #     # log.debug("Running...")
    #     # log.debug(locals())
    #     aaa = super(FuturesTreeModel, self).flags(index)
    #     # print(aaa)
    #     return aaa

    def hasChildren(self, parent) -> bool:
        # log.debug("Running...")
        # log.debug(locals())
        # print(traceback.print_stack(file=sys.stdout))
        # print("--")
        # print("parent")
        # print(parent.isValid())
        # print(parent.row())
        # print(parent.column())
        # print(parent.model())
        # print(parent.internalPointer())
        # if parent.internalPointer() is not None:
        #     print(parent.internalPointer().data)

        # log.info(f"parent.row: {parent.row()}, parent.col: {parent.column()}")

        # if not parent.isValid():
        #     return True

        if parent.row() < self.firstLevel.shape[0]:
            return True
        else:
            return False

        itemId = self.firstLevel.iloc[
            parent.row(),
        ]

        childrenCount = self._data[self._data["parentId"] == itemId.name]

        result = True if childrenCount.shape[0] > 0 else False

        # aaa = super(FuturesTreeModel, self).hasChildren(parent)
        # print("--")
        # print(aaa)
        # print("--")
        return result

    # # def headerData(self, section, orientation, role=Qt.DisplayRole) -> object:
    # #     # log.debug("Running...")
    # #     # log.debug(locals())
    # #     aaa = super(FuturesTreeModel, self).headerData(section, orientation, role)
    # #     # print(aaa)
    # #     return aaa

    # # def index(self, row: int, col: int, _parent=QModelIndex()) -> QModelIndex:
    # #     log.debug("Running...")
    # #     log.debug(locals())
    # #     print(row)
    # #     print(col)
    # #     print(_parent.row())
    # #     print(_parent.column())

    # #     aaa = super(FuturesTreeModel, self).index(row, col, _parent)
    # #     print(aaa)
    # #     return aaa

    # def insertColumns(self, column, count, parent) -> bool:
    #     log.debug("Running...")
    #     log.debug(locals())
    #     aaa = super(FuturesTreeModel, self).insertColumns(column, count, parent)
    #     print(aaa)
    #     return aaa

    # def insertRows(self, row, count, parent) -> bool:
    #     log.debug("Running...")
    #     log.debug(locals())
    #     aaa = super(FuturesTreeModel, self).insertRows(row, count, parent)
    #     print(aaa)
    #     return aaa

    # def itemData(self, index):
    #     log.debug("Running...")
    #     log.debug(locals())
    #     aaa = super(FuturesTreeModel, self).itemData(index)
    #     print(aaa)
    #     return aaa

    # def match(self, start, role, value, hits, flags):
    #     log.debug("Running...")
    #     log.debug(locals())
    #     aaa = super(FuturesTreeModel, self).match(start, role, value, hits, flags)
    #     print(aaa)
    #     return aaa

    # def mimeData(self, indexes) -> QMimeData:
    #     log.debug("Running...")
    #     log.debug(locals())
    #     aaa = super(FuturesTreeModel, self).mimeData(indexes)
    #     print(aaa)
    #     return aaa

    # def mimeTypes(self):
    #     log.debug("Running...")
    #     log.debug(locals())
    #     aaa = super(FuturesTreeModel, self).mimeTypes()
    #     print(aaa)
    #     return aaa

    # def moveColumns(self, sourceParent, sourceColumn, count, destinationParent, destinationChild) -> bool:
    #     log.debug("Running...")
    #     log.debug(locals())
    #     aaa = super(FuturesTreeModel, self).moveColumns(sourceParent, sourceColumn, count, destinationParent, destinationChild)
    #     print(aaa)
    #     return aaa

    # def moveRows(self, sourceParent, sourceRow, count, destinationParent, destinationChild) -> bool:
    #     log.debug("Running...")
    #     log.debug(locals())
    #     aaa = super(FuturesTreeModel, self).moveRows(sourceParent, sourceRow, count, destinationParent, destinationChild)
    #     print(aaa)
    #     return aaa

    # # def parent(self, child) -> QModelIndex:
    # #     log.debug("Running...")
    # #     log.debug(locals())
    # #     print(traceback.print_stack(file=sys.stdout))
    # #     print(child.row())
    # #     print(child.column())

    # #     aaa = super(FuturesTreeModel, self).parent(child)
    # #     print(aaa)
    # #     return aaa

    # def removeColumns(self, column, count, parent) -> bool:
    #     log.debug("Running...")
    #     log.debug(locals())
    #     aaa = super(FuturesTreeModel, self).removeColumns(column, count, parent)
    #     print(aaa)
    #     return aaa

    # def removeRows(self, row, count, parent) -> bool:
    #     log.debug("Running...")
    #     log.debug(locals())
    #     aaa = super(FuturesTreeModel, self).removeRows(row, count, parent)
    #     print(aaa)
    #     return aaa

    # def revert(self):
    #     log.debug("Running...")
    #     log.debug(locals())
    #     super(FuturesTreeModel, self).revert()

    # def roleNames(self):
    #     log.debug("Running...")
    #     log.debug(locals())
    #     aaa = super(FuturesTreeModel, self).roleNames()
    #     print(aaa)
    #     return aaa

    # # def rowCount(self, parent) -> int:
    # #     log.debug("Running...")
    # #     log.debug(locals())
    # #     aaa = super(FuturesTreeModel, self).rowCount(parent)
    # #     print(aaa)
    # #     return aaa

    # def setData(self, index, value, role) -> bool:
    #     log.debug("Running...")
    #     log.debug(locals())
    #     aaa = super(FuturesTreeModel, self).setData(index, value, role)
    #     print(aaa)
    #     return aaa

    # # def setHeaderData(self, section, orientation, value, role) -> bool:
    # #     log.debug("Running...")
    # #     log.debug(locals())
    # #     aaa = super(FuturesTreeModel, self).setHeaderData(section, orientation, value, role)
    # #     print(aaa)
    # #     return aaa

    # def setItemData(self, index, roles) -> bool:
    #     log.debug("Running...")
    #     log.debug(locals())
    #     aaa = super(FuturesTreeModel, self).setItemData(index, roles)
    #     print(aaa)
    #     return aaa

    # def sibling(self, row, column, idx) -> QModelIndex:
    #     # log.debug("Running...")
    #     # log.debug(locals())
    #     aaa = super(FuturesTreeModel, self).sibling(row, column, idx)
    #     # print(aaa)
    #     return aaa

    # def sort(self, column, order):
    #     log.debug("Running...")
    #     log.debug(locals())
    #     super(FuturesTreeModel, self).sort(column, order)

    # def span(self, index) -> QSize:
    #     log.debug("Running...")
    #     log.debug(locals())
    #     aaa = super(FuturesTreeModel, self).span(index)
    #     print(aaa)
    #     return aaa

    # def submit(self) -> bool:
    #     log.debug("Running...")
    #     log.debug(locals())
    #     aaa = super(FuturesTreeModel, self).submit()
    #     print(aaa)
    #     return aaa

    # def supportedDragActions(self) -> Qt.DropActions:
    #     log.debug("Running...")
    #     log.debug(locals())
    #     aaa = super(FuturesTreeModel, self).supportedDragActions()
    #     print(aaa)
    #     return aaa

    # def supportedDropActions(self) -> Qt.DropActions:
    #     log.debug("Running...")
    #     log.debug(locals())
    #     aaa = super(FuturesTreeModel, self).supportedDropActions()
    #     print(aaa)
    #     return aaa


import time
import os
from datetime import datetime

#need to install using pip install win10toast
from win10toast import ToastNotifier

toaster = ToastNotifier()
logFileName = 'log.txt' #storing user entered data

#sending notification that program is started

toaster.show_toast("ExerciseKa", "This will take care of your health", threaded=True, icon_path=None, duration=6) #Notification duration will be 6 second
while toaster.notification_active(): #checking is there any other notification or not if yes waiting for 0.5 second
    time.sleep(0.5)

#cheking does file is there or not

def checkFile(fileName):
    try:
        logFile = open(fileName,'r') # If file doesn't then goes to exception
    except FileNotFoundError:
        logFile = open(fileName,'w') # Creates a logfile
        data = ' '.join(['180','30','15'])
        logFile.write(data) #Write data
    finally:
        logFile.close() #Close file

def clear():
    os.system('cls||echo -e \\\\033c') # To clean terminal

def perfectTime():
    now = datetime.now() # using date time module
    hours = now.hour
    minute = now.minute
    perfectTime = int((hours*60)+minute)# converting hours to minutes then adding them
    return perfectTime #geting hours and minutes

clear() # A function define on line number 24
checkFile(logFileName)# Chacking files

#Giving options to users

file = open(logFileName,'r') # Opening file as read mode
data = file.read()#Reading data
file.close()#Closing file

clear()

data = data.split(' ')#Splitting up data

startingTime = perfectTime() #Getting the started time and function is define on line number 27

#calculating the time at which user should get notification

waterReminderTime = startingTime+int(data[0])
exerciseReminderTime = startingTime+int(data[1])
eyeReminderTime = startingTime+int(data[2])

#This while loop will run till program is not closed and 1 stands for True

while 1:
    currentTime = perfectTime() # this will refresh after every loop cycle

    #checking does reminder time is equl to current time if yes then showing up the notification

    if waterReminderTime == currentTime:
      waterReminderTime = waterReminderTime + int(data[0])
      toaster.show_toast("ExerciseKa","Time to drink water",threaded=True, icon_path=None, duration=6) #Notification duration will be 6 second
      while toaster.notification_active(): #checking is there any other notification or not if yes wating for 0.5 second
          time.sleep(10)
    if exerciseReminderTime == currentTime:
        exerciseReminderTime = exerciseReminderTime + int(data[1])
        toaster.show_toast("ExerciseKa","Time to do exercise", threaded=True, icon_path=None, duration=6)
        while toaster.notification_active():
            time.sleep(10)

    if eyeReminderTime == currentTime:
        eyeReminderTime = eyeReminderTime + int(data[2])
        toaster.show_toast("ExerciseKa","Time to do eye exercise ",threaded=True, icon_path=None, duration=6)
        while toaster.notification_active():
            time.sleep(10)

    #waiting for 1 sec before running loop again you can use print here to checking loop is running or not

    time.sleep(1)


import json

def test_add_address(client, address_1, address_2, city, country, state, zipcode):
    address_mutation = """mutation Address($input: AddressInput!) {
            addAddress(input: $input) {
                status
                id
            }
        }"""

    response = client.post('/api', json={'query': address_mutation, 'variables': {
        "input":
        {
            "country": country,
            "address1": address_1,
            "address2": address_2,
            "city": city,
            "state": state,
            "zipcode": zipcode
        }
    }
    })
    json_data = json.loads(response.data)
    data = json_data['data']
    register = data['addAddress']

    assert register['status']


import requests
import pytest
import json
import os

# BASE_URL = "http://192.168.1.11"
# BASE_URL = "http://localhost"
ip = os.environ["IP"]
BASE_URL = "http://" + ip
print(BASE_URL)
port = os.environ["PORT"]
# BASE_PORT = "8082"
BASE_PORT = port
PATH = "/albums"

FULL_URL = BASE_URL + ":" + BASE_PORT + PATH

def test_get_albums():
    print(FULL_URL)
    resp = requests.get(FULL_URL)
    assert resp.status_code == 200
    print(resp.status_code)
    print(resp.json())

def test_get_album_with_id():
    resp = requests.get(FULL_URL + "/2")
    print(resp.json())
    assert resp.status_code == 200

def test_post_create_album():
    body = json.dumps({'title': "Ero", "artist": "Enigma", "country": "India"})
    resp = requests.post(FULL_URL, body)
    print(resp.json())
    print(resp.status_code)
    get_all = requests.get(FULL_URL)
    print(get_all.json())
    assert len(get_all.json()) > 3


import hashlib
import json

from asgiref.sync import async_to_sync
from django.core.files.base import ContentFile
from django.utils.timezone import now

from venueless.celery_app import app
from venueless.core.services.world import notify_schedule_change
from venueless.core.tasks import WorldTask
from venueless.importers.conftool import (
    create_posters_from_conftool,
    fetch_schedule_from_conftool,
)
from venueless.storage.models import StoredFile

@app.task(base=WorldTask)
def conftool_update_schedule(world):
    u = world.config.get("conftool_url")
    p = world.config.get("conftool_password")

    if not u or not p or not world.config.get("pretalx").get("conftool"):
        return "invalid"

    d = fetch_schedule_from_conftool(u, p)
    v = d.pop("version")
    checksum = hashlib.sha256(json.dumps(d, sort_keys=True).encode()).hexdigest()
    d["version"] = v
    document = json.dumps(d, sort_keys=True)

    if StoredFile.objects.filter(
        world=world, filename=f"conftool_schedule_{checksum}.json"
    ).exists():
        return "unchanged"

    sf = StoredFile.objects.create(
        world=world,
        date=now(),
        filename=f"conftool_schedule_{checksum}.json",
        type="application/json",
        file=ContentFile(document, "schedule.json"),
        public=True,
    )
    world.config["pretalx"]["url"] = sf.file.url
    world.config["pretalx"]["conftool"] = True
    world.save()
    async_to_sync(notify_schedule_change)(world.id)
    return sf.pk

@app.task(base=WorldTask)
def conftool_sync_posters(world):
    u = world.config.get("conftool_url")
    p = world.config.get("conftool_password")
    create_posters_from_conftool(world, u, p)


import json
import PIL.Image as Image
import torch.utils.data as data
from torchvision import transforms as transforms
from typing import List, Dict

from .. import LabeledDataset
from . import config

class SslDataset(LabeledDataset):
    r"""Dataset class with region-level annotations"""

    def __init__(self, pre_transforms, transform, meta_data: List[Dict]):
        r"""Constructor for annotated region dataset

        Args:
            meta_data: dataset metadata
            transform: transform to apply on images
            crop_size: size of center crop
            image_size: the size to resize the images to
        """
        super(SslDataset, self).__init__(transform, meta_data)
        self.pre_transforms = pre_transforms

    def __getitem__(self, idx):
        filepath = self.filepaths[idx]
        image = Image.open(filepath)
        image = self.pre_transforms[self.labels[idx]](image)
        image = self.transform(image)
        return image, self.labels[idx]

    def __len__(self):
        return len(self.filepaths)

def get_ssl_data_loaders(meta_data_path, transform, transform_test,
                         batch_size=8, num_workers=4):
    with open(meta_data_path, "r") as f:
        meta_data = json.load(f)
    pre_transforms = [
        transforms.Compose([
            transforms.Resize((image_size, image_size))
        ]) for image_size in config.IMAGE_SIZES]
    train_set = SslDataset(pre_transforms, transform, meta_data["train"])
    train_loader = data.DataLoader(train_set, batch_size=batch_size,
                                   num_workers=num_workers, shuffle=True)
    val_set = SslDataset(pre_transforms, transform_test, meta_data["val"])
    val_loader = data.DataLoader(val_set, batch_size=batch_size,
                                 num_workers=num_workers, shuffle=False)
    return train_loader, val_loader


# -*- coding: utf-8 -*-
import pytest

@pytest.mark.parametrize(
    'params, expected',
    [
        ([[10, 20], [50, 60], [10, 40], [5, 15], [5, 10], [25, 55]],
            [1, 1, 2, 3, 3, 3]),
    ],
)
def test_book(params, expected):
    from leetcode.my_calendar_iii import MyCalendarThree

    s = MyCalendarThree()
    actual = []
    for param in params:
        actual.append(s.book(*param))
    assert expected == actual


from bs4 import BeautifulSoup
import requests
import csv
from datetime import datetime
import time

global TEST_MODE
TEST_MODE = False

global dataListDict
dataListDict = []

global keysList
keysList = ["Enroll date", "URL", "Register no.", "Given Name", "Profile Name", "Labs", "Quests"]

def addError(err, data):
    global dataListDict
    err = "E: " + err
    dataListDict.append({"Enroll date": data[0], "URL": data[3], "Register no.": data[2], "Given Name": data[1], "Profile Name": err})

def getDetailsForProfile(data):
    try:
        r = requests.get(data[3])
    except:
        addError("Invalid URL", data)
        return

    soup = BeautifulSoup(r.content, 'html.parser')

    name_h1 = soup.find("h1", {"class": "l-mbm"})
    labs_quests_p = soup.find("p", {"class": "public-profile__hero__details"})
    badges_divs = soup.find_all("div", {"class": "public-profile__badge"})

    if name_h1 is None:
        addError("Not found", data)
        return

    global dataListDict
    global keysList

    temp_dict = {}
    temp_dict["Enroll date"] = data[0]
    temp_dict["Given Name"] = data[1]
    temp_dict["Register no."] = data[2]
    temp_dict["URL"] = data[3]

    name = name_h1.text.strip()
    labs_quests = labs_quests_p.text.strip().rsplit("\n")

    temp_dict["Profile Name"] = name[:-1]
    temp_dict["Labs"] = labs_quests[0]
    temp_dict["Quests"] = labs_quests[3]

    for badge in badges_divs:
        b_arr = badge.text.strip().rsplit("\n")
        temp_dict[b_arr[0]] = b_arr[4]
        if b_arr[0] not in keysList:
            keysList.append(b_arr[0])

    dataListDict.append(temp_dict)

if __name__ == "__main__":
    start_time = time.time()
    if TEST_MODE:
        print("TEST_MODE: ON")
        file_name = 'test_urls.csv'
    else:
        file_name = 'profile_urls.csv'

    total_count = 0
    with open(file_name, mode ='r') as file:
        csvFile = csv.reader(file)
        next(csvFile)
        total_count = sum(1 for row in csvFile)
        print("Total entries:", total_count)

    with open(file_name, mode ='r') as file:
        csvFile = csv.reader(file)
        next(csvFile)

        i = 0
        for lines in csvFile:
            getDetailsForProfile(lines)
            i += 1
            if i % 5 == 0:
                print(i, "/", total_count, "profiles analysed")

    print("Saving data")

    todayFileName = datetime.today().strftime('%Y-%m-%d')
    if TEST_MODE:
        todayFileName = todayFileName + "-test.csv"
    else:
        todayFileName = ".\\CSV\\" + todayFileName + ".csv"

    with open(todayFileName, 'w', newline='') as csvfile:
        writer = csv.DictWriter(csvfile, fieldnames = keysList)
        writer.writeheader()
        writer.writerows(dataListDict)

    diff = time.time() - start_time
    print("Finished in", int(diff/60), "min", "{:7.4f}".format(float(diff%60)), "sec")


from heapq import heappush, heappop, heapify
from collections import namedtuple

class Node(namedtuple('Node', 'prob,value,children')):
    def __lt__(self, other):
        return self.prob < other.prob

def pnode(probs):
    nodes = [Node(p, x, []) for x, p in probs.items()]
    if not nodes:
        return
    heapify(nodes)
    while len(nodes) != 1:
        a, b = heappop(nodes), heappop(nodes)
        heappush(nodes, Node(a.prob + b.prob, None, [a, b]))
    return nodes[0]

def codes(node, prefix=()):
    if node.value is not None:
        yield node.value, prefix
        return
    left, right = node.children
    yield from codes(left, prefix + (0,))
    yield from codes(right, prefix + (1,))

if __name__ == '__main__':
    r = dict(codes(pnode({
        'a': 0.5,
        'b': 0.25,
        'c': 0.125,
        'd': 0.125,
    })))
    assert len(r['a']) == 1
    assert len(r['b']) == 2
    assert len(r['c']) == 3
    assert len(r['d']) == 3


from django.contrib.sites.models import Site
from django.db.models import F
from django.template import TemplateDoesNotExist
from django.template.loader import get_template
from django.views.generic import TemplateView

from .models import AboutViews

class PageMixin(object):
    """Mixin para pages.

    Variables de clase:
    * template_name: Template .html (requerido)
    * template_md: Template .md (no requerido)
    * context_md: Contexto para templates *.md

    Requiere una URLConf para cada vista.

    Si no tiene valor template_md, simplemente renderiza
    la pagina .html.

    get_context_md similar a get_context_data pero para template_md.
    """
    template_md = None
    context_md = {}

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        if self.template_md:
            try:
                context['template_md'] = get_template(self.template_md).render(
                    self.get_context_md()
                )
            except TemplateDoesNotExist:
                raise TemplateDoesNotExist('Template {} no existe'.format(self.template_md))
        return context

    def get_context_md(self):
        """Similar a get_context_data pero para el *.md."""
        return self.context_md

class CookieConsentView(PageMixin, TemplateView):
    """Muestra la política de cookies."""
    template_name = 'pages/cookie_consent.html'
    template_md = 'pages/cookie_consent.md'

    def get_context_md(self):
        context = super().get_context_md()
        context['site'] = Site.objects.get_current()
        return context

class AboutView(PageMixin, TemplateView):
    """Pagina de About.

    Cuanta el numero de visitas que tiene.
    """
    template_name = 'pages/about.html'
    model = AboutViews

    def dispatch(self, request, *args, **kwargs):
        if AboutViews.objects.count() == 0:
            AboutViews.objects.create()
        return super().dispatch(request, *args, **kwargs)

    def get(self, request, *args, **kwargs):
        if not request.user.is_superuser:
            AboutViews.objects.update(views=F('views') + 1)
        return super().get(request, *args, **kwargs)

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['views_about'] = AboutViews.objects.get(pk=1)
        return context


import scipy.optimize

from evaluation import *
from utils import get_rotation_matrix_safe

@dataclass
class RotationSolution:

    objective_fnc: float
    rotation_vector_normal: float
    rotation_vector: np.ndarray
    permutation: list

def get_distance(one, two, angular_distance=True, squared=True):

    if angular_distance:
        scalar_products = one[:, 0] * two[:, 0] + one[:, 1] * two[:, 1] + one[:, 2] * two[:, 2]
        scalar_products = np.clip(scalar_products, a_min=-1.0, a_max=1.0)
        angular_distances = np.arccos(scalar_products)
        if squared:
            dist = np.sum(angular_distances ** 2) / angular_distances.shape[0]
        else:
            dist = np.sum(np.abs(angular_distances)) / angular_distances.shape[0]
    else:
        norms = np.linalg.norm(one - two, axis=1)
        if squared:
            dist = np.sum(norms ** 2) / norms.shape[0]
        else:
            dist = np.sum(np.abs(norms)) / norms.shape[0]

    return dist

def rotation_sort_value(solution: RotationSolution):
    return math.sqrt(solution.objective_fnc) * 10 + solution.rotation_vector_normal

def find_sorted_rotations(normals1, normals2, zero_around_z):

    assert len(normals1.shape) == 2
    assert len(normals2.shape) == 2

    assert np.all(np.abs(np.linalg.norm(normals1, axis=1) - 1.0) < 10 ** -5)
    assert np.all(np.abs(np.linalg.norm(normals2, axis=1) - 1.0) < 10 ** -5)

    solutions = find_rotations_info(normals1, normals2, zero_around_z)
    solutions.sort(key=lambda solution: rotation_sort_value(solution))
    return solutions

class ObjFunction:

    def __init__(self, src_normals, dst_normals, zero_around_z):
        self.src_normals = src_normals
        self.dst_normals = dst_normals
        self.zero_around_z = zero_around_z

    def function_value(self, r):

        if self.zero_around_z:
            r = np.array([r[0], r[1], 0.0])

        R = get_rotation_matrix_safe(r)
        rotated = (R @ self.src_normals.T).T

        return get_distance(rotated, self.dst_normals)

def find_rotations_info(normals1, normals2, zero_around_z):

    swapped = False
    if len(normals1) > len(normals2):
        swapped = True
        normals1, normals2 = normals2, normals1

    len1 = len(normals1)
    len2 = len(normals2)

    solutions = []

    for cur_permutation in itertools.permutations(range(len2), len1):

        def min_function(r):
            target = normals2[list(cur_permutation)]
            return ObjFunction(normals1, target, zero_around_z).function_value(r)

        use_shgo = False
        if use_shgo:
            sf = 1.0
            if zero_around_z:
                bounds = [(-math.pi * sf, math.pi * sf), (-math.pi * sf, math.pi * sf)]
            else:
                bounds = [(-math.pi * sf, math.pi * sf), (-math.pi * sf, math.pi * sf), (-math.pi * sf, math.pi * sf)]
            solution = scipy.optimize.shgo(min_function, bounds)
            if solution.success:

                for i in range(solution.xl.shape[0]):
                    r_star = solution.xl[i]
                    value = min_function(r_star)

                if swapped:
                    r_star = -r_star
                if zero_around_z:
                    r_star = np.array([r_star[0], r_star[1], 0])
                solutions.append(RotationSolution(rotation_vector=r_star, objective_fnc=value, rotation_vector_normal=np.linalg.norm(r_star), permutation=list(cur_permutation)))
        else:
            if zero_around_z:
                initial = np.zeros(2)
            else:
                initial = np.zeros(3)
            r_star = scipy.optimize.fmin(min_function, initial, disp=False)
            value = min_function(r_star)
            if swapped:
                r_star = -r_star
            if zero_around_z:
                r_star = np.array([r_star[0], r_star[1], 0])
            solutions.append(RotationSolution(rotation_vector=r_star, objective_fnc=value, rotation_vector_normal=np.linalg.norm(r_star), permutation=list(cur_permutation)))

    return solutions


import torch
import torch.nn as nn
from .Attention import Attention
from models.BiLstm import Bilstm
import torch.nn.functional as F

class Vanilla(nn.Module):

    def __init__(self, args, embedding, pad):
        super(Vanilla, self).__init__()
        self.lstm = Bilstm(args, embedding, pad)
        self.attention = Attention(args)
        self.w = nn.Linear(args.embed_dim, args.class_num, bias=True)
        torch.nn.init.xavier_uniform_(self.w.weight)

    def forward(self, x, local, add_num, max_add, length):
        left_matrix, tar_matrix, right_matrix = self.lstm(x, local, add_num, max_add, length)
        x = self.attention(left_matrix, tar_matrix, right_matrix)
        logit = F.softmax(self.w(x), 0)
        return logit


from glob import glob
import string
from os.path import splitext
from time import strftime
from csv import writer
import xml.parsers.expat as expat
from xml.dom import minidom

class SamplewiseSequenceAligner:

# Description:
#     Given a CSV file that contains all peptide-peptideSource-volume info,
#     and a set of FASTA input files for one or multiple proteins,
#     Find the volume intensity statistics
#         for every amino acid in every protein.
#     And write the results in CSV format
#     in a format of both one amino acid per line and ten amino acids per line.
#
# Author:
#     Michael Xin Sun: xinsun@ucdavis.edu
#     at 01:50:37am, Tue, Feb 19, 2013
#
# Possible bug from the inputCSV provider:
#     I used to have an issue which takes something like
#     "sp|P05814|CASB_HUMAN" and ""sp|P05814|CASB_HUMAN"
#     to be two different protein IDs (see a quotation mark there?)
#     This issue comes from the bug who outputs the CSV for our input CSV
#     If you see two proteinIDs in the result CSV files
#     that only differs from a punctuation mark such as ",
#     feel free to email me so that I can fix this issue.
#
# PS:
#     Feel free to share this with other Lebrilla's Group lab members
#     If you find this program useful in your research or publication,
#     I'll be really happy if you guys can put me
#         in your paper as a co-author, or contributor.  ^_^
#     My sincere thanks in advance!

    def __init__(self, config):

        self.config = config
        self.csvInputFileName = config["csvInputFile"]
        self.keepZeroIntensities = config["keepZeroIntensities"]

        self.listOfSampleNames = []

        # Set up a dictionary
        # to store all key-value pairs of
        # proteinID and proteinDescription
        self.proteinDescriptionDict = {}

        """
        proteinDescriptionDict = {proteinID1: proteinID1Description,
                                  proteinID2: proteinID2Description,
                                  proteinID3: proteinID3Description}
                                  ......}
        """
        # Set up a dictionary
        # to store all key-value pairs of
        # proteinID and proteinSequence
        self.proteinSequenceDict = {}

        """
        proteinSequenceDict = {proteinID1: proteinSequence1,
                               proteinID2: proteinSequence2,
                               proteinID3: proteinSequence3,
                               ......}
        """
        # Set up a dictionary
        # to store all peptide info in the input CSV
        # which includes
        # peptideSource
        # peptideSequence
        # peptideVolume
        self.peptideInfoDict = {}

        """
        peptide info hierarchy:
        peptide source
            -> peptide sample name
                -> peptide number of phosphorylation sites
                    -> peptide sequence
                        -> peptide volume

        peptideInfoDict = {
            peptideSource1: {
                peptideSample1: {
                    peptideNumberOfPhosphorylationSites1: {
                        peptideSequence1:
                            peptideVolume1,
                        peptideSequence2:
                            peptideVolume2,
                        ...
                    }
                    peptideNumberOfPhosphorylationSites2: {
                        peptideSequence3:
                            peptideVolume3,
                        peptideSequence4:
                            peptideVolume4,
                        ...
                    }
                    ...
                }
                peptideSample2: {
                    peptideNumberOfPhosphorylationSites3: {
                        peptideSequence5:
                            peptideVolume5,
                        eptideSequence6:
                            peptideVolume6,
                        ...
                    }
                    peptideNumberOfPhosphorylationSites4: {
                        peptideSequence7:
                            peptideVolume7,
                        peptideSequence8:
                            peptideVolume8,
                        ...
                    }
                    ...
                }
                ...
            }
            peptideSource2: {
                peptideSample3: {
                    peptideNumberOfPhosphorylationSites5: {
                        peptideSequence9:
                            peptideVolume9,
                        peptideSequence10:
                            peptideVolume10,
                        ...
                    }
                    peptideNumberOfPhosphorylationSites6: {
                        peptideSequence11:
                            peptideVolume11,
                        peptideSequence12:
                            peptideVolume12,
                        ...
                    }
                    ...
                }
                peptideSample4: {
                    peptideNumberOfPhosphorylationSites7: {
                        peptideSequence13:
                            peptideVolume13,
                        eptideSequence14:
                            peptideVolume14,
                        ...
                    }
                    peptideNumberOfPhosphorylationSites8: {
                        peptideSequence15:
                            peptideVolume15,
                        peptideSequence16:
                            peptideVolume16,
                        ...
                    }
                    ...
                }
                ...
            }
            ...
        }

        """
        # Set up a dictionary
        # to store every volume for every amino acid in every parent protein
        self.proteinVolumeDict = {}

        """
        PREVIOUS: no sample names, no number of phosphorylation sites

        proteinVolumeDict = {proteinID1: ["zero",
                                          volumeForAA1inProteinID1,
                                          volumeForAA2inProteinID1,
                                          ...]
                             proteinID2: ["zero",
                                          volumeForAA1inProteinID2,
                                          volumeForAA2inProteinID2,
                                          ...]
                             }
        """

        """
        NOW: with sample names, with number of phosphorylation sites

        proteinVolumeDict = {
            proteinID1: {
                peptideSample1: {
                    peptideNumberOfPhosphorylationSites1:
                        ["zero",
                         volumeForAA1inProteinID1,
                         volumeForAA2inProteinID1,
                         ...],
                    peptideNumberOfPhosphorylationSites2:
                        ["zero",
                         volumeForAA1inProteinID1,
                         volumeForAA2inProteinID1,
                         ...],
                    ...
                }
                peptideSample2: {
                    peptideNumberOfPhosphorylationSites3:
                        ["zero",
                         volumeForAA1inProteinID1,
                         volumeForAA2inProteinID1,
                         ...],
                    peptideNumberOfPhosphorylationSites4:
                        ["zero",
                         volumeForAA1inProteinID1,
                         volumeForAA2inProteinID1,
                         ...],
                    ...
                }
                ...
            }
            proteinID2: {
                peptideSample3: {
                    peptideNumberOfPhosphorylationSites5:
                        ["zero",
                         volumeForAA1inProteinID2,
                         volumeForAA2inProteinID2,
                         ...],
                    peptideNumberOfPhosphorylationSites6:
                        ["zero",
                         volumeForAA1inProteinID2,
                         volumeForAA2inProteinID2,
                         ...],
                    ...
                }
                peptideSample4: {
                    peptideNumberOfPhosphorylationSites7:
                        ["zero",
                         volumeForAA1inProteinID2,
                         volumeForAA2inProteinID2,
                         ...],
                    peptideNumberOfPhosphorylationSites7:
                        ["zero",
                         volumeForAA1inProteinID2,
                         volumeForAA2inProteinID2,
                         ...],
                    ...
                }
                ...
            }
            ...
        }

        """
        self.peptideVolumeContributionDict = {}

        """
        with sample names, with number of phosphorylation sites

        peptideVolumeContributionDict = {
            peptideSource1: {
                peptideSample1: {
                    peptideNumberOfPhosphorylationSites1: {
                        peptideString1: [0,   0, 30,  0, ...],
                        peptideString2: [0,  20, 20,  0, ...],
                        ...
                    }
                    peptideNumberOfPhosphorylationSites2: {
                        peptideString1: [15, 15,  0,  0, ...],
                        peptideString2: [10, 10, 10,  0, ...],
                        ...
                    }
                    ...
                }
                peptideSample2: {
                    peptideNumberOfPhosphorylationSites3: {
                        peptideString1: [10, 10, 20,  0, ...],
                        peptideString2: [10, 15, 30, 10, ...],
                        ...
                    }
                    peptideNumberOfPhosphorylationSites4: {
                        peptideString1: [25, 55, 30, 10, ...],
                        peptideString2: [20, 15, 30, 10, ...],
                        ...
                    }
                    ...
                }
                ...
            }
            peptideSource2: {
                peptideSample3: {
                    peptideNumberOfPhosphorylationSites5: {
                        peptideString1: [0,   0, 40,  0, ...],
                        peptideString2: [0,  30, 30,  0, ...],
                        ...
                    }
                    peptideNumberOfPhosphorylationSites6: {
                        peptideString1: [25, 25,  0,  0, ...],
                        peptideString2: [20, 20, 20,  0, ...],
                        ...
                    }
                    ...
                }
                peptideSample4: {
                    peptideNumberOfPhosphorylationSites7: {
                        peptideString1: [20, 20, 30,  0, ...],
                        peptideString2: [20, 25, 40, 20, ...],
                        ...
                    }
                    peptideNumberOfPhosphorylationSites8: {
                        peptideString1: [35, 65, 40, 20, ...],
                        peptideString2: [30, 25, 40, 20, ...],
                        ...
                    }
                    ...
                }
                ...
            }
            ...
        }

        """

    def readFromFastas(self):

        # Function Description:
        #     Read and parse all FASTA input files
        #     that contains the parent protein information
        # Function Input:
        #     FASTA input files
        # Function Output:
        #     None

        print("Reading and Parsing fasta input files...")

        # Get the list of input file names
        # All files with extension
        # ".fasta", ".fa", ".fna", ".fsa" and ".mpfa"
        # are considered valid FASTA input files
        # The file extension listed here are case-insensitive
        # So ".fasta", ".Fasta", ".FASTA" will all be valid
        listOfFastaFileNames = [fastaFileName
                                for fastaFileName in glob("./fasta_input_files/*.*")
                                if (".fasta" in fastaFileName.lower() or
                                    ".fa" in fastaFileName.lower() or
                                    ".fna" in fastaFileName.lower() or
                                    ".fsa" in fastaFileName.lower() or
                                    ".mpfa" in fastaFileName.lower() or
                                       (".txt" in fastaFileName.lower() and
                                            (".fasta" in fastaFileName.lower() or
                                             ".fa" in fastaFileName.lower() or
                                             ".fna" in fastaFileName.lower() or
                                             ".fsa" in fastaFileName.lower() or
                                             ".mpfa" in fastaFileName.lower())))]

        self.listOfFastaFileNames = listOfFastaFileNames

        # For every FASTA file name in the lsit of all fasta file names:
        #     Read in data from single FASTA file
        for fastaFileName in listOfFastaFileNames:
            self.readFromSingleFasta(fastaFileName)

        print("Done.\n\n")

    def readFromSingleFasta(self, fastaFileName):

        # Function Description
        #     Read and parse a single FASTA input file
        #     that contains the parent protein information
        # Function Input:
        #     The file name of the single FASTA file
        # Function Output:
        #     None

        proteinDescriptionDict = self.proteinDescriptionDict
        proteinSequenceDict = self.proteinSequenceDict

        print("Reading and Parsing single fasta input file " + fastaFileName
              + "...")

        # Open one FASTA input file to read
        inFasta = open(fastaFileName, "r")
        fastaFirstLine = inFasta.readline()

        if fastaFirstLine[0] != ">":
            raise Exception("Error: Possibly corrupted FASTA file "
                            + fastaFileName + ": does NOT begin with '>'.")

        firstLineFields = fastaFirstLine.strip().split()

        # Read in proteinID for this FASTA input file
        proteinID = firstLineFields[0][1:]

        # Read in proteinDescription for this FASTA input file
        proteinDescription = " ".join([word
                                       for word in firstLineFields[1:]])

        # Set up the key-value pair for proteinID and proteinDescription
        proteinDescriptionDict[proteinID] = proteinDescription

        # Get the entire protein sequence in this FASTA input file
        proteinSequence = "".join([line.strip()
                                   for line in inFasta])

        inFasta.close()

        # Error out if we find one proteinID which has been already read
        if proteinID in proteinSequenceDict.keys():
            raise Exception("Error: Possibly repetitive FASTA file "
                            + fastaFileName
                            + ": repetitive protein ID '" + proteinID + ".")

        # Error out if the first letter in the protein sequence
        #     is not an uppercase letter
        # which means the FASTA file is possibly corrupted
        if proteinSequence[0] not in string.ascii_uppercase:
            raise Exception("Error: Possibly corrupted FASTA file "
                            + fastaFileName + ": second line does NOT begin with"
                            + "a capital letter for start point amino acid.")

        # append proteinSequence to " "
        # so that index 1 in proteinSequence in programming string
        # is the same as index 1 in proteinSequence in daily life
        proteinSequenceDict[proteinID] = " " + proteinSequence

        self.proteinDescriptionDict = proteinDescriptionDict
        self.proteinSequenceDict = proteinSequenceDict

        print("Done.")

    def readFromCsv(self):

        # Function Description:
        #     Read and parse CSV input file
        #     that contains peptide information,
        #     such as peptide sequence, peptide source and peptide volume
        # Function Input:
        #     Input CSV file that contains peptide information
        # Function Output:
        #     None

        peptideInfoDict = self.peptideInfoDict
        proteinSequenceDict = self.proteinSequenceDict
        listOfSampleNames = self.listOfSampleNames
        csvInputFileName = self.csvInputFileName

        numberOfPhosphorylationsAllowed = self.config["numberOfPhosphorylationsAllowed"]

        print("Reading and Parsing CSV input file" + csvInputFileName + "...")

        # Open CSV input file to read
        inCsv = open(csvInputFileName, "r")

        # Get the first line of csv input as header info
        csvFirstLine = inCsv.readline()
        csvHeaders = csvFirstLine.strip().split(",")

        # Variable of key-value pairs for column headers and column indices,
        # Which means the order of "Name", "Volumen", "Notes", "Sample", "Phospho"
        #     and other columns simply doesn't matter.
        csvHeaderColumnLookupDict = {}

        """
        csvHeaderColumnLookupDict = {"Name": someColumnNumberForName,
                                     "Volumen": someColumnNumberForVolumen,
                                     "Notes": someColumnNumberForNotes,
                                     "Sample": someColumnNumberForSample,
                                     "Phospho": someColumnNumberForPhospho,
                                     ......}
        """
        # Build key-value pairs for column headers and column indices
        for csvHeaderIndex in range(len(csvHeaders)):
            csvHeader = csvHeaders[csvHeaderIndex]
            if csvHeader in ["Name", "Volumen", "Notes", "Sample", "Phospho"]:
                if csvHeader in csvHeaderColumnLookupDict.keys():
                    raise Exception("Error: Possibly corrupted CSV file: "
                                    + "There are at least 2 columns with name '"
                                    + csvHeader + "'.")
                csvHeaderColumnLookupDict[csvHeader] = csvHeaderIndex
            else:
                raise Exception(csvHeader + " is an unknown header.")

        # Get all column indices for "Name", "Volumen", "Notes", "Sample" and "Phospho"
        # In the default case,
        # "Name" is the 1st column
        # "Volumen" is the 2nd column
        # "Notes" is the 3rd column
        # "Sample" is the 4th column
        # "Phospho" is the 5th column
        nameHeaderIndex = csvHeaderColumnLookupDict["Name"]
        volumenHeaderIndex = csvHeaderColumnLookupDict["Volumen"]
        notesHeaderIndex = csvHeaderColumnLookupDict["Notes"]
        sampleHeaderIndex = csvHeaderColumnLookupDict["Sample"]
        numberOfPhosphorylationSitesHeaderIndex = csvHeaderColumnLookupDict["Phospho"]

        # Read all peptide source, peptide sequence, peptide sample name,
        # peptide number of phosphorylation sites and peptide volume info
        # from the input CSV file

        for line in inCsv:

            fields = line.strip().split(",")

            peptideSource = fields[notesHeaderIndex].strip().split(";")[0].replace('"', "")
            peptideSample = fields[sampleHeaderIndex].strip()
            peptideNumberOfPhosphorylationSites = int(fields[
                    numberOfPhosphorylationSitesHeaderIndex].strip())
            if peptideNumberOfPhosphorylationSites not in numberOfPhosphorylationsAllowed:
                continue
            peptideSequence = fields[nameHeaderIndex].strip()
            peptideVolume = int(fields[volumenHeaderIndex].strip())

            """
            if peptideSequence not in listOfPeptideSequences:
                listOfPeptideSequences.append(peptideSequence)

            """
            proteinSequence = proteinSequenceDict[peptideSource]
            if peptideSource not in peptideInfoDict.keys():
                peptideInfoDict\
                        [peptideSource]\
                        = {}
            if peptideSample not in listOfSampleNames:
                listOfSampleNames.append(peptideSample)
            if peptideSample not in peptideInfoDict[peptideSource].keys():
                peptideInfoDict\
                        [peptideSource]\
                        [peptideSample]\
                        = {}
            if peptideNumberOfPhosphorylationSites not in \
                    peptideInfoDict\
                            [peptideSource]\
                            [peptideSample]\
                            .keys():
                peptideInfoDict\
                        [peptideSource]\
                        [peptideSample]\
                        [peptideNumberOfPhosphorylationSites]\
                        = {}
            if peptideSequence not in \
                    peptideInfoDict\
                            [peptideSource]\
                            [peptideSample]\
                            [peptideNumberOfPhosphorylationSites]\
                            .keys():
                peptideInfoDict\
                        [peptideSource]\
                        [peptideSample]\
                        [peptideNumberOfPhosphorylationSites]\
                        [peptideSequence]\
                        = peptideVolume
            else:
                peptideInfoDict\
                        [peptideSource]\
                        [peptideSample]\
                        [peptideNumberOfPhosphorylationSites]\
                        [peptideSequence]\
                        += peptideVolume

        inCsv.close()

        """
        self.listOfPeptideSequences = listOfPeptideSequences

        """
        self.peptideInfoDict = peptideInfoDict
        self.listOfSampleNames = listOfSampleNames
        #print(listOfSampleNames)

        print("Done.\n\n")

    def alignSequences(self):

        # Function Description
        #     Align and match peptide strings to protein strings
        #     And add up all the volume intensities
        #     for every amino acid for every protein
        #     in an amino-acid-wise way
        # Function Input:
        #     None
        # Function Output:
        #     None

        peptideInfoDict = self.peptideInfoDict
        proteinSequenceDict = self.proteinSequenceDict
        proteinVolumeDict = self.proteinVolumeDict
        listOfSampleNames = self.listOfSampleNames

        # Initialize proteinVolumeDict
        for peptideSource in peptideInfoDict.keys():
            if peptideSource in proteinSequenceDict.keys():
                proteinSequence = proteinSequenceDict[peptideSource]
            else:
                raise Exception("Peptide info not found for " + peptideSource)
            if peptideSource not in proteinVolumeDict.keys():
                proteinVolumeDict\
                        [peptideSource]\
                     = {}
            for peptideSample in peptideInfoDict[peptideSource].keys():
                if peptideSample not in proteinVolumeDict[peptideSource].keys():
                    proteinVolumeDict\
                            [peptideSource]\
                            [peptideSample] = {}
                for peptideNumberOfPhosphorylationSites in \
                        peptideInfoDict\
                                [peptideSource]\
                                [peptideSample].keys():
                    if peptideNumberOfPhosphorylationSites not in \
                            proteinVolumeDict\
                                    [peptideSource]\
                                    [peptideSample].keys():
                        proteinVolumeDict\
                                [peptideSource]\
                                [peptideSample]\
                                [peptideNumberOfPhosphorylationSites]\
                                = ["zero"] + [0] * (len(proteinSequence) - 1)

        #print(proteinVolumeDict)
        """
        # Initialize peptideVolumnContributionDict
        for peptideSource in peptideInfoDict.keys():
            proteinSequence = proteinSequenceDict[peptideSource]
            if peptideSource not in peptideVolumeContributionDict.keys():
                peptideVolumeContributionDict\
                        [peptideSource]\
                        = {}
            else:
                for peptideSample in peptideInfoDict[peptideSource].keys():
                    if peptideSample not in peptideVolumeContributionDict.keys():
                        peptideVolumeContributionDict\
                                [peptideSource]\
                                [peptideSample]\
                                = {}
                    else:
                        for peptideNumberOfPhosphorylationSites in \
                                peptideInfoDict\
                                        [peptideSource]\
                                        [peptideSample].keys():
                            if peptideNumberOfPhosphorylationSites not in \
                                    peptideVolumeContributionDict\
                                            [peptideSource]\
                                            [peptideSample].keys():
                                peptideVolumeContributionDict\
                                        [peptideSource]\
                                        [peptideSample]\
                                        [peptideNumberOfPhosphorylationSites]\
                                        = {}
                            else:
                                for peptideSequence in peptideInfoDict\
                                        [peptideSource]\
                                        [peptideSample]\
                                        [peptideNumberOfPhosphorylationSites].keys():
                                    if peptideSequence not in \
                                            peptideVolumeContributionDict\
                                                    [peptideSource]\
                                                    [peptideSample]\
                                                    [peptideNumberOfPhosphorylationSites].keys():
                                        peptideVolumeContributionDict\
                                                [peptideSource]\
                                                [peptideSample]\
                                                [peptideNumberOfPhosphorylationSites]\
                                                [peptideSequence]\
                                                = ["zero"] + [0] * (len(proteinSequence) - 1)
                                    else:
                                        pass

        """

        # Add up the volume intensity values
        #     for every amino acid in every parent protein
        for peptideSource in peptideInfoDict.keys():
            proteinSequence = proteinSequenceDict[peptideSource]
            for peptideSample in peptideInfoDict[peptideSource].keys():
                for peptideNumberOfPhosphorylationSites in \
                        peptideInfoDict[peptideSource]\
                                       [peptideSample].keys():
                    for peptideSequence in \
                            peptideInfoDict\
                                    [peptideSource]\
                                    [peptideSample]\
                                    [peptideNumberOfPhosphorylationSites].keys():
                        peptideVolume = peptideInfoDict\
                                                [peptideSource]\
                                                [peptideSample]\
                                                [peptideNumberOfPhosphorylationSites]\
                                                [peptideSequence]
                        matchedProteinStartingIndices = self.findAllSubstringIndices(
                                proteinSequence, peptideSequence)

                        aminoAcidVolume = round(peptideVolume / len(
                                                matchedProteinStartingIndices))
                        for i in matchedProteinStartingIndices:
                            for offset in range(len(peptideSequence)):
                                proteinVolumeDict\
                                        [peptideSource]\
                                        [peptideSample]\
                                        [peptideNumberOfPhosphorylationSites]\
                                        [i + offset]\
                                        += aminoAcidVolume

        self.proteinVolumeDict = proteinVolumeDict
        #print(proteinVolumeDict)
        #print(self.peptideVolumeContributionDict.keys())
        self.csvInputFileName = self.csvInputFileName.split("/")[-1]

        #for peptideSource in self.peptideVolumeContributionDict.keys():
        #    print(self.peptideVolumeContributionDict[peptideSource])

    def findAllSubstringIndices(self, parentString, subString):

        # Function Description:
        #     A TOY PROGRAM
        #     for all strings that is a substring of the parentString:
        #         return a list of substring-start indices
        #     the set of all substrings may overlap
        #
        #     for example:
        #         Input: "ABCDABC" and "ABC"
        #         Output: [0, 4]
        #     for example:
        #         Input: "ABCBCBCD" AND "BCBC"
        #         Output: [1, 3]
        #
        # Function Input:
        #     A parent string
        #     A subString
        # Function Output:
        #     A list of substring-start indices

        listIndex = []
        index = parentString.find(subString)
        while index >= 0:
            listIndex.append(index)
            index = parentString.find(subString, index + 1)
        return listIndex

    def writeToCsvOneAAPerLine(self):

        # Function Description:
        #     Write all peptide matching results of all proteins
        #     into a CSV output file
        #     in a format of one amino acid per line
        # Function Input:
        #     None
        # Function Output:
        #     A CSV output file that contains all peptide matching results of all proteins

        proteinDescriptionDict = self.proteinDescriptionDict
        proteinSequenceDict = self.proteinSequenceDict
        proteinVolumeDict = self.proteinVolumeDict
        peptideVolumeContributionDict = self.peptideVolumeContributionDict
        listOfSampleNames = self.listOfSampleNames
        numberOfPhosphorylationsAllowed = self.config["numberOfPhosphorylationsAllowed"]

        """
        print("\n".join(list(proteinDescriptionDict.keys())))
        print("")
        print("\n".join(list(proteinSequenceDict.keys())))
        print("")
        print("\n".join(list(proteinVolumeDict.keys())))
        print("")
        print("\n".join(list(peptideVolumeContributionDict.keys())))
        print("")

        """

        csvInputFileName = self.csvInputFileName

        print("Writing CSV output file, 1 amino acid per line...")

        print("where " + ', '.join([str(number)
                                for number in numberOfPhosphorylationsAllowed])
              + " phosphorylation sites are selected,")

        if self.keepZeroIntensities == True:
            print("Keeping all zero intensities...")
        else:
            print("Omitting all zero intensities...")

        # Add the date and time into the output file name
        if self.keepZeroIntensities == True:
            csvOutputFileName = ("CSV_1_" + splitext(csvInputFileName)[0]
                                 + "_protein_aa_volume_statistics_allowing_"
                                 + "_".join([str(number)
                                    for number in numberOfPhosphorylationsAllowed])
                                 + "_phosphorylations_including_zeros"
                                 + strftime("_%I_%M_%S_%p_%b_%d_%Y") + ".csv")
        else:
            csvOutputFileName = ("CSV_1_" + splitext(csvInputFileName)[0]
                                 + "_protein_aa_volume_statistics_allowing_"
                                 + "_".join([str(number)
                                    for number in numberOfPhosphorylationsAllowed])
                                 + "_phosphorylations_excluding_zeros"
                                 + strftime("_%I_%M_%S_%p_%b_%d_%Y") + ".csv")

        # Add "_1" to the output file name in case there's a name conflict
        listOfRepetitiveFileNames = [fileName
                                     for fileName in glob("*.csv")
                                     if fileName == csvOutputFileName]

        while listOfRepetitiveFileNames != []:
            baseOutput, extensionOutput = splitext(csvOutputFileName)
            csvOutputFileName = baseOutput + "_1" + extensionOutput

            listOfRepetitiveFileNames = [fileName
                                         for fileName in glob("*.csv")
                                         if fileName == csvOutputFileName]

        outCsv = open("./csv_output_files/" + csvOutputFileName, "wb")
        csvWriter = writer(outCsv)

        # Write a description of the output file
        csvWriter.writerow(["This file contains protein amino acid volume intensity"])
        csvWriter.writerow(["that comes from csv file in the csv input folder"])
        csvWriter.writerow(["for all fasta input files in the fasta input folder"])

        csvWriter.writerow(["Number of Phosphorylation sites allowed: "
                            + ', '.join([str(number)
                                for number in numberOfPhosphorylationsAllowed])])

        if self.keepZeroIntensities == True:
            csvWriter.writerow(["Amino acids with 0 volume intensity are also listed"])
        else:
            csvWriter.writerow(["Amino acids with 0 volume intensity are OMITTED"])

        csvWriter.writerow(["Right Click and 'Sort by Name' in the fasta input folder"])
        csvWriter.writerow(["That's the order that all output are organized."])
        csvWriter.writerow([""])

        # Get the list of proteinIDs from FASTA input files
        listOfProteinIDsFromFasta = list(proteinSequenceDict.keys())

        # Sort all FASTA files by Name in alphabetical order
        # which is same as "Sort by Name" using Right Click
        for proteinIDFromFasta in sorted(listOfProteinIDsFromFasta):

            if proteinIDFromFasta in proteinVolumeDict.keys():

                # Write proteinID and proteinDescription
                # so you can identify which protein it is
                csvWriter.writerow([proteinIDFromFasta])
                csvWriter.writerow([proteinDescriptionDict[proteinIDFromFasta]])

                proteinSequence = proteinSequenceDict[proteinIDFromFasta]
                proteinVolumeInfo = proteinVolumeDict[proteinIDFromFasta]

                # Write a table header
                csvWriter.writerow(["", "AA Index", "AA", "Volume"] + listOfSampleNames)

                for aminoAcidIndex in range(1, len(proteinSequence)):

                    aminoAcidVolumeForAllSamples = 0
                    listOfAminoAcidVolumes = []

                    for peptideSample in listOfSampleNames:

                        aminoAcidVolumePerSample = 0

                        if peptideSample not in proteinVolumeInfo.keys():
                            aminoAcidVolumePerSample = 0
                        else:
                            for peptideNumberOfPhosphorylationSites in \
                                    proteinVolumeInfo[peptideSample].keys():

                                aminoAcidVolumePerSamplePerPhosphorylationSite\
                                        = proteinVolumeInfo\
                                                [peptideSample]\
                                                [peptideNumberOfPhosphorylationSites]\
                                                [aminoAcidIndex]
                                aminoAcidVolumePerSample \
                                        += aminoAcidVolumePerSamplePerPhosphorylationSite

                        aminoAcidVolumeForAllSamples += aminoAcidVolumePerSample
                        listOfAminoAcidVolumes.append(aminoAcidVolumePerSample)

                    if self.keepZeroIntensities == True:

                        #print(peptideVolumeContribution)
                        #print("HELLO")
                        csvWriter.writerow(["",
                                            aminoAcidIndex,
                                            proteinSequence[aminoAcidIndex],
                                            aminoAcidVolumeForAllSamples] +
                                            listOfAminoAcidVolumes)

                    else:

                        # Write volume info,
                        # one volume per line,
                        # as long as the volume is NOT 0
                        if aminoAcidVolumeForAllSamples != 0:
                            #print("Hi")
                            csvWriter.writerow(["",
                                                aminoAcidIndex,
                                                proteinSequence[aminoAcidIndex],
                                                aminoAcidVolumeForAllSamples] +
                                                listOfAminoAcidVolumes)

            else:

                # If one proteinID is NOT matched with any peptide
                #     Write "NOT MATCHED AT ALL"
                csvWriter.writerow([proteinIDFromFasta])
                csvWriter.writerow([proteinDescriptionDict[proteinIDFromFasta]])

                #csvWriter.writerow(["", "AA Index", "AA", "Volume"])
                csvWriter.writerow(["", "NOT", "MATCHED", "AT", "ALL"])

            csvWriter.writerow([""])

        outCsv.close()

        print("Done.\n\n")

class configurationFromXML(dict):

    def __init__(self, configname):

        try:
            file = open(configname, "rb")
            configDom = minidom.parse(file)
        except expat.ExpatError:
            print("SamplewiseSequenceAlignerConfig.xml not in XML format, or corrupted:")
            for entry in sys.exc_info():
                print(entry)

        try:
            sequenceAlignerConfigNode = configDom.getElementsByTagName(
                    "sequencealignerconfig")[0]
            csvInputFileNode = sequenceAlignerConfigNode.getElementsByTagName(
                    "csvinputfile")[0]
            csvInputFolder = csvInputFileNode.getAttribute("folder").strip()
            csvInputFile = csvInputFileNode.getAttribute("filename").strip()
        except IndexError:
            print("Missing 1 or more required configuration fields: folder or filename")
            for entry in sys.exc_info():
                print(entry)

        csvInputFullFolder = "./" + csvInputFolder + "/"
        csvInputFile = csvInputFullFolder + csvInputFile
        print(csvInputFile)

        self["csvInputFile"] = csvInputFile

        try:
            phosphorylationNode = sequenceAlignerConfigNode.getElementsByTagName(
                    "phosphorylation")[0]
            numberOfPhosphorylationsAllowed = phosphorylationNode.getAttribute("allowed")
            if numberOfPhosphorylationsAllowed == "":
                raise Exception("Number of phosphorylations cannot be empty")
            numberOfPhosphorylationsAllowed = numberOfPhosphorylationsAllowed.strip().strip(",")
            numberOfPhosphorylationsAllowed = [int(number.strip())
                    for number in numberOfPhosphorylationsAllowed.split(",")]
        except IndexError:
            print("Missing 1 or more required configuration fields: ")
            for entry in sys.exc_info():
                print(entry)

        self["numberOfPhosphorylationsAllowed"] = numberOfPhosphorylationsAllowed

        try:
            keepZeroIntensitiesNode = sequenceAlignerConfigNode.getElementsByTagName(
                    "keepzerointensities")[0]
            keepZeroIntensities = bool(keepZeroIntensitiesNode.getAttribute("keep").strip())
        except IndexError:
            print("Missing 1 or more required configuration fields: keepzerointensities.keep")
            for entry in sys.exc_info():
                print(entry)

        self["keepZeroIntensities"] = keepZeroIntensities