コード例 #1
0
ファイル: test_utilities.py プロジェクト: LuweLuwerson/BORIS
 def test_error1(self):
     try:
         1 / 0
     except:
         r = utilities.error_info(sys.exc_info())
         assert str(r[0]) == 'division by zero'
         assert r[1] == "test_utilities.py"
コード例 #2
0
def import_subjects_from_clipboard(self):
    """
    import subjects from clipboard
    """
    try:
        cb = QApplication.clipboard()
        cb_text = cb.text()
        if not cb_text:
            QMessageBox.warning(None, programName, "The clipboard is empty",
                                QMessageBox.Ok | QMessageBox.Default,
                                QMessageBox.NoButton)
            return

        if self.twSubjects.rowCount():
            response = dialog.MessageDialog(
                programName,
                "Some subjects are already configured. Do you want to append subjects or replace them?",
                ["Append", "Replace", CANCEL])
            if response == CANCEL:
                return

            if response == "Replace":
                self.twSubjects.setRowCount(0)

        cb_text_splitted = cb_text.split("\n")

        if len(set([len(x.split("\t")) for x in cb_text_splitted])) != 1:
            QMessageBox.warning(None, programName, (
                "The clipboard content does not have a constant number of fields.<br>"
                "From your spreadsheet: CTRL + A (select all cells), CTRL + C (copy to clipboard)"
            ), QMessageBox.Ok | QMessageBox.Default, QMessageBox.NoButton)
            return

        for row in cb_text_splitted:
            if set(row.split("\t")) != set([""]):
                subject = {}
                for idx, field in enumerate(row.split("\t")):
                    if idx == 0:
                        subject["key"] = field.strip() if len(
                            field.strip()) == 1 else ""
                    if idx == 1:
                        subject[SUBJECT_NAME] = field.strip()
                    if idx == 2:
                        subject["description"] = field.strip()

                self.twSubjects.setRowCount(self.twSubjects.rowCount() + 1)

                for idx, field_name in enumerate(subjectsFields):
                    item = QTableWidgetItem(subject.get(field_name, ""))
                    self.twSubjects.setItem(self.twSubjects.rowCount() - 1,
                                            idx, item)
    except Exception:
        error_type, error_file_name, error_lineno = utilities.error_info(
            sys.exc_info())
        logging.critical(
            f"Error in function '{sys._getframe().f_code.co_name}': {error_type} {error_file_name} {error_lineno}"
        )
        dialog.error_message_box(sys._getframe().f_code.co_name, error_type,
                                 error_file_name, error_lineno)
コード例 #3
0
def error_message(task: str, exc_info: tuple) -> None:
    """
    Show details about the error in a message box
    write entry to log as CRITICAL
    """
    error_type, error_file_name, error_lineno = utilities.error_info(exc_info)
    logging.critical(f"Error during {task}: {error_type} in {error_file_name} at line #{error_lineno}")
    error_message_box(task, error_type, error_file_name, error_lineno)
コード例 #4
0
def error_message(task: str, exc_info: tuple) -> None:
    """
    show details about the error

    """
    error_type, error_file_name, error_lineno = utilities.error_info(exc_info)
    QMessageBox.critical(None, programName, (
        f"An error occured during {task}.<br>"
        f"BORIS version: {version.__version__}<br>"
        f"Error: {error_type}<br>"
        f"in {error_file_name} "
        f"at line # {error_lineno}<br><br>"
        "Please report this problem to improve the software at:<br>"
        '<a href="https://github.com/olivierfriard/BORIS/issues">https://github.com/olivierfriard/BORIS/issues</a>'
    ))
コード例 #5
0
def observation_to_behavioral_sequences(pj,
                                        selected_observations,
                                        parameters,
                                        behaviors_separator,
                                        separated_subjects,
                                        timed,
                                        file_name):

    try:
        with open(file_name, "w", encoding="utf-8") as out_file:
            for obs_id in selected_observations:
                # observation id
                out_file.write("\n" + f"# observation id: {obs_id}" + "\n")
                # observation description
                descr = pj[OBSERVATIONS][obs_id]["description"]
                if "\r\n" in descr:
                    descr = descr.replace("\r\n", "\n# ")
                elif "\r" in descr:
                    descr = descr.replace("\r", "\n# ")
                out_file.write(f"# observation description: {descr}\n\n")
                # media file name
                if pj[OBSERVATIONS][obs_id][TYPE] in [MEDIA]:
                    out_file.write(f"# Media file name: {', '.join([os.path.basename(x) for x in pj[OBSERVATIONS][obs_id][FILE][PLAYER1]])}\n\n")
                if pj[OBSERVATIONS][obs_id][TYPE] in [LIVE]:
                    out_file.write(f"# Live observation{os.linesep}{os.linesep}")

                # independent variables
                if INDEPENDENT_VARIABLES in pj[OBSERVATIONS][obs_id]:
                    out_file.write("# Independent variables\n")

                    for variable in pj[OBSERVATIONS][obs_id][INDEPENDENT_VARIABLES]:
                        out_file.write(f"# {variable}: {pj[OBSERVATIONS][obs_id][INDEPENDENT_VARIABLES][variable]}\n")
                out_file.write("\n")

                # one sequence for all subjects
                if not separated_subjects:
                    out = events_to_behavioral_sequences_all_subj(pj,
                                                                  obs_id,
                                                                  parameters[SELECTED_SUBJECTS],
                                                                  parameters,
                                                                  behaviors_separator)
                    if out:
                        out_file.write(out + "\n")

                # one sequence by subject
                if separated_subjects:
                    # selected subjects
                    for subject in parameters[SELECTED_SUBJECTS]:
                        out_file.write(f"\n# {subject if subject else NO_FOCAL_SUBJECT}:\n")

                        if not timed:
                            out = events_to_behavioral_sequences(pj,
                                                                obs_id,
                                                                subject,
                                                                parameters,
                                                                behaviors_separator)
                        if timed:
                            out = events_to_timed_behavioral_sequences(pj,
                                                                    obs_id,
                                                                    subject,
                                                                    parameters,
                                                                    0.001,
                                                                    behaviors_separator)

                        if out:
                            out_file.write(out + "\n")

            return True, ""

    except Exception:
        raise
        error_type, error_file_name, error_lineno = utilities.error_info(sys.exc_info())
        return False, f"{error_type} {error_file_name} {error_lineno}"
