예제 #1
0
    def add_media_from_dir(self, n_player, flag_path):
        """
        add all media from a selected directory

        Args:
            nPlayer (str): player
            flag_path (bool): True include full path of media else only basename
        """
        dirName = QFileDialog().getExistingDirectory(self, "Select directory")
        if dirName:
            r = ""
            for file_path in glob.glob(dirName + os.sep + "*"):
                if not self.check_media(n_player, file_path, flag_path):
                    if r != "Skip all non media files":
                        r = dialog.MessageDialog(programName, (
                            "The <b>{file_path}</b> file does not seem to be a media file."
                            "").format(file_path=file_path), [
                                "Continue", "Skip all non media files",
                                "Cancel"
                            ])
                        if r == "Cancel":
                            break

        self.cbVisualizeSpectrogram.setEnabled(self.twVideo1.rowCount() > 0)
        self.cbCloseCurrentBehaviorsBetweenVideo.setEnabled(
            self.twVideo1.rowCount() > 0)
예제 #2
0
    def add_media_from_dir(self, n_player, flag_path):
        """
        add all media from a selected directory

        Args:
            n_player (str): player
            flag_path (bool): True include full path of media else only basename
        """

        # check if project saved
        if (not flag_path) and (not self.project_file_name):
            QMessageBox.critical(self, programName, ("It is not possible to add media without full path "
                                                     "if the project is not saved"))
            return

        fd = QFileDialog()
        fd.setDirectory(os.path.expanduser("~") if flag_path else str(Path(self.project_path).parent))

        dir_name = fd.getExistingDirectory(self, "Select directory")
        if dir_name:
            r = ""
            for file_path in glob.glob(dir_name + os.sep + "*"):
                if not self.check_media(n_player, file_path, flag_path):
                    if r != "Skip all non media files":
                        r = dialog.MessageDialog(programName,
                                                 ("The <b>{file_path}</b> file does not seem to be a media file."
                                                  "").format(file_path=file_path),
                                                 ["Continue", "Skip all non media files", "Cancel"])
                        if r == "Cancel":
                            break


        self.cbVisualizeSpectrogram.setEnabled(self.twVideo1.rowCount() > 0)
        self.cb_visualize_waveform.setEnabled(self.twVideo1.rowCount() > 0)
예제 #3
0
    def newMap(self):
        """
        create a new map
        """

        if self.flagMapChanged:

            response = dialog.MessageDialog(programName + ' - Modifiers map creator', 'What to do about the current unsaved coding map?', ['Save', 'Discard', 'Cancel'])

            if response == 'Save':
                if not self.saveMap_clicked():
                    return

            if response == 'Cancel':
                return

        self.cancelMap()

        text, ok = QInputDialog.getText(self, "Behaviors coding map name", "Enter a name for the new coding map")
        if ok:
            self.mapName = text
        else:
            return

        if not self.mapName:
            QMessageBox.critical(self, "", "You must define a name for the new coding map")
            return

        self.setWindowTitle("{} - Behaviors coding map creator tool - {}".format(programName, self.mapName))

        self.btLoad.setVisible(True)
        self.statusBar().showMessage('Click "Load bitmap" button to select and load a bitmap into the viewer')
예제 #4
0
    def view_data_file_head(self):
        """
        view first parts of data file
        """
        if self.tw_data_files.selectedIndexes() or self.tw_data_files.rowCount() == 1:

            if self.tw_data_files.rowCount() == 1:
                header = self.return_file_header(self.tw_data_files.item(0, 0).text())
            else:
                header = self.return_file_header(self.tw_data_files.item(self.tw_data_files.selectedIndexes()[0].row(), 0).text())

            if header:
                dialog.MessageDialog(programName, "<pre>{}</pre>".format(header), [OK])

                '''
                self.data_file_head = dialog.ResultsWidget()
                #self.results.setWindowFlags(Qt.WindowStaysOnTopHint)
                self.data_file_head.resize(540, 340)
                self.data_file_head.setWindowTitle(programName + " - Data file first lines")
                self.data_file_head.lb.setText(os.path.basename(self.tw_data_files.item(self.tw_data_files.selectedIndexes()[0].row(), 0).text()))
                self.data_file_head.ptText.setReadOnly(True)
                self.data_file_head.ptText.appendHtml("<pre>" + text + "</pre>")
                
                self.data_file_head.show()
                '''
        else:
            QMessageBox.warning(self, programName, "Select a data file")
예제 #5
0
    def add_media_from_dir(self, n_player, flag_path):
        """
        add all media from a selected directory

        Args:
            nPlayer (str): player
            flag_path (bool): True include full path of media else only basename
        """
        
        fd = QFileDialog(self)
        fd.setDirectory(os.path.expanduser("~") if flag_path else str(Path(self.project_path).parent))

        dir_name = fd.getExistingDirectory(self, "Select directory")
        if dir_name:
            r = ""
            for file_path in glob.glob(dir_name + os.sep + "*"):
                if not self.check_media(n_player, file_path, flag_path):
                    if r != "Skip all non media files":
                        r = dialog.MessageDialog(programName,
                                                 ("<b>{file_path}</b> arquivo não parece ser um arquivo de mídia."
                                                  "").format(file_path=file_path),
                                                 ["Continue", "Skip all non media files", "Cancel"])
                        if r == "Cancel":
                            break


        self.cbVisualizeSpectrogram.setEnabled(self.twVideo1.rowCount() > 0)
        self.cbCloseCurrentBehaviorsBetweenVideo.setEnabled(self.twVideo1.rowCount() > 0)
예제 #6
0
    def tabWidgetModifiersSets_changed(self, tabIndex):
        """
        user changed the tab widget
        """

        print("tabIndex", tabIndex)
        print("self.modifiers_sets_dict", self.modifiers_sets_dict)

        # check if modifier field empty
        if self.leModifier.text() and tabIndex != self.tabMem:
            if dialog.MessageDialog(
                    programName,
                ("You are working on a behavior.<br>"
                 "If you change the modifier's set it will be lost.<br>"
                 "Do you want to change modifiers set"), [YES, NO]) == NO:
                self.tabWidgetModifiersSets.setCurrentIndex(self.tabMem)
                return

        if tabIndex != self.tabMem:
            self.lwModifiers.clear()
            self.leCode.clear()
            self.leModifier.clear()

            self.tabMem = tabIndex

            print(self.modifiers_sets_dict)

            if tabIndex != -1:
                self.leSetName.setText(
                    self.modifiers_sets_dict[str(tabIndex)]["name"])
                self.cbType.setCurrentIndex(
                    self.modifiers_sets_dict[str(tabIndex)]["type"])
                self.lwModifiers.addItems(
                    self.modifiers_sets_dict[str(tabIndex)]["values"])
예제 #7
0
 def pb_cancel_widget_clicked(self):
     if self.flag_modified:
         if dialog.MessageDialog(
                 "BORIS", "The converters were modified. Are sure to quit?",
             [CANCEL, OK]) == OK:
             self.reject()
     else:
         self.reject()
