create_measurement_list_SetupD.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238
  1. #!/usr/bin/env python3
  2. # -*- coding: utf-8 -*-
  3. """
  4. Log2List for setupD
  5. created June2022, based on pervious VTK2021 log2list
  6. @author: galizia
  7. Plan:
  8. 1) read a .log file (Till Photonics), create a .lst file for this animal
  9. 2) read a .txt file that comes from PAL/Chronos (Barcode Reader), add odor information to .lst file
  10. Structure:
  11. Data should be in subfolder 01_DATA
  12. List file goes into subfolder 02_LISTS
  13. Naming:
  14. Within Till, names are ODOR-CONC_NUMBER,
  15. e.g. ISOE-5_10
  16. """
  17. from view.python_core.measurement_list import MeasurementList
  18. from view.python_core.measurement_list.importers import get_importer_class
  19. from view.python_core.flags import FlagsManager
  20. from collections import OrderedDict
  21. import pandas as pd
  22. import logging
  23. logging.basicConfig(level=logging.INFO)
  24. # ------------------- Some parameters about experimental setup, data structure and output file type --------------------
  25. # 3 for single wavelength Till Photonics Measurements
  26. # 4 for two wavelength Till Photonics Measurements
  27. # 20 for Zeiss Confocal Measurements
  28. LE_loadExp = 3
  29. # Mother of all Folders of your dataset
  30. # On Windows, if you copy paths from the file explorer, make sure the string below is always of the form r"......"
  31. #STG_STG_STG_MotherOfAllFolders = r"/home/ajay/Nextcloud/VTK_2021/bee/HS_210521_test"
  32. #STG_STG_STG_MotherOfAllFolders = r"/Users/galizia/Documents/DATA/VTK_test/YT_VTK"
  33. #STG_STG_STG_MotherOfAllFolders = r"/Users/galizia/Documents/DATA/HS_210521_test"
  34. STG_MotherOfAllFolders = r"/Users/galizia/Nextcloud/VTK_2021/Bee_alarm_2022" # 01_DATA
  35. # path of the "Data" folder in VIEW organization containing the data
  36. # On Windows, if you copy paths from the file explorer, make sure the string below is always of the form r"......"
  37. STG_Datapath = r"01_DATA"
  38. # path of the "Lists" folder in VIEW organization containing the list files
  39. # On Windows, if you copy paths from the file explorer, make sure the string below is always of the form r"......"
  40. STG_OdorInfoPath = r"02_LISTS"
  41. # Choose measurement list output extension among ".lst", ".lst.xlsx", ".settings.xlsx"
  42. # VIEW does not support writing .xls list files anymore (nonetheless, it can read them and revise/update them to .xlsx)
  43. measurement_output_extension = ".lst.xlsx"
  44. # ------------------- A dictionary containing default values for metadata.----------------------------------------------
  45. # ------------------- Only metadata included in this dictionary will be written ----------------------------------------
  46. # ----Note that columns of the output measeurement list files will have the same order as below.------------------------
  47. default_values = OrderedDict()
  48. default_values['Measu'] = 0 # unique identifier for each line, corresponds to item in TILL photonics log file
  49. default_values['Label'] = "none"
  50. default_values['Odour'] = 'odor?' # stimulus name, maybe extracted from label in the function "custom_func" below
  51. default_values['OConc'] = 0 # odor concentration, maybe extracted from label in the function "custom_func" below
  52. default_values['Analyze'] = -1 # whether to analyze in VIEWoff. Default 1
  53. default_values['Cycle'] = 0 # how many ms per frame
  54. default_values['DBB1'] = 'none' # file name of raw data
  55. default_values['UTC'] = 0 # recording time, extracted from file
  56. default_values['PxSzX'] = '4.6' # um per pixel, 1.5625 for 50x air objective, measured by Hanna Schnell July 2017 on Till vision system, with a binning of 8
  57. default_values['PxSzY'] = '4.6' # um per pixel, 1.5625 for 50x air objective, measured by Hanna Schnell July 2017 on Till vision system, with a binning of 8
  58. default_values['Lambda'] = 0 # wavelength of stimulus. In TILL, from .log file, In Zeiss LSM, from .lsm file
  59. # These will be automatically filed for LE_loadExp=4
  60. default_values['dbb2'] = 'none' # file name of raw data in dual wavelength recordings (FURA)
  61. # To include more columns, uncomment entries below and specify a default value.
  62. # #
  63. # block for first stimulus
  64. # default_values['StimON'] = -1 # stimulus onset, unit: frames, count starts at frame 1.
  65. # default_values['StimOFF'] = -1 # stimulus offset, unit: frames, count starts at frame 1.
  66. # default_values['StimLen'] = 0 # stimulus onset in ms from beginning - alternative to StimON
  67. # default_values['StimONms'] = -1 # stimulus length in ms - alternative to StimOFF
  68. # #
  69. # block for second stimulus
  70. # default_values['Stim2ON'] = 0 # stimulus onset, unit: frames, count starts at frame 1.
  71. # default_values['Stim2OFF'] = 0 # stimulus offset, unit: frames, count starts at frame 1.
  72. # default_values['Stim2Len'] = 0 # stimulus onset in ms from beginning - alternative to StimON
  73. # default_values['Stim2ONms'] = -1 # stimulus length in ms - alternative to StimOFF
  74. # #
  75. # default_values['Age'] = -1
  76. # default_values['Sex'] = 'o'
  77. # default_values['Side'] = 'none'
  78. # default_values['Comment'] = 'none'
  79. # #
  80. # default_values['MTime'] = 0
  81. # default_values['Control'] = 0
  82. # default_values['Pharma'] = 'none'
  83. # default_values['PhTime'] = 0
  84. # default_values['PhConc'] = 0
  85. # default_values['ShiftX'] = 0
  86. # default_values['ShiftY'] = 0
  87. # default_values['StimISI'] = 0
  88. # default_values['setting'] = 'none'
  89. # default_values['dbb3'] = 'none'
  90. # default_values['PosZ'] = 0
  91. # default_values['Countl'] = 0
  92. # default_values['slvFlip'] = 0
  93. # ----------------------------------------------------------------------------------------------------------------------
  94. # ----------------- A function used to modify list entries after automatic parsing of metadata -------------------------
  95. # ----------------- This function indicates what needs to be done for a row --------------------------------------------
  96. # ----------------- The same is internally applied to all rows of the measurement list----------------------------------
  97. def get_odorinfo_from_label(label):
  98. # format for label is: concentration to the right, with a minus sign
  99. # Odor next to it, separated by underscore
  100. # IMPERARIVE: only ONE "-"
  101. # format ISOE-2_13 or MOL_18
  102. # is the information for a concentration present? Detect "-"
  103. parts = label.split("_")
  104. # take first part
  105. parts = parts[0]
  106. parts = parts.split("-")
  107. odor = parts[0]
  108. if len(parts) > 1:
  109. concentration = '-' + parts[1]
  110. else:
  111. concentration = '0'
  112. return [odor, concentration]
  113. def custom_func(list_row: pd.Series, animal_tag: str) -> pd.Series:
  114. list_row['StimON'] = 24
  115. list_row['StimLen'] = 1000
  116. list_row['Stim2ON'] = 36
  117. list_row['Stim2Len'] = 1000
  118. list_row['Comment'] = 'AlarmPheromone'
  119. list_row['Line'] = 'bee'
  120. # Examples:
  121. # list_row["StimON"] = 25
  122. (list_row["Odour"],list_row["OConc"]) = get_odorinfo_from_label(list_row["Label"])
  123. # if list_row["Measu"]
  124. # get Odor from another file based on the value of <animal_tag> and list_row["Label"]
  125. return list_row
  126. # ----------------------------------------------------------------------------------------------------------------------
  127. # ------------------ A function defining the criteria for excluding measurements ---------------------------------------
  128. # ------------------ Currently applicable only for tillvision setups ---------------------------------------------------
  129. def measurement_filter(s):
  130. # exclude blocks that have in the name "Snapshot" or "Delta"
  131. # or that do not have any "_"
  132. name = s["Label"]
  133. label_not_okay = name.count('Snapshot') > 0 or name.count('Delta') > 0 or name.count('_') < 1
  134. label_okay = not label_not_okay
  135. # exclude blocks with less than two frames or no calibration
  136. atleast_two_frames = False
  137. if type(s["Timing_ms"]) is str:
  138. if len(s["Timing_ms"].split(' ')) >= 2 and s["Timing_ms"] != "(No calibration available)":
  139. atleast_two_frames = True
  140. return label_okay and atleast_two_frames
  141. # ______________________________________________________________________________________________________________________
  142. # ------------------ names of columns that will be overwritten by old values -------------------------------------------
  143. # ------ these will only be used if a measurement list file with the same name as current output file exists -----------
  144. overwrite_old_values = ["Line", "PxSzX", "PxSzY", "Age", "Sex", "Prefer",
  145. "Comment", "Analyze", "Odour", "OConc"]
  146. # ______________________________________________________________________________________________________________________
  147. if __name__ == "__main__":
  148. # initialize a FlagsManager object with values specified above
  149. flags = FlagsManager()
  150. flags.update_flags({"STG_MotherOfAllFolders": STG_MotherOfAllFolders,
  151. "STG_OdorInfoPath": STG_OdorInfoPath,
  152. "STG_Datapath": STG_Datapath})
  153. # initialize importer
  154. importer_class = get_importer_class(LE_loadExp)
  155. importer = importer_class(default_values)
  156. # open a dialog for choosing raw data files
  157. # this returns a dictionary where keys are animal tags (STG_ReportTag) and
  158. # values are lists of associated raw data files
  159. animal_tag_raw_data_mapping = importer.ask_for_files(default_dir=flags["STG_Datapath"])
  160. # make sure some files were chosen
  161. assert len(animal_tag_raw_data_mapping) > 0, IOError("No files were chosen!")
  162. for animal_tag, raw_data_files in animal_tag_raw_data_mapping.items():
  163. # automatically parse metadata
  164. metadata_df = importer.import_metadata(raw_data_files=raw_data_files,
  165. measurement_filter=measurement_filter)
  166. # inform user if no usable measurements were found
  167. if metadata_df.shape[0] == 0:
  168. logging.info(f"No usable measurements we found among the files "
  169. f"chosen for the animal {animal_tag}. Not creating a list file")
  170. else:
  171. # create a new Measurement list object from parsed metadata
  172. measurement_list = MeasurementList.create_from_df(LE_loadExp=LE_loadExp,
  173. df=metadata_df)
  174. # apply custom modifications
  175. measurement_list.update_from_custom_func(custom_func=custom_func, animal_tag=animal_tag)
  176. # set anaylze to 0 if raw data files don't exist
  177. flags.update_flags({"STG_ReportTag": animal_tag})
  178. measurement_list.sanitize(flags=flags,
  179. data_file_extensions=importer.movie_data_extensions)
  180. # construct the name of the output file
  181. out_file = f"{flags.get_lst_file_stem()}{measurement_output_extension}"
  182. # write measurement file to list
  183. measurement_list.write_to_list_file(lst_fle=out_file, columns2write=default_values.keys(),
  184. overwrite_old_values=overwrite_old_values)