コード例 #6
0
def create_behaviors_bar_plot(pj: dict,
                       selected_observations: list,
                       param: dict,
                       plot_directory: str,
                       output_format: str,
                       plot_colors:list=BEHAVIORS_PLOT_COLORS):
    """
    time budget bar plot

    Args:
        pj (dict): project
        param (dict): parameters
        plot_directory (str): path of directory
        output_format (str): image format

    Returns:
        dict:
    """

    selected_subjects = param[SELECTED_SUBJECTS]
    selected_behaviors = param[SELECTED_BEHAVIORS]
    time_interval = param["time"]
    start_time = param[START_TIME]
    end_time = param[END_TIME]

    parameters = ["duration", "number of occurences"]

    ok, msg, db_connector = db_functions.load_aggregated_events_in_db(pj,
                                                                      selected_subjects,
                                                                      selected_observations,
                                                                      selected_behaviors)

    if not ok:
        return {"error": True, "message": msg}
    try:

        # extract all behaviors from ethogram for colors in plot
        all_behaviors = [pj[ETHOGRAM][x][BEHAVIOR_CODE] for x in utilities.sorted_keys(pj[ETHOGRAM])]

        for obs_id in selected_observations:

            cursor = db_connector.cursor()
            # distinct behaviors
            cursor.execute("SELECT distinct behavior FROM aggregated_events WHERE observation = ?",
                           (obs_id,))
            distinct_behav = [rows["behavior"] for rows in cursor.fetchall()]

            # add selected behaviors that are not observed
            '''
            if not param[EXCLUDE_BEHAVIORS]:
                for behavior in selected_behaviors:
                    if [x for x in distinct_behav if x == behavior] == []:
                        distinct_behav.append(behavior)
            '''

            # distinct subjects
            cursor.execute("SELECT distinct subject FROM aggregated_events WHERE observation = ?",
                           (obs_id,))
            distinct_subjects = [rows["subject"] for rows in cursor.fetchall()]

            behaviors = init_behav(pj[ETHOGRAM],
                                distinct_subjects,
                                distinct_behav,
                                parameters)

            # plot creation
            if len(distinct_subjects) > 1:
                fig, axs = plt.subplots(nrows=1, ncols=len(distinct_subjects), sharey=True)
                fig2, axs2 = plt.subplots(nrows=1, ncols=len(distinct_subjects), sharey=True)

            else:
                fig, ax = plt.subplots(nrows=1, ncols=len(distinct_subjects), sharey=True)
                axs = np.ndarray(shape=(1), dtype=type(ax))
                axs[0] = ax

                fig2, ax2 = plt.subplots(nrows=1, ncols=len(distinct_subjects), sharey=True)
                axs2 = np.ndarray(shape=(1), dtype=type(ax2))
                axs2[0] = ax2

            fig.suptitle("Durations of behaviors")
            fig2.suptitle("Number of occurences of behaviors")

            # if modifiers not to be included set modifiers to ""
            cursor.execute("UPDATE aggregated_events SET modifiers = ''")

            # time
            obs_length = project_functions.observation_total_length(pj[OBSERVATIONS][obs_id])
            if obs_length == -1:
                obs_length = 0

            if param["time"] == TIME_FULL_OBS:
                min_time = float(0)
                max_time = float(obs_length)

            if param["time"] == TIME_EVENTS:
                try:
                    min_time = float(pj[OBSERVATIONS][obs_id][EVENTS][0][0])
                except Exception:
                    min_time = float(0)
                try:
                    max_time = float(pj[OBSERVATIONS][obs_id][EVENTS][-1][0])
                except Exception:
                    max_time = float(obs_length)

            if param["time"] == TIME_ARBITRARY_INTERVAL:
                min_time = float(start_time)
                max_time = float(end_time)

            cursor.execute("UPDATE aggregated_events SET start = ? WHERE observation = ? AND start < ? AND stop BETWEEN ? AND ?",
                        (min_time, obs_id, min_time, min_time, max_time, ))
            cursor.execute("UPDATE aggregated_events SET stop = ? WHERE observation = ? AND stop > ? AND start BETWEEN ? AND ?",
                        (max_time, obs_id, max_time, min_time, max_time, ))
            cursor.execute("UPDATE aggregated_events SET start = ?, stop = ? WHERE observation = ? AND start < ? AND stop > ?",
                        (min_time, max_time, obs_id, min_time, max_time, ))

            for ax_idx, subject in enumerate(sorted(distinct_subjects)):

                for behavior in distinct_behav:

                    # number of occurences
                    cursor.execute(("SELECT COUNT(*) AS count FROM aggregated_events "
                                    "WHERE observation = ? AND subject = ? AND behavior = ?"),
                                (obs_id, subject, behavior, ))
                    for row in cursor.fetchall():
                        behaviors[subject][behavior]["number of occurences"] = 0 if row["count"] is None else row["count"]

                    # total duration
                    if STATE in project_functions.event_type(behavior, pj[ETHOGRAM]):
                        cursor.execute(("SELECT SUM(stop - start) AS duration FROM aggregated_events "
                                        "WHERE observation = ? AND subject = ? AND behavior = ?"),
                                    (obs_id, subject, behavior, ))
                        for row in cursor.fetchall():
                            behaviors[subject][behavior]["duration"] = 0 if row["duration"] is None else row["duration"]

                durations, n_occurences, colors, x_labels, colors_duration, x_labels_duration = [], [], [], [], [], []

                for behavior in sorted(distinct_behav):

                    if param[EXCLUDE_BEHAVIORS] and behaviors[subject][behavior]["number of occurences"] == 0:
                        continue

                    n_occurences.append(behaviors[subject][behavior]["number of occurences"])
                    x_labels.append(behavior)
                    try:
                        colors.append(utilities.behavior_color(plot_colors, all_behaviors.index(behavior)))
                    except Exception:
                        colors.append("darkgray")

                    if STATE in project_functions.event_type(behavior, pj[ETHOGRAM]):
                        durations.append(behaviors[subject][behavior]["duration"])
                        x_labels_duration.append(behavior)
                        try:
                            colors_duration.append(utilities.behavior_color(plot_colors, all_behaviors.index(behavior)))
                        except Exception:
                            colors_duration.append("darkgray")


                #width = 0.35       # the width of the bars: can also be len(x) sequence

                axs2[ax_idx].bar(np.arange(len(n_occurences)),
                                n_occurences,
                                #width,
                                color=colors
                                )


                axs[ax_idx].bar(np.arange(len(durations)),
                                durations,
                                #width,
                                color=colors_duration
                                )

                if ax_idx == 0:
                    axs[ax_idx].set_ylabel("Duration (s)")
                axs[ax_idx].set_xlabel("Behaviors")
                axs[ax_idx].set_title(f"{subject}")

                axs[ax_idx].set_xticks(np.arange(len(durations)))
                axs[ax_idx].set_xticklabels(x_labels_duration, rotation='vertical', fontsize=8)

                if ax_idx == 0:
                    axs2[ax_idx].set_ylabel("Number of occurences")
                axs2[ax_idx].set_xlabel("Behaviors")
                axs2[ax_idx].set_title(f"{subject}")

                axs2[ax_idx].set_xticks(np.arange(len(n_occurences)))
                axs2[ax_idx].set_xticklabels(x_labels, rotation='vertical', fontsize=8)


            fig.align_labels()
            fig.tight_layout(rect=[0, 0.03, 1, 0.95])

            fig2.align_labels()
            fig2.tight_layout(rect=[0, 0.03, 1, 0.95])


            if plot_directory:
                # output_file_name = f"{pathlib.Path(plot_directory) / utilities.safeFileName(obs_id)}.{output_format}"
                fig.savefig(f"{pathlib.Path(plot_directory) / utilities.safeFileName(obs_id)}.duration.{output_format}")
                fig2.savefig(f"{pathlib.Path(plot_directory) / utilities.safeFileName(obs_id)}.number_of_occurences.{output_format}")
                plt.close()
            else:
                fig.show()
                fig2.show()

        return {}

    except Exception:
        error_type, error_file_name, error_lineno = utilities.error_info(sys.exc_info())
        logging.critical(f"Error in time budget bar plot: {error_type} {error_file_name} {error_lineno}")
        return {"error": True, "exception": sys.exc_info()}