예제 #8
0
    def delete_converter(self):
        """Delete selected converter"""

        if self.tw_converters.selectedIndexes():
            if dialog.MessageDialog("BORIS", "Confirm converter deletion", [CANCEL, OK]) == OK:
                self.tw_converters.removeRow(self.tw_converters.selectedIndexes()[0].row())
        else:
            QMessageBox.warning(self, programName, "Select a converter in the table")
예제 #9
0
 def pbClose_clicked(self):
     if not self.flagSaved:
         response = dialog.MessageDialog(programName, "The current results are not saved. Do you want to save results before closing?",
                                         [YES, NO, CANCEL])
         if response == YES:
             self.pbSave_clicked()
         if response == CANCEL:
             return
     self.closeSignal.emit()
예제 #10
0
    def pb_pushed(self, button):

        if self.leModifier.text():
            if dialog.MessageDialog(programName, ("You are working on a behavior.<br>"
                                                  "If you close the window it will be lost.<br>"
                                                  "Do you want to change modifiers set"), ["Close", CANCEL]) == CANCEL:
                return
        if button == "ok":
            self.accept()
        if button == "cancel":
            self.reject()
예제 #11
0
    def generate_spectrogram(self):
        """
        generate spectrogram of all media files loaded in player #1
        """

        if self.cbVisualizeSpectrogram.isChecked():

            if dialog.MessageDialog(
                    programName,
                ("You choose to visualize the spectrogram for the media in player #1.<br>"
                 "Choose YES to generate the spectrogram.\n\n"
                 "Spectrogram generation can take some time for long media, be patient"
                 ), [YES, NO]) == YES:

                if not self.ffmpeg_cache_dir:
                    tmp_dir = tempfile.gettempdir()
                else:
                    tmp_dir = self.ffmpeg_cache_dir

                w = dialog.Info_widget()
                w.resize(350, 100)
                w.setWindowFlags(Qt.WindowStaysOnTopHint)
                w.setWindowTitle("BORIS")
                w.label.setText("Generating spectrogram...")

                #for media in self.pj[OBSERVATIONS][self.observationId][FILE][PLAYER1]:
                for row in range(self.twVideo1.rowCount()):

                    media_file_path = project_functions.media_full_path(
                        self.twVideo1.item(row, 0).text(), self.project_path)

                    if os.path.isfile(media_file_path):

                        process = plot_spectrogram.create_spectrogram_multiprocessing(
                            mediaFile=media_file_path,
                            tmp_dir=tmp_dir,
                            chunk_size=self.chunk_length,
                            ffmpeg_bin=self.ffmpeg_bin,
                            spectrogramHeight=self.spectrogramHeight,
                            spectrogram_color_map=self.spectrogram_color_map)
                        if process:
                            w.show()
                            while True:
                                QApplication.processEvents()
                                if not process.is_alive():
                                    w.hide()
                                    break
                    else:
                        QMessageBox.warning(
                            self, programName,
                            "<b>{}</b> file not found".format(media_file_path))
            else:
                self.cbVisualizeSpectrogram.setChecked(False)
예제 #12
0
    def save_results(self):
        """
        save results
        """

        extended_file_formats = ["Tab Separated Values (*.tsv)",
                                 "Comma Separated Values (*.csv)",
                                 "Open Document Spreadsheet ODS (*.ods)",
                                 "Microsoft Excel Spreadsheet XLSX (*.xlsx)",
                                 "Legacy Microsoft Excel Spreadsheet XLS (*.xls)",
                                 "HTML (*.html)"]
        file_formats = ["tsv",
                        "csv",
                        "ods",
                        "xlsx",
                        "xls",
                        "html"]

        file_name, filter_ = QFileDialog().getSaveFileName(None, "Save results", "", ";;".join(extended_file_formats))
        if not file_name:
            return

        output_format = file_formats[extended_file_formats.index(filter_)]

        if pathlib.Path(file_name).suffix != "." + output_format:
            file_name = str(pathlib.Path(file_name)) + "." + output_format
            # check if file with new extension already exists
            if pathlib.Path(file_name).is_file():
                if dialog.MessageDialog(programName,
                                        f"The file {file_name} already exists.",
                                        [CANCEL, OVERWRITE]) == CANCEL:
                    return

        if self.rb_details.isChecked():
            tablib_dataset = tablib.Dataset(headers=self.details_header)
        if self.rb_summary.isChecked():
            tablib_dataset = tablib.Dataset(headers=self.summary_header)
        tablib_dataset.title = utilities.safe_xl_worksheet_title(self.logic.text(), output_format)

        [tablib_dataset.append(x) for x in self.out]

        try:
            if output_format in ["csv", "tsv", "html"]:
                with open(file_name, "wb") as f:
                    f.write(str.encode(tablib_dataset.export(output_format)))

            if output_format in ["ods", "xlsx", "xls"]:
                with open(file_name, "wb") as f:
                    f.write(tablib_dataset.export(output_format))

        except Exception:
            QMessageBox.critical(self, programName, f"The file {file_name} can not be saved")
예제 #13
0
    def closeEvent(self, event):

        if self.flagMapChanged:

            response = dialog.MessageDialog("BORIS - Modifiers map creator", "What to do about the current unsaved modifiers coding map?", ["Save", "Discard", "Cancel"])

            if response == "Save":
                if not self.saveMap_clicked():
                    event.ignore()

            if response == "Cancel":
                event.ignore()
                return

        self.closed.emit()
        event.accept()
예제 #14
0
    def removeSet(self):
        """
        remove set of modifiers
        """

        if self.tabWidgetModifiersSets.currentIndex() != -1:
            if dialog.MessageDialog(
                    programName, "Confirm deletion of this set of modifiers?",
                [YES, NO]) == YES:
                index_to_delete = self.tabWidgetModifiersSets.currentIndex()

                for k in range(index_to_delete,
                               len(self.modifiers_sets_dict) - 1):
                    self.modifiers_sets_dict[str(
                        k)] = self.modifiers_sets_dict[str(k + 1)]
                # del last key
                del self.modifiers_sets_dict[str(
                    len(self.modifiers_sets_dict) - 1)]

                # remove all tabs
                while self.tabWidgetModifiersSets.count():
                    self.tabWidgetModifiersSets.removeTab(0)

                # recreate tabs
                for idx in sorted_keys(self.modifiers_sets_dict):
                    self.tabWidgetModifiersSets.addTab(QWidget(),
                                                       f"Set #{int(idx) + 1}")

                # set not visible and not available buttons and others elements
                if self.tabWidgetModifiersSets.currentIndex() == -1:
                    for w in [
                            self.lbSetName, self.lbType, self.lbValues,
                            self.leSetName, self.cbType, self.lwModifiers,
                            self.pbMoveUp, self.pbMoveDown,
                            self.pbRemoveModifier, self.pbRemoveSet,
                            self.pbMoveSetLeft, self.pbMoveSetRight
                    ]:
                        w.setVisible(False)
                    for w in [
                            self.leModifier, self.leCode, self.pbAddModifier,
                            self.pbModifyModifier
                    ]:
                        w.setEnabled(False)
        else:
            QMessageBox.information(
                self, programName,
                "It is not possible to remove the last modifiers' set.")
