jupyter-analysis-tools 1.7.0__tar.gz → 1.7.2__tar.gz

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 (55) hide show
  1. {jupyter_analysis_tools-1.7.0 → jupyter_analysis_tools-1.7.2}/.copier-answers.yml +1 -1
  2. {jupyter_analysis_tools-1.7.0 → jupyter_analysis_tools-1.7.2}/CHANGELOG.md +16 -0
  3. {jupyter_analysis_tools-1.7.0 → jupyter_analysis_tools-1.7.2}/PKG-INFO +19 -3
  4. {jupyter_analysis_tools-1.7.0 → jupyter_analysis_tools-1.7.2}/README.md +2 -2
  5. {jupyter_analysis_tools-1.7.0 → jupyter_analysis_tools-1.7.2}/docs/conf.py +1 -1
  6. {jupyter_analysis_tools-1.7.0 → jupyter_analysis_tools-1.7.2}/src/jupyter_analysis_tools/__init__.py +1 -1
  7. {jupyter_analysis_tools-1.7.0 → jupyter_analysis_tools-1.7.2}/src/jupyter_analysis_tools/datastore.py +201 -173
  8. {jupyter_analysis_tools-1.7.0 → jupyter_analysis_tools-1.7.2}/src/jupyter_analysis_tools.egg-info/PKG-INFO +19 -3
  9. {jupyter_analysis_tools-1.7.0 → jupyter_analysis_tools-1.7.2}/.editorconfig +0 -0
  10. {jupyter_analysis_tools-1.7.0 → jupyter_analysis_tools-1.7.2}/.pre-commit-config.yaml +0 -0
  11. {jupyter_analysis_tools-1.7.0 → jupyter_analysis_tools-1.7.2}/AUTHORS.rst +0 -0
  12. {jupyter_analysis_tools-1.7.0 → jupyter_analysis_tools-1.7.2}/CONTRIBUTING.rst +0 -0
  13. {jupyter_analysis_tools-1.7.0 → jupyter_analysis_tools-1.7.2}/LICENSE +0 -0
  14. {jupyter_analysis_tools-1.7.0 → jupyter_analysis_tools-1.7.2}/MANIFEST.in +0 -0
  15. {jupyter_analysis_tools-1.7.0 → jupyter_analysis_tools-1.7.2}/ci/requirements.txt +0 -0
  16. {jupyter_analysis_tools-1.7.0 → jupyter_analysis_tools-1.7.2}/docs/_templates/class.rst +0 -0
  17. {jupyter_analysis_tools-1.7.0 → jupyter_analysis_tools-1.7.2}/docs/_templates/module.rst +0 -0
  18. {jupyter_analysis_tools-1.7.0 → jupyter_analysis_tools-1.7.2}/docs/authors.rst +0 -0
  19. {jupyter_analysis_tools-1.7.0 → jupyter_analysis_tools-1.7.2}/docs/changelog.rst +0 -0
  20. {jupyter_analysis_tools-1.7.0 → jupyter_analysis_tools-1.7.2}/docs/contributing.rst +0 -0
  21. {jupyter_analysis_tools-1.7.0 → jupyter_analysis_tools-1.7.2}/docs/index.rst +0 -0
  22. {jupyter_analysis_tools-1.7.0 → jupyter_analysis_tools-1.7.2}/docs/installation.rst +0 -0
  23. {jupyter_analysis_tools-1.7.0 → jupyter_analysis_tools-1.7.2}/docs/readme.rst +0 -0
  24. {jupyter_analysis_tools-1.7.0 → jupyter_analysis_tools-1.7.2}/docs/reference/index.rst +0 -0
  25. {jupyter_analysis_tools-1.7.0 → jupyter_analysis_tools-1.7.2}/docs/requirements.txt +0 -0
  26. {jupyter_analysis_tools-1.7.0 → jupyter_analysis_tools-1.7.2}/docs/spelling_wordlist.txt +0 -0
  27. {jupyter_analysis_tools-1.7.0 → jupyter_analysis_tools-1.7.2}/docs/usage.rst +0 -0
  28. {jupyter_analysis_tools-1.7.0 → jupyter_analysis_tools-1.7.2}/pyproject.toml +0 -0
  29. {jupyter_analysis_tools-1.7.0 → jupyter_analysis_tools-1.7.2}/setup.cfg +0 -0
  30. {jupyter_analysis_tools-1.7.0 → jupyter_analysis_tools-1.7.2}/src/jupyter_analysis_tools/analysis.py +0 -0
  31. {jupyter_analysis_tools-1.7.0 → jupyter_analysis_tools-1.7.2}/src/jupyter_analysis_tools/binning.py +0 -0
  32. {jupyter_analysis_tools-1.7.0 → jupyter_analysis_tools-1.7.2}/src/jupyter_analysis_tools/datalocations.py +0 -0
  33. {jupyter_analysis_tools-1.7.0 → jupyter_analysis_tools-1.7.2}/src/jupyter_analysis_tools/distrib.py +0 -0
  34. {jupyter_analysis_tools-1.7.0 → jupyter_analysis_tools-1.7.2}/src/jupyter_analysis_tools/git.py +0 -0
  35. {jupyter_analysis_tools-1.7.0 → jupyter_analysis_tools-1.7.2}/src/jupyter_analysis_tools/plotting.py +0 -0
  36. {jupyter_analysis_tools-1.7.0 → jupyter_analysis_tools-1.7.2}/src/jupyter_analysis_tools/readdata.py +0 -0
  37. {jupyter_analysis_tools-1.7.0 → jupyter_analysis_tools-1.7.2}/src/jupyter_analysis_tools/ssfz2json.py +0 -0
  38. {jupyter_analysis_tools-1.7.0 → jupyter_analysis_tools-1.7.2}/src/jupyter_analysis_tools/ssfz_compare.py +0 -0
  39. {jupyter_analysis_tools-1.7.0 → jupyter_analysis_tools-1.7.2}/src/jupyter_analysis_tools/utils.py +0 -0
  40. {jupyter_analysis_tools-1.7.0 → jupyter_analysis_tools-1.7.2}/src/jupyter_analysis_tools/widgets.py +0 -0
  41. {jupyter_analysis_tools-1.7.0 → jupyter_analysis_tools-1.7.2}/src/jupyter_analysis_tools.egg-info/SOURCES.txt +0 -0
  42. {jupyter_analysis_tools-1.7.0 → jupyter_analysis_tools-1.7.2}/src/jupyter_analysis_tools.egg-info/dependency_links.txt +0 -0
  43. {jupyter_analysis_tools-1.7.0 → jupyter_analysis_tools-1.7.2}/src/jupyter_analysis_tools.egg-info/entry_points.txt +0 -0
  44. {jupyter_analysis_tools-1.7.0 → jupyter_analysis_tools-1.7.2}/src/jupyter_analysis_tools.egg-info/requires.txt +0 -0
  45. {jupyter_analysis_tools-1.7.0 → jupyter_analysis_tools-1.7.2}/src/jupyter_analysis_tools.egg-info/top_level.txt +0 -0
  46. {jupyter_analysis_tools-1.7.0 → jupyter_analysis_tools-1.7.2}/templates/CHANGELOG.md.j2 +0 -0
  47. {jupyter_analysis_tools-1.7.0 → jupyter_analysis_tools-1.7.2}/testdata/2015-03-20-Silica.ssf.json +0 -0
  48. {jupyter_analysis_tools-1.7.0 → jupyter_analysis_tools-1.7.2}/testdata/2015-03-20-Silica.ssfz +0 -0
  49. {jupyter_analysis_tools-1.7.0 → jupyter_analysis_tools-1.7.2}/testdata/S2842 water.json +0 -0
  50. {jupyter_analysis_tools-1.7.0 → jupyter_analysis_tools-1.7.2}/testdata/S2842 water.pdh +0 -0
  51. {jupyter_analysis_tools-1.7.0 → jupyter_analysis_tools-1.7.2}/testdata/S2843[9].pdh +0 -0
  52. {jupyter_analysis_tools-1.7.0 → jupyter_analysis_tools-1.7.2}/tests/readdata.py +0 -0
  53. {jupyter_analysis_tools-1.7.0 → jupyter_analysis_tools-1.7.2}/tests/requirements.txt +0 -0
  54. {jupyter_analysis_tools-1.7.0 → jupyter_analysis_tools-1.7.2}/tests/utils.py +0 -0
  55. {jupyter_analysis_tools-1.7.0 → jupyter_analysis_tools-1.7.2}/tox.ini +0 -0