コード例 #7
0
def synthetic_time_budget(pj: dict, selected_observations: list,
                          parameters_obs: dict):
    """
    create a synthetic time budget

    Args:
        pj (dict): project dictionary
        selected_observations (list): list of observations to include in time budget
        parameters_obs (dict):

    Returns:
        bool: True if everything OK
        str: message
        tablib.Dataset: dataset containing synthetic time budget data
    """
    try:
        selected_subjects = parameters_obs[SELECTED_SUBJECTS]
        selected_behaviors = parameters_obs[SELECTED_BEHAVIORS]
        include_modifiers = parameters_obs[INCLUDE_MODIFIERS]
        interval = parameters_obs["time"]
        start_time = parameters_obs["start time"]
        end_time = parameters_obs["end time"]

        parameters = [
            ["duration", "Total duration"],
            ["number", "Number of occurrences"],
            ["duration mean", "Duration mean"],
            ["duration stdev", "Duration std dev"],
            ["proportion of time", "Proportion of time"],
        ]

        data_report = tablib.Dataset()
        data_report.title = "Synthetic time budget"

        ok, msg, db_connector = db_functions.load_aggregated_events_in_db(
            pj, selected_subjects, selected_observations, selected_behaviors)

        if not ok:
            return False, msg, None

        db_connector.create_aggregate("stdev", 1, StdevFunc)
        cursor = db_connector.cursor()

        # modifiers
        if include_modifiers:
            cursor.execute(
                "SELECT distinct behavior, modifiers FROM aggregated_events")
            distinct_behav_modif = [[rows["behavior"], rows["modifiers"]]
                                    for rows in cursor.fetchall()]
        else:
            cursor.execute("SELECT distinct behavior FROM aggregated_events")
            distinct_behav_modif = [[rows["behavior"], ""]
                                    for rows in cursor.fetchall()]

        # add selected behaviors that are not observed
        for behav in selected_behaviors:
            if [x for x in distinct_behav_modif if x[0] == behav] == []:
                distinct_behav_modif.append([behav, ""])

        behaviors = init_behav_modif(pj[ETHOGRAM], selected_subjects,
                                     distinct_behav_modif, include_modifiers,
                                     parameters)

        param_header = ["", "Total length (s)"]
        subj_header, behav_header, modif_header = [""] * len(param_header), [
            ""
        ] * len(param_header), [""] * len(param_header)

        for subj in selected_subjects:
            for behavior_modifiers in distinct_behav_modif:
                behavior, modifiers = behavior_modifiers
                behavior_modifiers_str = "|".join(
                    behavior_modifiers) if modifiers else behavior
                for param in parameters:
                    subj_header.append(subj)
                    behav_header.append(behavior)
                    modif_header.append(modifiers)
                    param_header.append(param[1])
        '''
        if parameters_obs["group observations"]:
            cursor.execute("UPDATE aggregated_events SET observation = 'all' " )
            #selected_observations = ["all"]
        '''

        data_report.append(subj_header)
        data_report.append(behav_header)
        if include_modifiers:
            data_report.append(modif_header)
        data_report.append(param_header)

        # select time interval
        for obs_id in selected_observations:

            ok, msg, db_connector = db_functions.load_aggregated_events_in_db(
                pj, selected_subjects, [obs_id], selected_behaviors)

            if not ok:
                return False, msg, None

            db_connector.create_aggregate("stdev", 1, StdevFunc)
            cursor = db_connector.cursor()

            # if modifiers not to be included set modifiers to ""
            if not include_modifiers:
                cursor.execute("UPDATE aggregated_events SET modifiers = ''")

            # time
            obs_length = project_functions.observation_total_length(
                pj[OBSERVATIONS][obs_id])
            if obs_length == -1:
                obs_length = 0

            if interval == TIME_FULL_OBS:
                min_time = float(0)
                max_time = float(obs_length)

            if interval == TIME_EVENTS:
                try:
                    min_time = float(pj[OBSERVATIONS][obs_id][EVENTS][0][0])
                except Exception:
                    min_time = float(0)
                try:
                    max_time = float(pj[OBSERVATIONS][obs_id][EVENTS][-1][0])
                except Exception:
                    max_time = float(obs_length)

            if interval == TIME_ARBITRARY_INTERVAL:
                min_time = float(start_time)
                max_time = float(end_time)

            # adapt start and stop to the selected time interval
            cursor.execute(
                "UPDATE aggregated_events SET start = ? WHERE observation = ? AND start < ? AND stop BETWEEN ? AND ?",
                (
                    min_time,
                    obs_id,
                    min_time,
                    min_time,
                    max_time,
                ))
            cursor.execute(
                "UPDATE aggregated_events SET stop = ? WHERE observation = ? AND stop > ? AND start BETWEEN ? AND ?",
                (
                    max_time,
                    obs_id,
                    max_time,
                    min_time,
                    max_time,
                ))

            cursor.execute(
                "UPDATE aggregated_events SET start = ?, stop = ? WHERE observation = ? AND start < ? AND stop > ?",
                (
                    min_time,
                    max_time,
                    obs_id,
                    min_time,
                    max_time,
                ))

            cursor.execute(
                "DELETE FROM aggregated_events WHERE observation = ? AND (start < ? AND stop < ?) OR (start > ? AND stop > ?)",
                (
                    obs_id,
                    min_time,
                    min_time,
                    max_time,
                    max_time,
                ))

            for subject in selected_subjects:

                # check if behaviors are to exclude from total time
                time_to_subtract = 0
                if EXCLUDED_BEHAVIORS in parameters_obs:
                    for excluded_behav in parameters_obs[EXCLUDED_BEHAVIORS]:
                        cursor.execute((
                            "SELECT SUM(stop-start) "
                            "FROM aggregated_events "
                            "WHERE observation = ? AND subject = ? AND behavior = ? "
                        ), (
                            obs_id,
                            subject,
                            excluded_behav,
                        ))
                        for row in cursor.fetchall():
                            if row[0] is not None:
                                time_to_subtract += row[0]

                for behavior_modifiers in distinct_behav_modif:
                    behavior, modifiers = behavior_modifiers
                    behavior_modifiers_str = "|".join(
                        behavior_modifiers) if modifiers else behavior

                    cursor.execute((
                        "SELECT SUM(stop-start), COUNT(*), AVG(stop-start), stdev(stop-start) "
                        "FROM aggregated_events "
                        "WHERE observation = ? AND subject = ? AND behavior = ? AND modifiers = ? "
                    ), (
                        obs_id,
                        subject,
                        behavior,
                        modifiers,
                    ))

                    for row in cursor.fetchall():
                        behaviors[subject][behavior_modifiers_str][
                            "duration"] = (0 if row[0] is None else
                                           f"{row[0]:.3f}")

                        behaviors[subject][behavior_modifiers_str][
                            "number"] = 0 if row[1] is None else row[1]
                        behaviors[subject][behavior_modifiers_str][
                            "duration mean"] = (0 if row[2] is None else
                                                f"{row[2]:.3f}")
                        behaviors[subject][behavior_modifiers_str][
                            "duration stdev"] = (0 if row[3] is None else
                                                 f"{row[3]:.3f}")

                        if behavior not in parameters_obs[EXCLUDED_BEHAVIORS]:
                            try:
                                behaviors[subject][behavior_modifiers_str][
                                    "proportion of time"] = (
                                        0 if row[0] is None else
                                        f"{row[0] / ((max_time - min_time) - time_to_subtract):.3f}"
                                    )
                            except ZeroDivisionError:
                                behaviors[subject][behavior_modifiers_str][
                                    "proportion of time"] = "-"
                        else:
                            # behavior subtracted
                            behaviors[subject][behavior_modifiers_str][
                                "proportion of time"] = (
                                    0 if row[0] is None else
                                    f"{row[0] / (max_time - min_time):.3f}")

            columns = [obs_id, f"{max_time - min_time:0.3f}"]
            for subj in selected_subjects:
                for behavior_modifiers in distinct_behav_modif:
                    behavior, modifiers = behavior_modifiers
                    behavior_modifiers_str = "|".join(
                        behavior_modifiers) if modifiers else behavior

                    for param in parameters:
                        columns.append(
                            behaviors[subj][behavior_modifiers_str][param[0]])

            data_report.append(columns)
    except Exception:

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

        msg = f"Error type: {error_type}\nError file name: {error_file_name}\nError line number: {error_lineno}"

        logging.critical(msg)

        return (False, msg, tablib.Dataset())

    return True, msg, data_report