예제 #15
0
def observation_length(pj, selected_observations: list) -> tuple:
    """
    max length of selected observations
    total media length

    Args:
        selected_observations (list): list of selected observations

    Returns:
        float: maximum media length for all observations
        float: total media length for all observations
    """
    selectedObsTotalMediaLength = Decimal("0.0")
    max_obs_length = 0
    for obs_id in selected_observations:
        obs_length = observation_total_length(pj[OBSERVATIONS][obs_id])
        if obs_length in [Decimal("0"), Decimal("-1")]:
            selectedObsTotalMediaLength = -1
            break
        max_obs_length = max(max_obs_length, obs_length)
        selectedObsTotalMediaLength += obs_length

    # an observation media length is not available
    if selectedObsTotalMediaLength == -1:
        # propose to user to use max event time
        if dialog.MessageDialog(programName,
                                (f"A media length is not available for the observation <b>{obs_id}</b>.<br>"
                                 "Use last event time as media length?"),
                                [YES, NO]) == YES:
            maxTime = 0  # max length for all events all subjects
            max_length = 0
            for obs_id in selected_observations:
                if pj[OBSERVATIONS][obs_id][EVENTS]:
                    maxTime += max(pj[OBSERVATIONS][obs_id][EVENTS])[0]
                    max_length = max(max_length, max(pj[OBSERVATIONS][obs_id][EVENTS])[0])

            logging.debug(f"max time all events all subjects: {maxTime}")

            max_obs_length = max_length
            selectedObsTotalMediaLength = maxTime

        else:
            max_obs_length = -1
            selectedObsTotalMediaLength = Decimal("-1")

    return max_obs_length, selectedObsTotalMediaLength
예제 #16
0
    def generate_spectrogram(self):
        """
        generate spectrogram of all media files loaded in player #1
        """

        if self.cbVisualizeSpectrogram.isChecked():

            if dialog.MessageDialog(programName, ("Você escolhe visualizar o espectrograma para a mídia no player #1.<br>"
                                                  "Escolha SIM para gerar o espectrograma.\n\n"
                                                  "Geração de espectrograma pode levar algum tempo para mídia longa, seja paciente"), [YES, NO]) == YES:

                if not self.ffmpeg_cache_dir:
                    tmp_dir = tempfile.gettempdir()
                else:
                    tmp_dir = self.ffmpeg_cache_dir

                w = dialog.Info_widget()
                w.resize(350, 100)
                w.setWindowFlags(Qt.WindowStaysOnTopHint)
                w.setWindowTitle("eMOC")
                w.label.setText("Gerando espectrograma...")

                #for media in self.pj[OBSERVATIONS][self.observationId][FILE][PLAYER1]:
                for row in range(self.twVideo1.rowCount()):
                    
                    media_file_path = project_functions.media_full_path(self.twVideo1.item(row, 0).text(), self.project_path)
                    
                    if os.path.isfile(media_file_path):
                        
                        process = plot_spectrogram.create_spectrogram_multiprocessing(mediaFile=media_file_path,
                                                                                      tmp_dir=tmp_dir,
                                                                                      chunk_size=self.chunk_length,
                                                                                      ffmpeg_bin=self.ffmpeg_bin,
                                                                                      spectrogramHeight=self.spectrogramHeight,
                                                                                      spectrogram_color_map=self.spectrogram_color_map)
                        if process:
                            w.show()
                            while True:
                                QApplication.processEvents()
                                if not process.is_alive():
                                    w.hide()
                                    break
                    else:
                        QMessageBox.warning(self, programName , "<b>{}</b> Arquivo não encontrado".format(media_file_path))
            else:
                self.cbVisualizeSpectrogram.setChecked(False)
예제 #17
0
    def newMap(self):
        """
        create a new map
        """

        if self.flagMapChanged:

            response = dialog.MessageDialog(
                programName + ' - Modifiers map creator',
                'What to do about the current unsaved coding map?',
                ['Save', 'Discard', 'Cancel'])

            if response == 'Save':
                if not self.saveMap_clicked():
                    return

            if response == 'Cancel':
                return

        self.cancelMap()

        text, ok = QInputDialog.getText(self, 'Map name',
                                        'Enter a name for the new map')
        if ok:
            self.mapName = text
        else:
            return

        if self.mapName == '':
            QMessageBox.critical(self, '',
                                 'You must define a name for the new map')
            return

        if self.mapName in ['areas', 'bitmap']:
            QMessageBox.critical(self, '', 'This name is not allowed')
            return

        self.setWindowTitle(programName + ' - Map creator tool - ' +
                            self.mapName)

        self.btLoad.setVisible(True)
        '''self.btCancelMap.setVisible(True)'''

        self.statusBar().showMessage(
            'Click "Load bitmap" button to select and load a bitmap into the viewer'
        )
예제 #18
0
    def view_doubleClicked(self, index):

        if self.mode == config.MULTIPLE:
           return

        if self.mode == config.OPEN or self.mode == config.EDIT:
            self.done(2)
            return

        if self.mode == config.SELECT1:
            self.done(2)
            return

        response = dialog.MessageDialog(config.programName, "What do you want to do with this observation?", ["Open", "Edit", config.CANCEL])
        if response == "Open":
            self.done(2)
        if response == "Edit":
            self.done(3)
예제 #19
0
    def add_media_from_dir(self, n_player, flag_path):
        """
        add all media from a selected directory

        Args:
            n_player (str): player
            flag_path (bool): True include full path of media else only basename
        """

        # check if project saved
        if (not flag_path) and (not self.project_file_name):
            QMessageBox.critical(
                self, programName,
                ("It is not possible to add media without full path "
                 "if the project is not saved"))
            return

        fd = QFileDialog()
        fd.setDirectory(
            os.path.expanduser("~") if flag_path else str(
                Path(self.project_path).parent))

        dir_name = fd.getExistingDirectory(self, "Select directory")
        if dir_name:
            r = ""
            for file_path in glob.glob(dir_name + os.sep + "*"):
                r, msg = self.check_media(n_player, file_path, flag_path)
                if not r:
                    if r != "Skip all non media files":
                        r = dialog.MessageDialog(
                            programName, f"<b>{file_path}</b> {msg}",
                            ["Continue", "Skip all non media files", "Cancel"])
                        if r == "Cancel":
                            break

        for w in [
                self.cbVisualizeSpectrogram, self.cb_visualize_waveform,
                self.cb_observation_time_interval,
                self.cbCloseCurrentBehaviorsBetweenVideo
        ]:
            w.setEnabled(self.twVideo1.rowCount() > 0)

        # disabled for problems
        self.cbCloseCurrentBehaviorsBetweenVideo.setEnabled(False)
예제 #20
0
    def view_doubleClicked(self, index):

        if self.mode == config.MULTIPLE:
            return

        if self.mode == config.OPEN or self.mode == config.EDIT:
            self.done(2)
            return

        if self.mode == config.SELECT1:
            self.done(2)
            return

        response = dialog.MessageDialog(config.programName, "What do you want to do with this observation?",
                                        list(commands_index.keys()) + [config.CANCEL])
        if response == config.CANCEL:
            return
        else:
            self.done(commands_index[response])
