text: import collections
  from Qt import QtCore
  from pype.api import Logger
  from pypeapp.lib.log import _bootstrap_mongo_log, LOG_COLLECTION_NAME
  
  log = Logger().get_logger("LogModel", "LoggingModule")
  
  
  class LogModel(QtCore.QAbstractItemModel):
      COLUMNS = [
          "process_name",
          "hostname",
          "hostip",
          "username",
          "system_name",
          "started"
      ]
  
      colums_mapping = {
          "process_name": "Process Name",
          "process_id": "Process Id",
          "hostname": "Hostname",
          "hostip": "Host IP",
          "username": "Username",
          "system_name": "System name",
          "started": "Started at"
      }
      process_keys = [
          "process_id", "hostname", "hostip",
          "username", "system_name", "process_name"
      ]
      log_keys = [
          "timestamp", "level", "thread", "threadName", "message", "loggerName",
          "fileName", "module", "method", "lineNumber"
      ]
      default_value = "- Not set -"
      NodeRole = QtCore.Qt.UserRole + 1
  
      def __init__(self, parent=None):
          super(LogModel, self).__init__(parent)
          self._root_node = Node()
  
          self.dbcon = None
          # Crash if connection is not possible to skip this module
          database = _bootstrap_mongo_log()
          if LOG_COLLECTION_NAME in database.list_collection_names():
              self.dbcon = database[LOG_COLLECTION_NAME]
  
      def add_log(self, log):
          node = Node(log)
          self._root_node.add_child(node)
  
      def refresh(self):
          self.log_by_process = collections.defaultdict(list)
          self.process_info = {}
  
          self.clear()
          self.beginResetModel()
          if self.dbcon:
              result = self.dbcon.find({})
              for item in result:
                  process_id = item.get("process_id")
                  # backwards (in)compatibility
                  if not process_id:
                      continue
  
                  if process_id not in self.process_info:
                      proc_dict = {}
                      for key in self.process_keys:
                          proc_dict[key] = (
                              item.get(key) or self.default_value
                          )
                      self.process_info[process_id] = proc_dict
  
                  if "_logs" not in self.process_info[process_id]:
                      self.process_info[process_id]["_logs"] = []
  
                  log_item = {}
                  for key in self.log_keys:
                      log_item[key] = item.get(key) or self.default_value
  
                  if "exception" in item:
                      log_item["exception"] = item["exception"]
  
                  self.process_info[process_id]["_logs"].append(log_item)
  
          for item in self.process_info.values():
              item["_logs"] = sorted(
                  item["_logs"], key=lambda item: item["timestamp"]
              )
              item["started"] = item["_logs"][0]["timestamp"]
              self.add_log(item)
  
          self.endResetModel()
  
      def data(self, index, role):
          if not index.isValid():
              return None
  
          if role == QtCore.Qt.DisplayRole or role == QtCore.Qt.EditRole:
              node = index.internalPointer()
              column = index.column()
  
              key = self.COLUMNS[column]
              if key == "started":
                  return str(node.get(key, None))
              return node.get(key, None)
  
          if role == self.NodeRole:
              return index.internalPointer()
  
      def index(self, row, column, parent):
          """Return index for row/column under parent"""
  
          if not parent.isValid():
              parent_node = self._root_node
          else:
              parent_node = parent.internalPointer()
  
          child_item = parent_node.child(row)
          if child_item:
              return self.createIndex(row, column, child_item)
          return QtCore.QModelIndex()
  
      def rowCount(self, parent):
          node = self._root_node
          if parent.isValid():
              node = parent.internalPointer()
          return node.childCount()
  
      def columnCount(self, parent):
          return len(self.COLUMNS)
  
      def parent(self, index):
          return QtCore.QModelIndex()
  
      def headerData(self, section, orientation, role):
          if role == QtCore.Qt.DisplayRole:
              if section < len(self.COLUMNS):
                  key = self.COLUMNS[section]
                  return self.colums_mapping.get(key, key)
  
          super(LogModel, self).headerData(section, orientation, role)
  
      def flags(self, index):
          return (QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable)
  
      def clear(self):
          self.beginResetModel()
          self._root_node = Node()
          self.endResetModel()
  
  
  class Node(dict):
      """A node that can be represented in a tree view.
  
      The node can store data just like a dictionary.
  
      >>> data = {"name": "John", "score": 10}
      >>> node = Node(data)
      >>> assert node["name"] == "John"
  
      """
  
      def __init__(self, data=None):
          super(Node, self).__init__()
  
          self._children = list()
          self._parent = None
  
          if data is not None:
              assert isinstance(data, dict)
              self.update(data)
  
      def childCount(self):
          return len(self._children)
  
      def child(self, row):
          if row >= len(self._children):
              log.warning("Invalid row as child: {0}".format(row))
              return
  
          return self._children[row]
  
      def children(self):
          return self._children
  
      def parent(self):
          return self._parent
  
      def row(self):
          """
          Returns:
               int: Index of this node under parent"""
          if self._parent is not None:
              siblings = self.parent().children()
              return siblings.index(self)
  
      def add_child(self, child):
          """Add a child to this node"""
          child._parent = self
          self._children.append(child)
  