コード例 #8
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
コード例 #9
0
    def filter(self):
        """
        filter events
        """
        if not self.logic.text():
            return
        if self.logic.text().count('"') % 2:
            QMessageBox.warning(self, programName,
                                f'Wrong number of double quotes (")')
            return

        sb_list = re.findall('"([^"]*)"', self.logic.text())

        self.out = []
        flag_error = False
        for obs_id in self.events:
            logic = self.logic.text()
            for sb in set(sb_list):
                logic = logic.replace(f'"{sb}"',
                                      f'self.events[obs_id]["{sb}"]')
                if sb not in self.events[obs_id]:
                    self.events[obs_id][sb] = io([0, 0])

            try:
                eval_result = eval(logic)
                for i in eval_result:
                    if not i.empty:
                        self.out.append([
                            obs_id, "", f"{i.lower}", f"{i.upper}",
                            f"{i.upper - i.lower:.3f}"
                        ])
            except KeyError:
                self.out.append(
                    [obs_id, "subject / behavior not found", "NA", "NA", "NA"])
            except Exception:
                # out += f"Error in {self.logic.text()}" + "\n"
                error_type, error_file_name, error_lineno = utilities.error_info(
                    sys.exc_info())
                self.out.append([
                    obs_id, f"Error in {self.logic.text()}: {error_type} ",
                    "NA", "NA", "NA"
                ])
                flag_error = True

        self.tw.clear()

        if flag_error or self.rb_details.isChecked():

            self.lb_results.setText(
                f"Results ({len(self.out)} event{'s'*(len(self.out) > 1)})")

            self.tw.setRowCount(len(self.out))
            self.tw.setColumnCount(len(
                self.details_header))  # obs_id, comment, start, stop, duration
            self.tw.setHorizontalHeaderLabels(self.details_header)

        if not flag_error and self.rb_summary.isChecked():

            summary = {}
            for row in self.out:
                obs_id, _, start, stop, duration = row
                if obs_id not in summary:
                    summary[obs_id] = []
                summary[obs_id].append(float(duration))

            self.out = []
            for obs_id in summary:

                self.out.append([
                    obs_id,
                    str(len(summary[obs_id])),
                    str(round(sum(summary[obs_id]), 3)),
                    str(round(statistics.mean(summary[obs_id]), 3)),
                    str(round(statistics.stdev(summary[obs_id]), 3))
                    if len(summary[obs_id]) > 1 else "NA"
                ])

            self.lb_results.setText(
                f"Results ({len(summary)} observation{'s'*(len(summary) > 1)})"
            )
            self.tw.setRowCount(len(summary))
            self.tw.setColumnCount(len(
                self.summary_header))  # obs_id, mean, stdev
            self.tw.setHorizontalHeaderLabels(self.summary_header)

        for r in range(len(self.out)):
            for c in range(self.tw.columnCount()):
                item = QTableWidgetItem()
                item.setText(self.out[r][c])
                item.setFlags(Qt.ItemIsEnabled)
                self.tw.setItem(r, c, item)