예제 #21
0
def behavior_binary_table(pj: dict):
    """
    ask user for parameters for behavior binary table
    call create_behavior_binary_table
    """

    result, selected_observations = select_observations.select_observations(
        pj, MULTIPLE, "Select observations for the behavior binary table")

    if not selected_observations:
        return
    # check if state events are paired
    out = ""
    not_paired_obs_list = []
    for obs_id in selected_observations:
        r, msg = project_functions.check_state_events_obs(
            obs_id, pj[ETHOGRAM], pj[OBSERVATIONS][obs_id])

        if not r:
            out += f"Observation: <strong>{obs_id}</strong><br>{msg}<br>"
            not_paired_obs_list.append(obs_id)

    if out:
        out = f"The observations with UNPAIRED state events will be removed from the analysis<br><br>{out}"
        results = dialog.Results_dialog()
        results.setWindowTitle(f"{programName} - Check selected observations")
        results.ptText.setReadOnly(True)
        results.ptText.appendHtml(out)
        results.pbSave.setVisible(False)
        results.pbCancel.setVisible(True)

        if not results.exec_():
            return
    selected_observations = [
        x for x in selected_observations if x not in not_paired_obs_list
    ]
    if not selected_observations:
        return

    max_obs_length, selectedObsTotalMediaLength = project_functions.observation_length(
        pj, selected_observations)
    if max_obs_length == -1:  # media length not available, user choose to not use events
        return

    parameters = dialog.choose_obs_subj_behav_category(
        pj,
        selected_observations,
        maxTime=max_obs_length,
        flagShowIncludeModifiers=True,
        flagShowExcludeBehaviorsWoEvents=True,
        by_category=False)

    if not parameters[SELECTED_SUBJECTS] or not parameters[SELECTED_BEHAVIORS]:
        QMessageBox.warning(None, programName,
                            "Select subject(s) and behavior(s) to analyze")
        return

    # ask for time interval
    i, ok = QInputDialog.getDouble(None, "Behavior binary table",
                                   "Time interval (in seconds):", 1.0, 0.001,
                                   86400, 3)
    if not ok:
        return
    time_interval = utilities.float2decimal(i)
    '''
    iw = dialog.Info_widget()
    iw.lwi.setVisible(False)
    iw.resize(350, 200)
    iw.setWindowFlags(Qt.WindowStaysOnTopHint)

    iw.setWindowTitle("Behavior binary table")
    iw.label.setText("Creating the behavior binary table...")
    iw.show()
    QApplication.processEvents()
    '''

    results_df = create_behavior_binary_table(pj, selected_observations,
                                              parameters, time_interval)
    '''
    iw.hide()
    '''

    if "error" in results_df:
        QMessageBox.warning(None, programName, results_df["msg"])
        return

    # save results
    if len(selected_observations) == 1:
        extended_file_formats = [
            "Tab Separated Values (*.tsv)", "Comma Separated Values (*.csv)",
            "Open Document Spreadsheet ODS (*.ods)",
            "Microsoft Excel Spreadsheet XLSX (*.xlsx)",
            "Legacy Microsoft Excel Spreadsheet XLS (*.xls)", "HTML (*.html)"
        ]
        file_formats = ["tsv", "csv", "ods", "xlsx", "xls", "html"]

        file_name, filter_ = QFileDialog().getSaveFileName(
            None, "Save results", "", ";;".join(extended_file_formats))
        if not file_name:
            return

        output_format = file_formats[extended_file_formats.index(filter_)]

        if pathlib.Path(file_name).suffix != "." + output_format:
            file_name = str(pathlib.Path(file_name)) + "." + output_format
            # check if file with new extension already exists
            if pathlib.Path(file_name).is_file():
                if dialog.MessageDialog(
                        programName, f"The file {file_name} already exists.",
                    [CANCEL, OVERWRITE]) == CANCEL:
                    return
    else:
        items = ("Tab Separated Values (*.tsv)",
                 "Comma separated values (*.csv)",
                 "Open Document Spreadsheet (*.ods)",
                 "Microsoft Excel Spreadsheet XLSX (*.xlsx)",
                 "Legacy Microsoft Excel Spreadsheet XLS (*.xls)",
                 "HTML (*.html)")

        item, ok = QInputDialog.getItem(None, "Save results",
                                        "Available formats", items, 0, False)
        if not ok:
            return
        output_format = re.sub(".* \(\*\.", "", item)[:-1]

        export_dir = QFileDialog().getExistingDirectory(
            None,
            "Choose a directory to save results",
            os.path.expanduser("~"),
            options=QFileDialog.ShowDirsOnly)
        if not export_dir:
            return

    mem_command = ""
    for obs_id in results_df:

        for subject in results_df[obs_id]:

            if len(selected_observations) > 1:
                file_name_with_subject = str(
                    pathlib.Path(export_dir) /
                    utilities.safeFileName(obs_id + "_" +
                                           subject)) + "." + output_format
            else:
                file_name_with_subject = str(
                    os.path.splitext(file_name)[0] +
                    utilities.safeFileName("_" +
                                           subject)) + "." + output_format

            # check if file with new extension already exists
            if mem_command != OVERWRITE_ALL and pathlib.Path(
                    file_name_with_subject).is_file():
                if mem_command == "Skip all":
                    continue
                mem_command = dialog.MessageDialog(
                    programName,
                    f"The file {file_name_with_subject} already exists.",
                    [OVERWRITE, OVERWRITE_ALL, "Skip", "Skip all", CANCEL])
                if mem_command == CANCEL:
                    return
                if mem_command in ["Skip", "Skip all"]:
                    continue

            try:
                if output_format in ["csv", "tsv", "html"]:
                    with open(file_name_with_subject, "wb") as f:
                        f.write(
                            str.encode(results_df[obs_id][subject].export(
                                output_format)))

                if output_format in ["ods", "xlsx", "xls"]:
                    with open(file_name_with_subject, "wb") as f:
                        f.write(
                            results_df[obs_id][subject].export(output_format))

            except Exception:

                error_type, error_file_name, error_lineno = utilities.error_info(
                    sys.exc_info())
                logging.critical(
                    f"Error in behavior binary table function: {error_type} {error_file_name} {error_lineno}"
                )

                QMessageBox.critical(None, programName,
                                     f"Error saving file: {error_type}")
                return
