np-services 0.1.59__py3-none-any.whl → 0.1.73__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (207) hide show
  1. np_services/__init__.py +8 -8
  2. np_services/open_ephys.py +377 -378
  3. np_services/protocols.py +185 -185
  4. np_services/proxies.py +1489 -1488
  5. np_services/resources/mvr_connector.py +260 -260
  6. np_services/resources/zro.py +325 -325
  7. np_services/scripts/pretest.py +170 -73
  8. np_services/stim_computer_theme_changer.py +41 -41
  9. np_services/utils.py +167 -167
  10. {np_services-0.1.59.dist-info → np_services-0.1.73.dist-info}/METADATA +7 -8
  11. np_services-0.1.73.dist-info/RECORD +15 -0
  12. {np_services-0.1.59.dist-info → np_services-0.1.73.dist-info}/WHEEL +2 -1
  13. {np_services-0.1.59.dist-info → np_services-0.1.73.dist-info}/entry_points.txt +1 -1
  14. np_services-0.1.73.dist-info/top_level.txt +1 -0
  15. np_services/.mypy_cache/.gitignore +0 -2
  16. np_services/.mypy_cache/3.9/@plugins_snapshot.json +0 -1
  17. np_services/.mypy_cache/3.9/__future__.data.json +0 -1
  18. np_services/.mypy_cache/3.9/__future__.meta.json +0 -1
  19. np_services/.mypy_cache/3.9/_ast.data.json +0 -1
  20. np_services/.mypy_cache/3.9/_ast.meta.json +0 -1
  21. np_services/.mypy_cache/3.9/_codecs.data.json +0 -1
  22. np_services/.mypy_cache/3.9/_codecs.meta.json +0 -1
  23. np_services/.mypy_cache/3.9/_collections_abc.data.json +0 -1
  24. np_services/.mypy_cache/3.9/_collections_abc.meta.json +0 -1
  25. np_services/.mypy_cache/3.9/_ctypes.data.json +0 -1
  26. np_services/.mypy_cache/3.9/_ctypes.meta.json +0 -1
  27. np_services/.mypy_cache/3.9/_decimal.data.json +0 -1
  28. np_services/.mypy_cache/3.9/_decimal.meta.json +0 -1
  29. np_services/.mypy_cache/3.9/_random.data.json +0 -1
  30. np_services/.mypy_cache/3.9/_random.meta.json +0 -1
  31. np_services/.mypy_cache/3.9/_socket.data.json +0 -1
  32. np_services/.mypy_cache/3.9/_socket.meta.json +0 -1
  33. np_services/.mypy_cache/3.9/_thread.data.json +0 -1
  34. np_services/.mypy_cache/3.9/_thread.meta.json +0 -1
  35. np_services/.mypy_cache/3.9/_typeshed/__init__.data.json +0 -1
  36. np_services/.mypy_cache/3.9/_typeshed/__init__.meta.json +0 -1
  37. np_services/.mypy_cache/3.9/_warnings.data.json +0 -1
  38. np_services/.mypy_cache/3.9/_warnings.meta.json +0 -1
  39. np_services/.mypy_cache/3.9/_weakref.data.json +0 -1
  40. np_services/.mypy_cache/3.9/_weakref.meta.json +0 -1
  41. np_services/.mypy_cache/3.9/_weakrefset.data.json +0 -1
  42. np_services/.mypy_cache/3.9/_weakrefset.meta.json +0 -1
  43. np_services/.mypy_cache/3.9/_winapi.data.json +0 -1
  44. np_services/.mypy_cache/3.9/_winapi.meta.json +0 -1
  45. np_services/.mypy_cache/3.9/abc.data.json +0 -1
  46. np_services/.mypy_cache/3.9/abc.meta.json +0 -1
  47. np_services/.mypy_cache/3.9/array.data.json +0 -1
  48. np_services/.mypy_cache/3.9/array.meta.json +0 -1
  49. np_services/.mypy_cache/3.9/atexit.data.json +0 -1
  50. np_services/.mypy_cache/3.9/atexit.meta.json +0 -1
  51. np_services/.mypy_cache/3.9/builtins.data.json +0 -1
  52. np_services/.mypy_cache/3.9/builtins.meta.json +0 -1
  53. np_services/.mypy_cache/3.9/codecs.data.json +0 -1
  54. np_services/.mypy_cache/3.9/codecs.meta.json +0 -1
  55. np_services/.mypy_cache/3.9/collections/__init__.data.json +0 -1
  56. np_services/.mypy_cache/3.9/collections/__init__.meta.json +0 -1
  57. np_services/.mypy_cache/3.9/collections/abc.data.json +0 -1
  58. np_services/.mypy_cache/3.9/collections/abc.meta.json +0 -1
  59. np_services/.mypy_cache/3.9/contextlib.data.json +0 -1
  60. np_services/.mypy_cache/3.9/contextlib.meta.json +0 -1
  61. np_services/.mypy_cache/3.9/ctypes/__init__.data.json +0 -1
  62. np_services/.mypy_cache/3.9/ctypes/__init__.meta.json +0 -1
  63. np_services/.mypy_cache/3.9/datetime.data.json +0 -1
  64. np_services/.mypy_cache/3.9/datetime.meta.json +0 -1
  65. np_services/.mypy_cache/3.9/decimal.data.json +0 -1
  66. np_services/.mypy_cache/3.9/decimal.meta.json +0 -1
  67. np_services/.mypy_cache/3.9/email/__init__.data.json +0 -1
  68. np_services/.mypy_cache/3.9/email/__init__.meta.json +0 -1
  69. np_services/.mypy_cache/3.9/email/charset.data.json +0 -1
  70. np_services/.mypy_cache/3.9/email/charset.meta.json +0 -1
  71. np_services/.mypy_cache/3.9/email/contentmanager.data.json +0 -1
  72. np_services/.mypy_cache/3.9/email/contentmanager.meta.json +0 -1
  73. np_services/.mypy_cache/3.9/email/errors.data.json +0 -1
  74. np_services/.mypy_cache/3.9/email/errors.meta.json +0 -1
  75. np_services/.mypy_cache/3.9/email/header.data.json +0 -1
  76. np_services/.mypy_cache/3.9/email/header.meta.json +0 -1
  77. np_services/.mypy_cache/3.9/email/message.data.json +0 -1
  78. np_services/.mypy_cache/3.9/email/message.meta.json +0 -1
  79. np_services/.mypy_cache/3.9/email/policy.data.json +0 -1
  80. np_services/.mypy_cache/3.9/email/policy.meta.json +0 -1
  81. np_services/.mypy_cache/3.9/enum.data.json +0 -1
  82. np_services/.mypy_cache/3.9/enum.meta.json +0 -1
  83. np_services/.mypy_cache/3.9/errno.data.json +0 -1
  84. np_services/.mypy_cache/3.9/errno.meta.json +0 -1
  85. np_services/.mypy_cache/3.9/fractions.data.json +0 -1
  86. np_services/.mypy_cache/3.9/fractions.meta.json +0 -1
  87. np_services/.mypy_cache/3.9/genericpath.data.json +0 -1
  88. np_services/.mypy_cache/3.9/genericpath.meta.json +0 -1
  89. np_services/.mypy_cache/3.9/importlib/__init__.data.json +0 -1
  90. np_services/.mypy_cache/3.9/importlib/__init__.meta.json +0 -1
  91. np_services/.mypy_cache/3.9/importlib/abc.data.json +0 -1
  92. np_services/.mypy_cache/3.9/importlib/abc.meta.json +0 -1
  93. np_services/.mypy_cache/3.9/importlib/machinery.data.json +0 -1
  94. np_services/.mypy_cache/3.9/importlib/machinery.meta.json +0 -1
  95. np_services/.mypy_cache/3.9/importlib/metadata/__init__.data.json +0 -1
  96. np_services/.mypy_cache/3.9/importlib/metadata/__init__.meta.json +0 -1
  97. np_services/.mypy_cache/3.9/io.data.json +0 -1
  98. np_services/.mypy_cache/3.9/io.meta.json +0 -1
  99. np_services/.mypy_cache/3.9/json/__init__.data.json +0 -1
  100. np_services/.mypy_cache/3.9/json/__init__.meta.json +0 -1
  101. np_services/.mypy_cache/3.9/json/decoder.data.json +0 -1
  102. np_services/.mypy_cache/3.9/json/decoder.meta.json +0 -1
  103. np_services/.mypy_cache/3.9/json/encoder.data.json +0 -1
  104. np_services/.mypy_cache/3.9/json/encoder.meta.json +0 -1
  105. np_services/.mypy_cache/3.9/logging/__init__.data.json +0 -1
  106. np_services/.mypy_cache/3.9/logging/__init__.meta.json +0 -1
  107. np_services/.mypy_cache/3.9/math.data.json +0 -1
  108. np_services/.mypy_cache/3.9/math.meta.json +0 -1
  109. np_services/.mypy_cache/3.9/mmap.data.json +0 -1
  110. np_services/.mypy_cache/3.9/mmap.meta.json +0 -1
  111. np_services/.mypy_cache/3.9/np_services/__init__.data.json +0 -1
  112. np_services/.mypy_cache/3.9/np_services/__init__.meta.json +0 -1
  113. np_services/.mypy_cache/3.9/np_services/config.data.json +0 -1
  114. np_services/.mypy_cache/3.9/np_services/config.meta.json +0 -1
  115. np_services/.mypy_cache/3.9/np_services/protocols.data.json +0 -1
  116. np_services/.mypy_cache/3.9/np_services/protocols.meta.json +0 -1
  117. np_services/.mypy_cache/3.9/np_services/zro.data.json +0 -1
  118. np_services/.mypy_cache/3.9/np_services/zro.meta.json +0 -1
  119. np_services/.mypy_cache/3.9/ntpath.data.json +0 -1
  120. np_services/.mypy_cache/3.9/ntpath.meta.json +0 -1
  121. np_services/.mypy_cache/3.9/numbers.data.json +0 -1
  122. np_services/.mypy_cache/3.9/numbers.meta.json +0 -1
  123. np_services/.mypy_cache/3.9/os/__init__.data.json +0 -1
  124. np_services/.mypy_cache/3.9/os/__init__.meta.json +0 -1
  125. np_services/.mypy_cache/3.9/os/path.data.json +0 -1
  126. np_services/.mypy_cache/3.9/os/path.meta.json +0 -1
  127. np_services/.mypy_cache/3.9/pathlib.data.json +0 -1
  128. np_services/.mypy_cache/3.9/pathlib.meta.json +0 -1
  129. np_services/.mypy_cache/3.9/pickle.data.json +0 -1
  130. np_services/.mypy_cache/3.9/pickle.meta.json +0 -1
  131. np_services/.mypy_cache/3.9/platform.data.json +0 -1
  132. np_services/.mypy_cache/3.9/platform.meta.json +0 -1
  133. np_services/.mypy_cache/3.9/posixpath.data.json +0 -1
  134. np_services/.mypy_cache/3.9/posixpath.meta.json +0 -1
  135. np_services/.mypy_cache/3.9/random.data.json +0 -1
  136. np_services/.mypy_cache/3.9/random.meta.json +0 -1
  137. np_services/.mypy_cache/3.9/re.data.json +0 -1
  138. np_services/.mypy_cache/3.9/re.meta.json +0 -1
  139. np_services/.mypy_cache/3.9/shutil.data.json +0 -1
  140. np_services/.mypy_cache/3.9/shutil.meta.json +0 -1
  141. np_services/.mypy_cache/3.9/socket.data.json +0 -1
  142. np_services/.mypy_cache/3.9/socket.meta.json +0 -1
  143. np_services/.mypy_cache/3.9/sre_compile.data.json +0 -1
  144. np_services/.mypy_cache/3.9/sre_compile.meta.json +0 -1
  145. np_services/.mypy_cache/3.9/sre_constants.data.json +0 -1
  146. np_services/.mypy_cache/3.9/sre_constants.meta.json +0 -1
  147. np_services/.mypy_cache/3.9/sre_parse.data.json +0 -1
  148. np_services/.mypy_cache/3.9/sre_parse.meta.json +0 -1
  149. np_services/.mypy_cache/3.9/string.data.json +0 -1
  150. np_services/.mypy_cache/3.9/string.meta.json +0 -1
  151. np_services/.mypy_cache/3.9/subprocess.data.json +0 -1
  152. np_services/.mypy_cache/3.9/subprocess.meta.json +0 -1
  153. np_services/.mypy_cache/3.9/sys.data.json +0 -1
  154. np_services/.mypy_cache/3.9/sys.meta.json +0 -1
  155. np_services/.mypy_cache/3.9/threading.data.json +0 -1
  156. np_services/.mypy_cache/3.9/threading.meta.json +0 -1
  157. np_services/.mypy_cache/3.9/time.data.json +0 -1
  158. np_services/.mypy_cache/3.9/time.meta.json +0 -1
  159. np_services/.mypy_cache/3.9/types.data.json +0 -1
  160. np_services/.mypy_cache/3.9/types.meta.json +0 -1
  161. np_services/.mypy_cache/3.9/typing.data.json +0 -1
  162. np_services/.mypy_cache/3.9/typing.meta.json +0 -1
  163. np_services/.mypy_cache/3.9/typing_extensions.data.json +0 -1
  164. np_services/.mypy_cache/3.9/typing_extensions.meta.json +0 -1
  165. np_services/.mypy_cache/3.9/warnings.data.json +0 -1
  166. np_services/.mypy_cache/3.9/warnings.meta.json +0 -1
  167. np_services/.mypy_cache/3.9/weakref.data.json +0 -1
  168. np_services/.mypy_cache/3.9/weakref.meta.json +0 -1
  169. np_services/.mypy_cache/3.9/zmq/__init__.data.json +0 -1
  170. np_services/.mypy_cache/3.9/zmq/__init__.meta.json +0 -1
  171. np_services/.mypy_cache/3.9/zmq/_typing.data.json +0 -1
  172. np_services/.mypy_cache/3.9/zmq/_typing.meta.json +0 -1
  173. np_services/.mypy_cache/3.9/zmq/backend/__init__.data.json +0 -1
  174. np_services/.mypy_cache/3.9/zmq/backend/__init__.meta.json +0 -1
  175. np_services/.mypy_cache/3.9/zmq/backend/select.data.json +0 -1
  176. np_services/.mypy_cache/3.9/zmq/backend/select.meta.json +0 -1
  177. np_services/.mypy_cache/3.9/zmq/constants.data.json +0 -1
  178. np_services/.mypy_cache/3.9/zmq/constants.meta.json +0 -1
  179. np_services/.mypy_cache/3.9/zmq/error.data.json +0 -1
  180. np_services/.mypy_cache/3.9/zmq/error.meta.json +0 -1
  181. np_services/.mypy_cache/3.9/zmq/sugar/__init__.data.json +0 -1
  182. np_services/.mypy_cache/3.9/zmq/sugar/__init__.meta.json +0 -1
  183. np_services/.mypy_cache/3.9/zmq/sugar/attrsettr.data.json +0 -1
  184. np_services/.mypy_cache/3.9/zmq/sugar/attrsettr.meta.json +0 -1
  185. np_services/.mypy_cache/3.9/zmq/sugar/context.data.json +0 -1
  186. np_services/.mypy_cache/3.9/zmq/sugar/context.meta.json +0 -1
  187. np_services/.mypy_cache/3.9/zmq/sugar/frame.data.json +0 -1
  188. np_services/.mypy_cache/3.9/zmq/sugar/frame.meta.json +0 -1
  189. np_services/.mypy_cache/3.9/zmq/sugar/poll.data.json +0 -1
  190. np_services/.mypy_cache/3.9/zmq/sugar/poll.meta.json +0 -1
  191. np_services/.mypy_cache/3.9/zmq/sugar/socket.data.json +0 -1
  192. np_services/.mypy_cache/3.9/zmq/sugar/socket.meta.json +0 -1
  193. np_services/.mypy_cache/3.9/zmq/sugar/tracker.data.json +0 -1
  194. np_services/.mypy_cache/3.9/zmq/sugar/tracker.meta.json +0 -1
  195. np_services/.mypy_cache/3.9/zmq/sugar/version.data.json +0 -1
  196. np_services/.mypy_cache/3.9/zmq/sugar/version.meta.json +0 -1
  197. np_services/.mypy_cache/3.9/zmq/utils/__init__.data.json +0 -1
  198. np_services/.mypy_cache/3.9/zmq/utils/__init__.meta.json +0 -1
  199. np_services/.mypy_cache/3.9/zmq/utils/interop.data.json +0 -1
  200. np_services/.mypy_cache/3.9/zmq/utils/interop.meta.json +0 -1
  201. np_services/.mypy_cache/3.9/zmq/utils/jsonapi.data.json +0 -1
  202. np_services/.mypy_cache/3.9/zmq/utils/jsonapi.meta.json +0 -1
  203. np_services/.mypy_cache/CACHEDIR.TAG +0 -3
  204. np_services/resources/black_desktop.ps1 +0 -66
  205. np_services/resources/grey_desktop.ps1 +0 -66
  206. np_services/resources/reset_desktop.ps1 +0 -66
  207. np_services-0.1.59.dist-info/RECORD +0 -206
