kalbio 0.2.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.
kalbio/workspace.py ADDED
@@ -0,0 +1,315 @@
1
+ """Provides models and services for managing workspaces in the Kaleidoscope system.
2
+
3
+ This module contains data models for workspaces, workspace users, workspace groups,
4
+ and workspace events, along with a service class for interacting with workspace-related
5
+ API endpoints.
6
+
7
+ Classes:
8
+ WorkspaceAccessLevelEnum: Enumeration of possible access levels for workspace users.
9
+ Workspace: Model representing a workspace with its basic metadata.
10
+ WorkspaceUser: Model representing a user within a workspace, including access level.
11
+ WorkspaceGroup: Model representing a group of users within a workspace.
12
+ WorkspaceEvent: Model representing an event that occurred within a workspace.
13
+ WorkspaceService: Service class providing methods to interact with workspace API endpoints.
14
+
15
+ Example:
16
+ ```python
17
+ # get an instance of the workspace
18
+ workspace = client.workspace.get_workspace()
19
+
20
+ # get all members in the workspace
21
+ members = client.workspace.get_members()
22
+
23
+ # get all events, with search criteria
24
+ events = client.workspace.get_events(event_types=["create", "update"])
25
+ ```
26
+ """
27
+
28
+ from datetime import datetime
29
+ import json
30
+ import logging
31
+ from enum import Enum
32
+ from functools import lru_cache
33
+ from typing import List, Optional, TypedDict, Unpack
34
+ from kalbio._kaleidoscope_model import _KaleidoscopeBaseModel
35
+ from kalbio.client import KaleidoscopeClient
36
+ from pydantic import TypeAdapter
37
+
38
+ _logger = logging.getLogger(__name__)
39
+
40
+
41
+ class WorkspaceAccessLevelEnum(str, Enum):
42
+ """Enumeration of possible access levels for workspace users.
43
+
44
+ Attributes:
45
+ ADMIN: Administrator access with full privileges.
46
+ TEAM_MEMBER: Regular team member access.
47
+ GUEST: Guest access with limited privileges.
48
+ VIEWER: Read-only viewer access.
49
+ DEACTIVATED: Deactivated user status.
50
+ """
51
+
52
+ ADMIN = "admin"
53
+ TEAM_MEMBER = "team-member"
54
+ GUEST = "guest"
55
+ VIEWER = "viewer"
56
+ DEACTIVATED = "deactivated"
57
+
58
+
59
+ class Workspace(_KaleidoscopeBaseModel):
60
+ """A model representing a workspace in the Kaleidoscope system.
61
+
62
+ This class extends _KaleidoscopeBaseModel and provides a structured representation
63
+ of a workspace with its associated metadata and utility methods for serialization
64
+ and string representation.
65
+
66
+ Attributes:
67
+ id (str): UUID of the workspace
68
+ workspace_name (str): The name identifier for the workspace.
69
+ """
70
+
71
+ workspace_name: str
72
+
73
+ def __str__(self):
74
+ return f"{self.workspace_name}"
75
+
76
+
77
+ class WorkspaceUser(_KaleidoscopeBaseModel):
78
+ """Represents a user within a workspace with their access permissions and contact information.
79
+
80
+ This class models a workspace user, storing their identification, name preferences,
81
+ access level, and email address. It inherits from _KaleidoscopeBaseModel and provides
82
+ utility methods for serialization and string representation.
83
+
84
+ Attributes:
85
+ full_name (Optional[str]): The user's full legal or registered name.
86
+ preferred_name (Optional[str]): The name the user prefers to be called.
87
+ access_level (WorkspaceAccessLevelEnum): The user's permission level within the workspace.
88
+ email (str): The user's email address for communication and identification.
89
+ """
90
+
91
+ full_name: Optional[str]
92
+ preferred_name: Optional[str]
93
+ access_level: WorkspaceAccessLevelEnum
94
+ email: str
95
+
96
+ def __str__(self):
97
+ return f"{self.full_name}"
98
+
99
+
100
+ class WorkspaceGroup(_KaleidoscopeBaseModel):
101
+ """Represents a workspace group in the Kaleidoscope system.
102
+
103
+ A WorkspaceGroup is a collection of users and programs that are organized together
104
+ under a common group name and associated email address.
105
+
106
+ Attributes:
107
+ group_name (str): The name of the workspace group.
108
+ user_ids (List[str]): A list of user IDs that belong to this workspace group.
109
+ program_ids (List[str]): A list of program IDs associated with this workspace group.
110
+ email (str): The email address associated with this workspace group.
111
+ """
112
+
113
+ group_name: str
114
+ user_ids: List[str]
115
+ program_ids: List[str]
116
+ email: str
117
+
118
+ def __str__(self):
119
+ return f"{self.group_name}"
120
+
121
+
122
+ class WorkspaceEvent(_KaleidoscopeBaseModel):
123
+ """Represents a workspace event in the Kaleidoscope system.
124
+
125
+ This class models events that occur within a workspace, such as user actions,
126
+ resource modifications, or system-generated events. It inherits from
127
+ _KaleidoscopeBaseModel and provides methods for serialization and representation.
128
+
129
+ Attributes:
130
+ full_name (str): The full name of the user associated with the event.
131
+ preferred_name (Optional[str]): The preferred name of the user, if available.
132
+ is_bot (bool): Flag indicating whether the event was triggered by a bot.
133
+ event_attrs (dict): Additional attributes specific to the event type.
134
+ created_at (datetime): Timestamp when the event was created.
135
+ resource_id (Optional[str]): ID of the resource associated with the event, if applicable.
136
+ resource_type (Optional[str]): Type of the resource associated with the event, if applicable.
137
+ event_type (str): The type/category of the event.
138
+ event_type_version (int): Version number of the event type schema.
139
+ event_user_id (str): The unique identifier of the user who triggered the event.
140
+ parent_bulk_event_id (str): ID of the parent event if this is part of a bulk operation.
141
+ is_bulk (bool): Flag indicating whether this is a bulk event.
142
+ """
143
+
144
+ full_name: str
145
+ preferred_name: Optional[str]
146
+ is_bot: bool
147
+ event_attrs: dict
148
+ created_at: datetime
149
+ resource_id: Optional[str]
150
+ resource_type: Optional[str]
151
+ event_type: str
152
+ event_type_version: int
153
+ event_user_id: str
154
+ parent_bulk_event_id: str
155
+ is_bulk: bool
156
+
157
+ def __str__(self):
158
+ return f"{self.id}:{self.event_type}"
159
+
160
+
161
+ class WorkspaceService:
162
+ """Service class for managing workspace-related operations in Kaleidoscope.
163
+
164
+ This service provides methods to retrieve workspace information, members, groups,
165
+ and events. It uses caching (via lru_cache) for frequently accessed data like
166
+ workspace details, members, and groups to improve performance.
167
+
168
+ Example:
169
+ ```python
170
+ # get an instance of the workspace
171
+ workspace = client.workspace.get_workspace()
172
+
173
+ # get all members in the workspace
174
+ members = client.workspace.get_members()
175
+
176
+ # get all events, with search criteria
177
+ events = client.workspace.get_events(event_types=["create", "update"])
178
+ ```
179
+
180
+ Note:
181
+ Cached methods (get_workspace, get_members, get_groups) will automatically
182
+ clear their cache on error to ensure stale data is not returned on subsequent calls.
183
+ """
184
+
185
+ def __init__(self, client: KaleidoscopeClient):
186
+ self._client = client
187
+
188
+ @lru_cache
189
+ def get_workspace(self) -> Workspace | None:
190
+ """Retrieves the authenticated workspace.
191
+
192
+ This method caches its values.
193
+
194
+ Returns:
195
+ (Workspace | None): The active workspace, or None if an error occurs.
196
+ """
197
+ try:
198
+ resp = self._client._get("/workspaces/active")
199
+ return Workspace.model_validate(resp)
200
+ except Exception as e:
201
+ _logger.error(f"Error fetching workspace: {e}")
202
+ self.get_workspace.cache_clear()
203
+ return None
204
+
205
+ @lru_cache
206
+ def get_members(self) -> List[WorkspaceUser]:
207
+ """Retrieves the members of the authenticated workspace.
208
+
209
+ This method caches its values.
210
+
211
+ Returns:
212
+ List[WorkspaceUser]: The users in the workspace.
213
+
214
+ Note:
215
+ If an exception occurs during the API request, it logs the error,
216
+ clears the cache, and returns an empty list.
217
+ """
218
+ try:
219
+ resp = self._client._get("/workspaces/members")
220
+ return TypeAdapter(List[WorkspaceUser]).validate_python(resp)
221
+ except Exception as e:
222
+ _logger.error(f"Error fetching members: {e}")
223
+ self.get_members.cache_clear()
224
+ return []
225
+
226
+ def get_members_by_ids(self, ids: List[str]) -> List[WorkspaceUser]:
227
+ """Retrieves a list of WorkspaceUser objects whose IDs match the provided list.
228
+
229
+ Args:
230
+ ids (List[str]): A list of member IDs to filter by.
231
+
232
+ Returns:
233
+ List[WorkspaceUser]: A list of WorkspaceUser instances with IDs found in ids.
234
+ """
235
+ return [member for member in self.get_members() if member.id in ids]
236
+
237
+ @lru_cache
238
+ def get_groups(self) -> List[WorkspaceGroup]:
239
+ """Retrieves the groups of the authenticated workspace.
240
+
241
+ This method caches its values.
242
+
243
+ Returns:
244
+ List[WorkspaceGroup]: The groups in the workspace.
245
+
246
+ Note:
247
+ If an exception occurs during the API request, it logs the error,
248
+ clears the cache, and returns an empty list.
249
+ """
250
+ try:
251
+ resp = self._client._get("/workspaces/groups")
252
+ return TypeAdapter(List[WorkspaceGroup]).validate_python(resp)
253
+ except Exception as e:
254
+ _logger.error(f"Error fetching groups: {e}")
255
+ self.get_groups.cache_clear()
256
+ return []
257
+
258
+ def get_groups_by_ids(self, ids: List[str]) -> List[WorkspaceGroup]:
259
+ """Retrieves a list of WorkspaceGroup objects whose IDs match the provided list.
260
+
261
+ Args:
262
+ ids (List[str]): A list of group IDs to filter by.
263
+
264
+ Returns:
265
+ List[WorkspaceGroup]: A list of WorkspaceGroup instances with IDs found in ids.
266
+ """
267
+ return [group for group in self.get_groups() if group.id in ids]
268
+
269
+ class EventsQuery(TypedDict):
270
+ """TypedDict for workspace events query parameters.
271
+
272
+ Attributes:
273
+ page (Optional[int]): The page number for pagination.
274
+ page_size (Optional[int]): The number of items per page.
275
+ event_types (List[str]): List of event types to filter by.
276
+ resource_type (str): The type of resource to filter by.
277
+ event_user_ids (list[str]): List of user IDs to filter events by.
278
+ after_date (datetime): Filter events occurring after this date.
279
+ before_date (datetime): Filter events occurring before this date.
280
+ """
281
+
282
+ page: Optional[int]
283
+ page_size: Optional[int]
284
+ event_types: List[str]
285
+ resource_type: str
286
+ event_user_ids: list[str]
287
+ after_date: datetime
288
+ before_date: datetime
289
+
290
+ def get_events(self, **params: Unpack[EventsQuery]) -> List[WorkspaceEvent]:
291
+ """Searches for events using the provided query parameters.
292
+
293
+ Args:
294
+ **params (Unpack[EventsQuery]): Keyword arguments representing search criteria. Non-string values will be JSON-encoded before being sent.
295
+
296
+ Returns:
297
+ list[WorkspaceEvent]: A list of events matching the search criteria.
298
+ Returns an empty list if the response is empty.
299
+
300
+ Note:
301
+ If an exception occurs during the API request, it logs the error and returns an empty list.
302
+ """
303
+ try:
304
+ client_params = {
305
+ key: (value if isinstance(value, str) else json.dumps(value))
306
+ for key, value in params.items()
307
+ }
308
+ resp = self._client._get("/workspaces/events", client_params)
309
+ if resp is None:
310
+ return []
311
+
312
+ return resp
313
+ except Exception as e:
314
+ _logger.error(f"Error fetching events: {e}")
315
+ return []
@@ -0,0 +1,289 @@
1
+ Metadata-Version: 2.1
2
+ Name: kalbio
3
+ Version: 0.2.0
4
+ Summary: Python client for Kaleidoscope API
5
+ License: MIT
6
+ Author: Ahmed Elnaiem
7
+ Author-email: ahmed@kaleidoscope.bio
8
+ Requires-Python: >=3.10,<4.0
9
+ Classifier: License :: OSI Approved :: MIT License
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Programming Language :: Python :: 3.10
12
+ Classifier: Programming Language :: Python :: 3.11
13
+ Classifier: Programming Language :: Python :: 3.12
14
+ Requires-Dist: pydantic (>=2.9.2,<3.0.0)
15
+ Requires-Dist: requests (>=2.32.3,<3.0.0)
16
+ Project-URL: Documentation, https://api.docs.kaleidoscope.bio
17
+ Project-URL: Homepage, https://github.com/kaleidoscope-tech/kalbio
18
+ Project-URL: Repository, https://github.com/kaleidoscope-tech/kalbio
19
+ Description-Content-Type: text/markdown
20
+
21
+ # Kalbio
22
+
23
+ <!-- **Source Code**: [https://github.com/kaleidoscope-tech/kalbio](https://github.com/kaleidoscope-tech/kalbio)
24
+
25
+ --- -->
26
+
27
+ Kalbio is a Python Client library for building applications with the Kaleidoscope platform.
28
+
29
+ ## Key features
30
+
31
+ - **Type-safe API Client**: Built with Pydantic models for full type safety and IDE autocomplete support
32
+ - **Intuitive Resource Management**: Clean, object-oriented interface for programs, activities, records, and more
33
+ - **Advanced Search & Filtering**: Powerful query capabilities with type-safe filter operators
34
+ - **File Upload Support**: Direct file upload to record fields with support for multiple file types
35
+ - **Comprehensive Coverage**: Access all Kaleidoscope platform resources through a unified client
36
+ - **Bulk Operations**: Import and export capabilities for efficient data management
37
+
38
+ ## Requirements
39
+
40
+ - [Python](https://docs.python.org/3/) 3.12+
41
+ - [Pydantic](https://docs.pydantic.dev/) 2.9.2+
42
+ - [Requests](https://requests.readthedocs.io/) 2.32.3+
43
+
44
+ <!--
45
+ TODO
46
+
47
+ ## Installation
48
+
49
+ Clone the repository and install the client in editable mode:
50
+
51
+ ```bash
52
+ git clone https://github.com/kaleidoscope-tech/kalbio.git
53
+ cd kalbio
54
+ pip install -e .
55
+ ```
56
+ -->
57
+
58
+ ## Authentication
59
+
60
+ Set your API credentials as environment variables before running examples:
61
+
62
+ ```bash
63
+ export KALEIDOSCOPE_API_CLIENT_ID="your_client_id"
64
+ export KALEIDOSCOPE_API_CLIENT_SECRET="your_client_secret"
65
+ ```
66
+
67
+ ## Example
68
+
69
+ ### Create it
70
+
71
+ Create a file `main.py` with:
72
+
73
+ ```python
74
+ from kalbio.client import KaleidoscopeClient
75
+ from kalbio.activities import ActivityStatusEnum
76
+
77
+ # Initialize the client using environment variables
78
+ client = KaleidoscopeClient()
79
+
80
+ # # Or with explicit arguements
81
+ # client = KaleidoscopeClient(
82
+ # client_id="your_api_client_id",
83
+ # client_secret="your_api_client_secret"
84
+ # )
85
+
86
+ # Work with activities
87
+ activities = client.activities.get_activities()
88
+ for activity in activities:
89
+ print(f"Activity: {activity.title} - Status: {activity.status.value}")
90
+ ```
91
+
92
+ ### Run it
93
+
94
+ Run the program with:
95
+
96
+ ```bash
97
+ python main.py
98
+ ```
99
+
100
+ Output:
101
+
102
+ ```text
103
+ Activity: Experiment 1 - Status: to do
104
+ Activity: Task 1 - Status: to do
105
+ Activity: Drug Tests - Status: in progress
106
+ ```
107
+
108
+ ### Check it
109
+
110
+ You now have a fully functional Kaleidoscope API client.
111
+
112
+ ## Features
113
+
114
+ ### Intuitive API Design
115
+
116
+ Work with resources naturally:
117
+
118
+ ```python
119
+ # Records
120
+ record = client.records.get_record_by_id("record-uuid")
121
+
122
+ if record:
123
+ record.add_value(
124
+ field_id="field-uuid",
125
+ content="Experiment result",
126
+ activity_id="activity-uuid"
127
+ )
128
+
129
+ # Entity Types
130
+ entity_types = client.entity_types.get_types()
131
+ compound_type = client.entity_types.get_type_by_name("Compound")
132
+ ```
133
+
134
+ ### File Upload Support
135
+
136
+ Upload files directly to records:
137
+
138
+ ```python
139
+ # Upload a file to a record field
140
+ with open("experiment_data.csv", "rb") as file:
141
+ record.update_field_file(
142
+ field_id="data-field-id",
143
+ file_name="experiment_data.csv",
144
+ file_data=file,
145
+ file_type="text/csv",
146
+ activity_id="activity-uuid"
147
+ )
148
+ ```
149
+
150
+ ### Comprehensive Resource Management
151
+
152
+ Kalbio provides services for all Kaleidoscope resources:
153
+
154
+ - **Programs**: Organize your work
155
+ - **Activities**: Tasks, experiments, projects, etc.
156
+ - **Records**: Structured data storage
157
+ - **Entity Types**: Define your data schemas
158
+ - **Entity Fields**: Manage field definitions
159
+ - **Labels**: Tag and categorize
160
+ - **Imports/Exports**: Bulk data operations
161
+ - **Workspace**: User and group management
162
+
163
+ ## Interactive Documentation
164
+
165
+ ### Available Services
166
+
167
+ Once you have a client instance, you have access to:
168
+
169
+ | Service | Description |
170
+ | ---------------------- | ------------------------------------------- |
171
+ | `client.programs` | Program management |
172
+ | `client.activities` | Activities (tasks, experiments, projects) |
173
+ | `client.records` | Record CRUD operations |
174
+ | `client.entity_types` | Entity type definitions |
175
+ | `client.entity_fields` | Field definitions (key fields, data fields) |
176
+ | `client.labels` | Label management |
177
+ | `client.imports` | Import operations and templates |
178
+ | `client.exports` | Export operations |
179
+ | `client.workspace` | Workspace, users, and groups |
180
+
181
+ ### Activity Management
182
+
183
+ ```python
184
+ # Create an activity
185
+ new_activity = client.activities.create_activity(
186
+ title="Synthesis Experiment",
187
+ activity_type="experiment",
188
+ program_ids=["program-uuid"],
189
+ assigned_user_ids=["user-uuid"]
190
+ )
191
+
192
+ if new_activity:
193
+ # Update activity status
194
+ new_activity.update(status=ActivityStatusEnum.IN_PROGRESS)
195
+
196
+ # Add records to activity
197
+ new_activity.add_records(["record-uuid-1", "record-uuid-2"])
198
+
199
+ # Get activity data
200
+ record_data = new_activity.get_record_data()
201
+ ```
202
+
203
+ ### Record Operations
204
+
205
+ ```python
206
+ # Get record by ID
207
+ record = client.records.get_record_by_id("record-uuid")
208
+
209
+ if record:
210
+ # Add a value
211
+ record.add_value(
212
+ field_id="field-uuid",
213
+ content="High purity sample",
214
+ activity_id="activity-uuid"
215
+ )
216
+
217
+ # Get field value
218
+ value = record.get_value_content(field_id="field-uuid")
219
+
220
+ # Get values for specific activity
221
+ activity_values = record.get_activity_data(activity_id="activity-uuid")
222
+ ```
223
+
224
+ ## Full Example
225
+
226
+ Here's a complete workflow example:
227
+
228
+ ```python
229
+ # Initialize client
230
+ client = KaleidoscopeClient(
231
+ KALEIDOSCOPE_API_CLIENT_ID,
232
+ KALEIDOSCOPE_API_CLIENT_SECRET,
233
+ "https://api.kaleidoscope.bio",
234
+ )
235
+
236
+ # 1. Get or create a program
237
+ programs = client.programs.get_programs()
238
+ program = programs[0] if programs else None
239
+
240
+ if program:
241
+ print(f"Working with program: {program.title}")
242
+
243
+ # 2. Get entity types
244
+ entity_types = client.entity_types.get_types()
245
+ compound_type = next(
246
+ (et for et in entity_types if et.slice_name == "Compound"),
247
+ None
248
+ )
249
+
250
+ if compound_type:
251
+ # 3. Search for records
252
+ record_ids = client.records.search_records(
253
+ entity_slice_id=compound_type.id,
254
+ limit=10
255
+ )
256
+
257
+ print(f"Found {len(record_ids)} compound records")
258
+
259
+ # 4. Get detailed record information
260
+ if record_ids:
261
+ record = client.records.get_record_by_id(record_ids[0])
262
+ assert record
263
+ print(f"Record: {record.record_identifier}")
264
+
265
+ # 5. Create an activity
266
+ activity = client.activities.create_activity(
267
+ title="Quality Check",
268
+ activity_type="task",
269
+ program_ids=[program.id]
270
+ )
271
+ assert activity
272
+
273
+ # 6. Add data to record
274
+ record.add_value(
275
+ field_id="notes-field-id",
276
+ content="Quality check completed successfully",
277
+ activity_id=activity.id
278
+ )
279
+
280
+ # 7. Update activity status
281
+ activity.update(task_status=ActivityStatusEnum.COMPLETE)
282
+
283
+ print("Workflow completed successfully!")
284
+ ```
285
+
286
+ ## Support
287
+
288
+ For support, email [support@kaleidoscope.bio](mailto:support@kaleidoscope.bio).
289
+
@@ -0,0 +1,19 @@
1
+ kalbio/__init__.py,sha256=Zn1KFblwuFHiDRdRAiRnDBRkbPttWh44jKa5zG2ov0E,22
2
+ kalbio/_kaleidoscope_model.py,sha256=UnSpiubaEkoyOLjAmKzQGZdXOUMthuP0HiYU1Lo2sFM,4465
3
+ kalbio/activities.py,sha256=zvemwfuiPgTjhpMhzsHon7UselKmuqw9tWpM766m5F4,40621
4
+ kalbio/client.py,sha256=a2NP-FfIbIuRXQ-19KSxcbjyvCpfSXdkUiypgp2-jh8,16822
5
+ kalbio/dashboards.py,sha256=0P8lNiTmyaQj0y8B7AKf1VWU0qHuTUGJ_4xDVtr9jXA,9334
6
+ kalbio/entity_fields.py,sha256=HnvOSE9C_UB59ioY5mB5Sn_V3OwnfRuYZ_IJtEw2vUY,15184
7
+ kalbio/entity_types.py,sha256=THXGbIV9dFU2DvIuDd7Q29onmEs9KdeqsB_tq6ZXmR4,6527
8
+ kalbio/exports.py,sha256=xFdNzajrGUlPZx6tNhgqiUg1lVnrayVQW12d-SmKWA4,5236
9
+ kalbio/helpers.py,sha256=TBPVls1GzfUygDAwCldI5sXYTgBC2pEUSEU0OtrQi3I,1955
10
+ kalbio/imports.py,sha256=s7LIETRQtT6MXEmsbIT2FwFJdE3eh25ujbs4ZFbWYns,3523
11
+ kalbio/labels.py,sha256=Vy7PiZ8vFXIBbrCmdHyz7ANooO0-giTvTLH7WwU_Mo8,2704
12
+ kalbio/programs.py,sha256=SKVJbF08Pp4r7dXfy9NNQnJRnSh-kOpDVdib197EWUM,3017
13
+ kalbio/property_fields.py,sha256=eCYzSnG-HQB6ECHFNpaGkpm3azHe-lDYVNJUpq5TOtc,2763
14
+ kalbio/record_views.py,sha256=M3NEOhnN4JHA8ZO0I91L9pAVdVY0kmqSent3HehzFTI,7208
15
+ kalbio/records.py,sha256=BXuFCA90ViSMp_TtnWJYfQnJgH9Hz-5u4iT0wF0B1UM,40480
16
+ kalbio/workspace.py,sha256=YpeLtzo3o8um_CQLyTT8GnvkF6I195y43JhNN2GmM0M,11670
17
+ kalbio-0.2.0.dist-info/METADATA,sha256=QkC4c9VlAZ5NkvtwY85tOAFs4hRkK32Q8RVmnAjsPug,7969
18
+ kalbio-0.2.0.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
19
+ kalbio-0.2.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: poetry-core 1.9.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any