예제 #22
0
    def pbSave_clicked(self):
        """
        save time budget analysis results in TSV, CSV, ODS, XLS format
        """
        def complete(l: list, max_: int) -> list:
            """
            complete list with empty string until len = max

            Args:
                l (list): list to complete
                max_ (int): length of the returned list

            Returns:
                list: completed list
            """

            while len(l) < max_:
                l.append("")
            return l

        logging.debug("save time budget results to file")

        extended_file_formats = [
            "Tab Separated Values (*.tsv)", "Comma Separated Values (*.csv)",
            "Open Document Spreadsheet ODS (*.ods)",
            "Microsoft Excel Spreadsheet XLSX (*.xlsx)",
            "Legacy Microsoft Excel Spreadsheet XLS (*.xls)", "HTML (*.html)"
        ]
        file_formats = ["tsv", "csv", "ods", "xlsx", "xls", "html"]

        filediag_func = QFileDialog().getSaveFileName

        file_name, filter_ = filediag_func(self, "Save Time budget analysis",
                                           "",
                                           ";;".join(extended_file_formats))

        if not file_name:
            return

        outputFormat = file_formats[extended_file_formats.index(filter_)]
        if pathlib.Path(file_name).suffix != "." + outputFormat:
            file_name = str(pathlib.Path(file_name)) + "." + outputFormat
            # check if file with new extension already exists
            if pathlib.Path(file_name).is_file():
                if dialog.MessageDialog(
                        programName,
                        "The file {} already exists.".format(file_name),
                    [CANCEL, OVERWRITE]) == CANCEL:
                    return

        rows = []
        # observations list
        rows.append(["Observations:"])
        for idx in range(self.lw.count()):
            rows.append([""])
            rows.append([self.lw.item(idx).text()])

            if INDEPENDENT_VARIABLES in self.pj[OBSERVATIONS][self.lw.item(
                    idx).text()]:
                rows.append(["Independent variables:"])
                for var in self.pj[OBSERVATIONS][self.lw.item(
                        idx).text()][INDEPENDENT_VARIABLES]:
                    rows.append([
                        var, self.pj[OBSERVATIONS][self.lw.item(idx).text()]
                        [INDEPENDENT_VARIABLES][var]
                    ])

        if self.excluded_behaviors_list.text():
            s1, s2 = self.excluded_behaviors_list.text().split(": ")
            rows.extend([[""], [s1] + s2.split(", ")])

        rows.extend([[""], [""], ["Time budget:"]])

        # write header
        cols = []
        for col in range(self.twTB.columnCount()):
            cols.append(self.twTB.horizontalHeaderItem(col).text())

        rows.append(cols)
        rows.append([""])

        for row in range(self.twTB.rowCount()):
            values = []
            for col in range(self.twTB.columnCount()):
                values.append(intfloatstr(self.twTB.item(row, col).text()))

            rows.append(values)

        maxLen = max([len(r) for r in rows])
        data = tablib.Dataset()
        data.title = "Time budget"

        for row in rows:
            data.append(complete(row, maxLen))

        if outputFormat in ["tsv", "csv", "html"]:
            with open(file_name, "wb") as f:
                f.write(str.encode(data.export(outputFormat)))
            return

        if outputFormat in ["ods", "xlsx", "xls"]:
            with open(file_name, "wb") as f:
                f.write(data.export(outputFormat))
            return
예제 #23
0
    def extract_wav(self):
        """
        extract wav of all media files loaded in player #1
        """

        if self.cbVisualizeSpectrogram.isChecked() or self.cb_visualize_waveform.isChecked():
            flag_wav_produced = False
            # check if player 1 is selected
            flag_player1 = False
            for row in range(self.twVideo1.rowCount()):
                if self.twVideo1.cellWidget(row, 0).currentText() == "1":
                    flag_player1 = True

            if not flag_player1:
                QMessageBox.critical(self, programName , "The player #1 is not selected")
                self.cbVisualizeSpectrogram.setChecked(False)
                self.cb_visualize_waveform.setChecked(False)
                return

            if dialog.MessageDialog(programName, ("You choose to visualize the spectrogram or waveform for the media in player #1.<br>"
                                                  "The WAV will be extracted from the media files, be patient"), [YES, NO]) == YES:

                w = dialog.Info_widget()
                w.resize(350, 100)
                w.setWindowFlags(Qt.WindowStaysOnTopHint)
                w.setWindowTitle("BORIS")
                w.label.setText("Extracting WAV from media files...")

                for row in range(self.twVideo1.rowCount()):
                    # check if player 1
                    if self.twVideo1.cellWidget(row, 0).currentText() != "1":
                        continue

                    media_file_path = project_functions.media_full_path(self.twVideo1.item(row, MEDIA_FILE_PATH_IDX).text(),
                                                                        self.project_path)
                    if self.twVideo1.item(row, HAS_AUDIO_IDX).text() == "False":
                        QMessageBox.critical(self, programName , f"The media file {media_file_path} do not seem to have audio")
                        flag_wav_produced = False
                        break

                    if os.path.isfile(media_file_path):
                        w.show()
                        QApplication.processEvents()

                        if utilities.extract_wav(self.ffmpeg_bin, media_file_path, self.tmp_dir) == "":
                            QMessageBox.critical(self, programName ,
                                                 f"Error during extracting WAV of the media file {media_file_path}")
                            flag_wav_produced = False
                            break

                        w.hide()

                        flag_wav_produced = True
                    else:
                        QMessageBox.warning(self, programName , f"<b>{media_file_path}</b> file not found")

                if not flag_wav_produced:
                    self.cbVisualizeSpectrogram.setChecked(False)
                    self.cb_visualize_waveform.setChecked(False)
            else:
                self.cbVisualizeSpectrogram.setChecked(False)
                self.cb_visualize_waveform.setChecked(False)
예제 #24
0
    def openMap(self):
        """
        open a coding map from file

        load bitmap from data
        show it in view scene
        """
        if self.flagMapChanged:
            response = dialog.MessageDialog(programName + " - Behaviors coding map creator",
                                            "What to do about the current unsaved coding map?",
                                            ['Save', 'Discard', 'Cancel'])

            if (response == "Save" and not self.saveMap_clicked()) or (response == "Cancel"):
                return

        fn = QFileDialog(self).getOpenFileName(self, "Open a behaviors coding map", "", "Behaviors coding map (*.behav_coding_map);;All files (*)")
        fileName = fn[0] if type(fn) is tuple else fn

        if fileName:

            try:
                self.codingMap = json.loads(open(fileName, "r").read())
            except:
                QMessageBox.critical(self, programName, "The file {} seems not a behaviors coding map...".format(fileName))
                return              

            if "coding_map_type" not in self.codingMap or self.codingMap["coding_map_type"] != "BORIS behaviors coding map":
                QMessageBox.critical(self, programName, "The file {} seems not a BORIS behaviors coding map...".format(fileName))

            self.cancelMap()
            
            self.mapName = self.codingMap["name"]

            self.setWindowTitle("{} - Behaviors coding map creator - {}".format(programName, self.mapName))

            self.bitmapFileName = True

            self.fileName = fileName

            bitmapContent = binascii.a2b_base64(self.codingMap['bitmap'])

            self.pixmap.loadFromData(bitmapContent)

            self.view.setSceneRect(0, 0, self.pixmap.size().width(), self.pixmap.size().height())
            self.view.setMinimumHeight(self.pixmap.size().height())
            #self.view.setMaximumHeight(self.pixmap.size().height())
            pixItem = QGraphicsPixmapItem(self.pixmap)
            pixItem.setPos(0,0)
            self.view.scene().addItem(pixItem)

            for key in self.codingMap["areas"]:
                areaCode = self.codingMap["areas"][key]["code"]
                points = self.codingMap["areas"][key]["geometry"]
                
                newPolygon = QPolygonF()
                for p in points:
                    newPolygon.append(QPoint(p[0], p[1]))

                # draw polygon
                polygon = QGraphicsPolygonItem(None, None) if QT_VERSION_STR[0] == "4" else QGraphicsPolygonItem()
                polygon.setPolygon(newPolygon)
                clr = QColor()
                clr.setRgba(self.codingMap["areas"][key]["color"])
                polygon.setPen(QPen(clr, penWidth, penStyle, Qt.RoundCap, Qt.RoundJoin))
                polygon.setBrush(QBrush(clr, Qt.SolidPattern))

                self.view.scene().addItem(polygon)
                
                self.polygonsList2.append([areaCode, polygon])

            self.btNewArea.setVisible(True)
            self.btLoad.setVisible(False)

            self.saveMapAction.setEnabled(True)
            self.saveAsMapAction.setEnabled(True)
            self.addToProject.setEnabled(True)
            self.mapNameAction.setEnabled(True)

            self.update_area_list()

        else:
            self.statusBar().showMessage("No file", 5000)