@@ -1,50 +1,93 @@
1
+ from __future__ import annotations
2
+
3
+ import abc
1
4
  import argparse
5
+ import copy
2
6
  import contextlib
7
+ import dataclasses
3
8
  import functools
9
+ import json
4
10
  import os
5
11
  import pathlib
12
+ import sys
6
13
  import tempfile
7
14
  import time
8
15
  import logging
9
- from typing import Iterable, Literal
16
+ from typing import Iterable, Literal, Type
10
17
 
11
18
  import upath
12
19
 
13
20
 
14
- os.environ["USE_TEST_RIG"] = "0"
15
- os.environ["AIBS_RIG_ID"] = "NP.3"
16
-
17
21
  import np_session
18
22
  import np_services
19
23
  import np_config
20
24
  import npc_sync
21
- import npc_ephys
22
25
  import npc_mvr
23
26
  import npc_stim
24
27
 
25
28
  logger = logging.getLogger()
26
29
 
27
- DEFAULT_SERVICES = (np_services.MouseDirector, )
28
- DEFAULT_STIM = np_services.ScriptCamstim
29
- DEFAULT_RECORDERS = (np_services.Sync, np_services.OpenEphys, np_services.VideoMVR, )
30
+ DEFAULT_SERVICES: tuple[np_services.Testable, ...] = (np_services.MouseDirector, )
31
+ DEFAULT_RECORDERS: tuple[np_services.Startable, ...] = (np_services.Sync, np_services.VideoMVR, )
32
+
33
+ @dataclasses.dataclass
34
+ class PretestConfig:
35
+ check_sync_barcodes: bool = False
36
+ check_ephys_barcodes: bool = False # this is error prone with short recordings, so is separated from checking sync alone
37
+ check_licks: bool = False
38
+ check_opto: bool = False
39
+ check_audio: bool = False
40
+ check_running: bool = False
41
+
42
+ @property
43
+ def check_barcodes(self) -> bool:
44
+ return self.check_sync_barcodes or self.check_ephys_barcodes
45
+
46
+
47
+ class PretestSession(abc.ABC):
48
+
49
+ def __init__(self, pretest_config: PretestConfig) -> None:
50
+ self.pretest_config = pretest_config
51
+
52
+ @property
53
+ def services(self) -> tuple[np_services.Testable | np_services.Startable, ...]:
54
+ return DEFAULT_SERVICES + self.recorders + (self.stim, )
55
+
56
+ @property
57
+ def recorders(self) -> tuple[np_services.Startable, ...]:
58
+ if self.pretest_config.check_ephys_barcodes or self.pretest_config.check_sync_barcodes:
59
+ return DEFAULT_RECORDERS + (np_services.OpenEphys, )
60
+ return DEFAULT_RECORDERS
61
+
62
+ @property
63
+ @abc.abstractmethod
64
+ def stim(self) -> Type[np_services.Camstim]: ...
30
65
 