@@ -22,4 +22,4 @@ repo_main_branch: main
22
22
  repo_name: jupyter-analysis-tools
23
23
  repo_userorg: BAMresearch
24
24
  sphinx_theme: furo
25
- version: 1.7.0
25
+ version: 1.7.2
@@ -1,5 +1,21 @@
1
1
  # CHANGELOG
2
2
 
3
+ ## v1.7.2 (2026-01-30)
4
+
5
+ ### Bug fixes
6
+
7
+ * **DataStore.__init__**: allow token-based auth, docs added ([`d64f92f`](https://github.com/BAMresearch/jupyter-analysis-tools/commit/d64f92fdcba4851497ff6782e4beee1109eb86bd))
8
+
9
+ ### Code style
10
+
11
+ * **DataStore**: whitespace errors ([`f599040`](https://github.com/BAMresearch/jupyter-analysis-tools/commit/f5990401c80ec30a2748a5e2952ab806def207ac))
12
+
13
+ ## v1.7.1 (2025-12-11)
14
+
15
+ ### Bug fixes
16
+
17
+ * **DataStore.createObject(**: attempt to create new project/collection only if object was not found; save it finally as well ([`95fa9e0`](https://github.com/BAMresearch/jupyter-analysis-tools/commit/95fa9e042a26b423936caac64d3e8b115790e888))
18
+
3
19
  ## v1.7.0 (2025-12-11)
4
20
 
5
21
  ### Code style
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: jupyter-analysis-tools
3
- Version: 1.7.0
3
+ Version: 1.7.2
4
4
  Summary: Yet another Python library with helpers and utilities for data analysis and processing.
5
5
  Author-email: Ingo Breßler <ingo.bressler@bam.de>, "Brian R. Pauw" <brian.pauw@bam.de>
6
6
  License-Expression: MIT
@@ -36,10 +36,10 @@ Requires-Dist: ipywidgets
36
36
  Requires-Dist: pybis
37
37
  Dynamic: license-file
38
38
 
39
- # Jupyter Analysis Tools (v1.7.0)
39
+ # Jupyter Analysis Tools (v1.7.2)
40
40
 
41
41
  [![PyPI Package latest release](https://img.shields.io/pypi/v/jupyter-analysis-tools.svg)](https://pypi.org/project/jupyter-analysis-tools)
42
- [![Commits since latest release](https://img.shields.io/github/commits-since/BAMresearch/jupyter-analysis-tools/v1.7.0.svg)](https://github.com/BAMresearch/jupyter-analysis-tools/compare/v1.7.0...main)
42
+ [![Commits since latest release](https://img.shields.io/github/commits-since/BAMresearch/jupyter-analysis-tools/v1.7.2.svg)](https://github.com/BAMresearch/jupyter-analysis-tools/compare/v1.7.2...main)
43
43
  [![License](https://img.shields.io/pypi/l/jupyter-analysis-tools.svg)](https://en.wikipedia.org/wiki/MIT_license)
44
44
  [![Supported versions](https://img.shields.io/pypi/pyversions/jupyter-analysis-tools.svg)](https://pypi.org/project/jupyter-analysis-tools)
45
45
  [![PyPI Wheel](https://img.shields.io/pypi/wheel/jupyter-analysis-tools.svg)](https://pypi.org/project/jupyter-analysis-tools#files)
@@ -98,6 +98,22 @@ are installed:
98
98
 
99
99
  # CHANGELOG
100
100
 
101
+ ## v1.7.2 (2026-01-30)
102
+
103
+ ### Bug fixes
104
+
105
+ * **DataStore.__init__**: allow token-based auth, docs added ([`d64f92f`](https://github.com/BAMresearch/jupyter-analysis-tools/commit/d64f92fdcba4851497ff6782e4beee1109eb86bd))
106
+
107
+ ### Code style
108
+
109
+ * **DataStore**: whitespace errors ([`f599040`](https://github.com/BAMresearch/jupyter-analysis-tools/commit/f5990401c80ec30a2748a5e2952ab806def207ac))
110
+
111
+ ## v1.7.1 (2025-12-11)
112
+
113
+ ### Bug fixes
114
+
115
+ * **DataStore.createObject(**: attempt to create new project/collection only if object was not found; save it finally as well ([`95fa9e0`](https://github.com/BAMresearch/jupyter-analysis-tools/commit/95fa9e042a26b423936caac64d3e8b115790e888))
116
+
101
117
  ## v1.7.0 (2025-12-11)
102
118
 
103
119
  ### Code style
@@ -1,7 +1,7 @@
1
- # Jupyter Analysis Tools (v1.7.0)
1
+ # Jupyter Analysis Tools (v1.7.2)
2
2
 
3
3
  [![PyPI Package latest release](https://img.shields.io/pypi/v/jupyter-analysis-tools.svg)](https://pypi.org/project/jupyter-analysis-tools)
4
- [![Commits since latest release](https://img.shields.io/github/commits-since/BAMresearch/jupyter-analysis-tools/v1.7.0.svg)](https://github.com/BAMresearch/jupyter-analysis-tools/compare/v1.7.0...main)
4
+ [![Commits since latest release](https://img.shields.io/github/commits-since/BAMresearch/jupyter-analysis-tools/v1.7.2.svg)](https://github.com/BAMresearch/jupyter-analysis-tools/compare/v1.7.2...main)
5
5
  [![License](https://img.shields.io/pypi/l/jupyter-analysis-tools.svg)](https://en.wikipedia.org/wiki/MIT_license)
6
6
  [![Supported versions](https://img.shields.io/pypi/pyversions/jupyter-analysis-tools.svg)](https://pypi.org/project/jupyter-analysis-tools)
7
7
  [![PyPI Wheel](https://img.shields.io/pypi/wheel/jupyter-analysis-tools.svg)](https://pypi.org/project/jupyter-analysis-tools#files)
@@ -32,7 +32,7 @@ project = "Jupyter Analysis Tools"
32
32
  year = "2018-2025"
33
33
  author = "Ingo Breßler and Brian R. Pauw"
34
34
  copyright = "{0}, {1}".format(year, author)
35
- version = "1.7.0"
35
+ version = "1.7.2"
36
36
  release = version
37
37
  commit_id = None
38
38
  try:
@@ -1,7 +1,7 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  # __init__.py
3
3
 
4
- __version__ = "1.7.0"
4
+ __version__ = "1.7.2"
5
5
 
6
6
  from .binning import reBin
7
7
  from .git import checkRepo, isNBstripoutActivated, isNBstripoutInstalled, isRepo
@@ -1,173 +1,201 @@
1
- # -*- coding: utf-8 -*-
2
- # datastore.py
3
-
4
- import filecmp
5
- import getpass
6
- import tempfile
7
- import warnings
8
- from pathlib import Path
9
-
10
- from pybis import Openbis
11
-
12
-
13
- class DataStore:
14
- url = None
15
- token = None
16
- _availObj = None
17
- _userspace = None
18
-
19
- def __init__(self, url, username=None, tokenValidTo=None):
20
- self.url = url
21
- self.username = username
22
- if self.username is None:
23
- self.username = getpass.getuser()
24
- print(f"Working as user '{self.username}'.")
25
- # to generate PAT you need to login normally
26
- self.ds = Openbis(url=self.url, verify_certificates=True)
27
- # arg. *save_token* saves the openBIS token to ~/.pybis permanently
28
- self.ds.login(
29
- self.username,
30
- getpass.getpass(prompt=f"Password for {self.username}: "),
31
- save_token=False,
32
- )
33
- # create the PAT with the given name, don't store it
34
- self.token = self.ds.get_or_create_personal_access_token(
35
- "test-session", validTo=tokenValidTo
36
- )
37
-
38
- @property
39
- def userspace(self):
40
- uspace = self._userspace
41
- if uspace is None:
42
- allspaces = self.ds.get_spaces()
43
- uspace = allspaces.df[
44
- allspaces.df.code.str.endswith(self.username.upper())
45
- ].code.values[0]
46
- self._userspace = uspace
47
- return uspace
48
-
49
- @userspace.setter
50
- def userspace(self, name):
51
- name = name.upper()
52
- if name in self.ds.get_spaces().df.code.values:
53
- self._userspace = name
54
-
55
- @staticmethod
56
- def identifier(objects, code):
57
- return objects[objects.code == code].identifier.tolist()[0]
58
-
59
- def createProject(self, projectName, space, spacePrefix=None):
60
- """Finds the requested project in the DataStore.
61
- Matching project names can be limited to a given *spacePrefix*.
62
- If the project is not found, a new project with the given code in the given space
63
- is created."""
64
- # get available projects, accessible by the current user
65
- projectsAvail = self.ds.get_projects()
66
- if spacePrefix:
67
- projectsAvail = [prj for prj in projectsAvail if f"/{spacePrefix}_" in prj.identifier]
68
- projects = [prj for prj in projectsAvail if prj.code == projectName]
69
- assert len(projects) <= 1, f"Multiple projects found for '{projectName}'"
70
- dsProject = None
71
- if len(projects): # get the existing object
72
- dsProject = projects[0]
73
- else: # create it, if not found
74
- print(f"Creating project '{projectName}'")
75
- dsProject = self.ds.new_project(code=projectName, space=space)
76
- dsProject.save()
77
- assert dsProject
78
- return dsProject
79
-
80
- def createCollection(self, collName, projectObj, defaultObjType=None):
81
- collections = self.ds.get_collections(project=projectObj)
82
- dsColl = [coll for coll in collections if coll.code == collName.upper()]
83
- if len(dsColl):
84
- dsColl = dsColl[0]
85
- else: # create it, if not found
86
- print(f"Creating collection '{collName}'")
87
- dsColl = self.ds.new_collection(
88
- code=collName, type="COLLECTION", project=projectObj, props={"$name": collName}
89
- )
90
- dsColl.save()
91
- assert dsColl
92
- # update properties (name, default view and object type) if not set)
93
- props = dsColl.props.all() # props as dict
94
- propKey = "$name"
95
- if propKey in props and props[propKey] is None:
96
- props[propKey] = collName
97
- propKey = "$default_collection_view"
98
- if propKey in props.keys() and props[propKey] is None:
99
- propVal = [
100
- item
101
- for item in self.ds.get_vocabulary(propKey + "s").get_terms().df.code
102
- if "list" in item.lower()
103
- ]
104
- assert len(propVal)
105
- props[propKey] = propVal[0]
106
- if defaultObjType:
107
- propKey = "$default_object_type"
108
- if propKey in props.keys() and props[propKey] is None:
109
- props[propKey] = defaultObjType
110
- # print(f"Setting '{collName}' properties:\n {props}")
111
- dsColl.set_props(props)
112
- dsColl.save()
113
- return dsColl
114
-
115
- def createObject(
116
- self,
117
- projectName: str,
118
- collectionName: str = None,
119
- space: str = None,
120
- spacePrefix: str = None,
121
- objType: str = None,
122
- props: dict = None,
123
- ):
124
- dsProject = self.createProject(projectName, space, spacePrefix=spacePrefix)
125
- dsColl = None
126
- if collectionName is None: # collectionName is required
127
- return None
128
- dsColl = self.createCollection(collectionName, dsProject, defaultObjType=objType)
129
- obj = self.ds.get_objects(type=objType, where={"$name": props["$name"]}).objects
130
- if len(obj):
131
- obj = obj[0]
132
- prefix = objType
133
- msg = "'{}' exists already in {}! Updating ...".format(
134
- obj.props["$name"], obj.project.identifier
135
- )
136
- warnings.warn_explicit(msg, UserWarning, prefix, 0)
137
- else: # does not exist yet
138
- objName = f" '{props['$name']}'" if len(props.get("$name", "")) else ""
139
- print(f"Creating new {objType}{objName} in {dsColl.identifier}")
140
- obj = self.ds.new_object(type=objType, props=props, collection=dsColl)
141
- obj.set_props(props)
142
- return obj
143
-
144
- def findObjects(self, *args, **kwargs):
145
- return self.ds.get_objects(**kwargs)
146
-
147
- def uploadDataset(self, obj, datasetType, fpaths=[]):
148
- def _checkFile(localPath, remoteFiles):
149
- remoteFile = [f for f in remoteFiles if f.name == localPath.name]
150
- if not len(remoteFile): # file exists in the dataset as well
151
- return False
152
- return filecmp.cmp(localPath, remoteFile[0], shallow=False)
153
-
154
- if not len(fpaths):
155
- return # nothing to do
156
- for dataset in obj.get_datasets(type=datasetType):
157
- with tempfile.TemporaryDirectory() as tempdir:
158
- dataset.download(destination=tempdir)
159
- dsFiles = [f for f in Path(tempdir).rglob("*") if f.is_file()]
160
- if len(fpaths) == len(dsFiles):
161
- if all([_checkFile(fpath, dsFiles) for fpath in fpaths]):
162
- print(
163
- f"All local files of {datasetType} match files in dataset, "
164
- "not updating."
165
- )
166
- continue # skip deletion below
167
- print(f"Dataset {datasetType} needs update, deleting existing dataset:")
168
- dataset.delete("Needs update")
169
- if not len(obj.get_datasets(type=datasetType)): # didn't exist yet or all deleted
170
- dataset = self.ds.new_dataset(
171
- type=datasetType, collection=obj.collection, object=obj, files=fpaths
172
- )
173
- dataset.save()
1
+ # -*- coding: utf-8 -*-
2
+ # datastore.py
3
+
4
+ import filecmp
5
+ import getpass
6
+ import tempfile
7
+ import warnings
8
+ from pathlib import Path
9
+
10
+ from pybis import Openbis
11
+
12
+
13
+ class DataStore:
14
+ url = None
15
+ token = None
16
+ _availObj = None
17
+ _userspace = None
18
+
19
+ def __init__(self, url, username=None, token=None, tokenValidTo=None):
20
+ """
21
+ Initialize the datastore connection and authenticate with openBIS.
22
+
23
+ Args:
24
+ url (str): The URL of the openBIS server.
25
+ username (str, optional): The username for authentication.
26
+ Defaults to the current system user if not provided.
27
+ It will as for the password interactively.
28
+ token (str, optional): A personal access token as retrieved by DataStore.token earlier
29
+ as alternative to username/password authentication.
30
+ tokenValidTo (str, optional): The expiration datetime for the new personal access token
31
+ when it is created using username/password authentication.
32
+
33
+ Raises:
34
+ Exception: If authentication fails or connection to the server cannot be established.
35
+
36
+ Note:
37
+ - If username is not provided, it defaults to the current system user.
38
+ - Password is prompted interactively via getpass.
39
+ - A personal access token named "test-session" is automatically created/retrieved.
40
+ - Token is not persisted to disk.
41
+ """
42
+ self.url = url
43
+ self.username = username
44
+ if self.username is None:
45
+ self.username = getpass.getuser()
46
+ print(f"Working as user '{self.username}'.")
47
+ # to generate PAT you need to login normally
48
+ self.ds = Openbis(url=self.url, verify_certificates=True)
49
+ # arg. *save_token* saves the openBIS token to ~/.pybis permanently
50
+ if token is not None:
51
+ if hasattr(token, "permId"):
52
+ token = token.permId
53
+ self.ds.set_token(token)
54
+ else: # username/password login
55
+ self.ds.login(
56
+ self.username,
57
+ getpass.getpass(prompt=f"Password for {self.username}: "),
58
+ save_token=False,
59
+ )
60
+ # create the PAT with the given name, don't store it
61
+ self.token = self.ds.get_or_create_personal_access_token(
62
+ "test-session", validTo=tokenValidTo
63
+ )
64
+
65
+ @property
66
+ def userspace(self):
67
+ uspace = self._userspace
68
+ if uspace is None:
69
+ allspaces = self.ds.get_spaces()
70
+ uspace = allspaces.df[
71
+ allspaces.df.code.str.endswith(self.username.upper())
72
+ ].code.values[0]
73
+ self._userspace = uspace
74
+ return uspace
75
+
76
+ @userspace.setter
77
+ def userspace(self, name):
78
+ name = name.upper()
79
+ if name in self.ds.get_spaces().df.code.values:
80
+ self._userspace = name
81
+
82
+ @staticmethod
83
+ def identifier(objects, code):
84
+ return objects[objects.code == code].identifier.tolist()[0]
85
+
86
+ def createProject(self, projectName, space, spacePrefix=None):
87
+ """Finds the requested project in the DataStore.
88
+ Matching project names can be limited to a given *spacePrefix*.
89
+ If the project is not found, a new project with the given code in the given space
90
+ is created."""
91
+ # get available projects, accessible by the current user
92
+ projectsAvail = self.ds.get_projects()
93
+ if spacePrefix:
94
+ projectsAvail = [prj for prj in projectsAvail if f"/{spacePrefix}_" in prj.identifier]
95
+ projects = [prj for prj in projectsAvail if prj.code == projectName]
96
+ assert len(projects) <= 1, f"Multiple projects found for '{projectName}'"
97
+ dsProject = None
98
+ if len(projects): # get the existing object
99
+ dsProject = projects[0]
100
+ else: # create it, if not found
101
+ print(f"Creating project '{projectName}'")
102
+ dsProject = self.ds.new_project(code=projectName, space=space)
103
+ dsProject.save()
104
+ assert dsProject
105
+ return dsProject
106
+
107
+ def createCollection(self, collName, projectObj, defaultObjType=None):
108
+ collections = self.ds.get_collections(project=projectObj)
109
+ dsColl = [coll for coll in collections if coll.code == collName.upper()]
110
+ if len(dsColl):
111
+ dsColl = dsColl[0]
112
+ else: # create it, if not found
113
+ print(f"Creating collection '{collName}'")
114
+ dsColl = self.ds.new_collection(
115
+ code=collName, type="COLLECTION", project=projectObj, props={"$name": collName}
116
+ )
117
+ dsColl.save()
118
+ assert dsColl
119
+ # update properties (name, default view and object type) if not set)
120
+ props = dsColl.props.all() # props as dict
121
+ propKey = "$name"
122
+ if propKey in props and props[propKey] is None:
123
+ props[propKey] = collName
124
+ propKey = "$default_collection_view"
125
+ if propKey in props.keys() and props[propKey] is None:
126
+ propVal = [
127
+ item
128
+ for item in self.ds.get_vocabulary(propKey + "s").get_terms().df.code
129
+ if "list" in item.lower()
130
+ ]
131
+ assert len(propVal)
132
+ props[propKey] = propVal[0]
133
+ if defaultObjType:
134
+ propKey = "$default_object_type"
135
+ if propKey in props.keys() and props[propKey] is None:
136
+ props[propKey] = defaultObjType
137
+ # print(f"Setting '{collName}' properties:\n {props}")
138
+ dsColl.set_props(props)
139
+ dsColl.save()
140
+ return dsColl
141
+
142
+ def createObject(
143
+ self,
144
+ projectName: str,
145
+ collectionName: str = None,
146
+ space: str = None,
147
+ spacePrefix: str = None,
148
+ objType: str = None,
149
+ props: dict = None,
150
+ ):
151
+ assert space and len(space), "space is required!"
152
+ assert projectName and len(projectName), "projectName is required!"
153
+ assert collectionName and len(collectionName), "collectionName is required!"
154
+ obj = self.ds.get_objects(type=objType, where={"$name": props["$name"]}).objects
155
+ if len(obj):
156
+ obj = obj[0]
157
+ prefix = objType
158
+ msg = "'{}' exists already in {}! Updating ...".format(
159
+ obj.props["$name"], obj.project.identifier
160
+ )
161
+ warnings.warn_explicit(msg, UserWarning, prefix, 0)
162
+ else: # does not exist yet
163
+ dsProject = self.createProject(projectName, space, spacePrefix=spacePrefix)
164
+ dsColl = self.createCollection(collectionName, dsProject, defaultObjType=objType)
165
+ objName = f" '{props['$name']}'" if len(props.get("$name", "")) else ""
166
+ print(f"Creating new {objType}{objName} in {dsColl.identifier}")
167
+ obj = self.ds.new_object(type=objType, props=props, collection=dsColl)
168
+ obj.set_props(props)
169
+ obj.save()
170
+ return obj
171
+
172
+ def findObjects(self, *args, **kwargs):
173
+ return self.ds.get_objects(**kwargs)
174
+
175
+ def uploadDataset(self, obj, datasetType, fpaths=[]):
176
+ def _checkFile(localPath, remoteFiles):
177
+ remoteFile = [f for f in remoteFiles if f.name == localPath.name]
178
+ if not len(remoteFile): # file exists in the dataset as well
179
+ return False
180
+ return filecmp.cmp(localPath, remoteFile[0], shallow=False)
181
+
182
+ if not len(fpaths):
183
+ return # nothing to do
184
+ for dataset in obj.get_datasets(type=datasetType):
185
+ with tempfile.TemporaryDirectory() as tempdir:
186
+ dataset.download(destination=tempdir)
187
+ dsFiles = [f for f in Path(tempdir).rglob("*") if f.is_file()]
188
+ if len(fpaths) == len(dsFiles):
189
+ if all([_checkFile(fpath, dsFiles) for fpath in fpaths]):
190
+ print(
191
+ f"All local files of {datasetType} match files in dataset, "
192
+ "not updating."
193
+ )
194
+ continue # skip deletion below
195
+ print(f"Dataset {datasetType} needs update, deleting existing dataset:")
196
+ dataset.delete("Needs update")
197
+ if not len(obj.get_datasets(type=datasetType)): # didn't exist yet or all deleted
198
+ dataset = self.ds.new_dataset(
199
+ type=datasetType, collection=obj.collection, object=obj, files=fpaths
200
+ )
201
+ dataset.save()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: jupyter-analysis-tools
3
- Version: 1.7.0
3
+ Version: 1.7.2
4
4
  Summary: Yet another Python library with helpers and utilities for data analysis and processing.
5
5
  Author-email: Ingo Breßler <ingo.bressler@bam.de>, "Brian R. Pauw" <brian.pauw@bam.de>
6
6
  License-Expression: MIT
@@ -36,10 +36,10 @@ Requires-Dist: ipywidgets
36
36
  Requires-Dist: pybis
37
37
  Dynamic: license-file
38
38
 
39
- # Jupyter Analysis Tools (v1.7.0)
39
+ # Jupyter Analysis Tools (v1.7.2)
40
40
 
41
41
  [![PyPI Package latest release](https://img.shields.io/pypi/v/jupyter-analysis-tools.svg)](https://pypi.org/project/jupyter-analysis-tools)
42
- [![Commits since latest release](https://img.shields.io/github/commits-since/BAMresearch/jupyter-analysis-tools/v1.7.0.svg)](https://github.com/BAMresearch/jupyter-analysis-tools/compare/v1.7.0...main)
42
+ [![Commits since latest release](https://img.shields.io/github/commits-since/BAMresearch/jupyter-analysis-tools/v1.7.2.svg)](https://github.com/BAMresearch/jupyter-analysis-tools/compare/v1.7.2...main)
43
43
  [![License](https://img.shields.io/pypi/l/jupyter-analysis-tools.svg)](https://en.wikipedia.org/wiki/MIT_license)
44
44
  [![Supported versions](https://img.shields.io/pypi/pyversions/jupyter-analysis-tools.svg)](https://pypi.org/project/jupyter-analysis-tools)
45
45
  [![PyPI Wheel](https://img.shields.io/pypi/wheel/jupyter-analysis-tools.svg)](https://pypi.org/project/jupyter-analysis-tools#files)
@@ -98,6 +98,22 @@ are installed:
98
98
 
99
99
  # CHANGELOG
100
100
 
101
+ ## v1.7.2 (2026-01-30)
102
+
103
+ ### Bug fixes
104
+
105
+ * **DataStore.__init__**: allow token-based auth, docs added ([`d64f92f`](https://github.com/BAMresearch/jupyter-analysis-tools/commit/d64f92fdcba4851497ff6782e4beee1109eb86bd))
106
+
107
+ ### Code style
108
+
109
+ * **DataStore**: whitespace errors ([`f599040`](https://github.com/BAMresearch/jupyter-analysis-tools/commit/f5990401c80ec30a2748a5e2952ab806def207ac))
110
+
111
+ ## v1.7.1 (2025-12-11)
112
+
113
+ ### Bug fixes
114
+
115
+ * **DataStore.createObject(**: attempt to create new project/collection only if object was not found; save it finally as well ([`95fa9e0`](https://github.com/BAMresearch/jupyter-analysis-tools/commit/95fa9e042a26b423936caac64d3e8b115790e888))
116
+
101
117
  ## v1.7.0 (2025-12-11)
102
118
 
103
119
  ### Code style