コード例 #10
0
def import_indep_variables_from_project(self):
    """
    import independent variables from another project
    """

    try:
        fn = QFileDialog().getOpenFileName(
            self, "Import independent variables from project file", "",
            ("Project files (*.boris *.boris.gz);;"
             "All files (*)"))

        file_name = fn[0] if type(fn) is tuple else fn

        if file_name:

            _, _, project, _ = project_functions.open_project_json(file_name)

            if "error" in project:
                logging.debug(project["error"])
                QMessageBox.critical(self, programName, project["error"])
                return

            # independent variables
            if INDEPENDENT_VARIABLES in project and project[
                    INDEPENDENT_VARIABLES]:

                # check if variables are already present
                existing_var = []

                for r in range(self.twVariables.rowCount()):
                    existing_var.append(
                        self.twVariables.item(r, 0).text().strip().upper())

                for i in utilities.sorted_keys(project[INDEPENDENT_VARIABLES]):

                    self.twVariables.setRowCount(self.twVariables.rowCount() +
                                                 1)
                    flag_renamed = False
                    for idx, field in enumerate(tw_indVarFields):
                        item = QTableWidgetItem()
                        if field in project[INDEPENDENT_VARIABLES][i]:
                            if field == "label":
                                txt = project[INDEPENDENT_VARIABLES][i][
                                    "label"].strip()
                                while txt.upper() in existing_var:
                                    txt += "_2"
                                    flag_renamed = True
                            else:
                                txt = project[INDEPENDENT_VARIABLES][i][
                                    field].strip()
                            item.setText(txt)
                        else:
                            item.setText("")
                        self.twVariables.setItem(
                            self.twVariables.rowCount() - 1, idx, item)

                self.twVariables.resizeColumnsToContents()
                if flag_renamed:
                    QMessageBox.information(
                        self, programName,
                        "Some variables already present were renamed")

            else:
                QMessageBox.warning(
                    self, programName,
                    "No independent variables found in project")

    except Exception:
        error_type, error_file_name, error_lineno = utilities.error_info(
            sys.exc_info())
        logging.critical(
            f"Import independent variable from project: {error_type} {error_file_name} {error_lineno}"
        )

        dialog.error_message_box("Import independent variable from project",
                                 error_type, error_file_name, error_lineno)