31
- class DynamicRoutingPretest:
66
+ @abc.abstractmethod
67
+ def configure_services(self) -> None: ...
68
+
69
+ @abc.abstractmethod
70
+ def run_pretest_stim(self) -> None: ...
71
+
72
+ class DynamicRoutingPretest(PretestSession):
32
73
  """Modified version of class in np_workflows."""
74
+
33
75
  use_github: bool = True
76
+ task_name: str = "" # unused in pretest
34
77
 
78
+ @property
79
+ def stim(self) -> Type[np_services.ScriptCamstim]:
80
+ return np_services.ScriptCamstim
81
+
35
82
  @property
36
83
  def rig(self) -> np_config.Rig:
37
84
  return np_config.Rig()
38
-
39
- @property
40
- def config(self) -> dict:
41
- return self.rig.config
42
85
 
43
86
  @property
44
87
  def commit_hash(self) -> str:
45
88
  if hasattr(self, '_commit_hash'):
46
89
  return self._commit_hash
47
- self._commit_hash = self.config['dynamicrouting_task_script']['commit_hash']
90
+ self._commit_hash = self.rig.config['dynamicrouting_task_script']['commit_hash']
48
91
  return self.commit_hash
49
92
 
50
93
  @commit_hash.setter
@@ -55,7 +98,7 @@ class DynamicRoutingPretest:
55
98
  def github_url(self) -> str:
56
99
  if hasattr(self, '_github_url'):
57
100
  return self._github_url
58
- self._github_url = self.config['dynamicrouting_task_script']['url']
101
+ self._github_url = self.rig.config['dynamicrouting_task_script']['url']
59
102
  return self.github_url
60
103
 
61
104
  @github_url.setter
@@ -70,21 +113,6 @@ class DynamicRoutingPretest:
70
113
  def base_path(self) -> pathlib.Path:
71
114
  return pathlib.Path('//allen/programs/mindscope/workgroups/dynamicrouting/DynamicRoutingTask/')
72
115
 
73
- @property
74
- def task_name(self) -> str:
75
- """For sending to runTask.py and controlling implementation details of the task."""
76
- if hasattr(self, '_task_name'):
77
- return self._task_name
78
- return ""
79
-
80
- @task_name.setter
81
- def task_name(self, task_name: str) -> None:
82
- self._task_name = task_name
83
- if task_name not in self.preset_task_names:
84
- print(f"{task_name = !r} doesn't correspond to a preset value, but the attribute is updated anyway!")
85
- else:
86
- print(f"Updated {self.__class__.__name__}.{task_name = !r}")
87
-
88
116
  @property
89
117
  def mouse(self) -> np_session.Mouse:
90
118
  return np_session.Mouse(366122)
