def __init__(self, modifiers_str, subjects=[], parent=None): super().__init__() self.setupUi(self) self.subjects = subjects if not self.subjects: self.pb_add_subjects.setEnabled(False) self.pbAddModifier.clicked.connect(self.addModifier) self.pbAddModifier.setIcon(QIcon(":/frame_forward")) self.pbAddSet.clicked.connect(self.addSet) self.pbRemoveSet.clicked.connect(self.removeSet) self.pbModifyModifier.clicked.connect(self.modifyModifier) self.pbModifyModifier.setIcon(QIcon(":/frame_backward")) self.pbMoveUp.clicked.connect(self.moveModifierUp) self.pbMoveDown.clicked.connect(self.moveModifierDown) self.pbMoveSetLeft.clicked.connect(self.moveSetLeft) self.pbMoveSetRight.clicked.connect(self.moveSetRight) self.pbRemoveModifier.clicked.connect(self.removeModifier) self.pb_sort_modifiers.clicked.connect(self.sort_modifiers) self.pb_add_subjects.clicked.connect(self.add_subjects) self.pb_load_file.clicked.connect(self.add_modifiers_from_file) self.pbOK.clicked.connect(lambda: self.pb_pushed("ok")) self.pbCancel.clicked.connect(lambda: self.pb_pushed("cancel")) self.leSetName.textChanged.connect(self.set_name_changed) self.cbType.currentIndexChanged.connect(self.type_changed) dummy_dict = eval(modifiers_str) if modifiers_str else {} modif_values = [] for idx in sorted_keys(dummy_dict): modif_values.append(dummy_dict[idx]) self.modifiers_sets_dict = {} for modif in modif_values: self.modifiers_sets_dict[str(len(self.modifiers_sets_dict))] = dict(modif) self.tabWidgetModifiersSets.currentChanged.connect(self.tabWidgetModifiersSets_changed) # create tab for idx in sorted_keys(self.modifiers_sets_dict): self.tabWidgetModifiersSets.addTab(QWidget(), f"Set #{int(idx) + 1}") 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, self.pb_add_subjects, self.pb_load_file, self.pb_sort_modifiers]: w.setVisible(False) for w in [self.leModifier, self.leCode, self.pbAddModifier, self.pbModifyModifier]: w.setEnabled(False) # set first tab as active self.tabMem = 0
def __init__(self, modifiers_str, subjects=[], parent=None): super().__init__() self.setupUi(self) self.subjects = subjects self.pbAddModifier.clicked.connect(self.addModifier) self.pbAddModifier.setIcon(QIcon(":/frame_forward")) self.pbAddSet.clicked.connect(self.addSet) self.pbRemoveSet.clicked.connect(self.removeSet) self.pbModifyModifier.clicked.connect(self.modifyModifier) self.pbModifyModifier.setIcon(QIcon(":/frame_backward")) self.pbMoveUp.clicked.connect(self.moveModifierUp) self.pbMoveDown.clicked.connect(self.moveModifierDown) self.pbMoveSetLeft.clicked.connect(self.moveSetLeft) self.pbMoveSetRight.clicked.connect(self.moveSetRight) self.pbRemoveModifier.clicked.connect(self.removeModifier) self.pb_sort_modifiers.clicked.connect(self.sort_modifiers) self.pb_add_subjects.clicked.connect(self.add_subjects) self.pb_load_file.clicked.connect(self.add_modifiers_from_file) self.pbOK.clicked.connect(lambda: self.pb_pushed("ok")) self.pbCancel.clicked.connect(lambda: self.pb_pushed("cancel")) self.leSetName.textChanged.connect(self.set_name_changed) self.cbType.currentIndexChanged.connect(self.type_changed) dummy_dict = eval(modifiers_str) if modifiers_str else {} modif_values = [] for idx in sorted_keys(dummy_dict): modif_values.append(dummy_dict[idx]) self.modifiers_sets_dict = {} for modif in modif_values: self.modifiers_sets_dict[str(len(self.modifiers_sets_dict))] = dict(modif) self.tabWidgetModifiersSets.currentChanged.connect(self.tabWidgetModifiersSets_changed) # create tab for idx in sorted_keys(self.modifiers_sets_dict): self.tabWidgetModifiersSets.addTab(QWidget(), f"Set #{int(idx) + 1}") 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, self.pb_add_subjects, self.pb_load_file, self.pb_sort_modifiers]: w.setVisible(False) for w in [self.leModifier, self.leCode, self.pbAddModifier, self.pbModifyModifier]: w.setEnabled(False) # set first tab as active self.tabMem = 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.")
def test_str_keys(self): r = utilities.sorted_keys({ "10": "x", "0": "x", "1": "x", "11": "x", "05": "x" }) #print(r) assert r == ['0', '1', '5', '10', '11']
def pbOK_clicked(self): for idx in sorted_keys(self.modifiers_dict): if self.modifiers_dict[idx]["type"] == NUMERIC_MODIFIER: if self.modifiers_dict[idx]["widget"].text(): try: val = float(self.modifiers_dict[idx]["widget"].text()) except Exception: QMessageBox.warning(self, programName, "<b>{}</b> is not a numeric value".format(self.modifiers_dict[idx]["widget"].text())) return self.accept()
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.")
def get_modifiers(self): """ get modifiers returns list of selected modifiers """ modifiers = [] for idx in sorted_keys(self.modifiers_dict): if self.modifiers_dict[idx]["type"] in [ SINGLE_SELECTION, MULTI_SELECTION, NUMERIC_MODIFIER ]: self.modifiers_dict[idx]["selected"] = [] if self.modifiers_dict[idx]["type"] == MULTI_SELECTION: for j in range(self.modifiers_dict[idx]["widget"].count()): if self.modifiers_dict[idx]["widget"].item( j).checkState() == Qt.Checked: self.modifiers_dict[idx]["selected"].append( re.sub( " \(.*\)", "", self.modifiers_dict[idx] ["widget"].item(j).text())) if not self.modifiers_dict[idx]["selected"]: self.modifiers_dict[idx]["selected"].append("None") if self.modifiers_dict[idx]["type"] == SINGLE_SELECTION: for item in self.modifiers_dict[idx]["widget"].selectedItems(): self.modifiers_dict[idx]["selected"].append( re.sub(" \(.*\)", "", item.text())) if self.modifiers_dict[idx]["type"] == NUMERIC_MODIFIER: self.modifiers_dict[ idx]["selected"] = self.modifiers_dict[idx]["widget"].text( ) if self.modifiers_dict[idx]["widget"].text() else "None" ''' for widget in self.children(): if widget.objectName() == "lw_modifiers_classic": for item in widget.selectedItems(): modifiers.append(re.sub(" \(.*\)", "", item.text())) if widget.objectName() == "lw_modifiers_from_set": for idx in range(widget.count()): if widget.item(idx).checkState() == Qt.Checked: modifiers.append(widget.item(idx).text()) ''' return self.modifiers_dict
def get_modifiers(self): """ get modifiers returns list of selected modifiers """ modifiers = [] for idx in sorted_keys(self.modifiers_dict): if self.modifiers_dict[idx]["type"] in [SINGLE_SELECTION, MULTI_SELECTION, NUMERIC_MODIFIER]: self.modifiers_dict[idx]["selected"] = [] if self.modifiers_dict[idx]["type"] == MULTI_SELECTION: for j in range(self.modifiers_dict[idx]["widget"].count()): if self.modifiers_dict[idx]["widget"].item(j).checkState() == Qt.Checked: self.modifiers_dict[idx]["selected"].append( re.sub(" \(.*\)", "", self.modifiers_dict[idx]["widget"].item(j).text())) if not self.modifiers_dict[idx]["selected"]: self.modifiers_dict[idx]["selected"].append("None") if self.modifiers_dict[idx]["type"] == SINGLE_SELECTION: for item in self.modifiers_dict[idx]["widget"].selectedItems(): self.modifiers_dict[idx]["selected"].append(re.sub(" \(.*\)", "", item.text())) if self.modifiers_dict[idx]["type"] == NUMERIC_MODIFIER: self.modifiers_dict[idx]["selected"] = self.modifiers_dict[idx]["widget"].text() if self.modifiers_dict[idx]["widget"].text( ) else "None" ''' for widget in self.children(): if widget.objectName() == "lw_modifiers_classic": for item in widget.selectedItems(): modifiers.append(re.sub(" \(.*\)", "", item.text())) if widget.objectName() == "lw_modifiers_from_set": for idx in range(widget.count()): if widget.item(idx).checkState() == Qt.Checked: modifiers.append(widget.item(idx).text()) ''' return self.modifiers_dict
def create_events_plot(pj, selected_observations, parameters, plot_colors=BEHAVIORS_PLOT_COLORS, plot_directory="", file_format="png"): selected_subjects = parameters[SELECTED_SUBJECTS] selected_behaviors = parameters[SELECTED_BEHAVIORS] include_modifiers = parameters[INCLUDE_MODIFIERS] interval = parameters[TIME_INTERVAL] start_time = parameters[START_TIME] end_time = parameters[END_TIME] 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 cursor = db_connector.cursor() # if modifiers not to be included set modifiers to "" if not include_modifiers: cursor.execute("UPDATE aggregated_events SET modifiers = ''") cursor.execute( "SELECT distinct behavior, modifiers FROM aggregated_events") distinct_behav_modif = [[rows["behavior"], rows["modifiers"]] 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, "-"]) distinct_behav_modif = sorted(distinct_behav_modif) max_len = len(distinct_behav_modif) all_behaviors = [ pj[ETHOGRAM][x][BEHAVIOR_CODE] for x in utilities.sorted_keys(pj[ETHOGRAM]) ] par1 = 1 bar_height = 0.5 init = dt.datetime(2017, 1, 1) for obs_id in selected_observations: if len(selected_subjects) > 1: fig, axs = plt.subplots(figsize=(20, 8), nrows=len(selected_subjects), ncols=1, sharex=True) else: fig, ax = plt.subplots(figsize=(20, 8), nrows=len(selected_subjects), ncols=1, sharex=True) axs = np.ndarray(shape=(1), dtype=type(ax)) axs[0] = ax ok, msg, db_connector = db_functions.load_aggregated_events_in_db( pj, selected_subjects, [obs_id], selected_behaviors) cursor = db_connector.cursor() # if modifiers not to be included set modifiers to "" if not include_modifiers: cursor.execute("UPDATE aggregated_events SET modifiers = ''") cursor = db_connector.cursor() cursor.execute( "SELECT distinct behavior, modifiers FROM aggregated_events") distinct_behav_modif = [[rows["behavior"], rows["modifiers"]] for rows in cursor.fetchall()] # add selected behaviors that are not observed if not parameters["exclude behaviors"]: for behav in selected_behaviors: if [x for x in distinct_behav_modif if x[0] == behav] == []: distinct_behav_modif.append([behav, "-"]) distinct_behav_modif = sorted(distinct_behav_modif) max_len = len(distinct_behav_modif) # time obs_length = project_functions.observation_total_length( pj[OBSERVATIONS][obs_id]) if obs_length == -1: # media length not available interval = TIME_EVENTS 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) 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, )) ylabels = [" ".join(x) for x in distinct_behav_modif] for ax_idx, subject in enumerate(selected_subjects): if parameters["exclude behaviors"]: cursor.execute( "SELECT distinct behavior, modifiers FROM aggregated_events WHERE subject = ?", (subject, )) distinct_behav_modif = [[rows["behavior"], rows["modifiers"]] for rows in cursor.fetchall()] # add selected behaviors that are not observed if not parameters["exclude behaviors"]: for behav in selected_behaviors: if [x for x in distinct_behav_modif if x[0] == behav] == []: distinct_behav_modif.append([behav, "-"]) distinct_behav_modif = sorted(distinct_behav_modif) max_len = len(distinct_behav_modif) ylabels = [" ".join(x) for x in distinct_behav_modif] if not ax_idx: axs[ax_idx].set_title(f"Observation {obs_id}\n{subject}", fontsize=14) else: axs[ax_idx].set_title(subject, fontsize=14) bars = {} i = 0 for behavior_modifiers in distinct_behav_modif: behavior, modifiers = behavior_modifiers behavior_modifiers_str = "|".join( behavior_modifiers) if modifiers else behavior bars[behavior_modifiers_str] = [] # total duration cursor.execute(( "SELECT start,stop FROM aggregated_events " "WHERE observation = ? AND subject = ? AND behavior = ? AND modifiers = ?" ), ( obs_id, subject, behavior, modifiers, )) for row in cursor.fetchall(): bars[behavior_modifiers_str].append( (row["start"], row["stop"])) start_date = matplotlib.dates.date2num(init + dt.timedelta( seconds=row["start"])) end_date = matplotlib.dates.date2num(init + dt.timedelta( seconds=row["stop"] + POINT_EVENT_PLOT_DURATION * (row["stop"] == row["start"]))) try: bar_color = utilities.behavior_color( plot_colors, all_behaviors.index(behavior)) except Exception: bar_color = "darkgray" bar_color = POINT_EVENT_PLOT_COLOR if row["stop"] == row[ "start"] else bar_color # sage colors removed from matplotlib colors list if bar_color in ["sage", "darksage", "lightsage"]: bar_color = { "darksage": "#598556", "lightsage": "#bcecac", "sage": "#87ae73" }[bar_color] try: axs[ax_idx].barh((i * par1) + par1, end_date - start_date, left=start_date, height=bar_height, align="center", edgecolor=bar_color, color=bar_color, alpha=1) except Exception: axs[ax_idx].barh((i * par1) + par1, end_date - start_date, left=start_date, height=bar_height, align="center", edgecolor="darkgray", color="darkgray", alpha=1) i += 1 axs[ax_idx].set_ylim(bottom=0, top=(max_len * par1) + par1) pos = np.arange(par1, max_len * par1 + par1 + 1, par1) axs[ax_idx].set_yticks(pos[:len(ylabels)]) axs[ax_idx].set_yticklabels(ylabels, fontdict={"fontsize": 10}) axs[ax_idx].set_ylabel("Behaviors" + " (modifiers)" * include_modifiers, fontdict={"fontsize": 10}) axs[ax_idx].set_xlim( left=matplotlib.dates.date2num(init + dt.timedelta(seconds=min_time)), right=matplotlib.dates.date2num(init + dt.timedelta( seconds=max_time + 1))) axs[ax_idx].grid(color="g", linestyle=":") axs[ax_idx].xaxis_date() axs[ax_idx].xaxis.set_major_formatter(DateFormatter("%H:%M:%S")) axs[ax_idx].set_xlabel("Time (HH:MM:SS)", fontdict={"fontsize": 12}) axs[ax_idx].invert_yaxis() fig.autofmt_xdate() plt.tight_layout() if len(selected_observations) > 1: output_file_name = str( pathlib.Path( pathlib.Path(plot_directory) / utilities.safeFileName(obs_id)).with_suffix("." + file_format)) plt.savefig(output_file_name) else: plt.show()
def select_observations(pj: dict, mode: str, windows_title: str = "") -> tuple: """ allow user to select observations mode: accepted values: OPEN, EDIT, SINGLE, MULTIPLE, SELECT1 Args: pj (dict): BORIS project dictionary mode (str): mode foe selection: OPEN, EDIT, SINGLE, MULTIPLE, SELECT1 windows_title (str): title for windows Returns: str: selected mode: OPEN, EDIT, VIEW list: list of selected observations """ obsListFields = ["id", "date", "description", "subjects", "media"] indepVarHeader, column_type = [], [TEXT] * len(obsListFields) if INDEPENDENT_VARIABLES in pj: for idx in utilities.sorted_keys(pj[INDEPENDENT_VARIABLES]): indepVarHeader.append(pj[INDEPENDENT_VARIABLES][idx]["label"]) column_type.append(pj[INDEPENDENT_VARIABLES][idx]["type"]) data = [] for obs in sorted(list(pj[OBSERVATIONS].keys())): date = pj[OBSERVATIONS][obs]["date"].replace("T", " ") descr = utilities.eol2space(pj[OBSERVATIONS][obs][DESCRIPTION]) # subjects observedSubjects = [NO_FOCAL_SUBJECT if x == "" else x for x in project_functions.extract_observed_subjects(pj, [obs])] ''' removed 2020-01-13 if "" in observedSubjects: observedSubjects.remove("") ''' subjectsList = ", ".join(observedSubjects) mediaList = [] if pj[OBSERVATIONS][obs][TYPE] in [MEDIA]: if pj[OBSERVATIONS][obs][FILE]: for player in sorted(pj[OBSERVATIONS][obs][FILE].keys()): for media in pj[OBSERVATIONS][obs][FILE][player]: mediaList.append(f"#{player}: {media}") if len(mediaList) > 8: media = " ".join(mediaList) else: media = "\n".join(mediaList) elif pj[OBSERVATIONS][obs][TYPE] in [LIVE]: media = LIVE # independent variables indepvar = [] if INDEPENDENT_VARIABLES in pj[OBSERVATIONS][obs]: for var_label in indepVarHeader: if var_label in pj[OBSERVATIONS][obs][INDEPENDENT_VARIABLES]: indepvar.append(pj[OBSERVATIONS][obs][INDEPENDENT_VARIABLES][var_label]) else: indepvar.append("") data.append([obs, date, descr, subjectsList, media] + indepvar) obsList = observations_list.observationsList_widget(data, header=obsListFields + indepVarHeader, column_type=column_type) if windows_title: obsList.setWindowTitle(windows_title) obsList.pbOpen.setVisible(False) obsList.pbView.setVisible(False) obsList.pbEdit.setVisible(False) obsList.pbOk.setVisible(False) obsList.pbSelectAll.setVisible(False) obsList.pbUnSelectAll.setVisible(False) obsList.mode = mode if mode == OPEN: obsList.view.setSelectionMode(QAbstractItemView.SingleSelection) obsList.pbOpen.setVisible(True) if mode == VIEW: obsList.view.setSelectionMode(QAbstractItemView.SingleSelection) obsList.pbView.setVisible(True) if mode == EDIT: obsList.view.setSelectionMode(QAbstractItemView.SingleSelection) obsList.pbEdit.setVisible(True) if mode == SINGLE: obsList.view.setSelectionMode(QAbstractItemView.SingleSelection) obsList.pbOpen.setVisible(True) obsList.pbView.setVisible(True) obsList.pbEdit.setVisible(True) if mode == MULTIPLE: obsList.view.setSelectionMode(QAbstractItemView.MultiSelection) obsList.pbOk.setVisible(True) obsList.pbSelectAll.setVisible(True) obsList.pbUnSelectAll.setVisible(True) if mode == SELECT1: obsList.view.setSelectionMode(QAbstractItemView.SingleSelection) obsList.pbOk.setVisible(True) obsList.resize(900, 600) obsList.view.sortItems(0, Qt.AscendingOrder) for row in range(obsList.view.rowCount()): obsList.view.resizeRowToContents(row) selectedObs = [] result = obsList.exec_() if result: if obsList.view.selectedIndexes(): for idx in obsList.view.selectedIndexes(): if idx.column() == 0: # first column selectedObs.append(idx.data()) if result == 0: # cancel resultStr = "" if result == 1: # select resultStr = "ok" if result == 2: # open resultStr = OPEN if result == 3: # edit resultStr = EDIT if result == 4: # view resultStr = VIEW return resultStr, selectedObs
def test_str_keys(self): r = utilities.sorted_keys({"10": "x", "0": "x", "1": "x", "11": "x", "05": "x"}) #print(r) assert r == ['0', '1', '5', '10', '11']
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()}
def test_empty_dict(self): r = utilities.sorted_keys({}) assert r == []
def test_numeric_keys(self): r = utilities.sorted_keys({5: "a", 4: "7", 0: "z", 6: "a"}) #print(r) assert r == ['0', '4', '5', '6']
def __init__(self, code, modifiers_dict, currentModifier): super().__init__() self.setWindowTitle(programName) self.setWindowFlags(Qt.WindowStaysOnTopHint) self.modifiers_dict = dict(modifiers_dict) currentModifierList = currentModifier.split("|") V1layout = QVBoxLayout() label = QLabel() label.setText( f"Choose the modifier{'s' * (len(self.modifiers_dict) > 1)} for <b>{code}</b> behavior" ) V1layout.addWidget(label) Hlayout = QHBoxLayout() self.modifiersSetNumber = 0 for idx in sorted_keys(modifiers_dict): if self.modifiers_dict[idx]["type"] not in [ SINGLE_SELECTION, MULTI_SELECTION, NUMERIC_MODIFIER ]: continue V2layout = QVBoxLayout() self.modifiersSetNumber += 1 lb = QLabel() lb.setText(f"Modifier <b>{self.modifiers_dict[idx]['name']}</b>") V2layout.addWidget(lb) if self.modifiers_dict[idx]["type"] in [ SINGLE_SELECTION, MULTI_SELECTION ]: lw = QListWidget() self.modifiers_dict[idx]["widget"] = lw lw.setObjectName( f"lw_modifiers_({self.modifiers_dict[idx]['type']})") lw.installEventFilter(self) if self.modifiers_dict[idx]["type"] == SINGLE_SELECTION: item = QListWidgetItem("None") lw.addItem(item) item.setSelected(True) for modifier in self.modifiers_dict[idx]["values"]: item = QListWidgetItem(modifier) if self.modifiers_dict[idx]["type"] == MULTI_SELECTION: item.setCheckState(Qt.Unchecked) # previously selected try: if currentModifierList != [""] and re.sub( " \(.\)", "", modifier ) in currentModifierList[int(idx)].split(","): item.setCheckState(Qt.Checked) except Exception: # for old projects due to a fixed bug pass lw.addItem(item) if self.modifiers_dict[idx]["type"] == SINGLE_SELECTION: try: if currentModifierList != [""] and re.sub( " \(.\)", "", modifier) == currentModifierList[int(idx)]: item.setSelected(True) except Exception: # for old projects due to a fixed bug pass V2layout.addWidget(lw) if self.modifiers_dict[idx]["type"] in [NUMERIC_MODIFIER]: le = QLineEdit() self.modifiers_dict[idx]["widget"] = le if currentModifierList != [ "" ] and currentModifierList[int(idx)] != "None": le.setText(currentModifierList[int(idx)]) V2layout.addWidget(le) # vertical spacer spacerItem = QSpacerItem(20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding) V2layout.addItem(spacerItem) Hlayout.addLayout(V2layout) V1layout.addLayout(Hlayout) H2layout = QHBoxLayout() H2layout.addStretch(1) pbCancel = QPushButton(CANCEL) pbCancel.clicked.connect(self.reject) H2layout.addWidget(pbCancel) pbOK = QPushButton(OK) pbOK.setDefault(True) pbOK.clicked.connect(self.pbOK_clicked) H2layout.addWidget(pbOK) V1layout.addLayout(H2layout) self.setLayout(V1layout) self.installEventFilter(self) self.setMaximumSize(1024, 960)
def export_observations_list(pj: dict, selected_observations: list, file_name: str, output_format: str) -> bool: """ create file with a list of selected observations Args: pj (dict): project dictionary selected_observations (list): list of observations to export file_name (str): path of file to save list of observations output_format (str): format output Returns: bool: True of OK else False """ data = tablib.Dataset() data.headers = ["Observation id", "Date", "Description", "Subjects", "Media files/Live observation"] indep_var_header = [] if INDEPENDENT_VARIABLES in pj: for idx in utilities.sorted_keys(pj[INDEPENDENT_VARIABLES]): indep_var_header.append(pj[INDEPENDENT_VARIABLES][idx]["label"]) data.headers.extend(indep_var_header) for obs_id in selected_observations: subjects_list = sorted(list(set([x[EVENT_SUBJECT_FIELD_IDX] for x in pj[OBSERVATIONS][obs_id][EVENTS]]))) if "" in subjects_list: subjects_list = [NO_FOCAL_SUBJECT] + subjects_list subjects_list.remove("") subjects = ", ".join(subjects_list) if pj[OBSERVATIONS][obs_id][TYPE] == LIVE: media_files = ["Live observation"] elif pj[OBSERVATIONS][obs_id][TYPE] == MEDIA: media_files = [] if pj[OBSERVATIONS][obs_id][FILE]: for player in sorted(pj[OBSERVATIONS][obs_id][FILE].keys()): for media in pj[OBSERVATIONS][obs_id][FILE][player]: media_files.append("#{0}: {1}".format(player, media)) # independent variables indep_var = [] if INDEPENDENT_VARIABLES in pj[OBSERVATIONS][obs_id]: for var_label in indep_var_header: if var_label in pj[OBSERVATIONS][obs_id][INDEPENDENT_VARIABLES]: indep_var.append(pj[OBSERVATIONS][obs_id][INDEPENDENT_VARIABLES][var_label]) else: indep_var.append("") data.append( [obs_id, pj[OBSERVATIONS][obs_id]["date"], pj[OBSERVATIONS][obs_id]["description"], subjects, ", ".join(media_files)] + indep_var ) if output_format in ["tsv", "csv", "html"]: try: with open(file_name, "wb") as f: f.write(str.encode(data.export(output_format))) except Exception: return False if output_format in ["ods", "xlsx", "xls"]: try: with open(file_name, "wb") as f: f.write(data.export(output_format)) except: return False return True
def plot_time_ranges(pj, time_format, plot_colors, obs, obsId, minTime, videoLength, excludeBehaviorsWithoutEvents, line_width): """ create "hlines" matplotlib plot used by plot_event function (legacy) """ def on_draw(event): # http://matplotlib.org/faq/howto_faq.html#move-the-edge-of-an-axes-to-make-room-for-tick-labels bboxes = [] for label in labels: bbox = label.get_window_extent() bboxi = bbox.inverse_transformed(fig.transFigure) bboxes.append(bboxi) bbox = mtransforms.Bbox.union(bboxes) if fig.subplotpars.left < bbox.width: fig.subplots_adjust(left=1.1 * bbox.width) fig.canvas.draw() return False LINE_WIDTH = line_width all_behaviors, observedBehaviors = [], [] maxTime = 0 # max time in all events of all subjects # all behaviors defined in project without modifiers all_project_behaviors = [ pj[ETHOGRAM][idx]["code"] for idx in utilities.sorted_keys(pj[ETHOGRAM]) ] all_project_subjects = [NO_FOCAL_SUBJECT] + [ pj[SUBJECTS][idx]["name"] for idx in utilities.sorted_keys(pj[SUBJECTS]) ] for subject in obs: for behavior_modifiers_json in obs[subject]: behavior_modifiers = json.loads(behavior_modifiers_json) if not excludeBehaviorsWithoutEvents: observedBehaviors.append(behavior_modifiers_json) else: if obs[subject][behavior_modifiers_json]: observedBehaviors.append(behavior_modifiers_json) if not behavior_modifiers_json in all_behaviors: all_behaviors.append(behavior_modifiers_json) for t1, t2 in obs[subject][behavior_modifiers_json]: maxTime = max(maxTime, t1, t2) observedBehaviors.append("") lbl = [] if excludeBehaviorsWithoutEvents: for behav_modif_json in observedBehaviors: if not behav_modif_json: lbl.append("") continue behav_modif = json.loads(behav_modif_json) if len(behav_modif) == 2: lbl.append("{0} ({1})".format(behav_modif[0], behav_modif[1])) else: lbl.append(behav_modif[0]) else: all_behaviors.append('[""]') # empty json list element for behav_modif_json in all_behaviors: behav_modif = json.loads(behav_modif_json) if len(behav_modif) == 2: lbl.append("{0} ({1})".format(behav_modif[0], behav_modif[1])) else: lbl.append(behav_modif[0]) lbl = lbl[:] * len(obs) lbl = lbl[:-1] # remove last empty line fig = plt.figure(figsize=(20, 10)) fig.suptitle("Time diagram of observation {}".format(obsId), fontsize=14) ax = fig.add_subplot(111) labels = ax.set_yticklabels(lbl) ax.set_ylabel("Behaviors") if time_format == HHMMSS: fmtr = matplotlib.dates.DateFormatter("%H:%M:%S") # %H:%M:%S:%f ax.xaxis.set_major_formatter(fmtr) ax.set_xlabel("Time (hh:mm:ss)") else: ax.set_xlabel("Time (s)") plt.ylim(len(lbl), -0.5) if not videoLength: videoLength = maxTime if pj[OBSERVATIONS][obsId]["time offset"]: t0 = round(pj[OBSERVATIONS][obsId]["time offset"] + minTime) t1 = round(pj[OBSERVATIONS][obsId]["time offset"] + videoLength + 2) else: t0 = round(minTime) t1 = round(videoLength) subjectPosition = t0 + (t1 - t0) * 0.05 if time_format == HHMMSS: t0d = dt.datetime(1970, 1, 1, int(t0 / 3600), int((t0 - int(t0 / 3600) * 3600) / 60), int(t0 % 60), round(round(t0 % 1, 3) * 1000000)) t1d = dt.datetime(1970, 1, 1, int(t1 / 3600), int((t1 - int(t1 / 3600) * 3600) / 60), int(t1 % 60), round(round(t1 % 1, 3) * 1000000)) subjectPositiond = dt.datetime( 1970, 1, 1, int(subjectPosition / 3600), int((subjectPosition - int(subjectPosition / 3600) * 3600) / 60), int(subjectPosition % 60), round(round(subjectPosition % 1, 3) * 1000000)) if time_format == S: t0d, t1d = t0, t1 subjectPositiond = subjectPosition plt.xlim(t0d, t1d) plt.yticks(range(len(lbl) + 1), np.array(lbl)) count = 0 flagFirstSubject = True for subject in all_project_subjects: if subject not in obs: continue if not flagFirstSubject: if excludeBehaviorsWithoutEvents: count += 1 ax.axhline(y=(count - 1), linewidth=1, color="black") ax.hlines(np.array([count]), np.array([0]), np.array([0]), lw=LINE_WIDTH, color=col) else: flagFirstSubject = False ax.text(subjectPositiond, count - 0.5, subject) behaviors = obs[subject] x1, x2, y, col, pointsx, pointsy, guide = [], [], [], [], [], [], [] col_count = 0 for bm_json in all_behaviors: if bm_json in obs[subject]: if obs[subject][bm_json]: for t1, t2 in obs[subject][bm_json]: if t1 == t2: pointsx.append(t1) pointsy.append(count) ax.axhline(y=count, linewidth=1, color="lightgray", zorder=-1) else: x1.append(t1) x2.append(t2) y.append(count) col.append( utilities.behavior_color( plot_colors, all_project_behaviors.index( json.loads(bm_json)[0]))) ax.axhline(y=count, linewidth=1, color="lightgray", zorder=-1) count += 1 else: x1.append(0) x2.append(0) y.append(count) col.append("white") ax.axhline(y=count, linewidth=1, color="lightgray", zorder=-1) count += 1 else: if not excludeBehaviorsWithoutEvents: x1.append(0) x2.append(0) y.append(count) col.append("white") ax.axhline(y=count, linewidth=1, color="lightgray", zorder=-1) count += 1 col_count += 1 if time_format == HHMMSS: ax.hlines(np.array(y), np.array([ dt.datetime(1970, 1, 1, int(p / 3600), int((p - int(p / 3600) * 3600) / 60), int(p % 60), round(round(p % 1, 3) * 1e6)) for p in x1 ]), np.array([ dt.datetime(1970, 1, 1, int(p / 3600), int((p - int(p / 3600) * 3600) / 60), int(p % 60), round(round(p % 1, 3) * 1e6)) for p in x2 ]), lw=LINE_WIDTH, color=col) if time_format == S: ax.hlines(np.array(y), np.array(x1), np.array(x2), lw=LINE_WIDTH, color=col) if time_format == HHMMSS: ax.plot( np.array([ dt.datetime(1970, 1, 1, int(p / 3600), int((p - int(p / 3600) * 3600) / 60), int(p % 60), round(round(p % 1, 3) * 1e6)) for p in pointsx ]), pointsy, "r^") if time_format == S: ax.plot(pointsx, pointsy, "r^") fig.canvas.mpl_connect("draw_event", on_draw) plt.show() return True
def __init__(self, key, modifiers_dict, currentModifier): super().__init__() self.setWindowTitle(programName) self.setWindowFlags(Qt.WindowStaysOnTopHint) self.modifiers_dict = dict(modifiers_dict) currentModifierList = currentModifier.split("|") print("currentModifierList", currentModifierList) V1layout = QVBoxLayout() label = QLabel() label.setText("Choose the modifier{0} for <b>{1}</b> behavior".format( "s" * (len(self.modifiers_dict) > 1), key)) V1layout.addWidget(label) Hlayout = QHBoxLayout() self.modifiersSetNumber = 0 for idx in sorted_keys(modifiers_dict): V2layout = QVBoxLayout() self.modifiersSetNumber += 1 lb = QLabel() lb.setText("Modifier <b>{}</b>".format( self.modifiers_dict[idx]["name"])) V2layout.addWidget(lb) if self.modifiers_dict[idx]["type"] in [ SINGLE_SELECTION, MULTI_SELECTION ]: lw = QListWidget() self.modifiers_dict[idx]["widget"] = lw lw.setObjectName("lw_modifiers") lw.installEventFilter(self) if self.modifiers_dict[idx]["type"] == SINGLE_SELECTION: item = QListWidgetItem("None") lw.addItem(item) lw.setItemSelected( item, True ) if QT_VERSION_STR[0] == "4" else item.setSelected(True) #lw.setFixedHeight(len(modifiers_dict[idx]["values"])*20) for modifier in self.modifiers_dict[idx]["values"]: item = QListWidgetItem(modifier) if self.modifiers_dict[idx]["type"] == MULTI_SELECTION: item.setCheckState(Qt.Unchecked) # previously selected ''' print("\n\nmodifier:", modifier) print(re.sub(" \(.\)", "", modifier)) print("currentModifierList",currentModifierList) print("idx", idx) print(currentModifierList[int(idx)]) ''' try: if currentModifierList != [""] and re.sub( " \(.\)", "", modifier ) in currentModifierList[int(idx)].split(","): item.setCheckState(Qt.Checked) except: # for old projects due to a fixed bug pass lw.addItem(item) if self.modifiers_dict[idx]["type"] == SINGLE_SELECTION: try: if currentModifierList != [""] and re.sub( " \(.\)", "", modifier) == currentModifierList[int(idx)]: if QT_VERSION_STR[0] == "4": lw.setItemSelected(item, True) else: item.setSelected(True) except: # for old projects due to a fixed bug pass V2layout.addWidget(lw) if self.modifiers_dict[idx]["type"] in [NUMERIC_MODIFIER]: le = QLineEdit() self.modifiers_dict[idx]["widget"] = le if currentModifierList != [ "" ] and currentModifierList[int(idx)] != "None": le.setText(currentModifierList[int(idx)]) V2layout.addWidget(le) # vertical spacer spacerItem = QSpacerItem(20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding) V2layout.addItem(spacerItem) Hlayout.addLayout(V2layout) V1layout.addLayout(Hlayout) H2layout = QHBoxLayout() H2layout.addStretch(1) pbCancel = QPushButton(CANCEL) pbCancel.clicked.connect(self.reject) H2layout.addWidget(pbCancel) pbOK = QPushButton(OK) pbOK.setDefault(True) pbOK.clicked.connect(self.pbOK_clicked) H2layout.addWidget(pbOK) V1layout.addLayout(H2layout) self.setLayout(V1layout) self.installEventFilter(self) #self.setMinimumSize(630, 50) self.setMaximumSize(1024, 960)
def select_observations(pj: dict, mode: str) -> tuple: """ allow user to select observations mode: accepted values: OPEN, EDIT, SINGLE, MULTIPLE, SELECT1 Args: pj (dict): BORIS project dictionary mode (str): mode foe selection: OPEN, EDIT, SINGLE, MULTIPLE, SELECT1 Returns: str: selected mode: OPEN, EDIT, VIEW list: list of selected observations """ obsListFields = ["id", "date", "description", "subjects", "media"] indepVarHeader, column_type = [], [TEXT] * len(obsListFields) if INDEPENDENT_VARIABLES in pj: for idx in utilities.sorted_keys(pj[INDEPENDENT_VARIABLES]): indepVarHeader.append(pj[INDEPENDENT_VARIABLES][idx]["label"]) column_type.append(pj[INDEPENDENT_VARIABLES][idx]["type"]) data = [] for obs in sorted(list(pj[OBSERVATIONS].keys())): date = pj[OBSERVATIONS][obs]["date"].replace("T", " ") descr = utilities.eol2space(pj[OBSERVATIONS][obs]["description"]) # subjects observedSubjects = project_functions.extract_observed_subjects(pj, [obs]) # remove when No focal subject if "" in observedSubjects: observedSubjects.remove("") subjectsList = ", ".join(observedSubjects) mediaList = [] if pj[OBSERVATIONS][obs][TYPE] in [MEDIA]: if pj[OBSERVATIONS][obs][FILE]: for player in sorted(pj[OBSERVATIONS][obs][FILE].keys()): for media in pj[OBSERVATIONS][obs][FILE][player]: mediaList.append("#{0}: {1}".format(player, media)) if len(mediaList) > 8: media = " ".join(mediaList) else: media = "\n".join(mediaList) elif pj[OBSERVATIONS][obs][TYPE] in [LIVE]: media = LIVE # independent variables indepvar = [] if INDEPENDENT_VARIABLES in pj[OBSERVATIONS][obs]: for var_label in indepVarHeader: if var_label in pj[OBSERVATIONS][obs][INDEPENDENT_VARIABLES]: indepvar.append(pj[OBSERVATIONS][obs][INDEPENDENT_VARIABLES][var_label]) else: indepvar.append("") data.append([obs, date, descr, subjectsList, media] + indepvar) obsList = observations_list.observationsList_widget(data, header=obsListFields + indepVarHeader, column_type=column_type) obsList.pbOpen.setVisible(False) obsList.pbView.setVisible(False) obsList.pbEdit.setVisible(False) obsList.pbOk.setVisible(False) obsList.pbSelectAll.setVisible(False) obsList.pbUnSelectAll.setVisible(False) obsList.mode = mode if mode == OPEN: obsList.view.setSelectionMode(QAbstractItemView.SingleSelection) obsList.pbOpen.setVisible(True) if mode == VIEW: obsList.view.setSelectionMode(QAbstractItemView.SingleSelection) obsList.pbView.setVisible(True) if mode == EDIT: obsList.view.setSelectionMode(QAbstractItemView.SingleSelection) obsList.pbEdit.setVisible(True) if mode == SINGLE: obsList.view.setSelectionMode(QAbstractItemView.SingleSelection) obsList.pbOpen.setVisible(True) obsList.pbView.setVisible(True) obsList.pbEdit.setVisible(True) if mode == MULTIPLE: obsList.view.setSelectionMode(QAbstractItemView.MultiSelection) obsList.pbOk.setVisible(True) obsList.pbSelectAll.setVisible(True) obsList.pbUnSelectAll.setVisible(True) if mode == SELECT1: obsList.view.setSelectionMode(QAbstractItemView.SingleSelection) obsList.pbOk.setVisible(True) obsList.resize(900, 600) obsList.view.sortItems(0, Qt.AscendingOrder) for row in range(obsList.view.rowCount()): obsList.view.resizeRowToContents(row) selectedObs = [] result = obsList.exec_() if result: if obsList.view.selectedIndexes(): for idx in obsList.view.selectedIndexes(): if idx.column() == 0: # first column selectedObs.append(idx.data()) if result == 0: # cancel resultStr = "" if result == 1: # select resultStr = "ok" if result == 2: # open resultStr = OPEN if result == 3: # edit resultStr = EDIT if result == 4: # view resultStr = VIEW return resultStr, selectedObs
def create_events_plot(pj, selected_observations, parameters, plot_colors=BEHAVIORS_PLOT_COLORS, plot_directory="", file_format="png"): selected_subjects = parameters["selected subjects"] selected_behaviors = parameters["selected behaviors"] include_modifiers = parameters["include modifiers"] interval = parameters[TIME_INTERVAL] start_time = parameters[START_TIME] end_time = parameters[END_TIME] 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 cursor = db_connector.cursor() # if modifiers not to be included set modifiers to "" if not include_modifiers: cursor.execute("UPDATE aggregated_events SET modifiers = ''") cursor.execute("SELECT distinct behavior, modifiers FROM aggregated_events") distinct_behav_modif = [[rows["behavior"], rows["modifiers"]] 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, "-"]) distinct_behav_modif = sorted(distinct_behav_modif) max_len = len(distinct_behav_modif) all_behaviors = [pj[ETHOGRAM][x][BEHAVIOR_CODE] for x in utilities.sorted_keys(pj[ETHOGRAM])] par1 = 1 bar_height = 0.5 init = dt.datetime(2017, 1, 1) for obs_id in selected_observations: if len(selected_subjects) > 1: fig, axs = plt.subplots(figsize=(20, 8), nrows=len(selected_subjects), ncols=1, sharex=True) else: fig, ax = plt.subplots(figsize=(20, 8), nrows=len(selected_subjects), ncols=1, sharex=True) axs = np.ndarray(shape=(1), dtype=type(ax)) axs[0] = ax ok, msg, db_connector = db_functions.load_aggregated_events_in_db( pj, selected_subjects, [obs_id], selected_behaviors) cursor = db_connector.cursor() # if modifiers not to be included set modifiers to "" if not include_modifiers: cursor.execute("UPDATE aggregated_events SET modifiers = ''") cursor = db_connector.cursor() cursor.execute("SELECT distinct behavior, modifiers FROM aggregated_events") distinct_behav_modif = [[rows["behavior"], rows["modifiers"]] for rows in cursor.fetchall()] # add selected behaviors that are not observed if not parameters["exclude behaviors"]: for behav in selected_behaviors: if [x for x in distinct_behav_modif if x[0] == behav] == []: distinct_behav_modif.append([behav, "-"]) distinct_behav_modif = sorted(distinct_behav_modif) max_len = len(distinct_behav_modif) # time obs_length = project_functions.observation_total_length(pj[OBSERVATIONS][obs_id]) if obs_length == -1: # media length not available interval = TIME_EVENTS 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) 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, )) ylabels = [" ".join(x) for x in distinct_behav_modif] for ax_idx, subject in enumerate(selected_subjects): if parameters["exclude behaviors"]: cursor.execute("SELECT distinct behavior, modifiers FROM aggregated_events WHERE subject = ?", (subject, )) distinct_behav_modif = [[rows["behavior"], rows["modifiers"]] for rows in cursor.fetchall()] # add selected behaviors that are not observed if not parameters["exclude behaviors"]: for behav in selected_behaviors: if [x for x in distinct_behav_modif if x[0] == behav] == []: distinct_behav_modif.append([behav, "-"]) distinct_behav_modif = sorted(distinct_behav_modif) max_len = len(distinct_behav_modif) ylabels = [" ".join(x) for x in distinct_behav_modif] if not ax_idx: axs[ax_idx].set_title("Observation {}\n{}".format(obs_id, subject), fontsize=14) else: axs[ax_idx].set_title(subject, fontsize=14) bars = {} i = 0 for behavior_modifiers in distinct_behav_modif: behavior, modifiers = behavior_modifiers behavior_modifiers_str = "|".join(behavior_modifiers) if modifiers else behavior bars[behavior_modifiers_str] = [] # total duration cursor.execute(("SELECT start,stop FROM aggregated_events " "WHERE observation = ? AND subject = ? AND behavior = ? AND modifiers = ?"), (obs_id, subject, behavior, modifiers,)) for row in cursor.fetchall(): bars[behavior_modifiers_str].append((row["start"], row["stop"])) start_date = matplotlib.dates.date2num(init + dt.timedelta(seconds=row["start"])) end_date = matplotlib.dates.date2num( init + dt.timedelta(seconds=row["stop"] + POINT_EVENT_PLOT_DURATION * (row["stop"] == row["start"]))) try: bar_color = utilities.behavior_color(plot_colors, all_behaviors.index(behavior)) except Exception: bar_color = "darkgray" bar_color = POINT_EVENT_PLOT_COLOR if row["stop"] == row["start"] else bar_color # sage colors removed from matplotlib colors list if bar_color in ["sage", "darksage", "lightsage"]: bar_color = {"darksage": "#598556", "lightsage": "#bcecac", "sage": "#87ae73"}[bar_color] try: axs[ax_idx].barh((i * par1) + par1, end_date - start_date, left=start_date, height=bar_height, align="center", edgecolor=bar_color, color=bar_color, alpha=1) except Exception: axs[ax_idx].barh((i * par1) + par1, end_date - start_date, left=start_date, height=bar_height, align="center", edgecolor="darkgray", color="darkgray", alpha=1) i += 1 axs[ax_idx].set_ylim(bottom=0, top=(max_len * par1) + par1) pos = np.arange(par1, max_len * par1 + par1 + 1, par1) axs[ax_idx].set_yticks(pos[:len(ylabels)]) axs[ax_idx].set_yticklabels(ylabels, fontdict={"fontsize": 10}) axs[ax_idx].set_ylabel("Behaviors" + " (modifiers)" * include_modifiers, fontdict={"fontsize": 10}) axs[ax_idx].set_xlim(left=matplotlib.dates.date2num(init + dt.timedelta(seconds=min_time)), right=matplotlib.dates.date2num(init + dt.timedelta(seconds=max_time + 1))) axs[ax_idx].grid(color="g", linestyle=":") axs[ax_idx].xaxis_date() axs[ax_idx].xaxis.set_major_formatter(DateFormatter("%H:%M:%S")) axs[ax_idx].set_xlabel("Time (HH:MM:SS)", fontdict={"fontsize": 12}) axs[ax_idx].invert_yaxis() fig.autofmt_xdate() plt.tight_layout() if len(selected_observations) > 1: output_file_name = str(pathlib.Path(pathlib.Path(plot_directory) / utilities.safeFileName(obs_id)).with_suffix( "." + file_format)) plt.savefig(output_file_name) else: plt.show()
def plot_time_ranges(pj, time_format, plot_colors, obs, obsId, minTime, videoLength, excludeBehaviorsWithoutEvents, line_width): """ create "hlines" matplotlib plot used by plot_event function (legacy) """ def on_draw(event): # http://matplotlib.org/faq/howto_faq.html#move-the-edge-of-an-axes-to-make-room-for-tick-labels bboxes = [] for label in labels: bbox = label.get_window_extent() bboxi = bbox.inverse_transformed(fig.transFigure) bboxes.append(bboxi) bbox = mtransforms.Bbox.union(bboxes) if fig.subplotpars.left < bbox.width: fig.subplots_adjust(left=1.1*bbox.width) fig.canvas.draw() return False LINE_WIDTH = line_width all_behaviors, observedBehaviors = [], [] maxTime = 0 # max time in all events of all subjects # all behaviors defined in project without modifiers all_project_behaviors = [pj[ETHOGRAM][idx]["code"] for idx in utilities.sorted_keys(pj[ETHOGRAM])] all_project_subjects = [NO_FOCAL_SUBJECT] + [pj[SUBJECTS][idx]["name"] for idx in utilities.sorted_keys(pj[SUBJECTS])] for subject in obs: for behavior_modifiers_json in obs[subject]: behavior_modifiers = json.loads(behavior_modifiers_json) if not excludeBehaviorsWithoutEvents: observedBehaviors.append(behavior_modifiers_json) else: if obs[subject][behavior_modifiers_json]: observedBehaviors.append(behavior_modifiers_json) if not behavior_modifiers_json in all_behaviors: all_behaviors.append(behavior_modifiers_json) for t1, t2 in obs[subject][behavior_modifiers_json]: maxTime = max(maxTime, t1, t2) observedBehaviors.append("") lbl = [] if excludeBehaviorsWithoutEvents: for behav_modif_json in observedBehaviors: if not behav_modif_json: lbl.append("") continue behav_modif = json.loads(behav_modif_json) if len(behav_modif) == 2: lbl.append("{0} ({1})".format(behav_modif[0], behav_modif[1])) else: lbl.append(behav_modif[0]) else: all_behaviors.append('[""]') # empty json list element for behav_modif_json in all_behaviors: behav_modif = json.loads(behav_modif_json) if len(behav_modif) == 2: lbl.append("{0} ({1})".format(behav_modif[0], behav_modif[1])) else: lbl.append(behav_modif[0]) lbl = lbl[:] * len(obs) lbl = lbl[:-1] # remove last empty line fig = plt.figure(figsize=(20, 10)) fig.suptitle("Time diagram of observation {}".format(obsId), fontsize=14) ax = fig.add_subplot(111) labels = ax.set_yticklabels(lbl) ax.set_ylabel("Behaviors") if time_format == HHMMSS: fmtr = matplotlib.dates.DateFormatter("%H:%M:%S") # %H:%M:%S:%f ax.xaxis.set_major_formatter(fmtr) ax.set_xlabel("Time (hh:mm:ss)") else: ax.set_xlabel("Time (s)") plt.ylim(len(lbl), -0.5) if not videoLength: videoLength = maxTime if pj[OBSERVATIONS][obsId]["time offset"]: t0 = round(pj[OBSERVATIONS][obsId]["time offset"] + minTime) t1 = round(pj[OBSERVATIONS][obsId]["time offset"] + videoLength + 2) else: t0 = round(minTime) t1 = round(videoLength) subjectPosition = t0 + (t1 - t0) * 0.05 if time_format == HHMMSS: t0d = dt.datetime(1970, 1, 1, int(t0 / 3600), int((t0 - int(t0 / 3600) * 3600) / 60), int(t0 % 60), round(round(t0 % 1, 3) * 1000000)) t1d = dt.datetime(1970, 1, 1, int(t1 / 3600), int((t1 - int(t1 / 3600) * 3600) / 60), int(t1 % 60), round(round(t1 % 1, 3) * 1000000)) subjectPositiond = dt.datetime(1970, 1, 1, int(subjectPosition / 3600), int((subjectPosition - int(subjectPosition / 3600) * 3600) / 60), int(subjectPosition % 60), round(round(subjectPosition % 1, 3) * 1000000)) if time_format == S: t0d, t1d = t0, t1 subjectPositiond = subjectPosition plt.xlim(t0d, t1d) plt.yticks(range(len(lbl) + 1), np.array(lbl)) count = 0 flagFirstSubject = True for subject in all_project_subjects: if subject not in obs: continue if not flagFirstSubject: if excludeBehaviorsWithoutEvents: count += 1 ax.axhline(y=(count-1), linewidth=1, color="black") ax.hlines(np.array([count]), np.array([0]), np.array([0]), lw=LINE_WIDTH, color=col) else: flagFirstSubject = False ax.text(subjectPositiond, count - 0.5, subject) behaviors = obs[subject] x1, x2, y, col, pointsx, pointsy, guide = [], [], [], [], [], [], [] col_count = 0 for bm_json in all_behaviors: if bm_json in obs[subject]: if obs[subject][bm_json]: for t1, t2 in obs[subject][bm_json]: if t1 == t2: pointsx.append(t1) pointsy.append(count) ax.axhline(y=count, linewidth=1, color="lightgray", zorder=-1) else: x1.append(t1) x2.append(t2) y.append(count) col.append(utilities.behavior_color(plot_colors, all_project_behaviors.index(json.loads(bm_json)[0]))) ax.axhline(y=count, linewidth=1, color="lightgray", zorder=-1) count += 1 else: x1.append(0) x2.append(0) y.append(count) col.append("white") ax.axhline(y=count, linewidth=1, color="lightgray", zorder=-1) count += 1 else: if not excludeBehaviorsWithoutEvents: x1.append(0) x2.append(0) y.append(count) col.append("white") ax.axhline(y=count, linewidth=1, color="lightgray", zorder=-1) count += 1 col_count += 1 if time_format == HHMMSS: ax.hlines(np.array(y), np.array([dt.datetime(1970, 1, 1, int(p / 3600), int((p - int(p / 3600) * 3600) / 60), int(p % 60), round(round(p % 1, 3) * 1e6)) for p in x1]), np.array([dt.datetime(1970, 1, 1, int(p / 3600), int((p - int(p / 3600) * 3600) / 60), int(p % 60), round(round(p % 1, 3) * 1e6)) for p in x2]), lw=LINE_WIDTH, color=col) if time_format == S: ax.hlines(np.array(y), np.array(x1), np.array(x2), lw=LINE_WIDTH, color=col) if time_format == HHMMSS: ax.plot( np.array([ dt.datetime(1970, 1, 1, int(p / 3600), int((p - int(p / 3600) * 3600) / 60), int(p % 60), round(round(p % 1, 3) * 1e6)) for p in pointsx ]), pointsy, "r^") if time_format == S: ax.plot(pointsx, pointsy, "r^") fig.canvas.mpl_connect("draw_event", on_draw) plt.show() return True
def export_aggregated_events(pj: dict, parameters: dict, obsId: str): """ export aggregated events Args: pj (dict): BORIS project parameters (dict): subjects, behaviors obsId (str): observation id Returns: tablib.Dataset: """ logging.debug(f"function: export aggregated events {parameters} {obsId}") interval = parameters["time"] start_time = parameters[START_TIME] end_time = parameters[END_TIME] data = tablib.Dataset() observation = pj[OBSERVATIONS][obsId] duration1 = [] # in seconds if observation[TYPE] in [MEDIA]: try: for mediaFile in observation[FILE][PLAYER1]: if MEDIA_INFO in observation: duration1.append( observation[MEDIA_INFO]["length"][mediaFile]) except Exception: duration1 = [] obs_length = project_functions.observation_total_length( pj[OBSERVATIONS][obsId]) if obs_length == Decimal("-1"): # media length not available interval = TIME_EVENTS logging.debug(f"obs_length: {obs_length}") ok, msg, connector = db_functions.load_aggregated_events_in_db( pj, parameters[SELECTED_SUBJECTS], [obsId], parameters[SELECTED_BEHAVIORS]) if connector is None: logging.critical(f"error when loading aggregated events in DB") return data # time cursor = connector.cursor() if interval == TIME_FULL_OBS: min_time = float(0) max_time = float(obs_length) if interval == TIME_EVENTS: try: min_time = float(pj[OBSERVATIONS][obsId][EVENTS][0][0]) except Exception: min_time = float(0) try: max_time = float(pj[OBSERVATIONS][obsId][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, obsId, min_time, min_time, max_time, )) cursor.execute( "UPDATE aggregated_events SET stop = ? WHERE observation = ? AND stop > ? AND start BETWEEN ? AND ?", ( max_time, obsId, max_time, min_time, max_time, )) cursor.execute( "UPDATE aggregated_events SET start = ?, stop = ? WHERE observation = ? AND start < ? AND stop > ?", ( min_time, max_time, obsId, min_time, max_time, )) cursor.execute( "DELETE FROM aggregated_events WHERE observation = ? AND (start < ? AND stop < ?) OR (start > ? AND stop > ?)", ( obsId, min_time, min_time, max_time, max_time, )) behavioral_category = project_functions.behavior_category(pj[ETHOGRAM]) for subject in parameters[SELECTED_SUBJECTS]: for behavior in parameters[SELECTED_BEHAVIORS]: cursor.execute( "SELECT distinct modifiers FROM aggregated_events where subject=? AND behavior=? order by modifiers", ( subject, behavior, )) rows_distinct_modifiers = list(x[0] for x in cursor.fetchall()) for distinct_modifiers in rows_distinct_modifiers: cursor.execute(( "SELECT start, stop, type, modifiers, comment, comment_stop FROM aggregated_events " "WHERE subject = ? AND behavior = ? AND modifiers = ? ORDER by start" ), (subject, behavior, distinct_modifiers)) rows = list(cursor.fetchall()) for row in rows: if observation[TYPE] in [MEDIA]: if duration1: mediaFileIdx = [ idx1 for idx1, x in enumerate(duration1) if row["start"] >= sum(duration1[0:idx1]) ][-1] mediaFileString = observation[FILE][PLAYER1][ mediaFileIdx] try: fpsString = observation[MEDIA_INFO]["fps"][ observation[FILE][PLAYER1][mediaFileIdx]] except Exception: fpsString = "NA" else: try: if len(observation[FILE][PLAYER1]) == 1: mediaFileString = observation[FILE][ PLAYER1][0] else: mediaFileString = "NA" except Exception: mediaFileString = "NA" fpsString = "NA" if observation[TYPE] in [LIVE]: mediaFileString = "LIVE" fpsString = "NA" if row["type"] == POINT: row_data = [] row_data.extend([ obsId, observation["date"].replace("T", " "), mediaFileString, f"{obs_length:.3f}" if obs_length != Decimal("-1") else "NA", fpsString ]) # independent variables if INDEPENDENT_VARIABLES in pj: for idx_var in utilities.sorted_keys( pj[INDEPENDENT_VARIABLES]): if pj[INDEPENDENT_VARIABLES][idx_var][ "label"] in observation[ INDEPENDENT_VARIABLES]: row_data.append( observation[INDEPENDENT_VARIABLES][ pj[INDEPENDENT_VARIABLES][idx_var] ["label"]]) else: row_data.append("") row_data.extend([ subject, behavior, behavioral_category[behavior], row["modifiers"], POINT, f"{row['start']:.3f}", # start f"{row['stop']:.3f}", # stop "NA", # duration row["comment"], "" ]) data.append(row_data) if row["type"] == STATE: if idx % 2 == 0: row_data = [] row_data.extend([ obsId, observation["date"].replace("T", " "), mediaFileString, f"{obs_length:.3f}" if obs_length != Decimal("-1") else "NA", fpsString ]) # independent variables if INDEPENDENT_VARIABLES in pj: for idx_var in utilities.sorted_keys( pj[INDEPENDENT_VARIABLES]): if pj[INDEPENDENT_VARIABLES][idx_var][ "label"] in observation[ INDEPENDENT_VARIABLES]: row_data.append( observation[INDEPENDENT_VARIABLES] [pj[INDEPENDENT_VARIABLES][idx_var] ["label"]]) else: row_data.append("") row_data.extend([ subject, behavior, behavioral_category[behavior], row["modifiers"], STATE, f"{row['start']:.3f}", f"{row['stop']:.3f}", f"{row['stop'] - row['start']:.3f}", row["comment"], row["comment_stop"] ]) data.append(row_data) return data
def __init__(self, code, modifiers_dict, currentModifier): super().__init__() self.setWindowTitle(programName) self.setWindowFlags(Qt.WindowStaysOnTopHint) self.modifiers_dict = dict(modifiers_dict) currentModifierList = currentModifier.split("|") V1layout = QVBoxLayout() label = QLabel() label.setText(f"Choose the modifier{'s' * (len(self.modifiers_dict) > 1)} for <b>{code}</b> behavior") V1layout.addWidget(label) Hlayout = QHBoxLayout() self.modifiersSetNumber = 0 for idx in sorted_keys(modifiers_dict): if self.modifiers_dict[idx]["type"] not in [SINGLE_SELECTION, MULTI_SELECTION, NUMERIC_MODIFIER]: continue V2layout = QVBoxLayout() self.modifiersSetNumber += 1 lb = QLabel() lb.setText(f"Modifier <b>{self.modifiers_dict[idx]['name']}</b>") V2layout.addWidget(lb) if self.modifiers_dict[idx]["type"] in [SINGLE_SELECTION, MULTI_SELECTION]: lw = QListWidget() self.modifiers_dict[idx]["widget"] = lw lw.setObjectName(f"lw_modifiers_({self.modifiers_dict[idx]['type']})") lw.installEventFilter(self) if self.modifiers_dict[idx]["type"] == SINGLE_SELECTION: item = QListWidgetItem("None") lw.addItem(item) item.setSelected(True) for modifier in self.modifiers_dict[idx]["values"]: item = QListWidgetItem(modifier) if self.modifiers_dict[idx]["type"] == MULTI_SELECTION: item.setCheckState(Qt.Unchecked) # previously selected try: if currentModifierList != [""] and re.sub(" \(.\)", "", modifier) in currentModifierList[int(idx)].split(","): item.setCheckState(Qt.Checked) except Exception: # for old projects due to a fixed bug pass lw.addItem(item) if self.modifiers_dict[idx]["type"] == SINGLE_SELECTION: try: if currentModifierList != [""] and re.sub(" \(.\)", "", modifier) == currentModifierList[int(idx)]: item.setSelected(True) except Exception: # for old projects due to a fixed bug pass V2layout.addWidget(lw) if self.modifiers_dict[idx]["type"] in [NUMERIC_MODIFIER]: le = QLineEdit() self.modifiers_dict[idx]["widget"] = le if currentModifierList != [""] and currentModifierList[int(idx)] != "None": le.setText(currentModifierList[int(idx)]) V2layout.addWidget(le) # vertical spacer spacerItem = QSpacerItem(20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding) V2layout.addItem(spacerItem) Hlayout.addLayout(V2layout) V1layout.addLayout(Hlayout) H2layout = QHBoxLayout() H2layout.addStretch(1) pbCancel = QPushButton(CANCEL) pbCancel.clicked.connect(self.reject) H2layout.addWidget(pbCancel) pbOK = QPushButton(OK) pbOK.setDefault(True) pbOK.clicked.connect(self.pbOK_clicked) H2layout.addWidget(pbOK) V1layout.addLayout(H2layout) self.setLayout(V1layout) self.installEventFilter(self) self.setMaximumSize(1024, 960)
def export_observations_list(pj: dict, selected_observations: list, file_name: str, output_format: str) -> bool: """ create file with a list of selected observations Args: pj (dict): project dictionary selected_observations (list): list of observations to export file_name (str): path of file to save list of observations output_format (str): format output Returns: bool: True of OK else False """ data = tablib.Dataset() data.headers = [ "Observation id", "Date", "Description", "Subjects", "Media files/Live observation" ] indep_var_header = [] if INDEPENDENT_VARIABLES in pj: for idx in utilities.sorted_keys(pj[INDEPENDENT_VARIABLES]): indep_var_header.append(pj[INDEPENDENT_VARIABLES][idx]["label"]) data.headers.extend(indep_var_header) for obs_id in selected_observations: subjects_list = sorted( list( set([ x[EVENT_SUBJECT_FIELD_IDX] for x in pj[OBSERVATIONS][obs_id][EVENTS] ]))) if "" in subjects_list: subjects_list = [NO_FOCAL_SUBJECT] + subjects_list subjects_list.remove("") subjects = ", ".join(subjects_list) if pj[OBSERVATIONS][obs_id][TYPE] == LIVE: media_files = ["Live observation"] elif pj[OBSERVATIONS][obs_id][TYPE] == MEDIA: media_files = [] if pj[OBSERVATIONS][obs_id][FILE]: for player in sorted(pj[OBSERVATIONS][obs_id][FILE].keys()): for media in pj[OBSERVATIONS][obs_id][FILE][player]: media_files.append("#{0}: {1}".format(player, media)) # independent variables indep_var = [] if INDEPENDENT_VARIABLES in pj[OBSERVATIONS][obs_id]: for var_label in indep_var_header: if var_label in pj[OBSERVATIONS][obs_id][ INDEPENDENT_VARIABLES]: indep_var.append(pj[OBSERVATIONS][obs_id] [INDEPENDENT_VARIABLES][var_label]) else: indep_var.append("") data.append([ obs_id, pj[OBSERVATIONS][obs_id]["date"], pj[OBSERVATIONS][obs_id] ["description"], subjects, ", ".join(media_files) ] + indep_var) if output_format in ["tsv", "csv", "html"]: try: with open(file_name, "wb") as f: f.write(str.encode(data.export(output_format))) except Exception: return False if output_format in ["ods", "xlsx", "xls"]: try: with open(file_name, "wb") as f: f.write(data.export(output_format)) except: return False return True
def export_aggregated_events(pj: dict, parameters: dict, obsId: str): """ export aggregated events Args: pj (dict): BORIS project parameters (dict): subjects, behaviors obsId (str): observation id Returns: tablib.Dataset: """ logging.debug(f"function: export aggregated events {parameters} {obsId}") interval = parameters["time"] start_time = parameters[START_TIME] end_time = parameters[END_TIME] data = tablib.Dataset() observation = pj[OBSERVATIONS][obsId] duration1 = [] # in seconds if observation[TYPE] in [MEDIA]: try: for mediaFile in observation[FILE][PLAYER1]: if MEDIA_INFO in observation: duration1.append(observation[MEDIA_INFO]["length"][mediaFile]) except Exception: duration1 = [] obs_length = project_functions.observation_total_length(pj[OBSERVATIONS][obsId]) if obs_length == Decimal("-1"): # media length not available interval = TIME_EVENTS logging.debug(f"obs_length: {obs_length}") ok, msg, connector = db_functions.load_aggregated_events_in_db(pj, parameters[SELECTED_SUBJECTS], [obsId], parameters[SELECTED_BEHAVIORS]) if connector is None: logging.critical(f"error when loading aggregated events in DB") return data # time cursor = connector.cursor() if interval == TIME_FULL_OBS: min_time = float(0) max_time = float(obs_length) if interval == TIME_EVENTS: try: min_time = float(pj[OBSERVATIONS][obsId][EVENTS][0][0]) except Exception: min_time = float(0) try: max_time = float(pj[OBSERVATIONS][obsId][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, obsId, min_time, min_time, max_time, )) cursor.execute("UPDATE aggregated_events SET stop = ? WHERE observation = ? AND stop > ? AND start BETWEEN ? AND ?", (max_time, obsId, max_time, min_time, max_time, )) cursor.execute("UPDATE aggregated_events SET start = ?, stop = ? WHERE observation = ? AND start < ? AND stop > ?", (min_time, max_time, obsId, min_time, max_time, )) cursor.execute("DELETE FROM aggregated_events WHERE observation = ? AND (start < ? AND stop < ?) OR (start > ? AND stop > ?)", (obsId, min_time, min_time, max_time, max_time, )) behavioral_category = project_functions.behavior_category(pj[ETHOGRAM]) for subject in parameters[SELECTED_SUBJECTS]: for behavior in parameters[SELECTED_BEHAVIORS]: cursor.execute("SELECT distinct modifiers FROM aggregated_events where subject=? AND behavior=? order by modifiers", (subject, behavior,)) rows_distinct_modifiers = list(x[0] for x in cursor.fetchall()) for distinct_modifiers in rows_distinct_modifiers: cursor.execute(("SELECT start, stop, type, modifiers, comment, comment_stop FROM aggregated_events " "WHERE subject = ? AND behavior = ? AND modifiers = ? ORDER by start"), (subject, behavior, distinct_modifiers)) rows = list(cursor.fetchall()) for row in rows: if observation[TYPE] in [MEDIA]: if duration1: mediaFileIdx = [idx1 for idx1, x in enumerate(duration1) if row["start"] >= sum(duration1[0:idx1])][-1] mediaFileString = observation[FILE][PLAYER1][mediaFileIdx] try: fpsString = observation[MEDIA_INFO]["fps"][observation[FILE][PLAYER1][mediaFileIdx]] except Exception: fpsString = "NA" else: try: if len(observation[FILE][PLAYER1]) == 1: mediaFileString = observation[FILE][PLAYER1][0] else: mediaFileString = "NA" except Exception: mediaFileString = "NA" fpsString = "NA" if observation[TYPE] in [LIVE]: mediaFileString = "LIVE" fpsString = "NA" if row["type"] == POINT: row_data = [] row_data.extend([obsId, observation["date"].replace("T", " "), mediaFileString, f"{obs_length:.3f}" if obs_length != Decimal("-1") else "NA", fpsString]) # independent variables if INDEPENDENT_VARIABLES in pj: for idx_var in utilities.sorted_keys(pj[INDEPENDENT_VARIABLES]): if pj[INDEPENDENT_VARIABLES][idx_var]["label"] in observation[INDEPENDENT_VARIABLES]: row_data.append(observation[INDEPENDENT_VARIABLES][pj[INDEPENDENT_VARIABLES][idx_var]["label"]]) else: row_data.append("") row_data.extend([subject, behavior, behavioral_category[behavior], row["modifiers"], POINT, "{0:.3f}".format(row["start"]), # start "{0:.3f}".format(row["stop"]), # stop "NA", # duration row["comment"], "" ]) data.append(row_data) if row["type"] == STATE: if idx % 2 == 0: row_data = [] row_data.extend([obsId, observation["date"].replace("T", " "), mediaFileString, f"{obs_length:.3f}" if obs_length != Decimal("-1") else "NA", fpsString]) # independent variables if INDEPENDENT_VARIABLES in pj: for idx_var in utilities.sorted_keys(pj[INDEPENDENT_VARIABLES]): if pj[INDEPENDENT_VARIABLES][idx_var]["label"] in observation[INDEPENDENT_VARIABLES]: row_data.append(observation[INDEPENDENT_VARIABLES][pj[INDEPENDENT_VARIABLES][idx_var]["label"]]) else: row_data.append("") row_data.extend([subject, behavior, behavioral_category[behavior], row["modifiers"], STATE, "{0:.3f}".format(row["start"]), "{0:.3f}".format(row["stop"]), "{0:.3f}".format(row["stop"] - row["start"]), row["comment"], row["comment_stop"] ]) data.append(row_data) return data
print() ''' if args.project_info: if not args.command: if pj: print("Project name: {}".format(pj[PROJECT_NAME])) print("Project date: {}".format(pj[PROJECT_DATE])) print("Project description: {}".format(pj[PROJECT_DESCRIPTION])) print() if not observations_id_list: print("Ethogram\n========") print("Number of behaviors in ethogram: {}".format( len(pj[ETHOGRAM]))) for idx in utilities.sorted_keys(pj[ETHOGRAM]): print("Code: {}\tDescription: {}\tType: {}".format( pj[ETHOGRAM][idx][BEHAVIOR_CODE], pj[ETHOGRAM][idx]["description"], pj[ETHOGRAM][idx][TYPE])) '''print("Behaviors: {}".format(",".join([pj[ETHOGRAM][k]["code"] for k in utilities.sorted_keys(pj[ETHOGRAM])])))''' print() print("Subjects\n========") print("Number of subjects: {}".format(len(pj[SUBJECTS]))) for idx in utilities.sorted_keys(pj[SUBJECTS]): print("Name: {}\tDescription: {}".format( pj[SUBJECTS][idx]["name"], pj[SUBJECTS][idx]["description"])) print()
def test_numeric_keys(self): r = utilities.sorted_keys({5: "a", 4: "7", 0: "z", 6: "a"}) assert r == ['0', '4', '5', '6']
def __init__(self, modifiers_str, parent=None): super(addModifierDialog, self).__init__(parent) self.setupUi(self) self.pbAddModifier.clicked.connect(self.addModifier) self.pbAddModifier.setIcon(QIcon(":/frame_forward")) self.pbAddSet.clicked.connect(self.addSet) self.pbRemoveSet.clicked.connect(self.removeSet) self.pbModifyModifier.clicked.connect(self.modifyModifier) self.pbModifyModifier.setIcon(QIcon(":/frame_backward")) self.pbMoveUp.clicked.connect(self.moveModifierUp) self.pbMoveDown.clicked.connect(self.moveModifierDown) self.pbMoveSetLeft.clicked.connect(self.moveSetLeft) self.pbMoveSetRight.clicked.connect(self.moveSetRight) self.pbRemoveModifier.clicked.connect(self.removeModifier) self.pbOK.clicked.connect(self.accept) self.pbCancel.clicked.connect(self.reject) self.leSetName.textChanged.connect(self.set_name_changed) self.cbType.currentIndexChanged.connect(self.type_changed) dummy_dict = eval(modifiers_str) if modifiers_str else {} modif_values = [] for idx in sorted_keys(dummy_dict): modif_values.append(dummy_dict[idx]) print("modifiers ", modif_values) self.modifiers_sets_dict = {} for modif in modif_values: self.modifiers_sets_dict[str(len( self.modifiers_sets_dict))] = copy.deepcopy(modif) self.tabWidgetModifiersSets.currentChanged.connect( self.tabWidgetModifiersSets_changed) # create tab for idx in sorted_keys(self.modifiers_sets_dict): self.tabWidgetModifiersSets.addTab(QWidget(), "Set #{}".format(int(idx) + 1)) ''' if idx != "0": self.tabWidgetModifiersSets.addTab(QWidget(), "Set #{}".format(int(idx) + 1)) ''' print(self.tabWidgetModifiersSets.currentIndex()) 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) # set first tab as active ''' if self.modifiers_sets_dict: self.leSetName.setText(self.modifiers_sets_dict["0"]["name"]) self.cbType.setCurrentIndex(self.modifiers_sets_dict["0"]["type"]) self.lwModifiers.addItems(self.modifiers_sets_dict["0"]["values"]) ''' self.tabMem = 0
print() ''' if args.project_info: if not args.command: if pj: print("Project name: {}".format(pj[PROJECT_NAME])) print("Project date: {}".format(pj[PROJECT_DATE])) print("Project description: {}".format(pj[PROJECT_DESCRIPTION])) print() if not observations_id_list: print("Ethogram\n========") print("Number of behaviors in ethogram: {}".format(len(pj[ETHOGRAM]))) for idx in utilities.sorted_keys(pj[ETHOGRAM]): print("Code: {}\tDescription: {}\tType: {}".format(pj[ETHOGRAM][idx][BEHAVIOR_CODE], pj[ETHOGRAM][idx]["description"], pj[ETHOGRAM][idx][TYPE] )) '''print("Behaviors: {}".format(",".join([pj[ETHOGRAM][k]["code"] for k in utilities.sorted_keys(pj[ETHOGRAM])])))''' print() print("Subjects\n========") print("Number of subjects: {}".format(len(pj[SUBJECTS]))) for idx in utilities.sorted_keys(pj[SUBJECTS]): print("Name: {}\tDescription: {}".format(pj[SUBJECTS][idx]["name"], pj[SUBJECTS][idx]["description"])) print() print("Observations\n============") print("Number of observations: {}".format(len(pj[OBSERVATIONS])))
def choose_obs_subj_behav_category(pj: dict, selected_observations: list, maxTime=0, flagShowIncludeModifiers: bool = True, flagShowExcludeBehaviorsWoEvents: bool = True, by_category: bool = False, show_time: bool = False, timeFormat: str = HHMMSS): """ show window for: - selection of subjects - selection of behaviors (based on selected subjects) - selection of time interval - inclusion/exclusion of modifiers - inclusion/exclusion of behaviors without events (flagShowExcludeBehaviorsWoEvents == True) Returns: dict: {"selected subjects": selectedSubjects, "selected behaviors": selectedBehaviors, "include modifiers": True/False, "exclude behaviors": True/False, "time": TIME_FULL_OBS / TIME_EVENTS / TIME_ARBITRARY_INTERVAL "start time": startTime, "end time": endTime } """ paramPanelWindow = param_panel.Param_panel() paramPanelWindow.resize(600, 500) paramPanelWindow.setWindowTitle("Select subjects and behaviors") paramPanelWindow.selectedObservations = selected_observations paramPanelWindow.pj = pj if not flagShowIncludeModifiers: paramPanelWindow.cbIncludeModifiers.setVisible(False) if not flagShowExcludeBehaviorsWoEvents: paramPanelWindow.cbExcludeBehaviors.setVisible(False) if by_category: paramPanelWindow.cbIncludeModifiers.setVisible(False) paramPanelWindow.cbExcludeBehaviors.setVisible(False) paramPanelWindow.frm_time_interval.setEnabled(False) if timeFormat == HHMMSS: paramPanelWindow.teStartTime.setTime(QTime.fromString("00:00:00.000", "hh:mm:ss.zzz")) paramPanelWindow.teEndTime.setTime(QTime.fromString(utilities.seconds2time(maxTime), "hh:mm:ss.zzz")) paramPanelWindow.dsbStartTime.setVisible(False) paramPanelWindow.dsbEndTime.setVisible(False) if timeFormat == S: paramPanelWindow.dsbStartTime.setValue(0.0) paramPanelWindow.dsbEndTime.setValue(maxTime) paramPanelWindow.teStartTime.setVisible(False) paramPanelWindow.teEndTime.setVisible(False) # hide max time if not maxTime: paramPanelWindow.frm_time.setVisible(False) if selected_observations: observedSubjects = project_functions.extract_observed_subjects(pj, selected_observations) else: # load all subjects and "No focal subject" observedSubjects = [pj[SUBJECTS][x][SUBJECT_NAME] for x in pj[SUBJECTS]] + [""] selectedSubjects = [] # add 'No focal subject' if "" in observedSubjects: selectedSubjects.append(NO_FOCAL_SUBJECT) paramPanelWindow.item = QListWidgetItem(paramPanelWindow.lwSubjects) paramPanelWindow.ch = QCheckBox() paramPanelWindow.ch.setText(NO_FOCAL_SUBJECT) paramPanelWindow.ch.stateChanged.connect(paramPanelWindow.cb_changed) paramPanelWindow.ch.setChecked(True) paramPanelWindow.lwSubjects.setItemWidget(paramPanelWindow.item, paramPanelWindow.ch) all_subjects = [pj[SUBJECTS][x][SUBJECT_NAME] for x in utilities.sorted_keys(pj[SUBJECTS])] for subject in all_subjects: paramPanelWindow.item = QListWidgetItem(paramPanelWindow.lwSubjects) paramPanelWindow.ch = QCheckBox() paramPanelWindow.ch.setText(subject) paramPanelWindow.ch.stateChanged.connect(paramPanelWindow.cb_changed) if subject in observedSubjects: selectedSubjects.append(subject) paramPanelWindow.ch.setChecked(True) paramPanelWindow.lwSubjects.setItemWidget(paramPanelWindow.item, paramPanelWindow.ch) logging.debug(f'selectedSubjects: {selectedSubjects}') if selected_observations: observedBehaviors = paramPanelWindow.extract_observed_behaviors(selected_observations, selectedSubjects) # not sorted else: # load all behaviors observedBehaviors = [pj[ETHOGRAM][x][BEHAVIOR_CODE] for x in pj[ETHOGRAM]] logging.debug(f'observed behaviors: {observedBehaviors}') if BEHAVIORAL_CATEGORIES in pj: categories = pj[BEHAVIORAL_CATEGORIES][:] # check if behavior not included in a category try: if "" in [pj[ETHOGRAM][idx][BEHAVIOR_CATEGORY] for idx in pj[ETHOGRAM] if BEHAVIOR_CATEGORY in pj[ETHOGRAM][idx]]: categories += [""] except Exception: categories = ["###no category###"] 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) for behavior in [pj[ETHOGRAM][x][BEHAVIOR_CODE] for x in utilities.sorted_keys(pj[ETHOGRAM])]: if ((categories == ["###no category###"]) or (behavior in [pj[ETHOGRAM][x][BEHAVIOR_CODE] for x in pj[ETHOGRAM] if BEHAVIOR_CATEGORY in pj[ETHOGRAM][x] and pj[ETHOGRAM][x][BEHAVIOR_CATEGORY] == category])): paramPanelWindow.item = QListWidgetItem(behavior) if behavior in observedBehaviors: paramPanelWindow.item.setCheckState(Qt.Checked) else: 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 not paramPanelWindow.exec_(): return {SELECTED_SUBJECTS: [], SELECTED_BEHAVIORS: []} selectedSubjects = paramPanelWindow.selectedSubjects selectedBehaviors = paramPanelWindow.selectedBehaviors logging.debug(f"selected subjects: {selectedSubjects}") logging.debug(f"selected behaviors: {selectedBehaviors}") if timeFormat == HHMMSS: startTime = utilities.time2seconds(paramPanelWindow.teStartTime.time().toString(HHMMSSZZZ)) endTime = utilities.time2seconds(paramPanelWindow.teEndTime.time().toString(HHMMSSZZZ)) if timeFormat == S: startTime = Decimal(paramPanelWindow.dsbStartTime.value()) endTime = Decimal(paramPanelWindow.dsbEndTime.value()) if startTime > endTime: QMessageBox.warning(None, programName, "The start time is after the end time", QMessageBox.Ok | QMessageBox.Default, QMessageBox.NoButton) return {SELECTED_SUBJECTS: [], SELECTED_BEHAVIORS: []} if paramPanelWindow.rb_full.isChecked(): time_param = TIME_FULL_OBS if paramPanelWindow.rb_limit.isChecked(): time_param = TIME_EVENTS if paramPanelWindow.rb_interval.isChecked(): time_param = TIME_ARBITRARY_INTERVAL return {SELECTED_SUBJECTS: selectedSubjects, SELECTED_BEHAVIORS: selectedBehaviors, INCLUDE_MODIFIERS: paramPanelWindow.cbIncludeModifiers.isChecked(), EXCLUDE_BEHAVIORS: paramPanelWindow.cbExcludeBehaviors.isChecked(), "time": time_param, START_TIME: startTime, END_TIME: endTime }
def export_aggregated_events(pj, parameters, obsId): """ export aggregated events Args: pj (dict): BORIS project parameters (dict): subjects, behaviors obsId (str): observation id Returns: tablib.Dataset: """ data = tablib.Dataset() observation = pj[OBSERVATIONS][obsId] duration1 = [] # in seconds if observation[TYPE] in [MEDIA]: try: for mediaFile in observation[FILE][PLAYER1]: if "media_info" in observation: duration1.append( observation["media_info"]["length"][mediaFile]) except: duration1 = [] total_length = "{0:.3f}".format( project_functions.observation_total_length(observation)) ok, msg, connector = db_functions.load_aggregated_events_in_db( pj, parameters["selected subjects"], [obsId], parameters["selected behaviors"]) if not ok: data cursor = connector.cursor() for subject in parameters["selected subjects"]: for behavior in parameters["selected behaviors"]: cursor.execute( "select distinct modifiers from aggregated_events where subject=? AND behavior=? order by modifiers", ( subject, behavior, )) rows_distinct_modifiers = list(x[0].strip() for x in cursor.fetchall()) for distinct_modifiers in rows_distinct_modifiers: cursor.execute(( "SELECT start, stop, type, modifiers, comment, comment_stop FROM aggregated_events " "WHERE subject = ? AND behavior = ? AND modifiers = ? ORDER by start" ), (subject, behavior, distinct_modifiers)) rows = list(cursor.fetchall()) for row in rows: if observation[TYPE] in [MEDIA]: if duration1: mediaFileIdx = [ idx1 for idx1, x in enumerate(duration1) if row["start"] >= sum(duration1[0:idx1]) ][-1] mediaFileString = observation[FILE][PLAYER1][ mediaFileIdx] fpsString = observation["media_info"]["fps"][ observation[FILE][PLAYER1][mediaFileIdx]] else: mediaFileString = "-" fpsString = "NA" if observation[TYPE] in [LIVE]: mediaFileString = "LIVE" fpsString = "NA" #if POINT in project_functions.event_type(behavior, pj[ETHOGRAM]): if row["type"] == POINT: row_data = [] row_data.extend([ obsId, observation["date"].replace("T", " "), mediaFileString, total_length, fpsString ]) # independent variables if INDEPENDENT_VARIABLES in pj: for idx_var in utilities.sorted_keys( pj[INDEPENDENT_VARIABLES]): if pj[INDEPENDENT_VARIABLES][idx_var][ "label"] in observation[ INDEPENDENT_VARIABLES]: row_data.append( observation[INDEPENDENT_VARIABLES][ pj[INDEPENDENT_VARIABLES][idx_var] ["label"]]) else: row_data.append("") row_data.extend([ subject, behavior, row["modifiers"].strip(), POINT, "{0:.3f}".format(row["start"]), # start "{0:.3f}".format(row["stop"]), # stop "NA", # duration row["comment"], "" ]) data.append(row_data) #if STATE in project_functions.event_type(behavior, pj[ETHOGRAM]): if row["type"] == STATE: if idx % 2 == 0: row_data = [] row_data.extend([ obsId, observation["date"].replace("T", " "), mediaFileString, total_length, fpsString ]) # independent variables if INDEPENDENT_VARIABLES in pj: for idx_var in utilities.sorted_keys( pj[INDEPENDENT_VARIABLES]): if pj[INDEPENDENT_VARIABLES][idx_var][ "label"] in observation[ INDEPENDENT_VARIABLES]: row_data.append( observation[INDEPENDENT_VARIABLES] [pj[INDEPENDENT_VARIABLES][idx_var] ["label"]]) else: row_data.append("") row_data.extend([ subject, behavior, row["modifiers"].strip(), STATE, "{0:.3f}".format(row["start"]), "{0:.3f}".format(row["stop"]), "{0:.3f}".format(row["stop"] - row["start"]), row["comment"], row["comment_stop"] ]) data.append(row_data) return data