예제 #25
0
def errbox(parent, msg):
    md = dialog.MessageDialog(parent, "Error", msg, 1)
    md.ShowModal()
예제 #26
0
    def openMap(self):
        """
        load bitmap from data
        show it in view scene
        """
        if self.flagMapChanged:

            response = dialog.MessageDialog(programName + ' - Map creator', 'What to do about the current unsaved coding map?', ['Save', 'Discard', 'Cancel'])

            if response == "Save":
                if not self.saveMap_clicked():
                    return

            if response == "Cancel":
                return



        fn = QFileDialog(self).getOpenFileName(self, 'Open a coding map', '', 'BORIS coding map (*.boris_map);;All files (*)')
        fileName = fn[0] if type(fn) is tuple else fn

        if fileName:

            try:
                self.codingMap = json.loads( open( fileName , 'r').read() )
            except:
                QMessageBox.critical(self, programName, "The file {} seems not a behaviors coding map...".format(fileName))
                return              

            self.cancelMap()
            
            self.mapName = self.codingMap['name']

            self.setWindowTitle(programName + ' - Map creator tool - ' + self.mapName)

            self.bitmapFileName = True

            self.fileName = fileName

            self.areasList = self.codingMap['areas']   # dictionary of dictionaries
            bitmapContent = binascii.a2b_base64( self.codingMap['bitmap'] )

            self.pixmap.loadFromData(bitmapContent)

            self.btDeleteArea.setEnabled(False)


            self.view.setSceneRect(0, 0, self.pixmap.size().width(), self.pixmap.size().height())
            pixItem = QGraphicsPixmapItem(self.pixmap)
            pixItem.setPos(0,0)
            self.view.scene().addItem(pixItem)

            for areaCode in self.areasList:
                points = self.areasList[ areaCode ]['geometry']

                newPolygon = QPolygonF()
                for p in points:
                    newPolygon.append(QPoint(p[0], p[1]))


                clr = QColor( )
                clr.setRgba( self.areasList[ areaCode ]['color'] )

                # draw polygon
                '''
                if QT_VERSION_STR[0] == "4":
                    polygon = QGraphicsPolygonItem(None, None)
                else:
                    polygon = QGraphicsPolygonItem()
                '''

                polygon = QGraphicsPolygonItem(None, None) if QT_VERSION_STR[0] == "4" else QGraphicsPolygonItem()


                polygon.setPolygon(newPolygon)

                polygon.setPen(QPen(clr, penWidth, penStyle, Qt.RoundCap, Qt.RoundJoin))

                polygon.setBrush( QBrush( clr, Qt.SolidPattern ) )

                self.view.scene().addItem( polygon )
                self.polygonsList2[ areaCode ] = polygon


            '''self.btCancelMap.setVisible(True)'''
            self.btNewArea.setVisible(True)

            self.btLoad.setVisible(False)

            self.saveMapAction.setEnabled(True)
            self.saveAsMapAction.setEnabled(True)
            self.mapNameAction.setEnabled(True)
            self.statusBar().showMessage('Click "New area" to create a new area')
        else:
            self.statusBar().showMessage('No file', 5000)