@@ -135,9 +163,11 @@ class DynamicRoutingPretest:
135
163
 
136
164
  rig = str(self.rig).replace('.', '')
137
165
  locs_root = self.base_path / 'OptoGui' / f'{dirname}'
138
- available_locs = sorted(tuple(locs_root.glob(f"{file_prefix}_{self.mouse.id}_{rig}_*")), reverse=True)
166
+ # use any available locs file - as long as the light switches on the
167
+ # values don't matter
168
+ available_locs = sorted(tuple(locs_root.glob(f"{file_prefix}*")), reverse=True)
139
169
  if not available_locs:
140
- raise FileNotFoundError(f"No optotagging locs found for {self.mouse}/{rig} - have you run OptoGui?")
170
+ raise FileNotFoundError(f"No optotagging locs found - have you run OptoGui?")
141
171
  return available_locs[0]
142
172
 
143
173
 
@@ -197,16 +227,16 @@ class DynamicRoutingPretest:
197
227
 
198
228
  def run_script(self, stim: Literal['sound_test', 'mapping', 'task', 'opto', 'optotagging', 'spontaneous', 'spontaneous_rewards']) -> None:
199
229
 
200
- params = getattr(self, f'{stim.replace(" ", "_")}_params')
230
+ params = copy.deepcopy(getattr(self, f'{stim.replace(" ", "_")}_params'))
201
231
 
202
232
  # add mouse and user info for MPE
203
233
  params['mouse_id'] = str(self.mouse.id)
204
234
  params['user_id'] = 'ben.hardcastle'
205
235
 
206
- script: str = params['taskScript']
207
- params['taskScript'] = (self.task_script_base / script).as_posix()
236
+ if self.task_script_base.as_posix() not in params['taskScript']:
237
+ params['taskScript'] = (self.task_script_base / params['taskScript']).as_posix()
208
238
 
209
- params['maxTrials'] = 30
239
+ params['maxTrials'] = 30
210
240
 
211
241
  if self.use_github:
212
242
 
@@ -217,23 +247,53 @@ class DynamicRoutingPretest:
217
247
  }
218
248
  params['task_script_commit_hash'] = self.commit_hash
219
249
 