コード例 #11
0
def import_subjects_from_project(self):
    """
    import subjects from another project
    """

    try:
        fn = QFileDialog().getOpenFileName(
            self, "Import subjects from project file", "",
            ("Project files (*.boris *.boris.gz);;"
             "All files (*)"))
        file_name = fn[0] if type(fn) is tuple else fn

        if file_name:
            _, _, project, _ = project_functions.open_project_json(file_name)

            if "error" in project:
                logging.debug(project["error"])
                QMessageBox.critical(self, programName, project["error"])
                return

            # configuration of behaviours
            if SUBJECTS in project and project[SUBJECTS]:

                if self.twSubjects.rowCount():
                    response = dialog.MessageDialog(
                        programName,
                        ("There are subjects already configured. "
                         "Do you want to append subjects or replace them?"),
                        ['Append', 'Replace', 'Cancel'])

                    if response == "Replace":
                        self.twSubjects.setRowCount(0)

                    if response == CANCEL:
                        return

                for idx in utilities.sorted_keys(project[SUBJECTS]):

                    self.twSubjects.setRowCount(self.twSubjects.rowCount() + 1)

                    for idx2, sbjField in enumerate(subjectsFields):

                        if sbjField in project[SUBJECTS][idx]:
                            self.twSubjects.setItem(
                                self.twSubjects.rowCount() - 1, idx2,
                                QTableWidgetItem(
                                    project[SUBJECTS][idx][sbjField]))
                        else:
                            self.twSubjects.setItem(
                                self.twSubjects.rowCount() - 1, idx2,
                                QTableWidgetItem(""))

                self.twSubjects.resizeColumnsToContents()
            else:
                QMessageBox.warning(
                    self, programName,
                    "No subjects configuration found in project")
    except Exception:
        error_type, error_file_name, error_lineno = utilities.error_info(
            sys.exc_info())
        logging.critical(
            f"Error in function '{sys._getframe().f_code.co_name}': {error_type} {error_file_name} {error_lineno}"
        )
        dialog.error_message_box(sys._getframe().f_code.co_name, error_type,
                                 error_file_name, error_lineno)
コード例 #12
0
def import_behaviors_from_project(self):
    try:

        fn = QFileDialog().getOpenFileName(
            self, "Import behaviors from project file", "",
            ("Project files (*.boris *.boris.gz);;"
             "All files (*)"))
        file_name = fn[0] if type(fn) is tuple else fn

        if file_name:
            _, _, project, _ = project_functions.open_project_json(file_name)

            # import behavioral_categories
            if BEHAVIORAL_CATEGORIES in project:
                self.pj[BEHAVIORAL_CATEGORIES] = list(
                    project[BEHAVIORAL_CATEGORIES])

            # configuration of behaviours
            if ETHOGRAM in project and project[ETHOGRAM]:
                if self.twBehaviors.rowCount():
                    response = dialog.MessageDialog(
                        programName,
                        ("Some behaviors are already configured. "
                         "Do you want to append behaviors or replace them?"),
                        ["Append", "Replace", CANCEL])
                    if response == "Replace":
                        self.twBehaviors.setRowCount(0)
                    if response == CANCEL:
                        return

                behaviors_to_import = select_behaviors(
                    title="Select the behaviors to import",
                    text="Behaviors",
                    behavioral_categories=list(project[BEHAVIORAL_CATEGORIES]),
                    ethogram=dict(project[ETHOGRAM]),
                    behavior_type=[STATE_EVENT, POINT_EVENT])

                for i in utilities.sorted_keys(project[ETHOGRAM]):

                    if project[ETHOGRAM][i][
                            BEHAVIOR_CODE] not in behaviors_to_import:
                        continue

                    self.twBehaviors.setRowCount(self.twBehaviors.rowCount() +
                                                 1)

                    for field in project[ETHOGRAM][i]:

                        item = QTableWidgetItem()

                        if field == TYPE:
                            item.setText(project[ETHOGRAM][i][field])
                            item.setFlags(Qt.ItemIsEnabled)
                            item.setBackground(QColor(230, 230, 230))

                        else:
                            if field == "modifiers" and isinstance(
                                    project[ETHOGRAM][i][field], str):
                                modif_set_dict = {}
                                if project[ETHOGRAM][i][field]:
                                    modif_set_list = project[ETHOGRAM][i][
                                        field].split("|")
                                    for modif_set in modif_set_list:
                                        modif_set_dict[str(
                                            len(modif_set_dict))] = {
                                                "name": "",
                                                "type": SINGLE_SELECTION,
                                                "values": modif_set.split(",")
                                            }
                                project[ETHOGRAM][i][field] = dict(
                                    modif_set_dict)

                            item.setText(str(project[ETHOGRAM][i][field]))

                            if field not in ETHOGRAM_EDITABLE_FIELDS:
                                item.setFlags(Qt.ItemIsEnabled)
                                item.setBackground(QColor(230, 230, 230))

                        self.twBehaviors.setItem(
                            self.twBehaviors.rowCount() - 1,
                            behavioursFields[field], item)

                self.twBehaviors.resizeColumnsToContents()

            else:
                QMessageBox.warning(
                    self, programName,
                    "No behaviors configuration found in project")

    except Exception:
        error_type, error_file_name, error_lineno = utilities.error_info(
            sys.exc_info())
        logging.critical(
            f"Error in function '{sys._getframe().f_code.co_name}': {error_type} {error_file_name} {error_lineno}"
        )
        dialog.error_message_box(sys._getframe().f_code.co_name, error_type,
                                 error_file_name, error_lineno)
