endoreg-db 0.8.3.7__py3-none-any.whl → 0.8.3.9__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 endoreg-db might be problematic. Click here for more details.
- endoreg_db/management/commands/create_model_meta_from_huggingface.py +19 -5
- endoreg_db/management/commands/setup_endoreg_db.py +14 -1
- endoreg_db/models/metadata/model_meta_logic.py +62 -31
- {endoreg_db-0.8.3.7.dist-info → endoreg_db-0.8.3.9.dist-info}/METADATA +1 -1
- {endoreg_db-0.8.3.7.dist-info → endoreg_db-0.8.3.9.dist-info}/RECORD +7 -7
- {endoreg_db-0.8.3.7.dist-info → endoreg_db-0.8.3.9.dist-info}/WHEEL +0 -0
- {endoreg_db-0.8.3.7.dist-info → endoreg_db-0.8.3.9.dist-info}/licenses/LICENSE +0 -0
|
@@ -50,11 +50,17 @@ class Command(BaseCommand):
|
|
|
50
50
|
|
|
51
51
|
try:
|
|
52
52
|
# Download the model weights
|
|
53
|
-
weights_path = hf_hub_download(
|
|
53
|
+
weights_path = hf_hub_download(
|
|
54
|
+
repo_id=model_id,
|
|
55
|
+
filename="colo_segmentation_RegNetX800MF_base.ckpt",
|
|
56
|
+
local_dir="/tmp",
|
|
57
|
+
)
|
|
54
58
|
self.stdout.write(f"Downloaded weights to: {weights_path}")
|
|
55
59
|
|
|
56
60
|
# Get or create AI model
|
|
57
|
-
ai_model, created = AiModel.objects.get_or_create(
|
|
61
|
+
ai_model, created = AiModel.objects.get_or_create(
|
|
62
|
+
name=model_name, defaults={"description": f"Model from {model_id}"}
|
|
63
|
+
)
|
|
58
64
|
if created:
|
|
59
65
|
self.stdout.write(f"Created AI model: {ai_model.name}")
|
|
60
66
|
|
|
@@ -62,7 +68,9 @@ class Command(BaseCommand):
|
|
|
62
68
|
try:
|
|
63
69
|
labelset = LabelSet.objects.get(name=labelset_name)
|
|
64
70
|
except LabelSet.DoesNotExist:
|
|
65
|
-
self.stdout.write(
|
|
71
|
+
self.stdout.write(
|
|
72
|
+
self.style.ERROR(f"LabelSet '{labelset_name}' not found")
|
|
73
|
+
)
|
|
66
74
|
return
|
|
67
75
|
|
|
68
76
|
# Create ModelMeta
|
|
@@ -86,13 +94,19 @@ class Command(BaseCommand):
|
|
|
86
94
|
|
|
87
95
|
# Save the weights file to the model
|
|
88
96
|
with open(weights_path, "rb") as f:
|
|
89
|
-
model_meta.weights.save(
|
|
97
|
+
model_meta.weights.save(
|
|
98
|
+
f"{model_name}_v{version}_pytorch_model.bin", ContentFile(f.read())
|
|
99
|
+
)
|
|
90
100
|
|
|
91
101
|
# Set as active meta
|
|
92
102
|
ai_model.active_meta = model_meta
|
|
93
103
|
ai_model.save()
|
|
94
104
|
|
|
95
|
-
self.stdout.write(
|
|
105
|
+
self.stdout.write(
|
|
106
|
+
self.style.SUCCESS(
|
|
107
|
+
f"Successfully {'created' if created else 'updated'} ModelMeta: {model_meta}"
|
|
108
|
+
)
|
|
109
|
+
)
|
|
96
110
|
|
|
97
111
|
except Exception as e:
|
|
98
112
|
self.stdout.write(self.style.ERROR(f"Error creating ModelMeta: {e}"))
|
|
@@ -8,7 +8,7 @@ from pathlib import Path
|
|
|
8
8
|
|
|
9
9
|
from django.core.management import call_command
|
|
10
10
|
from django.core.management.base import BaseCommand
|
|
11
|
-
|
|
11
|
+
from endoreg_db.models import ModelMeta
|
|
12
12
|
|
|
13
13
|
class Command(BaseCommand):
|
|
14
14
|
help = """
|
|
@@ -137,6 +137,14 @@ class Command(BaseCommand):
|
|
|
137
137
|
def _find_model_weights_file(self):
|
|
138
138
|
"""Find the model weights file in various possible locations."""
|
|
139
139
|
# Check common locations for model weights
|
|
140
|
+
|
|
141
|
+
if not ModelMeta.objects.exists():
|
|
142
|
+
print("📦 No model metadata found — creating from Hugging Face...")
|
|
143
|
+
ModelMeta.setup_default_from_huggingface(
|
|
144
|
+
"wg-lux/colo_segmentation_RegNetX800MF_base",
|
|
145
|
+
labelset_name="multilabel_classification_colonoscopy_default"
|
|
146
|
+
)
|
|
147
|
+
print("✅ Default ModelMeta created.")
|
|
140
148
|
possible_paths = [
|
|
141
149
|
# Test assets (for development)
|
|
142
150
|
Path("tests/assets/colo_segmentation_RegNetX800MF_6.ckpt"),
|
|
@@ -154,7 +162,10 @@ class Command(BaseCommand):
|
|
|
154
162
|
return path
|
|
155
163
|
|
|
156
164
|
self.stdout.write("Model weights file not found in standard locations")
|
|
165
|
+
|
|
157
166
|
return None
|
|
167
|
+
|
|
168
|
+
|
|
158
169
|
|
|
159
170
|
def _verify_setup(self):
|
|
160
171
|
"""Verify that the setup was successful."""
|
|
@@ -194,3 +205,5 @@ class Command(BaseCommand):
|
|
|
194
205
|
self.stdout.write(f"Found {meta_count} model metadata record(s)")
|
|
195
206
|
|
|
196
207
|
self.stdout.write("Setup verification passed")
|
|
208
|
+
|
|
209
|
+
|
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
import shutil
|
|
2
|
+
from logging import getLogger
|
|
2
3
|
from pathlib import Path
|
|
3
|
-
from typing import
|
|
4
|
-
|
|
4
|
+
from typing import TYPE_CHECKING, Any, Optional, Type
|
|
5
|
+
|
|
5
6
|
from django.db import transaction
|
|
7
|
+
from huggingface_hub import hf_hub_download
|
|
6
8
|
|
|
7
9
|
# Assuming ModelMeta, AiModel, LabelSet are importable from the correct locations
|
|
8
10
|
# Adjust imports based on your project structure if necessary
|
|
9
11
|
from ..administration.ai.ai_model import AiModel
|
|
10
12
|
from ..label.label_set import LabelSet
|
|
11
|
-
from ..utils import
|
|
12
|
-
|
|
13
|
-
from logging import getLogger
|
|
13
|
+
from ..utils import STORAGE_DIR, WEIGHTS_DIR
|
|
14
14
|
|
|
15
15
|
logger = getLogger("ai_model")
|
|
16
16
|
|
|
17
17
|
if TYPE_CHECKING:
|
|
18
|
-
from .model_meta import ModelMeta
|
|
18
|
+
from .model_meta import ModelMeta # Import ModelMeta for type hinting
|
|
19
19
|
|
|
20
20
|
|
|
21
21
|
def get_latest_version_number_logic(
|
|
@@ -29,13 +29,13 @@ def get_latest_version_number_logic(
|
|
|
29
29
|
"""
|
|
30
30
|
versions_qs = cls.objects.filter(
|
|
31
31
|
name=meta_name, model__name=model_name
|
|
32
|
-
).values_list(
|
|
32
|
+
).values_list("version", flat=True)
|
|
33
33
|
|
|
34
34
|
max_v = 0
|
|
35
35
|
found_numeric_version = False
|
|
36
36
|
|
|
37
37
|
for v_str in versions_qs:
|
|
38
|
-
if v_str is None:
|
|
38
|
+
if v_str is None: # Skip None versions
|
|
39
39
|
continue
|
|
40
40
|
try:
|
|
41
41
|
v_int = int(v_str)
|
|
@@ -47,13 +47,13 @@ def get_latest_version_number_logic(
|
|
|
47
47
|
f"Warning: Could not parse version string '{v_str}' as an integer for "
|
|
48
48
|
f"meta_name='{meta_name}', model_name='{model_name}' while determining the max version."
|
|
49
49
|
)
|
|
50
|
-
|
|
50
|
+
|
|
51
51
|
return max_v if found_numeric_version else 0
|
|
52
52
|
|
|
53
53
|
|
|
54
54
|
@transaction.atomic
|
|
55
55
|
def create_from_file_logic(
|
|
56
|
-
cls: Type["ModelMeta"],
|
|
56
|
+
cls: Type["ModelMeta"], # cls is ModelMeta
|
|
57
57
|
meta_name: str,
|
|
58
58
|
model_name: str,
|
|
59
59
|
labelset_name: str,
|
|
@@ -94,11 +94,14 @@ def create_from_file_logic(
|
|
|
94
94
|
)
|
|
95
95
|
elif existing and bump_if_exists:
|
|
96
96
|
target_version = str(latest_version_num + 1)
|
|
97
|
-
logger.info(
|
|
97
|
+
logger.info(
|
|
98
|
+
f"Bumping version for {meta_name}/{model_name} to {target_version}"
|
|
99
|
+
)
|
|
98
100
|
else:
|
|
99
101
|
target_version = str(latest_version_num + 1)
|
|
100
|
-
logger.info(
|
|
101
|
-
|
|
102
|
+
logger.info(
|
|
103
|
+
f"Setting next version for {meta_name}/{model_name} to {target_version}"
|
|
104
|
+
)
|
|
102
105
|
|
|
103
106
|
# --- Prepare Weights File ---
|
|
104
107
|
source_weights_path = Path(weights_file).resolve()
|
|
@@ -108,7 +111,10 @@ def create_from_file_logic(
|
|
|
108
111
|
# Construct destination path within MEDIA_ROOT/WEIGHTS_DIR
|
|
109
112
|
weights_filename = source_weights_path.name
|
|
110
113
|
# Relative path for the FileField upload_to
|
|
111
|
-
relative_dest_path =
|
|
114
|
+
relative_dest_path = (
|
|
115
|
+
Path(WEIGHTS_DIR.relative_to(STORAGE_DIR))
|
|
116
|
+
/ f"{meta_name}_v{target_version}_{weights_filename}"
|
|
117
|
+
)
|
|
112
118
|
# Full path for shutil.copy
|
|
113
119
|
full_dest_path = STORAGE_DIR / relative_dest_path
|
|
114
120
|
|
|
@@ -125,8 +131,8 @@ def create_from_file_logic(
|
|
|
125
131
|
# --- Create/Update ModelMeta Instance ---
|
|
126
132
|
defaults = {
|
|
127
133
|
"labelset": label_set,
|
|
128
|
-
"weights": relative_dest_path.as_posix(),
|
|
129
|
-
**kwargs,
|
|
134
|
+
"weights": relative_dest_path.as_posix(), # Store relative path for FileField
|
|
135
|
+
**kwargs, # Pass through other fields like activation, mean, std, etc.
|
|
130
136
|
}
|
|
131
137
|
|
|
132
138
|
# Remove None values from defaults to avoid overriding model defaults unnecessarily
|
|
@@ -152,35 +158,39 @@ def create_from_file_logic(
|
|
|
152
158
|
|
|
153
159
|
return model_meta
|
|
154
160
|
|
|
161
|
+
|
|
155
162
|
# --- Add other logic functions referenced by ModelMeta here ---
|
|
156
163
|
# (get_latest_version_number_logic, get_activation_function_logic, etc.)
|
|
157
164
|
# Placeholder for get_activation_function_logic
|
|
158
165
|
def get_activation_function_logic(activation_name: str):
|
|
159
|
-
import torch.nn as nn
|
|
166
|
+
import torch.nn as nn # Import locally as it's specific to this function
|
|
167
|
+
|
|
160
168
|
if activation_name.lower() == "sigmoid":
|
|
161
169
|
return nn.Sigmoid()
|
|
162
170
|
elif activation_name.lower() == "softmax":
|
|
163
171
|
# Note: Softmax usually requires specifying the dimension
|
|
164
|
-
return nn.Softmax(dim=1)
|
|
172
|
+
return nn.Softmax(dim=1) # Assuming dim=1 (channels) is common
|
|
165
173
|
elif activation_name.lower() == "none":
|
|
166
174
|
return nn.Identity()
|
|
167
175
|
else:
|
|
168
176
|
# Consider adding more activations or raising an error
|
|
169
177
|
raise ValueError(f"Unsupported activation function: {activation_name}")
|
|
170
178
|
|
|
179
|
+
|
|
171
180
|
# Placeholder for get_inference_dataset_config_logic
|
|
172
181
|
def get_inference_dataset_config_logic(model_meta: "ModelMeta") -> dict:
|
|
173
182
|
# This would typically extract relevant fields from model_meta
|
|
174
183
|
# for configuring a dataset during inference
|
|
175
184
|
return {
|
|
176
|
-
"mean": [float(x) for x in model_meta.mean.split(
|
|
177
|
-
"std": [float(x) for x in model_meta.std.split(
|
|
178
|
-
"size_y": model_meta.size_y,
|
|
179
|
-
"size_x": model_meta.size_x,
|
|
180
|
-
"axes": [int(x) for x in model_meta.axes.split(
|
|
185
|
+
"mean": [float(x) for x in model_meta.mean.split(",")],
|
|
186
|
+
"std": [float(x) for x in model_meta.std.split(",")],
|
|
187
|
+
"size_y": model_meta.size_y, # Add size_y key
|
|
188
|
+
"size_x": model_meta.size_x, # Add size_x key
|
|
189
|
+
"axes": [int(x) for x in model_meta.axes.split(",")],
|
|
181
190
|
# Add other relevant config like normalization type, etc.
|
|
182
191
|
}
|
|
183
192
|
|
|
193
|
+
|
|
184
194
|
# Placeholder for get_config_dict_logic
|
|
185
195
|
def get_config_dict_logic(model_meta: "ModelMeta") -> dict:
|
|
186
196
|
# Returns a dictionary representation of the model's configuration
|
|
@@ -202,6 +212,7 @@ def get_config_dict_logic(model_meta: "ModelMeta") -> dict:
|
|
|
202
212
|
# Add any other relevant fields
|
|
203
213
|
}
|
|
204
214
|
|
|
215
|
+
|
|
205
216
|
# Placeholder for get_model_meta_by_name_version_logic
|
|
206
217
|
def get_model_meta_by_name_version_logic(
|
|
207
218
|
cls: Type["ModelMeta"],
|
|
@@ -227,17 +238,24 @@ def get_model_meta_by_name_version_logic(
|
|
|
227
238
|
) from exc
|
|
228
239
|
else:
|
|
229
240
|
# Get latest version
|
|
230
|
-
latest =
|
|
241
|
+
latest = (
|
|
242
|
+
cls.objects.filter(name=meta_name, model=ai_model)
|
|
243
|
+
.order_by("-date_created")
|
|
244
|
+
.first()
|
|
245
|
+
)
|
|
231
246
|
if latest:
|
|
232
247
|
return latest
|
|
233
248
|
else:
|
|
234
249
|
raise cls.DoesNotExist(
|
|
235
250
|
f"No ModelMeta found for '{meta_name}' and model '{model_name}'."
|
|
236
251
|
)
|
|
237
|
-
|
|
238
|
-
|
|
252
|
+
|
|
253
|
+
|
|
239
254
|
import re
|
|
240
255
|
|
|
256
|
+
from huggingface_hub import model_info
|
|
257
|
+
|
|
258
|
+
|
|
241
259
|
def infer_default_model_meta_from_hf(model_id: str) -> dict[str, Any]:
|
|
242
260
|
"""
|
|
243
261
|
Infers default model metadata (activation, normalization, input size)
|
|
@@ -248,7 +266,9 @@ def infer_default_model_meta_from_hf(model_id: str) -> dict[str, Any]:
|
|
|
248
266
|
"""
|
|
249
267
|
|
|
250
268
|
if not (info := model_info(model_id)):
|
|
251
|
-
logger.info(
|
|
269
|
+
logger.info(
|
|
270
|
+
f"Could not retrieve model info for {model_id}, using ColoReg segmentation defaults."
|
|
271
|
+
)
|
|
252
272
|
return {
|
|
253
273
|
"name": "wg-lux/colo_segmentation_RegNetX800MF_base",
|
|
254
274
|
"activation": "sigmoid",
|
|
@@ -295,18 +315,29 @@ def infer_default_model_meta_from_hf(model_id: str) -> dict[str, Any]:
|
|
|
295
315
|
"size_y": size_y,
|
|
296
316
|
"description": f"Inferred defaults for {model_id}",
|
|
297
317
|
}
|
|
298
|
-
|
|
299
|
-
|
|
318
|
+
|
|
319
|
+
|
|
320
|
+
def setup_default_from_huggingface_logic(
|
|
321
|
+
cls, model_id: str, labelset_name: str | None = None
|
|
322
|
+
):
|
|
300
323
|
"""
|
|
301
324
|
Downloads model weights from Hugging Face and auto-fills ModelMeta fields.
|
|
302
325
|
"""
|
|
303
326
|
meta = infer_default_model_meta_from_hf(model_id)
|
|
304
327
|
|
|
305
328
|
# Download weights
|
|
306
|
-
weights_path = hf_hub_download(
|
|
329
|
+
weights_path = hf_hub_download(
|
|
330
|
+
repo_id=model_id,
|
|
331
|
+
filename="colo_segmentation_RegNetX800MF_base.ckpt",
|
|
332
|
+
local_dir=WEIGHTS_DIR,
|
|
333
|
+
)
|
|
307
334
|
|
|
308
335
|
ai_model, _ = AiModel.objects.get_or_create(name=meta["name"])
|
|
309
|
-
labelset =
|
|
336
|
+
labelset = (
|
|
337
|
+
LabelSet.objects.first()
|
|
338
|
+
if not labelset_name
|
|
339
|
+
else LabelSet.objects.get(name=labelset_name)
|
|
340
|
+
)
|
|
310
341
|
|
|
311
342
|
return create_from_file_logic(
|
|
312
343
|
cls,
|
|
@@ -248,7 +248,7 @@ endoreg_db/management/__init__.py,sha256=3dsK9Mizq1veuWTcvSOyWMFT9VI8wtyk-P2K9Ri
|
|
|
248
248
|
endoreg_db/management/commands/__init__.py,sha256=Ch0jwQfNpOSr4O5KKMfYJ93dsesk1Afb-JtbRVyFXZs,21
|
|
249
249
|
endoreg_db/management/commands/anonymize_video.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
250
250
|
endoreg_db/management/commands/check_auth.py,sha256=TPiYeCZ5QcqIvR33xhbqXunO2nrcNAmHb_izoMTqgpg,5390
|
|
251
|
-
endoreg_db/management/commands/create_model_meta_from_huggingface.py,sha256=
|
|
251
|
+
endoreg_db/management/commands/create_model_meta_from_huggingface.py,sha256=NeTAl35RLI4Fad5V1W3ohyzuOl0LEzevlVRyLLeXrJM,3881
|
|
252
252
|
endoreg_db/management/commands/create_multilabel_model_meta.py,sha256=qeoyqcF2CWcnhniVRrlYbmJmwNwyZb-VQ0pjkr6arJU,7566
|
|
253
253
|
endoreg_db/management/commands/fix_missing_patient_data.py,sha256=5TPUTOQwI2fVh3Zd88o4ne0R8N_V98k0GZsI1gW0kGM,7766
|
|
254
254
|
endoreg_db/management/commands/fix_video_paths.py,sha256=7LLwc38oX3B_tYWbLJA43Li_KBO3m5Lyw0CF6YqN5rU,7145
|
|
@@ -290,7 +290,7 @@ endoreg_db/management/commands/load_unit_data.py,sha256=tcux-iL-ByT2ApgmHEkLllZS
|
|
|
290
290
|
endoreg_db/management/commands/load_user_groups.py,sha256=D7SK2FvZEHoE4TIXNGCjDw5_12MH9bpGZvoS7eEv0Os,1031
|
|
291
291
|
endoreg_db/management/commands/register_ai_model.py,sha256=KixTfuQR6TUfRmzB5GOos16BFOz7NL4TzLzBkgtPPgE,2510
|
|
292
292
|
endoreg_db/management/commands/reset_celery_schedule.py,sha256=U-m_FNRTw6LAwJoT9RUE4qrhmQXm7AyFToPcHYyJpIE,386
|
|
293
|
-
endoreg_db/management/commands/setup_endoreg_db.py,sha256=
|
|
293
|
+
endoreg_db/management/commands/setup_endoreg_db.py,sha256=efOXE6IQs4ey84tIncf6zXI2VVVRd7CYXbeuACFgkgI,9095
|
|
294
294
|
endoreg_db/management/commands/start_filewatcher.py,sha256=3jESBqRiYPa9f35--zd70qQaYnyT0tzRO_b_HJuyteQ,4093
|
|
295
295
|
endoreg_db/management/commands/storage_management.py,sha256=NpToX59ndwTFNmnSoeppmiPdMvpjSHH7mAdIe4SvUoI,22396
|
|
296
296
|
endoreg_db/management/commands/summarize_db_content.py,sha256=pOIz3qbY4Ktmh0zV_DKFx971VD0pPx027gCD7a47EL0,10766
|
|
@@ -464,7 +464,7 @@ endoreg_db/models/medical/risk/risk_type.py,sha256=kEugcaWSTEWH_Vxq4dcF80Iv1L4_K
|
|
|
464
464
|
endoreg_db/models/metadata/__init__.py,sha256=8I6oLj3YTmeaPGJpL0AWG5gLwp38QzrEggxSkTisv7c,474
|
|
465
465
|
endoreg_db/models/metadata/frame_ocr_result.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
466
466
|
endoreg_db/models/metadata/model_meta.py,sha256=F_r-PTLeNi4J-4EaGCQkGIguhdl7Bwba7_i56ZAjc-4,7589
|
|
467
|
-
endoreg_db/models/metadata/model_meta_logic.py,sha256=
|
|
467
|
+
endoreg_db/models/metadata/model_meta_logic.py,sha256=6w1YX8hVq40UXbVN1fvDO9OljwekBZaDVHEjVZecoV8,12252
|
|
468
468
|
endoreg_db/models/metadata/pdf_meta.py,sha256=BTmpSgqxmPKi0apcNjyrZAS4AFKCPXVdBd6VBeyyv6E,3174
|
|
469
469
|
endoreg_db/models/metadata/sensitive_meta.py,sha256=ekLHrW-b5uYcjfkRd0EW5ncx5ef8Bu-K6msDkpWCAbk,13034
|
|
470
470
|
endoreg_db/models/metadata/sensitive_meta_logic.py,sha256=by3eCW8CgglK1SHiDOepHhTOGaugswxJhkH0BZp7-gs,33909
|
|
@@ -786,7 +786,7 @@ endoreg_db/views/video/video_meta.py,sha256=C1wBMTtQb_yzEUrhFGAy2UHEWMk_CbU75WXX
|
|
|
786
786
|
endoreg_db/views/video/video_processing_history.py,sha256=mhFuS8RG5GV8E-lTtuD0qrq-bIpnUFp8vy9aERfC-J8,770
|
|
787
787
|
endoreg_db/views/video/video_remove_frames.py,sha256=2FmvNrSPM0fUXiBxINN6vBUUDCqDlBkNcGR3WsLDgKo,1696
|
|
788
788
|
endoreg_db/views/video/video_stream.py,sha256=kLyuf0ORTmsLeYUQkTQ6iRYqlIQozWhMMR3Lhfe_trk,12148
|
|
789
|
-
endoreg_db-0.8.3.
|
|
790
|
-
endoreg_db-0.8.3.
|
|
791
|
-
endoreg_db-0.8.3.
|
|
792
|
-
endoreg_db-0.8.3.
|
|
789
|
+
endoreg_db-0.8.3.9.dist-info/METADATA,sha256=ByxNYCIC-wIFfuG-7mfsrJ6KJumFz0K17X3-ABUuhCs,14758
|
|
790
|
+
endoreg_db-0.8.3.9.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
791
|
+
endoreg_db-0.8.3.9.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
|
792
|
+
endoreg_db-0.8.3.9.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|