220
- np_services.ScriptCamstim.script = self.camstim_script.read_text()
250
+ self.stim.script = self.camstim_script.read_text()
221
251
  else:
222
- np_services.ScriptCamstim.script = self.camstim_script.as_posix()
252
+ self.stim.script = self.camstim_script.as_posix()
223
253
 
224
- np_services.ScriptCamstim.params = params
254
+ self.stim.params = params
225
255
 
226
256
 
227
- np_services.ScriptCamstim.start()
257
+ self.stim.start()
228
258
  with contextlib.suppress(np_services.resources.zro.ZroError):
229
- while not np_services.ScriptCamstim.is_ready_to_start():
259
+ while not self.stim.is_ready_to_start():
230
260
  time.sleep(1)
231
261
 
232
262
 
233
263
  with contextlib.suppress(np_services.resources.zro.ZroError):
234
- np_services.ScriptCamstim.finalize()
235
-
236
- def configure_services(services: Iterable[np_services.Testable]) -> None:
264
+ self.stim.finalize()
265
+
266
+ def run_pretest_stim(self) -> None:
267
+ if self.pretest_config.check_audio:
268
+ print("Starting audio test - check for sound...", end="", flush=True)
269
+ self.run_script('sound_test')
270
+ print(" done")
271
+ print("Starting stim - check for opto, spin wheel and tap lick spout...", end="", flush=True)
272
+ self.run_script('optotagging') # vis stim with opto (for checking vsyncs, running, )
273
+ print(" done")
274
+
275
+ def configure_services(self) -> None:
276
+ self.stim.script = '//allen/programs/mindscope/workgroups/dynamicrouting/DynamicRoutingTask/runTask.py'
277
+ self.stim.data_root = pathlib.Path('//allen/programs/mindscope/workgroups/dynamicrouting/DynamicRoutingTask/Data/366122')
278
+
279
+
280
+ class LegacyNP0Pretest(PretestSession):
281
+ def __init__(self, pretest_config: PretestConfig) -> None:
282
+ self.pretest_config = pretest_config
283
+
284
+ @property
285
+ def stim(self) -> Type[np_services.SessionCamstim]:
286
+ return np_services.SessionCamstim
287
+
288
+ def run_pretest_stim(self) -> None:
289
+ self.stim.start()
290
+
291
+ def configure_services(self) -> None:
292
+ self.stim.lims_user_id = "ben.hardcastle"
293
+ self.stim.labtracks_mouse_id = 598796
294
+ self.stim.override_params = json.loads(pathlib.Path("//allen/programs/mindscope/workgroups/dynamicrouting/ben/np0_pretest/params.json").read_bytes())
295
+
296
+ def configure_services(session: PretestSession) -> None:
237
297
  """For each service, apply every key in self.config['service'] as an attribute."""
238
298
 
239
299
  def apply_config(service) -> None:
@@ -244,73 +304,110 @@ def configure_services(services: Iterable[np_services.Testable]) -> None:
244
304
  f"{service.__name__} | Configuring {service.__name__}.{key} = {getattr(service, key)}"
245
305
  )
246
306
 
247
- for service in services:
307
+ for service in session.services:
248
308
  for base in service.__class__.__bases__:
249
309
  apply_config(base)
250
310
  apply_config(service)
251
-
252
- np_services.ScriptCamstim.script = '//allen/programs/mindscope/workgroups/dynamicrouting/DynamicRoutingTask/runTask.py'
253
- np_services.ScriptCamstim.data_root = pathlib.Path('//allen/programs/mindscope/workgroups/dynamicrouting/DynamicRoutingTask/Data/366122')
254
-
311
+
255
312
  np_services.MouseDirector.user = 'ben.hardcastle'
256
313
  np_services.MouseDirector.mouse = 366122
314
+
315
+ session.configure_services()
257
316
 
258
- np_services.OpenEphys.folder = '_test_'
317
+ if session.pretest_config.check_barcodes:
318
+ np_services.OpenEphys.folder = '_test_'
259
319
 
260
320
 
261
321
  @functools.cache
262
322
  def get_temp_dir() -> pathlib.Path:
263
323
  return pathlib.Path(tempfile.mkdtemp())
264
324
 