コード例 #13
0
def select_behaviors(title="Record value from external data file",
                     text="Behaviors",
                     behavioral_categories=[],
                     ethogram={},
                     behavior_type=[STATE_EVENT, POINT_EVENT]):
    """
    allow user to select behaviors to import

    Args:
        title (str): title of dialog box
        text (str): text of dialog box
        behavioral_categories (list): behavioral categories
        ethogram (dict): ethogram

    """

    try:
        paramPanelWindow = param_panel.Param_panel()
        paramPanelWindow.resize(800, 600)
        paramPanelWindow.setWindowTitle(title)
        paramPanelWindow.lbBehaviors.setText(text)
        for w in [
                paramPanelWindow.lwSubjects,
                paramPanelWindow.pbSelectAllSubjects,
                paramPanelWindow.pbUnselectAllSubjects,
                paramPanelWindow.pbReverseSubjectsSelection,
                paramPanelWindow.lbSubjects,
                paramPanelWindow.cbIncludeModifiers,
                paramPanelWindow.cbExcludeBehaviors, paramPanelWindow.frm_time
        ]:
            w.setVisible(False)

        if behavioral_categories:
            categories = behavioral_categories
            # check if behavior not included in a category
            if "" in [
                    ethogram[idx][BEHAVIOR_CATEGORY] for idx in ethogram
                    if BEHAVIOR_CATEGORY in ethogram[idx]
            ]:
                categories += [""]
        else:
            categories = ["###no category###"]

        for category in categories:

            if category != "###no category###":

                if category == "":
                    paramPanelWindow.item = QListWidgetItem("No category")
                    paramPanelWindow.item.setData(34, "No category")
                else:
                    paramPanelWindow.item = QListWidgetItem(category)
                    paramPanelWindow.item.setData(34, category)

                font = QFont()
                font.setBold(True)
                paramPanelWindow.item.setFont(font)
                paramPanelWindow.item.setData(33, "category")
                paramPanelWindow.item.setData(35, False)

                paramPanelWindow.lwBehaviors.addItem(paramPanelWindow.item)

            # check if behavior type must be shown
            for behavior in [
                    ethogram[x][BEHAVIOR_CODE]
                    for x in utilities.sorted_keys(ethogram)
            ]:

                if ((categories == ["###no category###"]) or (behavior in [
                        ethogram[x][BEHAVIOR_CODE]
                        for x in ethogram if BEHAVIOR_CATEGORY in ethogram[x]
                        and ethogram[x][BEHAVIOR_CATEGORY] == category
                ])):

                    paramPanelWindow.item = QListWidgetItem(behavior)
                    paramPanelWindow.item.setCheckState(Qt.Unchecked)

                    if category != "###no category###":
                        paramPanelWindow.item.setData(33, "behavior")
                        if category == "":
                            paramPanelWindow.item.setData(34, "No category")
                        else:
                            paramPanelWindow.item.setData(34, category)

                    paramPanelWindow.lwBehaviors.addItem(paramPanelWindow.item)

        if paramPanelWindow.exec_():
            return paramPanelWindow.selectedBehaviors

        return []
    except Exception:
        error_type, error_file_name, error_lineno = utilities.error_info(
            sys.exc_info())
        logging.critical(
            f"Error in function '{sys._getframe().f_code.co_name}'': {error_type} {error_file_name} {error_lineno}"
        )
        dialog.error_message_box(sys._getframe().f_code.co_name, error_type,
                                 error_file_name, error_lineno)
コード例 #14
0
def import_from_text_file(self):

    if self.twBehaviors.rowCount():
        response = dialog.MessageDialog(
            programName,
            "There are behaviors already configured. Do you want to append behaviors or replace them?",
            ['Append', 'Replace', CANCEL])
        if response == CANCEL:
            return

    fn = QFileDialog().getOpenFileName(
        self, "Import behaviors from text file", "",
        "Text files (*.txt *.tsv *.csv);;All files (*)")
    fileName = fn[0] if type(fn) is tuple else fn

    if fileName:

        if self.twBehaviors.rowCount() and response == "Replace":
            self.twBehaviors.setRowCount(0)
        try:
            with open(fileName, mode="rb") as f:
                rows_b = f.read().splitlines()

            rows = []
            idx = 1
            for row in rows_b:
                try:
                    rows.append(row.decode("utf-8"))
                except Exception:
                    QMessageBox.critical(
                        None, programName,
                        (f"Error while reading file\nThe line # {idx}\n"
                         f"{row}\ncontains characters that are not readable."),
                        QMessageBox.Ok | QMessageBox.Default,
                        QMessageBox.NoButton)
                    return
                idx += 1

            fieldSeparator, fieldsNumber = check_text_file_type(rows)

            logging.debug(
                f"fields separator: {fieldSeparator}  fields number: {fieldsNumber}"
            )

            if fieldSeparator is None:
                QMessageBox.critical(
                    self, programName,
                    "Separator character not found! Use plain text file and TAB or comma as value separator"
                )
            else:

                for row in rows:

                    type_, key, code, description = "", "", "", ""

                    if fieldsNumber == 3:  # fields: type, key, code
                        type_, key, code = row.split(fieldSeparator)
                        description = ""
                    if fieldsNumber == 4:  # fields:  type, key, code, description
                        type_, key, code, description = row.split(
                            fieldSeparator)

                    if fieldsNumber > 4:
                        type_, key, code, description = row.split(
                            fieldSeparator)[:4]

                    behavior = {
                        "key": key,
                        "code": code,
                        "description": description,
                        "modifiers": "",
                        "excluded": "",
                        "coding map": "",
                        "category": ""
                    }

                    self.twBehaviors.setRowCount(self.twBehaviors.rowCount() +
                                                 1)

                    for field_type in behavioursFields:
                        if field_type == TYPE:
                            item = QTableWidgetItem(DEFAULT_BEHAVIOR_TYPE)
                            # add type combobox
                            if POINT in type_.upper():
                                item = QTableWidgetItem(POINT_EVENT)
                            if STATE in type_.upper():
                                item = QTableWidgetItem(STATE_EVENT)
                        else:
                            item = QTableWidgetItem(behavior[field_type])

                        if field_type not in ETHOGRAM_EDITABLE_FIELDS:
                            item.setFlags(Qt.ItemIsEnabled)
                            item.setBackground(QColor(230, 230, 230))

                        self.twBehaviors.setItem(
                            self.twBehaviors.rowCount() - 1,
                            behavioursFields[field_type], item)
        except Exception:
            error_type, error_file_name, error_lineno = utilities.error_info(
                sys.exc_info())
            logging.critical(
                f"Error in function '{sys._getframe().f_code.co_name}': {error_type} {error_file_name} {error_lineno}"
            )
            dialog.error_message_box(sys._getframe().f_code.co_name,
                                     error_type, error_file_name, error_lineno)
