sl-shared-assets 5.1.1__py3-none-any.whl → 6.0.0__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.

Potentially problematic release.


This version of sl-shared-assets might be problematic. Click here for more details.

@@ -8,7 +8,6 @@ Authors: Ivan Kondratyev (Inkaros), Kushaan Gupta, Natalie Yeung
8
8
  from ataraxis_base_utilities import console
9
9
 
10
10
  from .tools import (
11
- ProjectManifest,
12
11
  acquire_lock,
13
12
  release_lock,
14
13
  delete_directory,
@@ -90,7 +89,6 @@ __all__ = [
90
89
  "ProcessingPipelines",
91
90
  "ProcessingStatus",
92
91
  "ProcessingTracker",
93
- "ProjectManifest",
94
92
  "RawData",
95
93
  "RunTrainingDescriptor",
96
94
  "Server",
@@ -15,7 +15,7 @@ from ..data_classes import (
15
15
  )
16
16
 
17
17
  # Ensures that displayed CLICK help messages are formatted according to the lab standard.
18
- CONTEXT_SETTINGS = dict(max_content_width=120) # or any width you want
18
+ CONTEXT_SETTINGS = dict(max_content_width=120)
19
19
 
20
20
 
21
21
  @click.group("manage", context_settings=CONTEXT_SETTINGS)
@@ -342,6 +342,9 @@ class ProcessingTracker(YamlConfig):
342
342
  self._complete = True
343
343
  self._encountered_error = False
344
344
  self._save_state()
345
+ else:
346
+ # Otherwise, updates the completed job counter, but does not change any other state variables.
347
+ self._save_state()
345
348
 
346
349
  def abort(self) -> None:
347
350
  """Resets the pipeline tracker file to the default state.
@@ -524,11 +527,22 @@ class ProcessingPipeline:
524
527
  # to the server.
525
528
  elif tracker.is_running:
526
529
  self._pipeline_stage += 1
527
- self._submit_jobs()
530
+
531
+ # If the incremented stage is not a valid stage, the pipeline has actually been aborted and the tracker file
532
+ # does not properly reflect this state. Sets the internal state tracker appropriately and resets (removes)
533
+ # the tracker file from the server to prevent deadlocking further runtimes
534
+ if self._pipeline_stage not in self.jobs.keys():
535
+ sh.rmtree(self.local_tracker_path.parent) # Removes local temporary data
536
+ self.pipeline_status = ProcessingStatus.ABORTED
537
+ self.server.remove(remote_path=self.remote_tracker_path, is_dir=False)
538
+ else:
539
+ # Otherwise, submits the next batch of jobs to the server.
540
+ self._submit_jobs()
528
541
 
529
542
  # The final and the rarest state: the pipeline was aborted before it finished the runtime. Generally, this state
530
543
  # should not be encountered during most runtimes.
531
544
  else:
545
+ sh.rmtree(self.local_tracker_path.parent) # Removes local temporary data
532
546
  self.pipeline_status = ProcessingStatus.ABORTED
533
547
 
534
548
  def _submit_jobs(self) -> None:
@@ -4,7 +4,6 @@ integrity of the data. The tools from this package are used by most other data p
4
4
  from .transfer_tools import delete_directory, transfer_directory
5
5
  from .packaging_tools import calculate_directory_checksum
6
6
  from .project_management_tools import (
7
- ProjectManifest,
8
7
  acquire_lock,
9
8
  release_lock,
10
9
  archive_session,
@@ -14,7 +13,6 @@ from .project_management_tools import (
14
13
  )
15
14
 
16
15
  __all__ = [
17
- "ProjectManifest",
18
16
  "acquire_lock",
19
17
  "archive_session",
20
18
  "calculate_directory_checksum",
@@ -115,7 +115,7 @@ def calculate_directory_checksum(
115
115
  results = []
116
116
  if not batch:
117
117
  with tqdm(
118
- total=len(files), desc=f"Calculating checksum for {Path(*directory.parts[-6:])}", unit="files"
118
+ total=len(files), desc=f"Calculating checksum for {Path(*directory.parts[-6:])}", unit="file"
119
119
  ) as pbar:
120
120
  for future in as_completed(future_to_path):
121
121
  results.append(future.result())
@@ -661,236 +661,3 @@ def generate_project_manifest(
661
661
  # If the tracker indicates that the processing is still running, the runtime has encountered an error.
662
662
  if runtime_tracker.is_running:
663
663
  tracker.error(manager_id=manager_id)
664
-
665
-
666
- class ProjectManifest:
667
- """Wraps the contents of a Sun lab project manifest .feather file and exposes methods for visualizing and
668
- working with the data stored inside the file.
669
-
670
- This class functions as a high-level API for working with Sun lab projects. It is used both to visualize the
671
- current state of various projects and during automated data processing to determine which processing steps to
672
- apply to different sessions.
673
-
674
- Args:
675
- manifest_file: The path to the .feather manifest file that stores the target project's state data.
676
-
677
- Attributes:
678
- _data: Stores the manifest data as a Polars DataFrame.
679
- _animal_string: Determines whether animal IDs are stored as strings or unsigned integers.
680
- """
681
-
682
- def __init__(self, manifest_file: Path):
683
- # Reads the data from the target manifest file into the class attribute
684
- self._data: pl.DataFrame = pl.read_ipc(source=manifest_file, use_pyarrow=True)
685
-
686
- # Determines whether animal IDs are stored as strings or as numbers
687
- self._animal_string = False
688
- schema = self._data.collect_schema()
689
- if isinstance(schema["animal"], pl.String):
690
- self._animal_string = True
691
-
692
- def print_data(self) -> None:
693
- """Prints the entire contents of the manifest file to the terminal."""
694
- with pl.Config(
695
- set_tbl_rows=-1, # Displays all rows (-1 means unlimited)
696
- set_tbl_cols=-1, # Displays all columns (-1 means unlimited)
697
- set_tbl_hide_column_data_types=True,
698
- set_tbl_cell_alignment="LEFT",
699
- set_tbl_width_chars=250, # Sets table width to 200 characters
700
- set_fmt_str_lengths=600, # Allows longer strings to display properly (default is 32)
701
- ):
702
- print(self._data)
703
-
704
- def print_summary(self, animal: str | int | None = None) -> None:
705
- """Prints a summary view of the manifest file to the terminal, excluding the 'experimenter notes' data for
706
- each session.
707
-
708
- This data view is optimized for tracking which processing steps have been applied to each session inside the
709
- project.
710
-
711
- Args:
712
- animal: The ID of the animal for which to display the data. If an ID is provided, this method will only
713
- display the data for that animal. Otherwise, it will display the data for all animals.
714
- """
715
- summary_cols = [
716
- "animal",
717
- "date",
718
- "session",
719
- "type",
720
- "system",
721
- "complete",
722
- "integrity",
723
- "prepared",
724
- "suite2p",
725
- "behavior",
726
- "video",
727
- "archived",
728
- ]
729
-
730
- # Retrieves the data
731
- df = self._data.select(summary_cols)
732
-
733
- # Optionally filters the data for the target animal
734
- if animal is not None:
735
- # Ensures that the 'animal' argument has the same type as the data inside the DataFrame.
736
- if self._animal_string:
737
- animal = str(animal)
738
- else:
739
- animal = int(animal)
740
- df = df.filter(pl.col("animal") == animal)
741
-
742
- # Ensures the data displays properly
743
- with pl.Config(
744
- set_tbl_rows=-1,
745
- set_tbl_cols=-1,
746
- set_tbl_width_chars=250,
747
- set_tbl_hide_column_data_types=True,
748
- set_tbl_cell_alignment="CENTER",
749
- ):
750
- print(df)
751
-
752
- def print_notes(self, animal: str | int | None = None) -> None:
753
- """Prints only animal, session, and notes data from the manifest file.
754
-
755
- This data view is optimized for experimenters to check what sessions have been recorded for each animal in the
756
- project and refresh their memory on the outcomes of each session using experimenter notes.
757
-
758
- Args:
759
- animal: The ID of the animal for which to display the data. If an ID is provided, this method will only
760
- display the data for that animal. Otherwise, it will display the data for all animals.
761
- """
762
-
763
- # Pre-selects the columns to display
764
- df = self._data.select(["animal", "date", "session", "type", "system", "notes"])
765
-
766
- # Optionally filters the data for the target animal
767
- if animal is not None:
768
- # Ensures that the 'animal' argument has the same type as the data inside the DataFrame.
769
- if self._animal_string:
770
- animal = str(animal)
771
- else:
772
- animal = int(animal)
773
-
774
- df = df.filter(pl.col("animal") == animal)
775
-
776
- # Prints the extracted data
777
- with pl.Config(
778
- set_tbl_rows=-1,
779
- set_tbl_cols=-1,
780
- set_tbl_hide_column_data_types=True,
781
- set_tbl_cell_alignment="LEFT",
782
- set_tbl_width_chars=100, # Wider columns for notes
783
- set_fmt_str_lengths=800, # Allows very long strings for notes
784
- ):
785
- print(df)
786
-
787
- @property
788
- def animals(self) -> tuple[str, ...]:
789
- """Returns all unique animal IDs stored inside the manifest file.
790
-
791
- This provides a tuple of all animal IDs participating in the target project.
792
- """
793
-
794
- # If animal IDs are stored as integers, converts them to string to support consistent return types.
795
- return tuple(
796
- [str(animal) for animal in self._data.select("animal").unique().sort("animal").to_series().to_list()]
797
- )
798
-
799
- def _get_filtered_sessions(
800
- self,
801
- animal: str | int | None = None,
802
- exclude_incomplete: bool = True,
803
- ) -> tuple[str, ...]:
804
- """This worker method is used to get a list of sessions with optional filtering.
805
-
806
- User-facing methods call this worker under-the-hood to fetch the filtered tuple of sessions.
807
-
808
- Args:
809
- animal: An optional animal ID to filter the sessions. If set to None, the method returns sessions for all
810
- animals.
811
- exclude_incomplete: Determines whether to exclude sessions not marked as 'complete' from the output
812
- list.
813
-
814
- Returns:
815
- The tuple of session IDs matching the filter criteria.
816
-
817
- Raises:
818
- ValueError: If the specified animal is not found in the manifest file.
819
- """
820
- data = self._data
821
-
822
- # Filter by animal if specified
823
- if animal is not None:
824
- # Ensures that the 'animal' argument has the same type as the data inside the DataFrame.
825
- if self._animal_string:
826
- animal = str(animal)
827
- else:
828
- animal = int(animal)
829
-
830
- if animal not in self.animals:
831
- message = f"Animal ID '{animal}' not found in the project manifest. Available animals: {self.animals}."
832
- console.error(message=message, error=ValueError)
833
-
834
- data = data.filter(pl.col("animal") == animal)
835
-
836
- # Optionally filters out incomplete sessions
837
- if exclude_incomplete:
838
- data = data.filter(pl.col("complete") == 1)
839
-
840
- # Formats and returns session IDs to the caller
841
- sessions = data.select("session").sort("session").to_series().to_list()
842
- return tuple(sessions)
843
-
844
- @property
845
- def sessions(self) -> tuple[str, ...]:
846
- """Returns all session IDs stored inside the manifest file.
847
-
848
- This property provides a tuple of all sessions, independent of the participating animal, that were recorded as
849
- part of the target project. Use the get_sessions() method to get the list of session tuples with filtering.
850
- """
851
- return self._get_filtered_sessions(animal=None, exclude_incomplete=False)
852
-
853
- def get_sessions(
854
- self,
855
- animal: str | int | None = None,
856
- exclude_incomplete: bool = True,
857
- ) -> tuple[str, ...]:
858
- """Returns requested session IDs based on selected filtering criteria.
859
-
860
- This method provides a tuple of sessions based on the specified filters. If no animal is specified, returns
861
- sessions for all animals in the project.
862
-
863
- Args:
864
- animal: An optional animal ID to filter the sessions. If set to None, the method returns sessions for all
865
- animals.
866
- exclude_incomplete: Determines whether to exclude sessions not marked as 'complete' from the output
867
- list.
868
-
869
- Returns:
870
- The tuple of session IDs matching the filter criteria.
871
-
872
- Raises:
873
- ValueError: If the specified animal is not found in the manifest file.
874
- """
875
- return self._get_filtered_sessions(
876
- animal=animal,
877
- exclude_incomplete=exclude_incomplete,
878
- )
879
-
880
- def get_session_info(self, session: str) -> pl.DataFrame:
881
- """Returns a Polars DataFrame that stores detailed information for the specified session.
882
-
883
- Since session IDs are unique, it is expected that filtering by session ID is enough to get the requested
884
- information.
885
-
886
- Args:
887
- session: The ID of the session for which to retrieve the data.
888
-
889
- Returns:
890
- A Polars DataFrame with the following columns: 'animal', 'date', 'notes', 'session', 'type', 'system',
891
- 'complete', 'integrity', 'suite2p', 'behavior', 'video', 'archived'.
892
- """
893
-
894
- df = self._data
895
- df = df.filter(pl.col("session").eq(session))
896
- return df
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sl-shared-assets
3
- Version: 5.1.1
3
+ Version: 6.0.0
4
4
  Summary: Provides data acquisition and processing assets shared between Sun (NeuroAI) lab libraries.
5
5
  Project-URL: Homepage, https://github.com/Sun-Lab-NBB/sl-shared-assets
6
6
  Project-URL: Documentation, https://sl-shared-assets-api-docs.netlify.app/
@@ -686,12 +686,10 @@ Classifier: Development Status :: 5 - Production/Stable
686
686
  Classifier: Intended Audience :: Developers
687
687
  Classifier: License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)
688
688
  Classifier: Operating System :: OS Independent
689
- Classifier: Programming Language :: Python :: 3.11
690
- Classifier: Programming Language :: Python :: 3.12
691
689
  Classifier: Programming Language :: Python :: 3.13
692
690
  Classifier: Topic :: Scientific/Engineering
693
691
  Classifier: Typing :: Typed
694
- Requires-Python: >=3.11
692
+ Requires-Python: <3.14,>=3.13
695
693
  Requires-Dist: appdirs<2,>=1
696
694
  Requires-Dist: ataraxis-base-utilities<4,>=3
697
695
  Requires-Dist: ataraxis-data-structures<4,>=3
@@ -1,7 +1,7 @@
1
- sl_shared_assets/__init__.py,sha256=t3naIsPh7nKh4kFKBTOvEUHt3Wke3zH1KYSF6ON91x0,2830
1
+ sl_shared_assets/__init__.py,sha256=rFNpMl0SFSQhBnCnmEYWsqf_4v0zmwt1xvSFFblUic0,2786
2
2
  sl_shared_assets/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
3
  sl_shared_assets/command_line_interfaces/__init__.py,sha256=dbcCiuBCe1uqz6d1D-dFpSkRwYP603tEGkc1RbZcI6w,126
4
- sl_shared_assets/command_line_interfaces/configure.py,sha256=lAWhLzJ7UmdvUZlges9Hg5pnblQuXSDlgzr12y41108,6140
4
+ sl_shared_assets/command_line_interfaces/configure.py,sha256=ebzej7m2gZtJW_lH6m7ERIv1BcvYfqXNBxlUf0vXFq4,6115
5
5
  sl_shared_assets/command_line_interfaces/manage.py,sha256=-Ac5AufjiIpIYs2SSSstIAEHryZWsDHPt_cgWrNp8O4,10786
6
6
  sl_shared_assets/data_classes/__init__.py,sha256=7Cslz_z0K2VmgXjOR1u-NBySoWYfUNawBHh_1xLiFZ0,2092
7
7
  sl_shared_assets/data_classes/configuration_data.py,sha256=9gVo7bQShCMqAuau8d9BML6RrQyA4wg8ZFol1RJfJ7g,41334
@@ -10,14 +10,14 @@ sl_shared_assets/data_classes/session_data.py,sha256=gfzdFnBXwawWXQWiwZ3UXQeYaja
10
10
  sl_shared_assets/data_classes/surgery_data.py,sha256=-rENeSjAPmoIc-fp_skI4-EW_uxbK0Ps9aTLPWLkTSQ,7624
11
11
  sl_shared_assets/server/__init__.py,sha256=cIZFNE8pPPujp_FxIt6yT22L-4V4XBb1e_x4cnTrtSI,685
12
12
  sl_shared_assets/server/job.py,sha256=_u27gXwSfg3ztCemAtiskC9KnDfqF-RvWlTYYk0wHvQ,18704
13
- sl_shared_assets/server/pipeline.py,sha256=Uqg210L6N71OHTy4--8iocRz_bV5cBSUOVZnOk_u1zg,32157
13
+ sl_shared_assets/server/pipeline.py,sha256=OtpF28Nd8590RoKkyGUBwLdOxVD692cLMX6mM-Rhicw,33134
14
14
  sl_shared_assets/server/server.py,sha256=FEBx9isTswndAKFaZ6MM4qyFp75FcE2ZWUiwc_uycMI,32825
15
- sl_shared_assets/tools/__init__.py,sha256=gMyKO3ZA70pmG7Hh8fIA5CLrkOXtN5pQk2o4NrKUjXM,813
16
- sl_shared_assets/tools/packaging_tools.py,sha256=9kwUSQQRCOycpNA7ovthCTXYX1HCC7p1iDnCYz2K2B8,6978
17
- sl_shared_assets/tools/project_management_tools.py,sha256=I1EqSapoQU2zspoMc_OxOrUMw_iYIqX6uIfmZZ3paGU,43205
15
+ sl_shared_assets/tools/__init__.py,sha256=mz0YCAKm4UMYtLErBLXIRJ-QxkHV9S9j5BHrDZMqVWg,769
16
+ sl_shared_assets/tools/packaging_tools.py,sha256=4MRYvXQ6PfW1bB_uB5ngDHR0NbgJQW2kCwZw_7U-224,6977
17
+ sl_shared_assets/tools/project_management_tools.py,sha256=7oqR5NSA0pLHZyS-gKIJwajfGkdz1ENgJpuKyxBdN_8,33836
18
18
  sl_shared_assets/tools/transfer_tools.py,sha256=6lwk4zyHWWB0QeWQageiQ1pPIGUV0IhBNNzsbgQ_qH4,9026
19
- sl_shared_assets-5.1.1.dist-info/METADATA,sha256=pjm-qXxbdiIPJoOmiTUbOuQS0KnXIMMh5u1nJ6NNVYQ,46884
20
- sl_shared_assets-5.1.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
21
- sl_shared_assets-5.1.1.dist-info/entry_points.txt,sha256=19NzFPG5CcW-4RhEacTcX3J2TbrvmjOE4tlLCH2O5wI,161
22
- sl_shared_assets-5.1.1.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
23
- sl_shared_assets-5.1.1.dist-info/RECORD,,
19
+ sl_shared_assets-6.0.0.dist-info/METADATA,sha256=mnepeiK1pc5pX8autsg1mDaNBHMmbyf2MtfI0CodsgM,46788
20
+ sl_shared_assets-6.0.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
21
+ sl_shared_assets-6.0.0.dist-info/entry_points.txt,sha256=19NzFPG5CcW-4RhEacTcX3J2TbrvmjOE4tlLCH2O5wI,161
22
+ sl_shared_assets-6.0.0.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
23
+ sl_shared_assets-6.0.0.dist-info/RECORD,,