265
- def main(
266
- recorders: Iterable[np_services.Testable] = DEFAULT_RECORDERS,
267
- stim: np_services.Startable = DEFAULT_STIM,
268
- other: Iterable[np_services.Testable] = DEFAULT_SERVICES,
269
- check_licks: bool = False,
270
- check_opto: bool = False,
271
- check_audio: bool = False,
325
+ def run_pretest(
326
+ config: PretestConfig = PretestConfig(),
272
327
  ) -> None:
273
328
  print("Starting pretest")
274
- configure_services((*recorders, stim, *other))
275
- for service in (*recorders, stim, *other):
329
+ session: PretestSession
330
+ if np_config.Rig().idx == 0:
331
+ session = LegacyNP0Pretest(config)
332
+ else:
333
+ session = DynamicRoutingPretest(config)
334
+ configure_services(session)
335
+
336
+ for service in session.services:
276
337
  if isinstance(service, np_services.Initializable):
277
338
  service.initialize()
278
339
 
279
- stoppables = tuple(_ for _ in recorders if isinstance(_, np_services.Stoppable))
340
+ stoppables = tuple(_ for _ in session.recorders if isinstance(_, np_services.Stoppable))
280
341
  with np_services.stop_on_error(*stoppables):
281
342
  for service in stoppables:
282
343
  if isinstance(service, np_services.Startable):
283
344
  service.start()
284
345
  t0 = time.time()
285
- DynamicRoutingPretest().run_script('optotagging')
346
+ session.run_pretest_stim()
286
347
  t1 = time.time()
287
- time.sleep(max(0, 70 - (t1 - t0))) # long enough to capture 2 sets of barcodes on sync/openephys (cannot scale time with 1 set)
348
+ if not config.check_barcodes:
349
+ min_wait_time = 0
350
+ elif config.check_sync_barcodes and not config.check_ephys_barcodes:
351
+ min_wait_time = 35 # long enough to capture 1 sets of barcodes on sync
352
+ else:
353
+ min_wait_time = 70 # long enough to capture 2 sets of barcodes on sync/openephys (cannot scale time with 1 set)
354
+ time.sleep(max(0, min_wait_time - (t1 - t0)))
288
355
  for service in reversed(stoppables):
289
356
  if isinstance(service, np_services.Stoppable):
290
357
  service.stop()
291
358
 
292
- for service in (*recorders, stim, *other):
359
+ for service in session.services:
293
360
  if isinstance(service, np_services.Finalizable):
294
361
  service.finalize()
295
362
 
296
- np_services.VideoMVR.sync_path = np_services.OpenEphys.sync_path = stim.sync_path = np_services.Sync.data_files[0]
363
+ np_services.VideoMVR.sync_path = np_services.OpenEphys.sync_path = session.stim.sync_path = np_services.Sync.data_files[0]
297
364
 
298
- for service in (*recorders, stim, *other):
365
+ # validate
366
+ for service in session.services:
299
367
  if isinstance(service, np_services.Validatable):
300
- service.validate()
301
- if any((check_licks, check_opto, check_audio)):
368
+ if service is not np_services.OpenEphys:
369
+ service.validate()
370
+ elif config.check_ephys_barcodes:
371
+ # try validating ephys without sync (currently error prone with short pretest-like recordings)
372
+ import npc_ephys
373
+ npc_ephys.validate_ephys(
374
+ root_paths=service.data_files,
375
+ sync_path_or_dataset=False,
376
+ ignore_small_folders=False,
377
+ )
378
+ else:
379
+ # barcodes on sync will be validated, open ephys will be assumed to work correctly
380
+ continue
381
+ assert np_services.Sync.data_files is not None, "No sync file found"
382
+ assert session.stim.data_files is not None, "No stim file found"
383
+
384
+ if any((config.check_licks, config.check_opto, config.check_audio)):
302
385
  npc_sync.SyncDataset(np_services.Sync.data_files[0]).validate(
303
- licks=check_licks, opto=check_opto, audio=check_audio,
386
+ licks=config.check_licks, opto=config.check_opto, audio=config.check_audio,
304
387
  )
388
+ if config.check_running:
389
+ speed, timestamps = npc_stim.get_running_speed_from_stim_files(*session.stim.data_files, sync=np_services.Sync.data_files[0])
390
+ if not speed.size or not timestamps.size:
391
+ raise AssertionError("No running data found")
305
392
 
306
- def parse_args() -> dict:
393
+ def parse_args() -> PretestConfig:
307
394
  parser = argparse.ArgumentParser(description="Run pretest")
395
+ parser.add_argument("--check_ephys_barcodes", action="store_true", help="Check barcodes from Arduino are being received on open ephys and time-alignment is possible (currently error prone with short pretest-like recordings)", default=False)
396
+ parser.add_argument("--check_sync_barcodes", action="store_true", help="Check barcodes from Arduino are being received on sync", default=False)
308
397
  parser.add_argument("--check_licks", action="store_true", help="Check lick sensor line on sync", default=False)
309
398
  parser.add_argument("--check_opto", action="store_true", help="Check opto-running line on sync", default=False)
310
399
  parser.add_argument("--check_audio", action="store_true", help="Check audio-running line on sync", default=False)
311
- return vars(parser.parse_args())
400
+ parser.add_argument("--check_running", action="store_true", help="Check running-wheel encoder data in stim files", default=False)
401
+ return PretestConfig(**vars(parser.parse_args()))
402
+
403
+ def main() -> None:
404
+ logging.basicConfig(
405
+ level="INFO",
406
+ format="%(name)s | %(levelname)s | %(message)s",
407
+ datefmt="%H:%M:%S",
408
+ stream=sys.stdout,
409
+ )
410
+ run_pretest(parse_args())
312
411
 
313
412
  if __name__ == '__main__':
314
- main(
315
- **parse_args()
316
- )
413
+ main()
@@ -1,42 +1,42 @@
1
- import pathlib
2
- from typing import ClassVar
3
-
4
- import fabric
5
- import np_config
6
-
7
- from np_services.proxies import ScriptCamstim
8
-
9
- class DesktopThemeChanger(ScriptCamstim):
10
- """Base class for setting the background wallpaper on a remote machine,
11
- then hiding all desktop icons, hiding the taskbar, minimizing all windows"""
12
- local_file: ClassVar[str | pathlib.Path]
13
- remote_file: ClassVar[str | pathlib.Path]
14
-
15
- extra_args: ClassVar[list[str]] = []
16
- ssh: ClassVar[fabric.Connection]
17
- user: ClassVar[str] = 'svc_neuropix'
18
- password: ClassVar[str] = np_config.fetch('logins')['svc_neuropix']['password']
19
-
20
- # @classmethod
21
- # def initialize(cls):
22
- # super().initialize()
23
- # cls.get_ssh().put(cls.local_file, cls.remote_file)
24
-
25
- @classmethod
26
- def start(cls):
27
- cls.get_ssh().run(f'powershell.exe -ExecutionPolicy bypass {cls.remote_file}')
28
-
29
- class DarkDesktopChanger(DesktopThemeChanger):
30
- local_file = pathlib.Path(__file__).parent / 'resources' / 'black_wallpaper.ps1'
31
- # remote_file: ClassVar[str | pathlib.Path] = 'c:/users/svc_neuropix/desktop/black_wallpaper.ps1'
32
- remote_file: ClassVar[str | pathlib.Path] = R'\\allen\programs\mindscope\workgroups\dynamicrouting\ben\black_wallpaper.ps1'
33
-
34
- class GreyDesktopChanger(DesktopThemeChanger):
35
- local_file = pathlib.Path(__file__).parent / 'resources' / 'grey_wallpaper.ps1'
36
- # remote_file: ClassVar[str | pathlib.Path] = 'c:/users/svc_neuropix/desktop/grey_wallpaper.ps1'
37
- remote_file: ClassVar[str | pathlib.Path] = R'\\allen\programs\mindscope\workgroups\dynamicrouting\ben\grey_wallpaper.ps1'
38
-
39
- class DesktopResetter(DesktopThemeChanger):
40
- local_file = pathlib.Path(__file__).parent / 'resources' / 'reset_wallpaper.ps1'
41
- # remote_file: ClassVar[str | pathlib.Path] = 'c:/users/svc_neuropix/desktop/reset_wallpaper.ps1'
1
+ import pathlib
2
+ from typing import ClassVar
3
+
4
+ import fabric
5
+ import np_config
6
+
7
+ from np_services.proxies import ScriptCamstim
8
+
9
+ class DesktopThemeChanger(ScriptCamstim):
10
+ """Base class for setting the background wallpaper on a remote machine,
11
+ then hiding all desktop icons, hiding the taskbar, minimizing all windows"""
12
+ local_file: ClassVar[str | pathlib.Path]
13
+ remote_file: ClassVar[str | pathlib.Path]
14
+
15
+ extra_args: ClassVar[list[str]] = []
16
+ ssh: ClassVar[fabric.Connection]
17
+ user: ClassVar[str] = 'svc_neuropix'
18
+ password: ClassVar[str] = np_config.fetch('logins')['svc_neuropix']['password']
19
+
20
+ # @classmethod
21
+ # def initialize(cls):
22
+ # super().initialize()
23
+ # cls.get_ssh().put(cls.local_file, cls.remote_file)
24
+
25
+ @classmethod
26
+ def start(cls):
27
+ cls.get_ssh().run(f'powershell.exe -ExecutionPolicy bypass {cls.remote_file}')
28
+
29
+ class DarkDesktopChanger(DesktopThemeChanger):
30
+ local_file = pathlib.Path(__file__).parent / 'resources' / 'black_wallpaper.ps1'
31
+ # remote_file: ClassVar[str | pathlib.Path] = 'c:/users/svc_neuropix/desktop/black_wallpaper.ps1'
32
+ remote_file: ClassVar[str | pathlib.Path] = R'\\allen\programs\mindscope\workgroups\dynamicrouting\ben\black_wallpaper.ps1'
33
+
34
+ class GreyDesktopChanger(DesktopThemeChanger):
35
+ local_file = pathlib.Path(__file__).parent / 'resources' / 'grey_wallpaper.ps1'
36
+ # remote_file: ClassVar[str | pathlib.Path] = 'c:/users/svc_neuropix/desktop/grey_wallpaper.ps1'
37
+ remote_file: ClassVar[str | pathlib.Path] = R'\\allen\programs\mindscope\workgroups\dynamicrouting\ben\grey_wallpaper.ps1'
38
+
39
+ class DesktopResetter(DesktopThemeChanger):
40
+ local_file = pathlib.Path(__file__).parent / 'resources' / 'reset_wallpaper.ps1'
41
+ # remote_file: ClassVar[str | pathlib.Path] = 'c:/users/svc_neuropix/desktop/reset_wallpaper.ps1'
42
42
  remote_file: ClassVar[str | pathlib.Path] = R'\\allen\programs\mindscope\workgroups\dynamicrouting\ben\reset_wallpaper.ps1'