コード例 #15
0
def import_from_JWatcher(self):
    """
    import behaviors configuration from JWatcher (GDF file)
    """
    try:
        if self.twBehaviors.rowCount():
            response = dialog.MessageDialog(
                programName,
                "There are behaviors already configured. Do you want to append behaviors or replace them?",
                ["Append", "Replace", CANCEL])
            if response == CANCEL:
                return

        fn = QFileDialog().getOpenFileName(
            self, "Import behaviors from JWatcher", "",
            "Global Definition File (*.gdf);;All files (*)")
        fileName = fn[0] if type(fn) is tuple else fn

        if fileName:
            if self.twBehaviors.rowCount() and response == "Replace":
                self.twBehaviors.setRowCount(0)

            with open(fileName, "r") as f:
                rows = f.readlines()

            for idx, row in enumerate(rows):
                if row and row[0] == "#":
                    continue

                if "Behavior.name." in row and "=" in row:
                    key, code = row.split('=')
                    key = key.replace("Behavior.name.", "")
                    # read description
                    if idx < len(rows) and "Behavior.description." in rows[
                            idx + 1]:
                        description = rows[idx + 1].split("=")[-1]

                    behavior = {
                        "key": key,
                        "code": code,
                        "description": description,
                        "modifiers": "",
                        "excluded": "",
                        "coding map": "",
                        "category": ""
                    }

                    self.twBehaviors.setRowCount(self.twBehaviors.rowCount() +
                                                 1)

                    for field_type in behavioursFields:
                        if field_type == TYPE:
                            item = QTableWidgetItem(DEFAULT_BEHAVIOR_TYPE)
                        else:
                            item = QTableWidgetItem(behavior[field_type])

                        if field_type in [
                                TYPE, "excluded", "category", "coding map",
                                "modifiers"
                        ]:
                            item.setFlags(Qt.ItemIsEnabled)
                            item.setBackground(QColor(230, 230, 230))

                        self.twBehaviors.setItem(
                            self.twBehaviors.rowCount() - 1,
                            behavioursFields[field_type], item)
    except Exception:
        error_type, error_file_name, error_lineno = utilities.error_info(
            sys.exc_info())
        logging.critical(
            f"Error in function '{sys._getframe().f_code.co_name}'': {error_type} {error_file_name} {error_lineno}"
        )
        dialog.error_message_box(sys._getframe().f_code.co_name, error_type,
                                 error_file_name, error_lineno)
コード例 #16
0
def import_behaviors_from_clipboard(self):
    """
    import ethogram from clipboard
    """

    try:
        cb = QApplication.clipboard()
        cb_text = cb.text()
        if not cb_text:
            QMessageBox.warning(None, programName, "The clipboard is empty",
                                QMessageBox.Ok | QMessageBox.Default,
                                QMessageBox.NoButton)
            return

        if self.twBehaviors.rowCount():
            response = dialog.MessageDialog(
                programName,
                "Some behaviors are already configured. Do you want to append behaviors or replace them?",
                ["Append", "Replace", CANCEL])
            if response == CANCEL:
                return

            if response == "Replace":
                self.twBehaviors.setRowCount(0)

        cb_text_splitted = cb_text.split("\n")
        while "" in cb_text_splitted:
            cb_text_splitted.remove("")

        if len(set([len(x.split("\t")) for x in cb_text_splitted])) != 1:
            QMessageBox.warning(None, programName, (
                "The clipboard content does not have a constant number of fields.<br>"
                "From your spreadsheet: CTRL + A (select all cells), CTRL + C (copy to clipboard)"
            ), QMessageBox.Ok | QMessageBox.Default, QMessageBox.NoButton)
            return

        for row in cb_text_splitted:
            if set(row.split("\t")) != set([""]):
                behavior = {"type": DEFAULT_BEHAVIOR_TYPE}
                for idx, field in enumerate(row.split("\t")):
                    if idx == 0:
                        behavior["type"] = STATE_EVENT if STATE in field.upper(
                        ) else (POINT_EVENT if POINT in field.upper() else "")
                    if idx == 1:
                        behavior["key"] = field.strip() if len(
                            field.strip()) == 1 else ""
                    if idx == 2:
                        behavior["code"] = field.strip()
                    if idx == 3:
                        behavior["description"] = field.strip()
                    if idx == 4:
                        behavior["category"] = field.strip()

                self.twBehaviors.setRowCount(self.twBehaviors.rowCount() + 1)

                for field_type in behavioursFields:
                    if field_type == TYPE:
                        item = QTableWidgetItem(
                            behavior.get("type", DEFAULT_BEHAVIOR_TYPE))
                    else:
                        item = QTableWidgetItem(behavior.get(field_type, ""))

                    if field_type not in ETHOGRAM_EDITABLE_FIELDS:  # [TYPE, "excluded", "coding map", "modifiers", "category"]:
                        item.setFlags(Qt.ItemIsEnabled)
                        item.setBackground(QColor(230, 230, 230))

                    self.twBehaviors.setItem(self.twBehaviors.rowCount() - 1,
                                             behavioursFields[field_type],
                                             item)
    except Exception:
        error_type, error_file_name, error_lineno = utilities.error_info(
            sys.exc_info())
        logging.critical(
            f"Error in function '{sys._getframe().f_code.co_name}': {error_type} {error_file_name} {error_lineno}"
        )
        dialog.error_message_box(sys._getframe().f_code.co_name, error_type,
                                 error_file_name, error_lineno)