예제 #27
0
def export_events_jwatcher(parameters: list, obsId: str, observation: list,
                           ethogram: dict, file_name: str, output_format: str):
    """
    export events jwatcher .dat format

    Args:
        parameters (dict): subjects, behaviors
        obsId (str): observation id
        observation (dict): observation
        ethogram (dict): ethogram of project
        file_name (str): file name for exporting events
        output_format (str): Not used for compatibility with export_events function

    Returns:
        bool: result: True if OK else False
        str: error message
    """
    try:
        for subject in parameters["selected subjects"]:

            # select events for current subject
            events = []
            for event in observation[EVENTS]:
                if event[SUBJECT_EVENT_FIELD] == subject or (
                        subject == "No focal subject"
                        and event[SUBJECT_EVENT_FIELD] == ""):
                    events.append(event)

            if not events:
                continue

            total_length = 0  # in seconds
            if observation[EVENTS]:
                total_length = observation[EVENTS][-1][0] - observation[
                    EVENTS][0][0]  # last event time - first event time

            file_name_subject = str(
                pathlib.Path(file_name).parent /
                pathlib.Path(file_name).stem) + "_" + subject + ".dat"

            rows = ["FirstLineOfData"]  # to be completed
            rows.append(
                "#-----------------------------------------------------------")
            rows.append(f"# Name: {pathlib.Path(file_name_subject).name}")
            rows.append("# Format: Focal Data File 1.0")
            rows.append(f"# Updated: {datetime.datetime.now().isoformat()}")
            rows.append(
                "#-----------------------------------------------------------")
            rows.append("")
            rows.append(
                f"FocalMasterFile={pathlib.Path(file_name_subject).with_suffix('.fmf')}"
            )
            rows.append("")

            rows.append(f"# Observation started: {observation['date']}")
            try:
                start_time = datetime.datetime.strptime(
                    observation["date"], '%Y-%m-%dT%H:%M:%S')
            except ValueError:
                start_time = datetime.datetime(1970, 1, 1, 0, 0)
            start_time_epoch = int(
                (start_time -
                 datetime.datetime(1970, 1, 1, 0, 0)).total_seconds() * 1000)
            rows.append(f"StartTime={start_time_epoch}")

            stop_time = (
                start_time +
                datetime.timedelta(seconds=float(total_length))).isoformat()
            stop_time_epoch = int(start_time_epoch +
                                  float(total_length) * 1000)

            rows.append(f"# Observation stopped: {stop_time}")
            rows.append(f"StopTime={stop_time_epoch}")

            rows.extend([""] * 3)
            rows.append("#BEGIN DATA")
            rows[0] = f"FirstLineOfData={len(rows) + 1}"

            all_observed_behaviors = []
            mem_number_of_state_events = {}
            for event in events:
                behav_code = event[EVENT_BEHAVIOR_FIELD_IDX]

                try:
                    behavior_key = [
                        ethogram[k][BEHAVIOR_KEY] for k in ethogram
                        if ethogram[k][BEHAVIOR_CODE] == behav_code
                    ][0]
                except Exception:
                    # coded behavior not defined in ethogram
                    continue
                if [
                        ethogram[k][TYPE] for k in ethogram
                        if ethogram[k][BEHAVIOR_CODE] == behav_code
                ] == [STATE_EVENT]:
                    if behav_code in mem_number_of_state_events:
                        mem_number_of_state_events[behav_code] += 1
                    else:
                        mem_number_of_state_events[behav_code] = 1
                    # skip the STOP event in case of STATE
                    if mem_number_of_state_events[behav_code] % 2 == 0:
                        continue

                rows.append(
                    f"{int(event[EVENT_TIME_FIELD_IDX] * 1000)}, {behavior_key}"
                )
                if (event[EVENT_BEHAVIOR_FIELD_IDX],
                        behavior_key) not in all_observed_behaviors:
                    all_observed_behaviors.append(
                        (event[EVENT_BEHAVIOR_FIELD_IDX], behavior_key))

            rows.append(f"{int(events[-1][0] * 1000)}, EOF\n")

            try:
                with open(file_name_subject, "w") as f_out:
                    f_out.write("\n".join(rows))
            except Exception:
                return False, f"File DAT not created for subject {subject}: {sys.exc_info()[1]}"

            # create fmf file
            fmf_file_path = pathlib.Path(file_name_subject).with_suffix(".fmf")
            fmf_creation_answer = ""
            if fmf_file_path.exists():
                fmf_creation_answer = dialog.MessageDialog(
                    programName,
                    (f"The {fmf_file_path} file already exists.<br>"
                     "What do you want to do?"),
                    [OVERWRITE, "Skip file creation", CANCEL])

                if fmf_creation_answer == CANCEL:
                    return True, ""

            rows = []
            rows.append(
                "#-----------------------------------------------------------")
            rows.append(
                f"# Name: {pathlib.Path(file_name_subject).with_suffix('.fmf').name}"
            )
            rows.append("# Format: Focal Master File 1.0")
            rows.append(f"# Updated: {datetime.datetime.now().isoformat()}")
            rows.append(
                "#-----------------------------------------------------------")
            for (behav, key) in all_observed_behaviors:
                rows.append(f"Behaviour.name.{key}={behav}")
                behav_description = [
                    ethogram[k][DESCRIPTION] for k in ethogram
                    if ethogram[k][BEHAVIOR_CODE] == behav
                ][0]
                rows.append(f"Behaviour.description.{key}={behav_description}")

            rows.append(
                f"DurationMilliseconds={int(float(total_length) * 1000)}")
            rows.append("CountUp=false")
            rows.append("Question.1=")
            rows.append("Question.2=")
            rows.append("Question.3=")
            rows.append("Question.4=")
            rows.append("Question.5=")
            rows.append("Question.6=")
            rows.append("Notes=")
            rows.append("Supplementary=\n")

            if fmf_creation_answer == OVERWRITE or fmf_creation_answer == "":
                try:
                    with open(fmf_file_path, "w") as f_out:
                        f_out.write("\n".join(rows))
                except Exception:
                    return False, f"File FMF not created: {sys.exc_info()[1]}"

            # create FAF file
            faf_file_path = pathlib.Path(file_name_subject).with_suffix(".faf")
            faf_creation_answer = ""
            if faf_file_path.exists():
                faf_creation_answer = dialog.MessageDialog(
                    programName,
                    (f"The {faf_file_path} file already exists.<br>"
                     "What do you want to do?"),
                    [OVERWRITE, "Skip file creation", CANCEL])
                if faf_creation_answer == CANCEL:
                    return True, ""

            rows = []
            rows.append(
                "#-----------------------------------------------------------")
            rows.append("# Name: {}".format(
                pathlib.Path(file_name_subject).with_suffix(".faf").name))
            rows.append("# Format: Focal Analysis Master File 1.0")
            rows.append("# Updated: {}".format(
                datetime.datetime.now().isoformat()))
            rows.append(
                "#-----------------------------------------------------------")
            rows.append("FocalMasterFile={}".format(
                str(pathlib.Path(file_name_subject).with_suffix(".fmf"))))
            rows.append("")
            rows.append("TimeBinDuration=0.0")
            rows.append("EndWithLastCompleteBin=true")
            rows.append("")
            rows.append("ScoreFromBeginning=true")
            rows.append("ScoreFromBehavior=false")
            rows.append("ScoreFromFirstBehavior=false")
            rows.append("ScoreFromOffset=false")
            rows.append("")
            rows.append("Offset=0.0")
            rows.append("BehaviorToScoreFrom=")
            rows.append("")
            rows.append("OutOfSightCode=")
            rows.append("")
            rows.append("Report.StateNaturalInterval.Occurrence=false")
            rows.append("Report.StateNaturalInterval.TotalTime=false")
            rows.append("Report.StateNaturalInterval.Average=false")
            rows.append("Report.StateNaturalInterval.StandardDeviation=false")
            rows.append("Report.StateNaturalInterval.ProportionOfTime=false")
            rows.append(
                "Report.StateNaturalInterval.ProportionOfTimeInSight=false")
            rows.append(
                "Report.StateNaturalInterval.ConditionalProportionOfTime=false"
            )
            rows.append("")
            rows.append("Report.StateNaturalDuration.Occurrence=false")
            rows.append("Report.StateNaturalDuration.TotalTime=false")
            rows.append("Report.StateNaturalDuration.Average=false")
            rows.append("Report.StateNaturalDuration.StandardDeviation=false")
            rows.append("Report.StateNaturalDuration.ProportionOfTime=false")
            rows.append(
                "Report.StateNaturalDuration.ProportionOfTimeInSight=false")
            rows.append(
                "Report.StateNaturalDuration.ConditionalProportionOfTime=false"
            )
            rows.append("")
            rows.append("Report.StateAllInterval.Occurrence=false")
            rows.append("Report.StateAllInterval.TotalTime=false")
            rows.append("Report.StateAllInterval.Average=false")
            rows.append("Report.StateAllInterval.StandardDeviation=false")
            rows.append("Report.StateAllInterval.ProportionOfTime=false")
            rows.append(
                "Report.StateAllInterval.ProportionOfTimeInSight=false")
            rows.append(
                "Report.StateAllInterval.ConditionalProportionOfTime=false")
            rows.append("")
            rows.append("Report.StateAllDuration.Occurrence=true")
            rows.append("Report.StateAllDuration.TotalTime=true")
            rows.append("Report.StateAllDuration.Average=true")
            rows.append("Report.StateAllDuration.StandardDeviation=false")
            rows.append("Report.StateAllDuration.ProportionOfTime=false")
            rows.append("Report.StateAllDuration.ProportionOfTimeInSight=true")
            rows.append(
                "Report.StateAllDuration.ConditionalProportionOfTime=false")
            rows.append("")
            rows.append("Report.EventNaturalInterval.EventCount=false")
            rows.append("Report.EventNaturalInterval.Occurrence=false")
            rows.append("Report.EventNaturalInterval.Average=false")
            rows.append("Report.EventNaturalInterval.StandardDeviation=false")
            rows.append(
                "Report.EventNaturalInterval.ConditionalNatEventCount=false")
            rows.append("Report.EventNaturalInterval.ConditionalNatRate=false")
            rows.append(
                "Report.EventNaturalInterval.ConditionalNatIntervalOccurance=false"
            )
            rows.append(
                "Report.EventNaturalInterval.ConditionalNatIntervalAverage=false"
            )
            rows.append(
                "Report.EventNaturalInterval.ConditionalNatIntervalStandardDeviation=false"
            )
            rows.append(
                "Report.EventNaturalInterval.ConditionalAllEventCount=false")
            rows.append("Report.EventNaturalInterval.ConditionalAllRate=false")
            rows.append(
                "Report.EventNaturalInterval.ConditionalAllIntervalOccurance=false"
            )
            rows.append(
                "Report.EventNaturalInterval.ConditionalAllIntervalAverage=false"
            )
            rows.append(
                "Report.EventNaturalInterval.ConditionalAllIntervalStandardDeviation=false"
            )
            rows.append("")
            rows.append("AllCodesMutuallyExclusive=true")
            rows.append("")

            for (behav, key) in all_observed_behaviors:
                rows.append(f"Behavior.isModified.{key}=false")
                rows.append(f"Behavior.isSubtracted.{key}=false")
                rows.append(f"Behavior.isIgnored.{key}=false")
                rows.append(f"Behavior.isEventAnalyzed.{key}=false")
                rows.append(f"Behavior.switchesOff.{key}=")
                rows.append("")

            if faf_creation_answer == "" or faf_creation_answer == OVERWRITE:
                try:
                    with open(
                            pathlib.Path(file_name_subject).with_suffix(
                                ".faf"), "w") as f_out:
                        f_out.write("\n".join(rows))
                except Exception:
                    return False, f"File FAF not created: {sys.exc_info()[1]}"

        return True, ""

    except Exception:
        logging.critical("Error during exporting the events for JWatcher")
        dialog.error_message("exporting the events for JWatcher",
                             sys.exc_info())
        return False, ""
예제 #28
0
    def pbSave_clicked(self):
        """
        save time budget analysis results in TSV, CSV, ODS, XLS format
        """
        def complete(l: list, max_: int) -> list:
            """
            complete list with empty string until len = max

            Args:
                l (list): list to complete
                max_ (int): length of the returned list

            Returns:
                list: completed list
            """

            while len(l) < max_:
                l.append("")
            return l

        logging.debug("save time budget results to file")

        extended_file_formats = [
            "Tab Separated Values (*.tsv)", "Comma Separated Values (*.csv)",
            "Open Document Spreadsheet ODS (*.ods)",
            "Microsoft Excel Spreadsheet XLSX (*.xlsx)",
            "Legacy Microsoft Excel Spreadsheet XLS (*.xls)", "HTML (*.html)"
        ]
        file_formats = ["tsv", "csv", "ods", "xlsx", "xls", "html"]

        file_name, filter_ = QFileDialog().getSaveFileName(
            self, "Save Time budget analysis", "",
            ";;".join(extended_file_formats))

        if not file_name:
            return

        outputFormat = file_formats[extended_file_formats.index(filter_)]
        if pathlib.Path(file_name).suffix != "." + outputFormat:
            file_name = str(pathlib.Path(file_name)) + "." + outputFormat
            # check if file with new extension already exists
            if pathlib.Path(file_name).is_file():
                if dialog.MessageDialog(
                        programName, f"The file {file_name} already exists.",
                    [CANCEL, OVERWRITE]) == CANCEL:
                    return

        rows = []

        # 1 observation
        if (self.lw.count() == 1 and self.config_param.get(
                TIME_BUDGET_FORMAT, DEFAULT_TIME_BUDGET_FORMAT)
                == COMPACT_TIME_BUDGET_FORMAT):
            col1, indep_var_label = [], []
            # add obs id
            col1.append(self.lw.item(0).text())
            # add obs date
            col1.append(self.pj[OBSERVATIONS][self.lw.item(0).text()].get(
                "date", ""))

            # description
            col1.append(
                utilities.eol2space(
                    self.pj[OBSERVATIONS][self.lw.item(0).text()].get(
                        DESCRIPTION, "")))
            header = ["Observation id", "Observation date", "Description"]

            # indep var
            for var in self.pj[OBSERVATIONS][self.lw.item(0).text()].get(
                    INDEPENDENT_VARIABLES, {}):
                indep_var_label.append(var)
                col1.append(self.pj[OBSERVATIONS][self.lw.item(0).text()]
                            [INDEPENDENT_VARIABLES][var])

            header.extend(indep_var_label)

            col1.extend([
                f"{self.min_time:0.3f}", f"{self.max_time:0.3f}",
                f"{self.max_time - self.min_time:0.3f}"
            ])
            header.extend([
                "Time budget start", "Time budget stop", "Time budget duration"
            ])

            for col_idx in range(self.twTB.columnCount()):
                header.append(self.twTB.horizontalHeaderItem(col_idx).text())
            rows.append(header)

            for row_idx in range(self.twTB.rowCount()):
                values = []
                for col_idx in range(self.twTB.columnCount()):
                    values.append(
                        intfloatstr(self.twTB.item(row_idx, col_idx).text()))
                rows.append(col1 + values)

        else:
            # observations list
            rows.append(["Observations:"])
            for idx in range(self.lw.count()):
                rows.append([""])
                rows.append(["Observation id", self.lw.item(idx).text()])
                rows.append([
                    "Observation date",
                    self.pj[OBSERVATIONS][self.lw.item(idx).text()].get(
                        "date", "")
                ])
                rows.append([
                    "Description",
                    utilities.eol2space(
                        self.pj[OBSERVATIONS][self.lw.item(idx).text()].get(
                            DESCRIPTION, ""))
                ])

                if INDEPENDENT_VARIABLES in self.pj[OBSERVATIONS][self.lw.item(
                        idx).text()]:
                    rows.append(["Independent variables:"])
                    for var in self.pj[OBSERVATIONS][self.lw.item(
                            idx).text()][INDEPENDENT_VARIABLES]:
                        rows.append([
                            var, self.pj[OBSERVATIONS][self.lw.item(
                                idx).text()][INDEPENDENT_VARIABLES][var]
                        ])

            if self.excluded_behaviors_list.text():
                s1, s2 = self.excluded_behaviors_list.text().split(": ")
                rows.extend([[""], [s1] + s2.split(", ")])

            rows.extend([[""], [""], ["Time budget:"]])

            # write header
            header = []
            for col_idx in range(self.twTB.columnCount()):
                header.append(self.twTB.horizontalHeaderItem(col_idx).text())

            rows.append(header)
            rows.append([""])

            for row in range(self.twTB.rowCount()):
                values = []
                for col_idx in range(self.twTB.columnCount()):
                    values.append(
                        intfloatstr(self.twTB.item(row, col_idx).text()))

                rows.append(values)

        max_row_length = max([len(r) for r in rows])
        data = tablib.Dataset()
        data.title = "Time budget"

        for row in rows:
            data.append(complete(row, max_row_length))

        if outputFormat in ["tsv", "csv", "html"]:
            with open(file_name, "wb") as f:
                f.write(str.encode(data.export(outputFormat)))
            return

        if outputFormat in ["ods", "xlsx", "xls"]:
            with open(file_name, "wb") as f:
                f.write(data.export(outputFormat))
            return