def export_events(parameters, obsId, observation, ethogram, file_name, output_format): """ export events Args: parameters (dict): subjects, behaviors obsId (str): observation id observation (dict): observation ethogram (dict): ethogram of project file_name (str): file name for exporting events output_format (str): output for exporting events Returns: bool: result: True if OK else False str: error message """ total_length = f"{project_functions.observation_total_length(observation):.3f}" eventsWithStatus = project_functions.events_start_stop( ethogram, observation[EVENTS]) # check max number of modifiers max_modifiers = 0 for event in eventsWithStatus: for c in pj_events_fields: if c == "modifier" and event[pj_obs_fields[c]]: max_modifiers = max(max_modifiers, len(event[pj_obs_fields[c]].split("|"))) # media file number mediaNb = 0 if observation["type"] == MEDIA: for player in observation[FILE]: mediaNb += len(observation[FILE][player]) rows = [] # observation id rows.append(["Observation id", obsId]) rows.append([""]) # media file name if observation["type"] in [MEDIA]: rows.append(["Media file(s)"]) elif observation["type"] in [LIVE]: rows.append(["Live observation"]) else: rows.append(["?"]) rows.append([""]) if observation[TYPE] in [MEDIA]: for player in sorted(list(observation[FILE].keys())): for media in observation[FILE][player]: rows.append([f"Player #{player}", media]) rows.append([""]) # date if "date" in observation: rows.append( ["Observation date", observation["date"].replace("T", " ")]) rows.append([""]) # description if "description" in observation: rows.append( ["Description", utilities.eol2space(observation["description"])]) rows.append([""]) # time offset if "time offset" in observation: rows.append(["Time offset (s)", observation["time offset"]]) rows.append([""]) # independent variables if INDEPENDENT_VARIABLES in observation: rows.extend([["independent variables"], ["variable", "value"]]) for variable in observation[INDEPENDENT_VARIABLES]: rows.append( [variable, observation[INDEPENDENT_VARIABLES][variable]]) rows.append([""]) # write table header col = 0 header = ["Time"] header.extend(["Media file path", "Total length", "FPS"]) header.extend(["Subject", "Behavior", "Behavioral category"]) behavioral_category = project_functions.behavior_category(ethogram) for x in range(1, max_modifiers + 1): header.append(f"Modifier {x}") header.extend(["Comment", "Status"]) rows.append(header) duration1 = [] # in seconds if observation["type"] in [MEDIA]: try: for mediaFile in observation[FILE][PLAYER1]: duration1.append(observation[MEDIA_INFO]["length"][mediaFile]) except KeyError: pass for event in eventsWithStatus: if (((event[SUBJECT_EVENT_FIELD] in parameters["selected subjects"]) or (event[SUBJECT_EVENT_FIELD] == "" and NO_FOCAL_SUBJECT in parameters["selected subjects"])) and (event[BEHAVIOR_EVENT_FIELD] in parameters["selected behaviors"])): fields = [] fields.append( utilities.intfloatstr(str(event[EVENT_TIME_FIELD_IDX]))) if observation["type"] in [MEDIA]: time_ = event[EVENT_TIME_FIELD_IDX] - observation[TIME_OFFSET] if time_ < 0: time_ = 0 if duration1: mediaFileIdx = [ idx1 for idx1, x in enumerate(duration1) if time_ >= sum(duration1[0:idx1]) ][-1] fields.append(observation[FILE][PLAYER1][mediaFileIdx]) fields.append(total_length) # FPS try: fields.append(observation[MEDIA_INFO]["fps"][ observation[FILE][PLAYER1][mediaFileIdx]]) # fps except KeyError: fields.append("NA") else: fields.append("NA") # media file fields.append("NA") # FPS if observation["type"] in [LIVE]: fields.append(LIVE) # media fields.append(total_length) # total length fields.append("NA") # FPS fields.append(event[EVENT_SUBJECT_FIELD_IDX]) fields.append(event[EVENT_BEHAVIOR_FIELD_IDX]) # behavioral category try: behav_category = behavioral_category[ event[EVENT_BEHAVIOR_FIELD_IDX]] except Exception: behav_category = "" fields.append(behav_category) # modifiers if max_modifiers: modifiers = event[EVENT_MODIFIER_FIELD_IDX].split("|") while len(modifiers) < max_modifiers: modifiers.append("") for m in modifiers: fields.append(m) # comment fields.append(event[EVENT_COMMENT_FIELD_IDX].replace( os.linesep, " ")) # status fields.append(event[-1]) rows.append(fields) maxLen = max([len(r) for r in rows]) data = tablib.Dataset() data.title = utilities.safe_xl_worksheet_title(obsId, output_format) ''' if output_format in ["xls", "xlsx"]: for forbidden_char in EXCEL_FORBIDDEN_CHARACTERS: data.title = data.title.replace(forbidden_char, " ") if output_format in ["xls"]: if len(data.title) > 31: data.title = data.title[0:31] ''' for row in rows: data.append(utilities.complete(row, maxLen)) r, msg = dataset_write(data, file_name, output_format) return r, msg
def pbSave_clicked(self): """ save time budget analysis results in TSV, CSV, ODS, XLS format """ def complete(l: list, max_: int) -> list: """ complete list with empty string until len = max Args: l (list): list to complete max_ (int): length of the returned list Returns: list: completed list """ while len(l) < max_: l.append("") return l logging.debug("save time budget results to file") extended_file_formats = [ "Tab Separated Values (*.tsv)", "Comma Separated Values (*.csv)", "Open Document Spreadsheet ODS (*.ods)", "Microsoft Excel Spreadsheet XLSX (*.xlsx)", "Legacy Microsoft Excel Spreadsheet XLS (*.xls)", "HTML (*.html)" ] file_formats = ["tsv", "csv", "ods", "xlsx", "xls", "html"] file_name, filter_ = QFileDialog().getSaveFileName( self, "Save Time budget analysis", "", ";;".join(extended_file_formats)) if not file_name: return outputFormat = file_formats[extended_file_formats.index(filter_)] if pathlib.Path(file_name).suffix != "." + outputFormat: file_name = str(pathlib.Path(file_name)) + "." + outputFormat # check if file with new extension already exists if pathlib.Path(file_name).is_file(): if dialog.MessageDialog( programName, f"The file {file_name} already exists.", [CANCEL, OVERWRITE]) == CANCEL: return rows = [] # 1 observation if (self.lw.count() == 1 and self.config_param.get( TIME_BUDGET_FORMAT, DEFAULT_TIME_BUDGET_FORMAT) == COMPACT_TIME_BUDGET_FORMAT): col1, indep_var_label = [], [] # add obs id col1.append(self.lw.item(0).text()) # add obs date col1.append(self.pj[OBSERVATIONS][self.lw.item(0).text()].get( "date", "")) # description col1.append( utilities.eol2space( self.pj[OBSERVATIONS][self.lw.item(0).text()].get( DESCRIPTION, ""))) header = ["Observation id", "Observation date", "Description"] # indep var for var in self.pj[OBSERVATIONS][self.lw.item(0).text()].get( INDEPENDENT_VARIABLES, {}): indep_var_label.append(var) col1.append(self.pj[OBSERVATIONS][self.lw.item(0).text()] [INDEPENDENT_VARIABLES][var]) header.extend(indep_var_label) col1.extend([ f"{self.min_time:0.3f}", f"{self.max_time:0.3f}", f"{self.max_time - self.min_time:0.3f}" ]) header.extend([ "Time budget start", "Time budget stop", "Time budget duration" ]) for col_idx in range(self.twTB.columnCount()): header.append(self.twTB.horizontalHeaderItem(col_idx).text()) rows.append(header) for row_idx in range(self.twTB.rowCount()): values = [] for col_idx in range(self.twTB.columnCount()): values.append( intfloatstr(self.twTB.item(row_idx, col_idx).text())) rows.append(col1 + values) else: # observations list rows.append(["Observations:"]) for idx in range(self.lw.count()): rows.append([""]) rows.append(["Observation id", self.lw.item(idx).text()]) rows.append([ "Observation date", self.pj[OBSERVATIONS][self.lw.item(idx).text()].get( "date", "") ]) rows.append([ "Description", utilities.eol2space( self.pj[OBSERVATIONS][self.lw.item(idx).text()].get( DESCRIPTION, "")) ]) if INDEPENDENT_VARIABLES in self.pj[OBSERVATIONS][self.lw.item( idx).text()]: rows.append(["Independent variables:"]) for var in self.pj[OBSERVATIONS][self.lw.item( idx).text()][INDEPENDENT_VARIABLES]: rows.append([ var, self.pj[OBSERVATIONS][self.lw.item( idx).text()][INDEPENDENT_VARIABLES][var] ]) if self.excluded_behaviors_list.text(): s1, s2 = self.excluded_behaviors_list.text().split(": ") rows.extend([[""], [s1] + s2.split(", ")]) rows.extend([[""], [""], ["Time budget:"]]) # write header header = [] for col_idx in range(self.twTB.columnCount()): header.append(self.twTB.horizontalHeaderItem(col_idx).text()) rows.append(header) rows.append([""]) for row in range(self.twTB.rowCount()): values = [] for col_idx in range(self.twTB.columnCount()): values.append( intfloatstr(self.twTB.item(row, col_idx).text())) rows.append(values) max_row_length = max([len(r) for r in rows]) data = tablib.Dataset() data.title = "Time budget" for row in rows: data.append(complete(row, max_row_length)) if outputFormat in ["tsv", "csv", "html"]: with open(file_name, "wb") as f: f.write(str.encode(data.export(outputFormat))) return if outputFormat in ["ods", "xlsx", "xls"]: with open(file_name, "wb") as f: f.write(data.export(outputFormat)) return
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_r(self): assert utilities.eol2space("aaa\rbbb") == "aaa bbb"
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 export_events(parameters, obsId, observation, ethogram, file_name, output_format): """ export events Args: parameters (dict): subjects, behaviors obsId (str): observation id observation (dict): observation ethogram (dict): ethogram of project file_name (str): file name for exporting events output_format (str): output for exporting events Returns: bool: result: True if OK else False str: error message """ total_length = "{0:.3f}".format(project_functions.observation_total_length(observation)) eventsWithStatus = project_functions.events_start_stop(ethogram, observation[EVENTS]) # check max number of modifiers max_modifiers = 0 for event in eventsWithStatus: for c in pj_events_fields: if c == "modifier" and event[pj_obs_fields[c]]: max_modifiers = max(max_modifiers, len(event[pj_obs_fields[c]].split("|"))) # media file number mediaNb = 0 if observation["type"] == MEDIA: for player in observation[FILE]: mediaNb += len(observation[FILE][player]) rows = [] # observation id rows.append(["Observation id", obsId]) rows.append([""]) # media file name if observation["type"] in [MEDIA]: rows.append(["Media file(s)"]) elif observation["type"] in [LIVE]: rows.append(["Live observation"]) else: rows.append(["?"]) rows.append([""]) if observation[TYPE] in [MEDIA]: for player in sorted(list(observation[FILE].keys())): for media in observation[FILE][player]: rows.append(["Player #{0}".format(player), media]) rows.append([""]) # date if "date" in observation: rows.append(["Observation date", observation["date"].replace("T", " ")]) rows.append([""]) # description if "description" in observation: rows.append(["Description", utilities.eol2space(observation["description"])]) rows.append([""]) # time offset if "time offset" in observation: rows.append(["Time offset (s)", observation["time offset"]]) rows.append([""]) # independent variables if INDEPENDENT_VARIABLES in observation: rows.extend([["independent variables"], ["variable", "value"]]) for variable in observation[INDEPENDENT_VARIABLES]: rows.append([variable, observation[INDEPENDENT_VARIABLES][variable]]) rows.append([""]) # write table header col = 0 header = ["Time"] header.extend(["Media file path", "Total length", "FPS"]) header.extend(["Subject", "Behavior", "Behavioral category"]) behavioral_category = project_functions.behavior_category(ethogram) for x in range(1, max_modifiers + 1): header.append("Modifier {}".format(x)) header.extend(["Comment", "Status"]) rows.append(header) duration1 = [] # in seconds if observation["type"] in [MEDIA]: try: for mediaFile in observation[FILE][PLAYER1]: duration1.append(observation[MEDIA_INFO]["length"][mediaFile]) except KeyError: pass for event in eventsWithStatus: if (((event[SUBJECT_EVENT_FIELD] in parameters["selected subjects"]) or (event[SUBJECT_EVENT_FIELD] == "" and NO_FOCAL_SUBJECT in parameters["selected subjects"])) and (event[BEHAVIOR_EVENT_FIELD] in parameters["selected behaviors"])): fields = [] fields.append(utilities.intfloatstr(str(event[EVENT_TIME_FIELD_IDX]))) if observation["type"] in [MEDIA]: time_ = event[EVENT_TIME_FIELD_IDX] - observation[TIME_OFFSET] if time_ < 0: time_ = 0 if duration1: mediaFileIdx = [idx1 for idx1, x in enumerate(duration1) if time_ >= sum(duration1[0:idx1])][-1] fields.append(observation[FILE][PLAYER1][mediaFileIdx]) fields.append(total_length) # FPS try: fields.append(observation[MEDIA_INFO]["fps"][observation[FILE][PLAYER1][mediaFileIdx]]) # fps except KeyError: fields.append("NA") else: fields.append("NA") # media file fields.append("NA") # FPS if observation["type"] in [LIVE]: fields.append(LIVE) # media fields.append(total_length) # total length fields.append("NA") # FPS fields.append(event[EVENT_SUBJECT_FIELD_IDX]) fields.append(event[EVENT_BEHAVIOR_FIELD_IDX]) # behavioral category try: behav_category = behavioral_category[event[EVENT_BEHAVIOR_FIELD_IDX]] except Exception: behav_category = "" fields.append(behav_category) # modifiers if max_modifiers: modifiers = event[EVENT_MODIFIER_FIELD_IDX].split("|") while len(modifiers) < max_modifiers: modifiers.append("") for m in modifiers: fields.append(m) # comment fields.append(event[EVENT_COMMENT_FIELD_IDX].replace(os.linesep, " ")) # status fields.append(event[-1]) rows.append(fields) maxLen = max([len(r) for r in rows]) data = tablib.Dataset() data.title = obsId # check if worksheet name will be > 31 char if output_format in ["xls", "xlsx"]: for forbidden_char in EXCEL_FORBIDDEN_CHARACTERS: data.title = data.title.replace(forbidden_char, " ") if output_format in ["xls"]: if len(data.title) > 31: data.title = data.title[0:31] for row in rows: data.append(utilities.complete(row, maxLen)) r, msg = dataset_write(data, file_name, output_format) return r, msg