• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

int-brain-lab / iblrig / 11177497220

04 Oct 2024 09:27AM UTC coverage: 48.017% (+1.2%) from 46.79%
11177497220

Pull #724

github

76a728
web-flow
Merge d585cdc2b into 646ec9386
Pull Request #724: 8.24.3

5 of 6 new or added lines in 4 files covered. (83.33%)

1030 existing lines in 22 files now uncovered.

4188 of 8722 relevant lines covered (48.02%)

0.96 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

37.17
/iblrig/gui/tab_data.py
1
import platform
2✔
2
import subprocess
2✔
3
from datetime import datetime
2✔
4
from typing import NamedTuple
2✔
5

6
import pandas as pd
2✔
7
from PyQt5.Qt import pyqtSlot
2✔
8
from PyQt5.QtCore import (
2✔
9
    QDateTime,
10
    QModelIndex,
11
    QRegExp,
12
    QSettings,
13
    QSortFilterProxyModel,
14
    Qt,
15
    QThread,
16
    pyqtSignal,
17
)
18
from PyQt5.QtWidgets import QHeaderView, QStyledItemDelegate, QWidget
2✔
19

20
from iblqt.core import DataFrameTableModel
2✔
21
from iblrig.gui.ui_tab_data import Ui_TabData
2✔
22
from iblrig.path_helper import get_local_and_remote_paths
2✔
23
from iblrig.transfer_experiments import CopyState, SessionCopier
2✔
24
from iblutil.util import dir_size
2✔
25

26
if platform.system() == 'Windows':
2✔
27
    from os import startfile
1✔
28

29
COPY_STATE_STRINGS = {
2✔
30
    CopyState.HARD_RESET: 'Hard Reset',
31
    CopyState.NOT_REGISTERED: 'Not Registered',
32
    CopyState.PENDING: 'Copy Pending',
33
    CopyState.COMPLETE: 'Copy Complete',
34
    CopyState.FINALIZED: 'Copy Finalized',
35
}
36

37
SESSIONS_GLOB = r'*/[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]/[0-9][0-9][0-9]/'
2✔
38

39

40
def sizeof_fmt(num, suffix='B'):
2✔
41
    for unit in ('', 'K', 'M', 'G', 'T', 'P', 'E', 'Z'):
×
42
        if abs(num) < 1024.0:
×
43
            return f'{num:3.1f} {unit}{suffix}'
×
44
        num /= 1024.0
×
45
    return f'{num:.1f} Y{suffix}'
×
46

47

48
class Column(NamedTuple):
2✔
49
    name: str
2✔
50
    hidden: bool = False
2✔
51
    resizeMode: QHeaderView.ResizeMode = QHeaderView.Fixed
2✔
52
    sectionWidth: int = 130
2✔
53

54

55
COLUMNS = (
2✔
56
    Column(name='Directory', hidden=True),
57
    Column(name='Subject', resizeMode=QHeaderView.Stretch),
58
    Column(name='Date'),
59
    Column(name='Copy Status', sectionWidth=100),
60
    Column(name='Size', sectionWidth=75),
61
)
62

63

64
class DataItemDelegate(QStyledItemDelegate):
2✔
65
    def initStyleOption(self, option, index):
2✔
66
        super().initStyleOption(option, index)
×
67
        header_text = index.model().headerData(index.column(), Qt.Horizontal, Qt.DisplayRole)
×
68
        if 'Size' in header_text:
×
69
            option.text = sizeof_fmt(index.data())
×
70
            option.displayAlignment = Qt.AlignRight | Qt.AlignVCenter
×
71

72

73
class TabData(QWidget, Ui_TabData):
2✔
74
    def __init__(self, *args, **kwargs):
2✔
75
        super().__init__(*args, **kwargs)
×
76
        self.setupUi(self)
×
77
        self.settings = QSettings()
×
78
        self.localSubjectsPath = get_local_and_remote_paths().local_subjects_folder
×
79

80
        # create empty DataFrameTableModel
81
        data = pd.DataFrame(None, index=[], columns=[c.name for c in COLUMNS])
×
NEW
82
        self.tableModel = DataFrameTableModel(dataFrame=data)
×
83

84
        # create filter proxy & assign it to view
85
        self.tableProxy = QSortFilterProxyModel()
×
86
        self.tableProxy.setSourceModel(self.tableModel)
×
87
        self.tableProxy.setFilterKeyColumn(1)
×
88

89
        # define view
90
        self.tableView.setModel(self.tableProxy)
×
91
        header = self.tableView.horizontalHeader()
×
92
        header.setDefaultAlignment(Qt.AlignLeft)
×
93
        for idx, column in enumerate(COLUMNS):
×
94
            self.tableView.setColumnHidden(idx, column.hidden)
×
95
            if not column.hidden:
×
96
                if column.resizeMode == QHeaderView.Fixed:
×
97
                    header.resizeSection(idx, column.sectionWidth)
×
98
                else:
99
                    header.setSectionResizeMode(idx, column.resizeMode)
×
100
        self.tableView.setItemDelegate(DataItemDelegate(self.tableView))
×
101
        self.tableView.sortByColumn(
×
102
            self.settings.value('sortColumn', [c.name for c in COLUMNS].index('Date'), int),
103
            self.settings.value('sortOrder', Qt.AscendingOrder, Qt.SortOrder),
104
        )
105

106
        # define worker for assembling data
107
        self.dataWorker = DataWorker(self)
×
108

109
        # connect signals to slots
110
        self.dataWorker.initialized.connect(self.tableModel.setDataFrame)
×
111
        self.dataWorker.update.connect(self.tableModel.setData)
×
112
        self.dataWorker.started.connect(lambda: self.pushButtonUpdate.setEnabled(False))
×
113
        self.dataWorker.lazyLoadComplete.connect(lambda: self.pushButtonUpdate.setEnabled(True))
×
114
        self.tableView.doubleClicked.connect(self._openDir)
×
115
        self.tableView.horizontalHeader().sectionClicked.connect(self._storeSort)
×
116
        self.pushButtonUpdate.clicked.connect(self.dataWorker.start)
×
117
        self.lineEditFilter.textChanged.connect(self._filter)
×
118

119
    @pyqtSlot(str)
2✔
120
    def _filter(self, text: str):
2✔
121
        self.tableProxy.setFilterRegExp(QRegExp(text, Qt.CaseInsensitive))
×
122

123
    def showEvent(self, a0):
2✔
124
        if self.tableModel.rowCount() == 0:
×
125
            self.dataWorker.start()
×
126

127
    @pyqtSlot(QModelIndex)
2✔
128
    def _openDir(self, index: QModelIndex):
2✔
129
        directory = self.tableView.model().itemData(index.siblingAtColumn(0))[0]
×
130
        if platform.system() == 'Windows':
×
131
            startfile(directory)
×
132
        elif platform.system() == 'Darwin':
×
133
            subprocess.Popen(['open', directory])
×
134
        else:
135
            subprocess.Popen(['xdg-open', directory])
×
136

137
    @pyqtSlot(int)
2✔
138
    def _storeSort(self, index: int):
2✔
139
        self.settings.setValue('sortColumn', self.tableView.horizontalHeader().sortIndicatorSection())
×
140
        self.settings.setValue('sortOrder', self.tableView.horizontalHeader().sortIndicatorOrder())
×
141

142

143
class DataWorker(QThread):
2✔
144
    initialized = pyqtSignal(pd.DataFrame)
2✔
145
    update = pyqtSignal(QModelIndex, object)
2✔
146
    lazyLoadComplete = pyqtSignal()
2✔
147

148
    def __init__(self, parent: TabData):
2✔
149
        super().__init__(parent)
×
150
        self.localSubjectsPath = parent.localSubjectsPath
×
151
        self.tableModel = parent.tableModel
×
152
        self.tableModel.modelReset.connect(self.lazyLoadStatus)
×
153

154
    def run(self):
2✔
155
        data = []
×
156
        for session_dir in self.localSubjectsPath.glob(SESSIONS_GLOB):
×
157
            # make sure we're dealing with a directory
158
            if not session_dir.is_dir():
×
159
                continue
×
160

161
            # get folder creation time (cross-check with name of directory)
162
            date = datetime.strptime(session_dir.parent.name, '%Y-%m-%d')
×
163
            time = datetime.fromtimestamp(session_dir.stat().st_ctime)
×
164
            date = time if time.date() == date.date() else date
×
165

166
            # append data
167
            data.append(
×
168
                [
169
                    session_dir,
170
                    session_dir.parents[1].name,
171
                    QDateTime.fromTime_t(int(date.timestamp())),
172
                    '',  # will be lazy-loaded in a separate step
173
                    float(dir_size(session_dir)),
174
                ]
175
            )
176
        data = pd.DataFrame(data=data, columns=[c.name for c in COLUMNS])
×
177
        self.initialized.emit(data)
×
178

179
    def lazyLoadStatus(self):
2✔
180
        col_status = self.tableModel.dataFrame.columns.get_loc('Copy Status')
×
181
        for row, row_data in self.tableModel.dataFrame.iterrows():
×
182
            state = SessionCopier(row_data['Directory']).state
×
183
            state = COPY_STATE_STRINGS.get(state, 'N/A')
×
184
            index = self.tableModel.index(row, col_status)
×
185
            self.update.emit(index, state)
×
186
        self.lazyLoadComplete.emit()
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc